quaint-daybreak-28499
12/25/2025, 2:47 PMtall-airline-16756
12/25/2025, 2:47 PMtall-airline-16756
12/25/2025, 2:48 PMstreamRequest := &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):
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 | DeepWikiquaint-daybreak-28499
12/25/2025, 2:52 PMtall-airline-16756
12/25/2025, 2:52 PMnpm install livekit-server-sdk
• Set env vars: LIVEKIT_HOST, LIVEKIT_API_KEY, LIVEKIT_API_SECRET.
1) Initialize EgressClient
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)
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)
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
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 | DeepWikiquaint-daybreak-28499
12/25/2025, 3:11 PMtall-airline-16756
12/25/2025, 3:11 PMlivekit-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:
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 | DeepWikiquaint-daybreak-28499
12/25/2025, 3:23 PMtall-airline-16756
12/25/2025, 3:23 PM<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.quaint-daybreak-28499
12/25/2025, 3:35 PMtall-airline-16756
12/25/2025, 3:35 PMfile 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)
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
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 Egressquaint-daybreak-28499
12/26/2025, 10:52 AMtall-airline-16756
12/26/2025, 10:52 AMquaint-daybreak-28499
12/26/2025, 10:54 AMtall-airline-16756
12/26/2025, 10:54 AM// 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