broad-teacher-94403
02/18/2026, 4:06 PMtall-airline-16756
02/18/2026, 4:06 PMtall-airline-16756
02/18/2026, 4:07 PMffmpeg -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 | DeepWikibroad-teacher-94403
02/18/2026, 4:09 PMtall-airline-16756
02/18/2026, 4:10 PMffmpeg -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:
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 | DeepWikifresh-television-67623
02/18/2026, 4:15 PMfile_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)tall-airline-16756
02/18/2026, 4:16 PMRoomCompositeEgressRequest 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)
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 Egressfresh-television-67623
02/18/2026, 4:19 PMtall-airline-16756
02/18/2026, 4:19 PMcpu_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?broad-teacher-94403
02/18/2026, 4:21 PMtall-airline-16756
02/18/2026, 4:21 PM