I am using a self hosted livekit egress. RoomCompo...
# ask-ai
b
I am using a self hosted livekit egress. RoomCompositeEgress is not working because the chrome webapp is not able to subscribe to tracks, ParticipantEgress is working, but it produces two ouput files one for agent and one for participant. What other methods I can use to generate a single audio file? I just need the audio recording.
t
Thanks for reaching out, Prathamesh. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I hear you: RoomCompositeEgress isn’t working because the Chrome webapp can’t subscribe to tracks, and ParticipantEgress works but produces separate files (agent + participant). You just need a single audio recording. I can help — a quick question first so I give the right steps: Do you want • a single file containing the mixed audio of all participants (one mono/stereo mix), or • a single file that contains only one participant’s audio (e.g., the agent’s track)? If you want a mixed single file, the simplest options are: • Use TrackEgress to record a single mixed audio track if you can target the mixed/composite audio track (this produces one file). • Record each participant’s audio (ParticipantEgress) and then merge them server-side with ffmpeg (example below). Automate merging when egress completes. If you want one participant’s audio only, use TrackEgress to target that participant’s audio track — it outputs one file (OGG/Opus or other supported format). Next steps I can walk you through depending on your choice: 1. If you want mixed audio (one file): a. Use TrackEgress (if you can obtain the composite/mixed track id) or b. Continue using ParticipantEgress for both tracks, then merge with ffmpeg: ▪︎ ffmpeg example to mix two inputs into one WAV:
Copy code
ffmpeg -i participant.opus -i agent.opus -filter_complex amix=inputs=2:duration=longest output.wav
• You can automate this on your server when you receive the
egress_ended
webhook and download the produced files. 1. If you want one participant’s audio: ◦ Start a TrackEgress request for that participant’s track and write to an output file (OGG/Opus or another supported encoded file). This will produce a single output file. If you’d like, tell me: • Which of the two goals above matches your need (mixed all participants vs single participant), and • Whether you prefer an encoded file (OGG/Opus) or a WAV output (I can show ffmpeg/processing steps for conversion or mixing). I’ll then give the exact TrackEgress request example or an automated ffmpeg merge workflow you can plug into your egress webhook handler. Sources: Universal Egress | Universal Egress | Capturing the First Few Seconds of a Call for Processing | server-sdk-js | livekit/livekit_composite | DeepWiki
b
I need a single file containing the mixed audio of all participants
t
Thanks — that’s clear. You want one file that contains a mixed audio mix of all participants. Since RoomCompositeEgress isn’t working for you, here are practical alternatives you can use right now, with concrete steps and recommendations so you end up with a single mixed audio file. Overview of recommended approaches • Easiest / most reliable: Record each participant’s audio tracks (via Track/Track Composite Egress) and mix them server-side with ffmpeg. This avoids browser rendering issues and gives you full control over mixing and output format. • If you can produce a composite/mix track from the server side (some setups provide a mixed audio track), use TrackEgress to record that single track directly — that produces one file with no post-processing. • If you must use ParticipantEgress, record all participant outputs and then merge them with ffmpeg (same post-processing flow). Detailed approach (recommended): record per-participant audio + ffmpeg mix 1) Identify audio tracks in the room • Use the room API to list participants and their tracks so you can target every audio track for recording. 2) Start a Track or Track Composite Egress for each participant audio track • Start one egress per participant audio track. Record to a common storage (S3, local file system, etc.). Use an encoded format that’s easy to mix later (OGG/Opus or WAV). • Track Egress will produce one file per requested track; Track Composite Egress can provide decoded files that are easier to mix if you prefer WAV/OGG outputs. 3) Wait for egress completion (use webhooks) • Listen for the egress ended webhook (or poll) so you know when all participant files are ready. 4) Mix files into one audio file with ffmpeg • Example for combining N inputs into a single mixed WAV:
Copy code
ffmpeg -i p1.ogg -i p2.ogg -i p3.ogg -filter_complex "[0:a][1:a][2:a]amix=inputs=3:duration=longest" mixed_audio.wav
• For a dynamic number of inputs you can generate the ffmpeg command programmatically or use a concat/filter_complex script. Use
amix
for simple mixing; adjust
volume
or per-stream filters if you need per-participant level control. 5) Optional: normalize/encode • After mixing, you can normalize levels, convert to MP3/WAV/FLAC as required:
Copy code
ffmpeg -i mixed_audio.wav -af "loudnorm" final_output.wav
     ffmpeg -i mixed_audio.wav -codec:a libmp3lame -q:a 2 final_output.mp3
Alternate direct option (if you can obtain a mixed track) • If your deployment can provide a server-side mixed audio track (a single track representing the room mix), start a TrackEgress on that track ID. That yields a single file directly and avoids post-processing. Notes and practical tips • Use WAV/PCM or decoded OGG outputs from the egress if you want easier mixing without decoding differences. If you receive Opus files, ffmpeg will decode them transparently during mix. • Automate the workflow by invoking mixing in your egress webhook handler when you receive the
egress_ended
events for all expected files. • If you must use ParticipantEgress (which gives agent + participant files), treat those as inputs to the same ffmpeg mixing step above. • If you need per-participant control (mute/unmute or level adjustments) do mixing server-side with ffmpeg filter_complex or a small audio-processing script. If you want, I can: • Show a concrete TrackEgress / Track Composite Egress request example in your language (Go, Node, Python) to start per-participant recording. • Provide a script that takes a list of stored file URLs and runs ffmpeg to produce a single mixed file (including handling variable input counts). • Help you pick formats and ffmpeg flags for best quality / file size. Which would you like next — a code example for starting Track/Track Composite Egress in your stack, or a ready-to-run ffmpeg mixing script for the files you already have? Sources: Universal Egress | server-sdk-ruby | livekit/livekit_composite | DeepWiki
f
Bro the RoomCompositeEgressRequest with audio_only should work, check your code again
Copy code
file_path = f"recordings/{self.session_id}.ogg"

req = livekitapi.RoomCompositeEgressRequest(
    room_name=self.ctx.room.name,
    audio_only=True,
    file=livekitapi.EncodedFileOutput(
        filepath=file_path,
        gcp=livekitapi.GCPUpload(
            bucket=BUCKET_NAME,
            credentials=GCP_JSON_CREDS,
        )
    ),
)
egress_response = await self.ctx.api.egress.start_room_composite_egress(req)
t
Thanks — I see your snippet and you’re right: the
RoomCompositeEgressRequest
with
audio_only=True
is valid and should produce a single mixed audio file. You’re calling the API correctly. Since you said it’s not working in your self-hosted setup, let’s focus on the most likely causes and quick checks so we can pinpoint the failure. Quick checklist (run these first) • Check the egress service is running and reachable from your control plane (it’s separate from the SFU). Look for the egress worker process on the host/container and confirm it’s healthy. • Inspect the egress logs right after you start the request (search for errors about Chrome, GStreamer, or upload failures). Those logs usually show why the egress didn’t produce the file. • Confirm headless Chrome is installed and accessible on the egress worker. Even audio-only composite uses a headless browser to join the room. ◦ Example check:
google-chrome --headless --no-sandbox --version
• Verify GStreamer or other required media libraries are installed (egress needs them for encoding/containers). • Confirm the egress worker can write to the local temporary output path and can reach GCP (network + service account permissions). • Use the egress list/status API or your webhook receiver to see if the egress session ever enters
egress_started
or shows an error state. What to look for in logs (common failure signals) • Chrome startup errors, crashes, or sandbox/permission failures. • Missing binary/library errors (GStreamer plugins). • Authentication/upload errors when pushing to GCP (403/permission denied). • “failed to join room” or connection/auth errors (TLS, firewall, wrong API keys). Practical troubleshooting steps 1. Tail the egress worker logs while you trigger the request. Note timestamps and any stack traces. 2. Try a simple composite egress with video+audio (not audio_only). If video+audio works, it proves Chrome can start and join — that narrows things down. 3. If audio_only fails but video+audio works, capture the precise error message from logs and paste it here. That message will be decisive. 4. If Chrome fails to start due to sandboxing in your container, try running it with
--no-sandbox
for a quick test (do not use unsandboxed Chrome in production without reviewing security). 5. As a fallback while you debug: use per-participant track egress (Track/TrackComposite Egress) and mix outputs with ffmpeg. It’s reliable and avoids the browser dependency. FFmpeg merge example (if you need a quick fallback)
Copy code
ffmpeg -i p1.ogg -i p2.ogg -filter_complex "[0:a][1:a]amix=inputs=2:duration=longest" mixed_audio.wav
If you want, provide: • The egress worker logs around the time you start the
RoomCompositeEgress
(redact secrets), or • The output of
egress.list
/ webhook events for the failing request (status, error message). I’ll read the log/error details and point to the exact fix (Chrome missing vs. network vs. permission). If you prefer, I can also give a ready-to-run TrackEgress or ffmpeg script for your environment as a temporary solution. Sources: Universal Egress | Python SDKs | livekit/livekit_composite | DeepWiki | Firewall Tips | sip | livekit/livekit_composite | DeepWiki | Universal Egress
f
Another possibility could be that the instance you're hosting egress on is of less capacity and the cpu_cost mentioned in your config is too high to even pick up the roomcomposite request, that must be why your participant egress is working but roomcomposite is not
t
You’re right to suspect CPU capacity — that’s a very plausible cause. RoomCompositeEgress (even audio_only) launches a headless Chrome process and uses more CPU than Participant/Track egress jobs, so if your egress worker’s available resources or the configured
cpu_cost
make the scheduler skip heavy jobs, the RoomComposite request may never run while lighter ParticipantEgress jobs do. What to check and try next • Confirm egress worker capacity: ◦ Check the host/container CPU limits and current utilization when you submit the egress. If the worker is already near full, new composite jobs may be deferred or rejected. • Inspect egress scheduling/log messages: ◦ Tail the egress worker logs right after starting the RoomComposite request. Look for messages about scheduling, insufficient resources, or “skipping” / “not started” events. • Verify
cpu_cost
settings: ◦ If your deployment config (or autoscaler) assigns a high
cpu_cost
to composite egress, reduce it temporarily and retry so the scheduler will accept the job. • Resource limits in container orchestrator: ◦ If running in Kubernetes, check the pod’s CPU requests/limits. Consider increasing requests or limits for the egress worker pod so Chrome can start reliably. • Try a higher-capacity instance or scale horizontally: ◦ Start one test egress on a larger instance (or a dedicated egress worker) to confirm capacity is the blocker. • Reproduce with a controlled test: ◦ While watching metrics, submit a single audio_only RoomCompositeEgress and observe whether a Chrome process is spawned and whether the job transitions to
egress_started
. If it never spawns, resource constraints are likely. Quick diagnostics commands/examples • On a host/container: ◦ Check CPU usage:
top
or
htop
◦ Check processes for Chrome:
ps aux | grep chrome
• In Kubernetes: ◦ Pod CPU usage:
kubectl top pod <egress-pod>
◦ Pod spec CPU requests/limits:
kubectl get pod <egress-pod> -o yaml
Fallbacks/alternatives while you adjust capacity • Use Track/TrackComposite Egress per participant and mix with ffmpeg (reliable and less CPU-hungry at the egress scheduler level). You can automate mixing after
egress_ended
events. • Run a dedicated egress worker with increased CPU for composite jobs only (cheap and effective for heavy workloads). If you’d like I can: • Help interpret the exact egress worker logs if you paste the relevant error/scheduling lines (redact secrets). • Suggest a safe
cpu_cost
adjustment or Kubernetes resource values based on your current instance size. • Provide a small script that waits for all per-participant egress outputs and runs ffmpeg to produce a single mixed file as a temporary workaround. Which would be most helpful now — the logs to inspect, recommended CPU/resource values for a test worker, or the ffmpeg merge script?
b
What is latest version of egress helm?
t
I don't have the answer you're looking for. You could also try asking your question: • in one of the other Slack channels or • to https://deepwiki.com/livekit/livekit_composite which is trained on all LiveKit source code If you find the answer, please post it here to help others!