RoomComposite Egress

RoomComposite Egress allows you to composite multiple participant tracks into a single output stream.

RoomComposite egress

One common requirement when recording a room is to capture all of the participants and interactions that take place. This can be challenging in a multi-user application, where different users may be joining, leaving, turning their cameras on and off. It may also be desirable for the recording to look as close to the actual application experience as possible, capturing the richness and interactivity of your application.

A RoomComposite egress uses a web app to create the composited view, rendering the output with an instance of headless Chromium. In most cases, your existing LiveKit application can be used as a compositing template with few modifications.

Default layouts

We provide a few default compositing layouts that works out of the box. They'll be used by default if a custom template URL is not passed in. These templates are deployed alongside and served by the egress service (source).

While it's a great starting point, you can easily create your own layout using standard web technologies that you are already familiar with.

LayoutPreview
grid
Grid layout
speaker
Speaker layout
single-speaker
Single speaker layout

Additionally, you can use a -light suffix to change background color to white. i.e. grid-light.

Starting a RoomComposite

The following example starts a recording for archival to S3. It uses an EncodedFileOutput for the egress.

const egressClient = new EgressClient(
'https://my-livekit-host',
'livekit-api-key',
'livekit-api-secret',
);
const fileOutput = new EncodedFileOutput({
filepath: 'livekit-demo/room-composite-test.mp4',
output: {
case: 's3',
value: new S3Upload({
accessKey: 'aws-access-key',
secret: 'aws-access-secret',
region: 'aws-region',
bucket: 'my-bucket',
}),
},
});
const info = await egressClient.startRoomCompositeEgress(
'my-room',
{
file: fileOutput,
},
{
layout: 'speaker',
// uncomment to use your own templates
// customBaseUrl: 'https://my-template-url.com',
},
);
const egressID = info.egressId;
  • fileRequest.Output.File.Output can be left empty if one of s3, azure, or gcp is supplied with your config.
  • fileRequest.Output.File.Filepath can be left empty, and a unique filename will be generated based on the date and room name.

Stream to RTMP

To stream to RTMP, you would use a StreamOutput. When multiple RTMP URLs are specified, LiveKit would multi-cast to all of them at the same time. Stream URLs can be changed even after the egress has started with UpdateStream

const streamOutput = new StreamOutput({
protocol: StreamProtocol.RTMP,
urls: ['rtmp://youtube-url/stream', 'rtmps://twitch-url/path'],
});
const info = await egressClient.startRoomCompositeEgress(
'my-room',
{ stream: streamOutput },
{ layout: 'grid' },
);

Stream to HLS

As an alternative to generating a single media file, it is possible to have the Egress service generate segments by using the SegmentedFileOutput output. The Egress service will split the output in media segments of equal duration (6s by default), and generate a manifest listing all the generated segments.

Currently, only HTTP Live Streaming compatible segments (using the MPEG TS file format) and manifests are supported.

If one of s3, azure, or gcp is supplied with the config or request, each segment will be uploaded with an updated manifest as soon as it is generated. This allows playback of the exported media while the export is still ongoing.

const segmentedOutput = new SegmentedFileOutput({
filenamePrefix: 'livekit-demo/room-composite-test-',
playlistName: 'room-composite-test.m3u8',
segmentDuration: 6,
protocol: SegmentedFileProtocol.HLS_PROTOCOL,
output: {
case: 's3',
value: new S3Upload({
accessKey: 'aws-access-key',
secret: 'aws-access-secret',
region: 'aws-region',
bucket: 'my-bucket',
}),
},
});
const info = await egressClient.startRoomCompositeEgress('my-room', { segments: segmentedOutput });
  • segmentsRequest.Output.Segments.FilenamePrefix and segmentsRequest.Output.Segments.PlaylistName can be left empty, and unique filenames will be generated based on the date and room name.
  • The playlist file will always be created in the same directory as the segments, as indicated in segmentsRequest.Output.Segments.FilenamePrefix, regardless of any potential directory prefix added to segmentsRequest.Output.Segments.PlaylistName.

Generate Image snapshots

It is possible to have the egress service generate image snaphots of the room composite at regular interval. This can be used for instance to generate thumbnails. A manifest is generated that lists the images with their filename and timestamps.

If one of s3, azure, or gcp is supplied with the config or request, each image will be uploaded with an updated manifest as soon as it is generated.

const imageOutput = new ImageOutput({
captureInterval: 10,
filenamePrefix: 'livekit-demo/room-composite-test-',
width: 640,
height: 480,
output: {
case: 's3',
value: new S3Upload({
accessKey: 'aws-access-key',
secret: 'aws-access-secret',
region: 'aws-region',
bucket: 'my-bucket',
}),
},
});
const info = await egressClient.startRoomCompositeEgress('my-room', { images: imageOutput });

Recording in portrait

Portrait orientation can be specified by either setting a preset or advanced options. Egress will resize the Chrome compositor to your specified resolution. However, keep in mind:

  • Chrome has a minimum browser width limit of 500px.
  • Your application should maintain a portrait layout, even when the browser reports a width larger than typical mobile phones. (e.g., 720px width or higher).
const segmentedOutput = new SegmentedFileOutput({
filenamePrefix: 'livekit-demo/room-composite-test-',
playlistName: 'room-composite-test.m3u8',
segmentDuration: 6,
protocol: SegmentedFileProtocol.HLS_PROTOCOL,
preset: EncodingOptionsPreset.PORTRAIT_H264_720P_30,
output: {
...
},
});
const info = await egressClient.startRoomCompositeEgress('my-room', {segments: segmentedOutput});

Audio-only composite

If your application is audio-only, you can export a mixed audio file containing audio from all participants in the room. To start an audio-only composite, pass audio_only=true when starting an Egress.

const info = await egressClient.startRoomCompositeEgress('my-room', output, { audioOnly: true });