Once streams reach the Media Server, they can be mixed into one. It doesn't matter how each of them was published — WebRTC, RTMP, RTSP, MPEG-TS over SRT, or any other supported protocol.
Overview
Mixing happens entirely on the server side. The Media Server decodes the incoming streams, places their pictures on one canvas, mixes their sound and encodes the result as a new stream, the output stream of the mixer.
The mixed stream is like any other on the server: it can be played over WebRTC, HLS, RTSP or RTMP, recorded, and pushed to YouTube or another server. Viewers play one stream instead of several, however many participants there are.
Mixing means transcoding, so every mixer takes server CPU: plan about two cores per mixer.
stream1stream2
stream1.audio
stream1.video
stream2.audio
stream2.video
Mixer
stream1.videostream2.video
How WebRTC mixing works
Every participant's application uses an SDK to publish the camera and microphone as usual, and the name of the stream says which mixer it joins: everything after the # sign is the name of the mixer.
For a stream named alice#room-42 the Media Server creates the room-42 mixer, if there is none yet, and adds the stream to it. The next stream, bob#room-42, joins the same mixer, and viewers play the mixed stream room-42.
The publishers do not have to know about the mixer. Your backend can mix any streams published on the Media Server through the REST API, whatever protocol they came in with: WebRTC, RTMP, RTSP, MPEG-TS or any other supported protocol.
The backend creates a mixer with /mixer/startup, giving it an identifier like mixer://room-42 and the name of the output stream, and adds streams to it by their names with /mixer/add. /mixer/remove takes a stream out when a participant leaves, and /mixer/terminate stops the mixer.
alicebobPOST /mixer/startup200 OKPOST /mixer/add alice200 OKPOST /mixer/add bob200 OK
alice.audio
alice.video
bob.audio
bob.video
Mixerroom-42
alice.videobob.video
REST API
One mixer, a stream added for every participant:
const api = 'https://media.example.com:8444/rest-api/v3';const post = (method, body) => fetch(`${api}/mixer/${method}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), });const uri = 'mixer://room-42';await post('startup', { uri, localStreamName: 'room-42' });await post('add', { uri, remoteStreamName: 'alice' });await post('add', { uri, remoteStreamName: 'bob' });// when the meeting is overawait post('terminate', { uri });
localStreamName names the mixed stream that viewers play; an optional streamLabel in add puts a caption over the participant’s picture.
The mixer places the pictures on its canvas by a layout. By default it is a grid: every participant gets a cell of the same size, and the grid rebuilds itself as streams join and leave. A zero padding grid fills the frame edge to edge, and one more variant crops every picture around its centre, where a speaker’s face usually is.
A layout of your own is described in XML, with fixed positions that streams take by name, or written as a Java class.
An XML layout is a folder of .mix files, one for each number of participants, named after it. Here is the grid above as four such files: a video element is a participant’s picture, and row and div elements place them on the canvas.
Saved as 4_participants.mix. The folder is given to a mixer as mixerLayoutDir in /mixer/startup, or to every mixer with mixer_layout_dir.
When one of the streams is a screen share, it takes the main place: the other participants line up above, below, beside or around it, or float over it as picture in picture.
In a video conference everyone should hear the others, but not themselves. With MCU turned on, mixer_mcu_audio, the mixer makes, besides the common mixed stream, a stream for every participant with the sound of everyone in the room except them, so nobody hears the echo of their own voice.
Only the sound is mixed for every participant. The video mixer stays one for the whole room and shows all the participants in a grid.
With mixer_mcu_video turned on, every participant’s stream also carries the room’s video: the same grid for everyone, so a participant sees themselves in it too, but hears only the others.
MixerKiri
alicebobkiri
Bob
alicebobkiri
Alice
Each participant publishes into the room and plays the stream the mixer makes for them, from any SDK:
Web SDK
In a browser, the Web SDK publishes the camera into the room and plays the stream made for the participant:
The mixer names the stream for Alice after the room and her stream, without the #: room-42-aliceroom-42, with the sound of everyone in the room except her.
A mixer is created with /mixer/startup, and most of what it does is set there: the size and frame rate of its canvas, the layout, where a screen share goes and the voice activity frame.
All of it but the canvas size and frame rate can be changed later with /mixer/set_parameter, without stopping the mixer. The video bitrate and MCU are set on the server, for every mixer.
A participant’s sound and picture in the mix are changed on the fly with /mixer/setAudioVideo.
Let’s take a closer look at the available controls.
Every control above is a REST API parameter, or a server setting for the bitrate and MCU. With the post helper from the REST API example:
Create the mixer
The canvas, the layout, the screen share, the stream names and the voice activity frame:
No setup, no installation. Open the demo in two or more browser tabs, enter the same room name and different user names, and press Join: every participant publishes into the mixer, sees all of the room in one mixed stream and hears everyone but themselves.
LLcast will deploy a dedicated Media Server just for you, ready to mix your streams. Publish over WebRTC, RTMP, SRT or RTSP, mix them into one stream by name or over the REST API, and play, record or push the result.