Through the stream-mixing service, multiple published media streams can compose a single stream , which allows audiences to play just one stream to improve quality and reduce performance cost.
ZEGOCLOUD supports using stream mixing with three methods, manual stream mix, automatic stream mix, and full-automatic stream mix. The following table shows their differences:
Method | Manual |
Auto |
Full-Auto |
---|---|---|---|
Description | Customize the stream mixing tasks and content, including input stream, layout, etc. Supports mixing audio/video streams. |
In specified rooms, automatically mixing the audio streams. Only supports mixing the audio streams. |
In each room, automatically mix the audio streams. Only supports mixing the audio streams. |
Scenario | Co-hosting across rooms, video/audio streams mixing, and specified stream mixing, etc. |
Audio-only scenarios. |
Audio-only scenarios. |
Pros | Flexible and enable you to implement logic according to business needs. |
It reduces the complexity of integration, and does not required to manage the audio stream list of the specified rooms. |
With very low complexity of integration, and does not required to manage the in-room audio stream mixing task and audio stream list. |
Relation with the server | The client side initiates the stream mixing task and manages the stream list. |
The client side initiates the stream mixing task, and the ZEGO server automatically manages the in-room input stream list. |
The ZEGO server manages the stream mixing tasks and the in-room stream list. |
You can use stream mixing for various use cases. The following are some typical examples:
Before you begin, make sure you complete the following:
With the manual stream mixing, you can customize the stream mixing tasks. which is often used for multiple users to co-host an interactive live streaming event and co-hosting across rooms.
To implement the stream mixing with SDK or ZEGO server API, refer to the Start stream mixing and Stop stream mixing.
The following describes how to implement the manual stream mixing in detail.
ZegoMixerTask
is a class defined in the SDK for configuring a stream mixing task. The configuration parameters include the layout of input streams, the outputs, and others.
/// Stream mixing task object.
@interface ZegoMixerTask: NSObject
-(instancetype)init NS_UNAVAILABLE;
/// Construct a stream mixing task object using TaskID.
-(instancetype)initWithTaskID:(NSString *)taskID;
/// Task ID of the stream mixing task.
@property (nonatomic, copy, readonly) NSString *taskID;
/// Set up the audio configuration of the mixed stream.
-(void)setAudioConfig:(ZegoMixerAudioConfig *)audioConfig;
/// Set up the video configuration of the mixed stream.
-(void)setVideoConfig:(ZegoMixerVideoConfig *)videoConfig;
/// Set up the list of input streams.
-(void)setInputList:(NSArray<ZegoMixerInput *> *)inputList;
/// Set up the list of mixing outputs.
-(void)setOutputList:(NSArray<ZegoMixerOutput *> *)outputList;
/// Set up the watermark of the mixed stream.
-(void)setWatermark:(ZegoWatermark *)watermark;
/// Set up the background image of the mixed stream.
-(void)setBackgroundImageURL:(NSString *)backgroundImageURL;
@end
Create a stream mixing task object using the constructor initWithTaskID
, and then call the instance methods to set up the input and output parameters.
ZegoMixerTask *task = [[ZegoMixerTask alloc] initWithTaskID:@"task-1"];
// Save the stream mixing task object.
self.mixerTask = task;
If the streams to be mixed are all audio-only, no video settings are required.
The default settings are:
ZegoMixerVideoConfig *videoConfig = [ZegoMixerVideoConfig defaultConfig];
[task setVideoConfig:videoConfig];
The default audio bitrate is 48 Kbps.
[task setAudioConfig:[ZegoMixerAudioConfig defaultConfig]];
For each input stream, create a ZegoMixerInput
object and set up the video layout settings by setting up the layout
property of the ZegoMixerInput
object.
ContentType
parameter of the input stream), there is no need to set the layout
parameter.The layout of the input stream takes the upper left corner of the mixed output stream view as the origin of the coordinate system. Set the layout of the input stream according to the origin, that is, pass new Rect(left, top, width, height)
into the layout
parameter of the input stream.
In addition, the layer level of the input stream is determined by the position of the input stream in the input stream list. The further back the order in the list, the higher the layer level.
The following describes the Rect
parameter:
Parameter | Description |
---|---|
left |
Corresponds to the x coordinate of the upper left corner of the input stream view. |
top |
Corresponds to the y coordinate of the upper left corner of the input stream view. |
width |
Corresponds to the width of the input stream view. |
height |
Corresponds to the height of the input stream view. |
Let's suppose the following:
layout
parameter is new Rect(50, 300, 150, 150)
.Then, the position of this stream in the mixed stream can be illustrated as follows:
You can use the following example code to implement common output layouts: two views side by side, four views tiled vertically, one large view tiled with two small views suspended.
In the following examples, the resolution of the video screen layout is set as 360 × 640.
/** Configure the first input stream, including the streamID (the real ID of the input stream), input type, layout, etc.*/
CGRect firstRect = CGRectMake(0, 0, 180, 640);
ZegoMixerInput *firstInput = [[ZegoMixerInput alloc] initWithStreamID:@"streamID_1" contentType:ZegoMixerInputContentTypeVideo layout:firstRect];
firstInput.renderMode = ZegoMixRenderModeFill;
firstInput.label.text = @"text watermark";
firstInput.label.left = 0;
firstInput.label.font.type = ZegoFontTypeSourceHanSans;
firstInput.label.top = 0;
firstInput.label.font.color = 123456;
firstInput.label.font.size = 24;
firstInput.label.font.transparency = 50;
/** Configue the second input stream. */
CGRect secondRect = CGRectMake(180, 0, 180, 640);
ZegoMixerInput *secondInput = [[ZegoMixerInput alloc] initWithStreamID:@"streamID_2" contentType:ZegoMixerInputContentTypeVideo layout:secondRect];
secondInput.renderMode = ZegoMixRenderModeFill;
secondInput.label.text = @"text watermark";
secondInput.label.left = 0;
secondInput.label.font.type = ZegoFontTypeSourceHanSans;
secondInput.label.top = 0;
secondInput.label.font.color = 123456;
secondInput.label.font.size = 24;
secondInput.label.font.transparency = 50;
/** Set up the input steam list for the stream mixing task. */
NSArray<ZegoMixerInput *> *inputArray = @[firstInput, secondInput];
[task setInputList:inputArray];
ZegoMixerInput *firstInput = [[ZegoMixerInput alloc] initWithStreamID:@"streamID_1" contentType:ZegoMixerInputContentTypeVideo layout:CGRectMake(0, 0, 180, 320)];
firstInput.renderMode = ZegoMixRenderModeFill;
firstInput.label.text = @"text watermark";
firstInput.label.left = 0;
firstInput.label.font.type = ZegoFontTypeSourceHanSans;
firstInput.label.top = 0;
firstInput.label.font.color = 123456;
firstInput.label.font.size = 24;
firstInput.label.font.transparency = 50;
ZegoMixerInput *secondInput = [[ZegoMixerInput alloc] initWithStreamID:@"streamID_2" contentType:ZegoMixerInputContentTypeVideo layout:CGRectMake(180, 0, 180, 320)];
secondInput.renderMode = ZegoMixRenderModeFill;
secondInput.label.text = @"text watermark";
secondInput.label.left = 0;
secondInput.label.font.type = ZegoFontTypeSourceHanSans;
secondInput.label.top = 0;
secondInput.label.font.color = 123456;
secondInput.label.font.size = 24;
secondInput.label.font.transparency = 50;
ZegoMixerInput *thirdInput = [[ZegoMixerInput alloc] initWithStreamID:@"streamID_3" contentType:ZegoMixerInputContentTypeVideo layout:CGRectMake(0, 320, 180, 320)];
thirdInput.renderMode = ZegoMixRenderModeFill;
thirdInput.label.text = @"text watermark";
thirdInput.label.left = 0;
thirdInput.label.font.type = ZegoFontTypeSourceHanSans;
thirdInput.label.top = 0;
thirdInput.label.font.color = 123456;
thirdInput.label.font.size = 24;
thirdInput.label.font.transparency = 50;
ZegoMixerInput *forthInput = [[ZegoMixerInput alloc] initWithStreamID:@"streamID_4" contentType:ZegoMixerInputContentTypeVideo layout:CGRectMake(180, 320, 180, 320)];
forthInput.renderMode = ZegoMixRenderModeFill;
forthInput.label.text = @"text watermark";
forthInput.label.left = 0;
forthInput.label.font.type = ZegoFontTypeSourceHanSans;
forthInput.label.top = 0;
forthInput.label.font.color = 123456;
forthInput.label.font.size = 24;
forthInput.label.font.transparency = 50;
/** Set up the input steam list for the stream mixing task. */
NSArray<ZegoMixerInput *> *inputArray = @[firstInput, secondInput, thirdInput, forthInput];
[task setInputList:inputArray];
The layer level of the input stream is determined by the position of the input stream in the input stream list. The further back the order in the list, the higher the layer level. As shown in the code below, the layer of input stream 2 and input stream 3 is higher than that of input stream 1, so streams 2 and 3 hover over the screen of input stream 1.
ZegoMixerInput *firstInput = [[ZegoMixerInput alloc] initWithStreamID:@"streamID_1" contentType:ZegoMixerInputContentTypeVideo layout:CGRectMake(0, 0, 360, 640)];
ZegoMixerInput *secondInput = [[ZegoMixerInput alloc] initWithStreamID:@"streamID_2" contentType:ZegoMixerInputContentTypeVideo layout:CGRectMake(230, 200, 110, 200)];
ZegoMixerInput *thirdInput = [[ZegoMixerInput alloc] initWithStreamID:@"streamID_3" contentType:ZegoMixerInputContentTypeVideo layout:CGRectMake(230, 420, 110, 200)];
/** Set up the input steam list for the stream mixing task. */
NSArray<ZegoMixerInput *> *inputArray = @[firstInput, secondInput, thirdInput];
[task setInputList:inputArray];
A stream mixing task can have up to 3 mixing outputs.
In the following code example, the stream mixing task generates an output with the stream name output-stream
, which will be published to the ZEGO server.
NSArray<ZegoMixerOutput *> *outputArray = @[[[ZegoMixerOutput alloc] initWithTarget:@"output-stream"]];
[task setOutputList:outputArray];
If you need to add a watermark to the output stream, contact ZEGOCLOUD Technical Support to get the URL of the watermark image.
In the following code example, a ZEGO Logo watermark is placed at the upper-left corner of the output layout.
ZegoWatermark *watermark = [[ZegoWatermark alloc] initWithImageURL:@"preset-id://zegowp.png" layout:CGRectMake(0, 0, videoConfig.resolution.width/2, videoConfig.resolution.height/20)];
[task setWatermark:watermark];
If you need to add a background image to the output stream, contact ZEGOCLOUD Technical Support to get the URL of the background image.
[task setBackgroundImageURL:@"preset-id://zegobg.png"];
If you need to receive the event notification related to the sound level of the mixed stream, set the enableSoundLevel
parameter to True
to listening for the event callback.
The SDK sends out the sound level of each stream through the onMixerSoundLevelUpdate
callback when you play the mixed stream.
You can use advanced configurations to achieve some special requirements, for example, to specify the video encoding format.
For more information about the advanced configurations, contact ZEGOCLOUD Technical Support.
There is no need to set up advanced configurations for common scenarios.
// Specify the video encoding format of the output stream a vp8, which requires a particular streaming protocol.
NSDictionary *config = @{@"video_encode": @"vp8"};
[task setAdvancedConfig: config];
// If the video coding format of the output stream is set to vp8, then you need to set the audio codec to LOW3 to make it take effect.
ZegoMixerAudioConfig *audioConfig = [ZegoMixerAudioConfig defaultConfig];
audioConfig.codecID = ZegoAudioCodecIDLow3;
[task setAudioConfig:audioConfig];
After configuring the ZegoMixerTask
object, call the startMixerTask
method to start the stream mixing task. If the stream mixing task doesn't start successfully, handle the failure in the callback that returns the execution result.
/// Start the stream mixing task.
/// @param task: The stream mixing task object.
/// @param callback: The callback to return the result of starting up the task.
-(void)startMixerTask:(ZegoMixerTask *)task callback:(nullable ZegoMixerStartCallback)callback;
[self.engine startMixerTask:task callback:^(ZegoMixerStartResult * _Nonnull result) {
if (result.errorCode == 0) {
NSLog(@"Start mixing task success.");
} else {
NSLog(@"Start mixing task failed.");
}
}];
When you need to change the stream mixing task, for example, when the number of input streams increases or decrease, or the bitrate of the output stream needs to be adjusted, you can update the stream mixing task object with the new configurations and then call the startMixerTask
method again.
When updating the configuration of a stream mixing task, you must not change the taskID
.
The following sample code demonstrates adding an input stream to a stream mixing task that is in progress, with a top-middle-bottom vertical layout.
CGRect firstRect = CGRectMake(0, 0, videoConfig.resolution.width, videoConfig.resolution.height/3);
ZegoMixerInput *firstInput = [[ZegoMixerInput alloc] initWithContentType:ZegoMixerInputContentTypeVideo streamID:@"stream-1" layout:firstRect];
CGRect secondRect = CGRectMake(0, videoConfig.resolution.height/3, videoConfig.resolution.width, videoConfig.resolution.height*(2/3));
ZegoMixerInput *secondInput = [[ZegoMixerInput alloc] initWithContentType:ZegoMixerInputContentTypeVideo streamID:@"stream-2" layout:secondRect];
CGRect thirdRect =CGRectMake(0, videoConfig.resolution.height*(2/3), videoConfig.resolution.width, videoConfig.resolution.height);
ZegoMixerInput *thirdInput = [[ZegoMixerInput alloc] initWithContentType:ZegoMixerInputContentTypeVideo streamID:@"stream-3" layout:thirdRect];
NSArray<ZegoMixerInput *> *inputArray = @[firstInput, secondInput, thirdInput];
// Reconfigure the input stream list.
[self.mixerTask setInputList:inputArray];
// Call the startMixerTask method again to let the new configurations take effect.
[self.engine startMixerTask:self.mixerTask callback:^(ZegoMixerStartResult * _Nonnull result) {
if (result.errorCode == 0) {
NSLog(@"Start mixer task success");
} else {
NSLog(@"Start mixer task fail");
}
}];
To stop a stream mixing task, call the stopMixerTask
method, with the corresponding task ID passed to the taskID
parameter.
/// Stop the stream mixing task.
/// @param taskID: The taskID of the steam mixing task to be stopped.
-(void)stopMixerTask:(NSString *)taskID;
// Pass in the taskID of the steam mixing task to be stopped.
[self.engine stopMixerTask:self.mixerTask.taskID];
ZegoAutoMixerTask
is a class defined in the SDK for configuring a stream mixing task. The configuration parameters include the layout of input streams, the outputs, and others.
/// Stream mixing task object.
@interface ZegoAutoMixerTask : NSObject
/// Task ID of the stream mixing task.
@property (nonatomic, copy) NSString *taskID;
/// Room ID of the stream mixing task.
@property (nonatomic, copy) NSString *roomID;
/// Set up the audio configuration of the mixed stream.
@property (nonatomic, strong) ZegoMixerAudioConfig *audioConfig;
/// Set up the list of mixing outputs.
@property (nonatomic, strong) NSArray<ZegoMixerOutput *> *outputList;
/// Set up the event callback related sound level of the mixed stream.
@property (nonatomic, assign) BOOL enableSoundLevel;
@end
Create an auto stream mixing task object and then call the instance methods to set up the input and output parameters.
ZegoAutoMixerTask *task = [[ZegoAutoMixerTask alloc] init];
task.taskID = @"taskID1";
task.roomID = @"roomID1";
To set up the audio configuration of the auto mixed audio streams, call the ZegoMixerAudioConfig
method, including the audio bitrate, channels, codec ID, and audio mix mode.
ZegoMixerAudioConfig *audioConfig = [ZegoMixerAudioConfig init];
audioConfig.bitrate = 48;/** Audio bitrate, unit: kbps, use 48 kbps by default. This can't be changed after stream mixing starts. */
audioConfig.channel = ZegoAudioChannelMono;/** Audio channel, use ZegoAudioChannelMono by default. */
audioConfig.codecID = ZegoAudioCodecIDNormal;/** Codec ID, use ZEGO_AUDIO_CODEC_ID_DEFAULT by default.*/
audioConfig.mixMode = ZegoAudioMixModeRaw;/** Audio mix mode, use ZegoAudioMixModeRaw by default. */
[task setAudioConfig:audioConfig];
To modify the audio channel, set the channel
property accordingly. The following are the supported channels:
Enumerated value | Description | Scenario |
---|---|---|
ZegoAudioChannelUnknown | Unkown | - |
ZegoAudioChannelMono | Mono | Mono-only scenario. |
ZegoAudioChannelStereo | Stereo | Stereo scenario. |
To modify the codec ID, set the codecID
property accordingly. The following are the supported codec IDs:
Enumerated value | Description |
---|---|
ZegoAudioCodecIDDefault | default |
ZegoAudioCodecIDNormal | Normal |
ZegoAudioCodecIDNormal2 | Normal2 |
ZegoAudioCodecIDNormal3 | Normal3 |
ZegoAudioCodecIDLow | Low |
ZegoAudioCodecIDLow2 | Low2 |
ZegoAudioCodecIDLow3 | Low3 |
To modify the mix mode, set the mixMode
property accordingly. The following are the supported audio mix modes:
Enumerated value | Description | Scenario |
---|---|---|
ZegoAudioMixModeRaw | Default mode. | Scenarios with no special need for audio. |
ZegoAudioMixModeFocused | Focus mode. Highlights the sound of a stream in a multi-channel audio stream. | The scenario where the sound of a stream needs to be highlighted. |
To set up the auto mixing outputs, call the ZegoMixerOutput
method to set the output list.
And you can play the mixed stream according to the output-stream
of the outputs.
NSArray<ZegoMixerOutput *> *outputArray = @[[[ZegoMixerOutput alloc] initWithTarget:@"output-stream"]];/** Mixing output target, URL or streamID. */
[task setOutputList:outputArray];
```tOutputList:outputArray];
If you need to receive the event notification related to the sound level of the auto mixed stream, set the enableSoundLevel
parameter to True
to listening for the event callback.
The SDK sends out the sound level of each stream through the onAutoMixerSoundLevelUpdate
callback when you play the mixed stream.
After configuring the ZegoAutoMixerTask
object, call the startAutoMixerTask
method to start the stream mixing task.
And the SDK sends out the result notifications through the onAutoMixerSoundLevelUpdate
callback.
[[ZegoExpressEngine sharedEngine] startAutoMixerTask:task callback:^(int errorCode, NSDictionary * _Nullable extendedData) {
if (errorCode == 0) {
/// Auto stream mixing task started successfully.
}
}];
To stop a stream mixing task, call the stopAutoMixerTask
method.
stopAutoMixerTask
method to stop the auto stream mixing task first. (In order to avoid the situation that when a host has started the next auto stream mixing stream task and mixed his/her stream with other hosts, while the audience is still playing the output stream of the old auto stream mixing task.)// Pass in the task object of the steam mixing task to be stopped.
[[ZegoExpressEngine sharedEngine] stopAutoMixerTask:self.autoMixerTask callback:^(int errorCode) {
if(errorCode == 0) {
///Auto stream mixing task stopped successfully.
}
}];
To fully-automatical mix the audio streams in each room, contact the ZEGOCLOUD Technical Support.
Can I forward the mixed stream to a third-party CDN or even multiple CDNs?
Yes. To forward the mixed stream to a third-party CDN, specify the CDN URL in the target
property of the ZegoMixerOutput
object. The URL must be in RTMP format: rtmp://xxxxxxxx
.
To forward the mixed stream to multiple CDNs, create multiple ZegoMixerOutput
objects accordingly, and put them into the outputList
of the ZegoMixerTask
object.
When the aspect ratio of the ZegoMixerInput
's layout
parameter doesn't match the resolution of the input stream itself, how does the SDK handle the video cropping?
The SDK scales the video proportionally.
Let's suppose the following:
720 × 1280
, whose aspect ratio is 9:16
. layout
parameter of the ZegoMixerInput
object is CGRectMake(0, 0, 100, 100)
, whose aspect ratio is 1:1
.Then, the SDK displays the middle part of the video and cuts off the upper part and lower part.
The hosts co-hosting a live streaming session want their respective viewers to see their own video in the main (larger) video view on the screen after mixing. How to do the stream mixing for them?
The hosts can start a stream mixing task with their own output stream layout.
Let's suppose the following:
Host A and Host B are co-hosting a live streaming session.
Host A is publishing Stream A.
Host B is publishing Stream B.
Then, Host A and Host B can respectively start a stream mixing task with different video layouts:
For a live streaming session with two hosts, what are the pros and cons of the following two options for starting the stream mixing?
A. To start the stream mixing immediately after the first host starts publishing a stream.
B. To start the stream mixing only after both hosts start publishing streams.
Option | Pros | Cons |
---|---|---|
Option A | It's easy to implement. The viewers can play the mixed stream right at the beginning and don't need to do any stream switching in the middle. | It costs more CDN usage because the stream mixing starts earlier. |
Option B | It costs less CDN usage because the stream mixing starts later. | The implementation is more complicated compared to option A. The viewers need to play the first host's stream at the beginning and then switch to the mixed stream later. |
Does the SDK support round or square video layout for stream mixing?
The SDK doesn't support a round video layout for stream mixing.
You can set up square video layouts by configuring the layout
parameter of the input streams.