How to record session audio?
# ask-ai
q
How to record session audio?
t
Thanks for reaching out, Heorhi. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — you're asking how to record session audio. You can record audio in LiveKit using Universal Egress. Here are the options and when to use each: • Track Egress — export a single raw audio track (ideal for efficient storage, transcripts, or streaming raw PCM). Audio tracks are delivered as OGG or via WebSocket binary frames for real‑time processing. ◦ Use this when you only need one participant’s raw audio. • Track Composite Egress — combine a single participant’s audio and video into one synchronized file (e.g., MP4) for per-participant recordings. ◦ Use this when you want a file per participant with audio+video. • Room Composite Egress — record the whole room’s audio (and video) into a single file or stream (MP4 or RTMP). LiveKit launches a headless renderer to capture the room layout. ◦ Use this for session archives or live streaming the full room. Quick examples: • Start a room composite (Go SDK):
Copy code
streamRequest := &livekit.RoomCompositeEgressRequest{
  RoomName: "my-room",
  Layout: "speaker-dark",
  Output: &livekit.RoomCompositeEgressRequest_Stream{
    Stream: &livekit.StreamOutput{
      Protocol: livekit.StreamProtocol_RTMP,
      Urls: []string{"<rtmp://live.twitch.tv/app/<stream-key>>"},
    },
  },
}
info, err := egressClient.StartRoomCompositeEgress(ctx, streamRequest)
• Start a track egress (Go SDK):
Copy code
egressInfo, err := c.StartTrackEgress(ctx, &livekit.TrackEgressRequest{
  RoomName: "room",
  TrackId: "trackID",
  Output: &livekit.TrackEgressRequest_WebsocketUrl{
    WebsocketUrl: "<wss://your-server/>",
  },
})
Next step: which outcome do you want — a single room file, per-participant files, or raw audio for transcription/processing? Also tell me which SDK (Go, JS, etc.) you're using and whether you want cloud storage (S3) or streaming (RTMP/WebSocket), and I’ll give the exact command/snippet. Sources: Universal Egress | sip | livekit/livekit_composite | DeepWiki | Transcriber | LiveKit docs | server-sdk-js | livekit/livekit_composite | DeepWiki
q
I need track egress for nodejs
t
Thanks — you're asking for Node.js code to record a track using Track Egress. Below are concise, ready-to-use examples and key notes so you can start recording a single audio track. Prerequisites • Install SDK:
Copy code
npm install livekit-server-sdk
• Set env vars:
LIVEKIT_HOST
,
LIVEKIT_API_KEY
,
LIVEKIT_API_SECRET
. 1) Initialize EgressClient
Copy code
import { EgressClient } from "livekit-server-sdk";

const egressClient = new EgressClient({
  host: process.env.LIVEKIT_HOST,
  apiKey: process.env.LIVEKIT_API_KEY,
  secret: process.env.LIVEKIT_API_SECRET,
});
2) Start Track Egress — save audio file (OGG)
Copy code
const resp = await egressClient.startTrackEgress({
  roomName: "my-room",
  trackId: "track_sid_or_source", // e.g., track SID or "mic"
  output: {
    file: {
      filename: "audio-recording.ogg",
      // for S3:
      s3: {
        accessKey: "AWS_ACCESS_KEY",
        secret: "AWS_SECRET",
        region: "us-west-2",
        bucket: "my-bucket",
        // optional: key/prefix if required by SDK shape
      },
      // OR for local path depending on SDK support, use filename/path
    },
  },
});
console.log("egressId:", resp.egressId);
3) Start Track Egress — stream raw audio to your server via WebSocket (real-time processing / transcription)
Copy code
const resp = await egressClient.startTrackEgress({
  roomName: "my-room",
  trackId: "track_sid_or_source",
  output: {
    websocketUrl: "<wss://your-server.example.com/egress-audio>",
  },
});
console.log("egressId:", resp.egressId);
• Your WebSocket server will receive binary frames with raw audio (and JSON text frames for mute/unmute events). Use it to pipe into STT or real-time processors. 4) Stop egress
Copy code
await egressClient.stopEgress(resp.egressId);
Webhooks • Listen for
egress_ended
to get final storage URL or status. Use the SDK WebhookReceiver to verify events. Key notes • Track egress outputs audio as OGG (Opus) when saved to file. •
trackId
can be a track SID or a source identifier like
mic
or
screen_share_audio
. • Use WebSocket output for real-time needs; use file/S3 for archived recordings. Would you like a complete runnable example (Express server + WebSocket handler + webhook handler) tailored to S3 or local files? Sources: Universal Egress | server-sdk-js | livekit/livekit_composite | DeepWiki
q
can you please give startTrackEgress with config for google s3 client
t
Thanks — below is a concise Node.js example using
livekit-server-sdk
to start Track Egress and upload the resulting audio (OGG) to Google Cloud Storage via its S3-compatible endpoint. Prereqs: • npm install livekit-server-sdk • Set env vars: LIVEKIT_HOST, LIVEKIT_API_KEY, LIVEKIT_API_SECRET, GCS_ACCESS_KEY_ID, GCS_SECRET_ACCESS_KEY Example:
Copy code
import { EgressClient } from "livekit-server-sdk";

const egressClient = new EgressClient({
  host: process.env.LIVEKIT_HOST,
  apiKey: process.env.LIVEKIT_API_KEY,
  secret: process.env.LIVEKIT_API_SECRET,
});

async function startTrackEgressToGCS() {
  const roomName = "my-room";
  const trackId = "track_sid_or_source"; // e.g., track SID or "mic"

  const resp = await egressClient.startTrackEgress({
    roomName,
    trackId,
    // audio-only -> OGG/Opus
    output: {
      file: {
        // file metadata; final storage handled by s3 config below
        filename: "audio-recording.ogg",
        // S3 upload config pointed at GCS S3-compatible endpoint
        s3: {
          endpoint: "<https://storage.googleapis.com>",
          accessKey: process.env.GCS_ACCESS_KEY_ID,
          secret: process.env.GCS_SECRET_ACCESS_KEY,
          bucket: "your-gcs-bucket-name",
          region: "auto",
          // optional: prefix/key template for objects
          prefix: "recordings/audio-",
        },
      },
    },
  });

  console.log("started track egress, egressId=", resp.egressId);
  return resp.egressId;
}

startTrackEgressToGCS().catch((err) => console.error(err));
Notes: • GCS requires interoperable S3 credentials (Access Key / Secret) and the bucket must exist. • The recorded audio file will be OGG (Opus). Use webhooks (
egress_ended
) to get the final object URL when upload completes. Would you like a webhook handler example to capture the uploaded file URL after egress completes? Sources: Universal Egress | server-sdk-js | livekit/livekit_composite | DeepWiki | LiveKit Pricing | server-sdk-ruby | livekit/livekit_composite | DeepWiki
q
or should I use GCPUpload ?
t
Good question — both can work, but pick based on your environment and credentials. • Use GCPUpload when you can provide native GCP credentials (service account JSON) and want a direct, supported upload path to Google Cloud Storage. This is simpler and avoids having to enable or manage S3 interoperability. • Use S3 (GCS S3-compatible endpoint) when you already have interoperable S3 credentials for GCS or need an S3-compatible workflow (same code/config used for other S3 providers). This requires enabling interoperable keys in GCS and using the
<https://storage.googleapis.com>
endpoint. Recommendation: • If you control the GCP project and can create a service account, use GCPUpload for ease and cleaner permissions. • If you must reuse S3 tooling or shared S3 credentials, use the S3 option pointing to GCS’s S3 endpoint. If you want, tell me which you prefer (GCP service account or S3 interoperable keys) and I’ll provide the exact Node.js
startTrackEgress
snippet for that option.
q
Api want a DirectFileOutput instance, how to create it ?
t
You're on the right track — you don't construct a DirectFileOutput class directly in Node.js. Pass a plain object matching the DirectFileOutput protobuf shape inside the
file
output when calling
startTrackEgress
. Below are two concise Node.js examples: one using native GCP upload (
gcp
) and one using S3-style upload to GCS (
s3
) via the DirectFileOutput object. Prereqs: • npm install livekit-server-sdk • Set LIVEKIT_HOST, LIVEKIT_API_KEY, LIVEKIT_API_SECRET 1) Using GCPUpload (service account JSON)
Copy code
import { EgressClient } from "livekit-server-sdk";
import fs from "fs";

const egressClient = new EgressClient({
  host: process.env.LIVEKIT_HOST,
  apiKey: process.env.LIVEKIT_API_KEY,
  secret: process.env.LIVEKIT_API_SECRET,
});

async function startEgressGCP() {
  const saJson = fs.readFileSync("./gcs-service-account.json", "utf8"); // service account JSON

  const resp = await egressClient.startTrackEgress({
    roomName: "my-room",
    trackId: "track_sid_or_source",
    output: {
      file: {
        filename: "audio-recording.ogg",
        directFileOutput: {
          gcp: {
            credentials: saJson,      // JSON string
            bucket: "my-gcs-bucket",
            // optional: prefix or object key template if supported
          },
        },
      },
    },
  });

  console.log("egressId:", resp.egressId);
}
2) Using S3-style DirectFileOutput pointed at GCS S3 endpoint
Copy code
import { EgressClient } from "livekit-server-sdk";

const egressClient = new EgressClient({
  host: process.env.LIVEKIT_HOST,
  apiKey: process.env.LIVEKIT_API_KEY,
  secret: process.env.LIVEKIT_API_SECRET,
});

async function startEgressS3ToGCS() {
  const resp = await egressClient.startTrackEgress({
    roomName: "my-room",
    trackId: "track_sid_or_source",
    output: {
      file: {
        filename: "audio-recording.ogg",
        directFileOutput: {
          s3: {
            endpoint: "<https://storage.googleapis.com>",
            accessKey: process.env.GCS_ACCESS_KEY_ID,
            secret: process.env.GCS_SECRET_ACCESS_KEY,
            bucket: "my-gcs-bucket",
            region: "auto",
            // optional: prefix
          },
        },
      },
    },
  });

  console.log("egressId:", resp.egressId);
}
Notes: • For GCP use a service account JSON string in
gcp.credentials
. This avoids enabling S3 interoperability. • For S3 to GCS, use GCS interoperable keys and endpoint
<https://storage.googleapis.com>
. •
filename
extension determines file type (audio ->
.ogg
for Opus). • The SDK expects plain objects; it serializes to the protobuf
DirectFileOutput
for you. • Use the
egress_ended
webhook to get the final uploaded object URL. Want a full runnable example (including webhook handler) for one of these options? Which one do you prefer: GCP (
gcp
) or S3 (
s3
)? Sources: Universal Egress
q
what if I need to recort all room participants speach ?
t
You can capture all participants’ speech by using Room Composite Egress (records the whole room mix) or by starting Track Egress for every participant (produces one file per participant). Which to use depends on your needs: • Room Composite Egress — single file that mixes all participants (audio-only or audio+video). Use this for session archives or a single transcript of the whole room. • Track Egress for each participant — separate recordings per participant (one audio file per track). Use this if you need per-speaker files or separate transcripts. Which do you want: a single mixed audio file for the whole room, or individual files per participant? Also tell me whether you prefer Node.js examples for Room Composite (recommended for a single mixed recording) or a script to enumerate participants and start Track Egress for each.
q
Please give nodejs example of room composite egress
t
Acknowledged — you want a Node.js example for Room Composite Egress to record the whole room. Below is a concise, runnable example showing: • initializing the EgressClient • starting Room Composite Egress (output to Google Cloud Storage via GCPUpload or S3) • minimal webhook handler to receive the final file URL Prereqs: • npm install livekit-server-sdk express body-parser • env: LIVEKIT_HOST, LIVEKIT_API_KEY, LIVEKIT_API_SECRET • For GCPUpload: have a service account JSON file • For S3-to-GCS: have interoperable GCS S3 keys if using S3 path Example (choose GCP or S3 block for output):
Copy code
// server.ts
import express from "express";
import bodyParser from "body-parser";
import fs from "fs";
import { EgressClient, WebhookReceiver } from "livekit-server-sdk";

const app = express();
app.use(bodyParser.raw({ type: "*/*" }));

const egressClient = new EgressClient({
  host: process.env.LIVEKIT_HOST!,
  apiKey: process.env.LIVEKIT_API_KEY!,
  secret: process.env.LIVEKIT_API_SECRET!,
});

// Start room composite egress (GCPUpload)
export async function startRoomCompositeToGCP(roomName: string) {
  const saJson = fs.readFileSync("./gcs-service-account.json", "utf8"); // service account JSON
  const resp = await egressClient.startRoomCompositeEgress({
    roomName,
    layout: "speaker-dark", // or "grid"
    output: {
      file: {
        filename: `${roomName}-recording.mp4`,
        directFileOutput: {
          gcp: {
            credentials: saJson,
            bucket: "your-gcs-bucket",
            // optional: prefix
          },
        },
      },
    },
    // optional: preset, resolution, etc.
  });
  console.log("egress started:", resp.egressId);
  return resp.egressId;
}

// Start room composite egress (S3 -> GCS compatible endpoint)
export async function startRoomCompositeToS3(roomName: string) {
  const resp = await egressClient.startRoomCompositeEgress({
    roomName,
    layout: "speaker-dark",
    output: {
      file: {
        filename: `${roomName}-recording.mp4`,
        directFileOutput: {
          s3: {
            endpoint: "<https://storage.googleapis.com>",
            accessKey: process.env.GCS_ACCESS_KEY_ID,
            secret: process.env.GCS_SECRET_ACCESS_KEY,
            bucket: "your-gcs-bucket",
            region: "auto",
            // optional: prefix
          },
        },
      },
    },
  });
  console.log("egress started:", resp.egressId);
  return resp.egressId;
}

// Webhook receiver to get egress_ended and file URL
const receiver = new WebhookReceiver(process.env.LIVEKIT_API_KEY!, process.env.LIVEKIT_API_SECRET!);

<http://app.post|app.post>("/webhook", (req, res) => {
  try {
    const event = receiver.receive(req.body, req.header("Authorization") || "");
    if (event.event === "egress_ended") {
      console.log("Egress ended:", event.egress_info);
      // File location: event.egress_info.file?.location or check storage fields
    }
    res.status(200).send("OK");
  } catch (err) {
    console.error("webhook verification failed:", err);
    res.status(401).send("invalid");
  }
});

app.listen(3000, () => console.log("listening :3000"));
Usage: • Call startRoomCompositeToGCP("my-room") or startRoomCompositeToS3("my-room") from your server to begin recording the room. • Use the webhook
/webhook
to capture
egress_ended
and retrieve the uploaded file URL. If you want, I can: • Provide a version that streams to RTMP instead of file. • Add example presets/options (resolution, layout custom template). • Show how to start egress only when room has participants. Which would you like? Sources: server-sdk-js | livekit/livekit_composite | DeepWiki | Universal Egress