混流是把多路音视频流从云端合并成一路流的技术,也称合流。开发者只需要拉取合并后的流就能看到房间内所有成员的画面,听到房间内所有成员的声音,无需分别管理房间内的每一条流。
本篇文档主要介绍通过客户端发起混流的操作说明,如果你需要通过自己的服务端发起混流,请参考 服务端 API - 开始混流。
ZEGO 支持手动混流、自动混流和全自动混流三种方式,三种混流方式的区别如下:
混流方式 | 手动混流 |
自动混流 |
全自动混流 |
---|---|---|---|
含义 | 自定义控制混流任务和混流内容,包括输入流、混流布局等。支持手动混视频流和音频流。 |
指定房间,自动将房间内的所有音频流进行混流。只支持自动混音频流。 |
每个房间都自动混音频流。只支持全自动混音频流。 |
应用场景 | 合并多个视频画面和声音时可用,比如在线课堂中老师和学生画面的直播,娱乐场景中的跨房间连麦,特殊场景中需混合指定几条流等;设备不支持同时拉多条流或者设备性能较差的场景。 |
将房间内所有音频流合为一条流时使用自动混流,比如语聊房、合唱。 |
不想做任何开发,房间内所有音频流合为一条流时使用全自动混流,比如语聊房、合唱。 |
优势 | 灵活性强,能够根据业务需要实现逻辑。 |
降低了开发者接入的复杂程度,不需要管理指定房间音频流的生命周期。 |
开发者接入复杂程度很低,不需要管理所有房间音频混流任务的生命周期以及音频流的生命周期。 |
发起方式 | 用户客户端或用户服务端发起混流任务,用户客户端维护流的生命周期。 |
用户客户端发起混流任务,ZEGO 服务端自动维护房间内流的生命周期(即输入流列表)。 |
联系 ZEGO 技术支持开通全自动混流,ZEGO 服务端维护混流任务和房间内流的生命周期(即输入流列表)。 |
请参考 下载示例源码 获取源码。
相关源码请查看 “/ZegoExpressExample/Examples/Others/Mixer” 目录下的文件。
在实现混流功能之前,请确保:
混流功能不是默认开启的,使用前请在 ZEGO 控制台 自助开通(开通步骤请参考 项目管理 - 服务配置 中的“混流”),或联系 ZEGO 技术支持开通。
混流的主要流程如下:
手动混流可自定义控制混流任务和混流内容,包括输入流、混流布局等,常用于多人互动直播和跨房间连麦场景。支持手动混视频流和音频流。
开发者可通过 SDK 或 ZEGO 服务端 API 实现手动混流功能,服务端相关接口请参考 开始混流 和 停止混流。
以下介绍如何使用 SDK 实现手动混流。
请参考 快速开始 - 实现流程 的 “创建引擎” 和 “登录房间” 完成房间登录。
ZegoMixerTask 是 SDK 中定义的混流任务配置对象,其中包含输入流布局、输出流等信息。
// 混流任务对象
@interface ZegoMixerTask : NSObject
- (instancetype)init NS_UNAVAILABLE;
// 通过 TaskID 构造一个混流任务对象
- (instancetype)initWithTaskID:(NSString *)taskID;
// 混流任务 ID
@property (nonatomic, copy, readonly) NSString *taskID;
// 设置混流任务对象的音频配置
- (void)setAudioConfig:(ZegoMixerAudioConfig *)audioConfig;
// 设置混流任务对象的视频配置
- (void)setVideoConfig:(ZegoMixerVideoConfig *)videoConfig;
// 设置混流任务对象的输入流列表
- (void)setInputList:(NSArray<ZegoMixerInput *> *)inputList;
// 设置混流任务对象的输出列表
- (void)setOutputList:(NSArray<ZegoMixerOutput *> *)outputList;
// 设置混流任务对象的水印
- (void)setWatermark:(ZegoWatermark *)watermark;
// 设置混流任务对象的背景图片
- (void)setBackgroundImageURL:(NSString *)backgroundImageURL;
@end
通过构造函数 initWithTaskID 新建一个混流任务对象,然后调用实例方法分别设置输入、输出等参数。
ZegoMixerTask *task = [[ZegoMixerTask alloc] initWithTaskID:@"task-1"];
// 保存该混流任务对象
self.mixerTask = task;
开发者可以调用 ZegoMixerVideoConfig 方法,配置混流任务的视频参数(帧率、码率、分辨率)。
如果要混的流都是纯音频,则不用进行设置。
视频的帧率、码率、分辨率的默认值分别为 15 fps、600 kbps、360p。
混流输出的最大帧率默认限制在 20 帧以内,如果需要输出更大帧率,请联系 ZEGO 技术支持进行配置。
ZegoMixerVideoConfig *videoConfig = [[ZegoMixerVideoConfig alloc] init];
[task setVideoConfig:videoConfig];
开发者可以调用 ZegoMixerAudioConfig 方法,配置混流任务的音频码率、声道数、音频编码。
音频的码率 bitrate 默认值为 48 kbps。
[task setAudioConfig:[ZegoMixerAudioConfig defaultConfig]];
根据实际业务场景,定义输入的视频流 ZegoMixerInput 列表,设置其中每条视频流的 “layout” 参数来对每条输入流的画面进行布局,由 ZEGO 实时音视频云服务器将输入流进行混合,输出在一个画面中的混流。
输入流的布局以输出混流画面的左上角为坐标系原点,参考原点设置输入流的布局,即将 CGRect(left, top, width, height)
传入输入流的 “layout” 参数。此外,输入流的图层层次由输入流在输入流列表中的位置决定,在列表中的位置越后,表示图层层次越高。
Rect
参数说明如下:
参数 | 描述 |
---|---|
left |
对应输入流画面左上角的 x 坐标。 |
top |
对应输入流画面左上角的 y 坐标。 |
width |
对应输入流画面的宽度。 |
height |
对应输入流画面的高度。 |
以上参数在不同的开发平台可能有所差异,具体请以各端的文档为准。
假设启动一个输出画面为 375×667 分辨率的混流任务,输入流为一条大小为 150×150,位于距左侧 50,距顶部 300 的混流,则需将 CGRect(50, 300, 150, 150)
传入输入流的 “layout” 参数。
那么这条输入流在最终的输出混流中的位置如下所示:
开发者可参考以下示例代码实现常见的混流布局:两个画面水平平铺、四个画面水平垂直平铺、一个大画面铺满和两个小画面悬浮。
以下布局示例皆以 360×640 分辨率进行说明。
// 填写第一条输入流配置,每条输入流需要设置 Stream ID (该参数中的值必须是输入流的实际 ID),输入流类型,布局等等
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;
// 填写第二条输入流配置
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;
// 设置混流输入
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;
// 设置混流输入
NSArray<ZegoMixerInput *> *inputArray = @[firstInput, secondInput, thirdInput, forthInput];
[task setInputList:inputArray];
输入流的图层层次由输入流在输入流列表中的位置决定,在列表中的位置越后,表示图层层次越高。如以下示例代码所示,第 2 条输入流的图层层次和第 3 条输入流的图层层次则比第 1 条输入流的层次要高,则第 2 条和第 3 条流悬浮第 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)];
// 设置混流输入
NSArray<ZegoMixerInput *> *inputArray = @[firstInput, secondInput, thirdInput];
[task setInputList:inputArray];
混流输出最多可设置 3 个。当输出目标为 URL 格式时,目前只支持 RTMP URL 格式:rtmp://xxxxxxxx,且不能传入两个相同的混流输出的地址。
以下代码演示输出到 ZEGO 服务器(流 ID 为 “output-stream”):
NSArray<ZegoMixerOutput *> *outputArray = @[[[ZegoMixerOutput alloc] initWithTarget:@"output-stream"]];
[task setOutputList:outputArray];
如果需要水印图片的 URL,请联系 ZEGO 技术支持获取。
以下代码演示设置一个 ZEGO 的图片水印放置于画面左上角:
ZegoWatermark *watermark = [[ZegoWatermark alloc] initWithImageURL:@"preset-id://zegowp.png" layout:CGRectMake(0, 0, videoConfig.resolution.width/2, videoConfig.resolution.height/20)];
[task setWatermark:watermark];
如果需要背景图片的 URL,请联系 ZEGO 技术支持获取。
[task setBackgroundImageURL:@"preset-id://zegobg.png"];
可通过设置 enableSoundLevel 参数选择是否开启混流的声浪回调通知,开启后(取值为 “True”)用户拉混流时可通过 onMixerSoundLevelUpdate 回调收到每条单流的声浪信息。
[task enableSoundLevel:YES];
高级配置适用于一些定制化需求,例如:配置视频编码格式。
如果需要了解具体支持的配置项信息,请联系 ZEGO 技术支持。
普通场景无需设置高级配置。
// 指定混流输出视频格式为 vp8 ,使用特定的推流协议才能生效。
NSDictionary *config = @{@"video_encode": @"vp8"};
[task setAdvancedConfig: config];
// 如果混流输出视频格式设为 vp8,请同步设置音频编码格式为 LOW3,设置方可生效。
ZegoMixerAudioConfig *audioConfig = [ZegoMixerAudioConfig defaultConfig];
audioConfig.codecID = ZegoAudioCodecIDLow3;
[task setAudioConfig:audioConfig];
完成了 ZegoMixerTask 混流任务对象的配置后,调用开始混流的接口以启动这个混流任务,并在回调 Block 中处理启动混流任务失败的逻辑。
如果需要 Web 端播放混流 CDN 资源,在使用 CDN 录制时,音频编码请选择 AAC-LC,由于部分浏览器(如 Google Chrome 和 Microsoft Edge)不兼容 HE-AAC 音频编码格式,会导致录制文件无法播放。
[self.engine startMixerTask:task callback:^(ZegoMixerStartResult * _Nonnull result) {
if (result.errorCode == 0) {
NSLog(@"Start mixer task success");
} else {
NSLog(@"Start mixer task fail");
}
}];
当混流信息发生变更时,例如混流的输入流列表发生增减、调整混流视频输出码率等,修改该混流任务对象的参数,然后再调用一次 startMixerTask 接口即可更新配置。
更新混流任务的配置时,“taskID” 不可更改。
以下代码演示进行混流任务途中添加一条输入流,上中下布局:
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];
// 重新设置之前保存的混流对象的输入流列表
[self.mixerTask setInputList:inputArray];
// 再调用一次启动混流任务接口,即可更新混流配置
[self.engine startMixerTask:self.mixerTask callback:^(ZegoMixerStartResult * _Nonnull result) {
if (result.errorCode == 0) {
NSLog(@"Start mixer task success");
} else {
NSLog(@"Start mixer task fail");
}
}];
// 传入正在混流中的 taskID 以停止该混流任务
[self.engine stopMixerTask:self.mixerTask.taskID];
请参考 快速开始 - 实现流程 中的的 “创建引擎” 和 “登录房间” 完成初始化和房间登录。
ZegoAutoMixerTask 是 SDK 中定义的自动混流任务配置对象,通过配置该对象可定制化自动混流任务。
新建一个自动混流任务对象,然后分别设置输入、输出等参数。
ZegoAutoMixerTask *task = [[ZegoAutoMixerTask alloc] init];
task.taskID = @"taskID1";
task.roomID = @"roomID1";
通过 ZegoMixerAudioConfig 设置自动混流音频相关配置,主要包括音频码率、声道数、编码 ID,以及多路音频流混音模式。
ZegoMixerAudioConfig *audioConfig = [ZegoMixerAudioConfig init];
// 音频码率,单位为 kbps,默认为 48 kbps,开始混流任务后不能修改
audioConfig.bitrate = 48;
// 音频声道,默认为 ZegoAudioChannelMono 单声道
audioConfig.channel = ZegoAudioChannelMono;
// 编码 ID,默认为 ZEGO_AUDIO_CODEC_ID_DEFAULT
audioConfig.codecID = ZegoAudioCodecIDNormal;
// 多路音频流混音模式,默认为 ZegoAudioMixModeRaw
audioConfig.mixMode = ZegoAudioMixModeRaw;
[task setAudioConfig:audioConfig];
通过 channel 参数可以修改音频声道,目前支持如下音频声道:
枚举值 | 说明 | 适用场景 |
---|---|---|
ZegoAudioChannelUnknown | 未知。 | - |
ZegoAudioChannelMono | 单声道。 | 只有单声道的场景。 |
ZegoAudioChannelStereo | 双声道。 | 有双声道的场景。 |
通过 codecID 参数可以修改编码 ID,目前支持如下编码 ID:
枚举值 | 说明 | 适用场景 |
---|---|---|
ZegoAudioCodecIDDefault | 默认值。 | 根据调用 createEngineWithProfile 时的 [scenario] 决定。 |
ZegoAudioCodecIDNormal | 码率范围 10 kbps ~ 128 kbps;支持双声道;延迟在 500ms 左右。与 Web SDK 互通时需要服务端转码;转推 CDN 时不需要服务端云转码。 | 可用于 RTC 和 CDN 推流。 |
ZegoAudioCodecIDNormal2 | 兼容性好,码率范围 16 kbps ~ 192 kbps;支持双声道;延迟 350ms 左右;相同码率下(较低码率),音质弱于 [Normal]。与 Web SDK 互通时需要服务端转码;转推 CDN 时不需要服务端云转码。 | 可用于 RTC 和 CDN 推流。 |
ZegoAudioCodecIDNormal3 | 不推荐使用。 | 仅可用于 RTC 推流。 |
ZegoAudioCodecIDLow | 不推荐使用。 | 仅可用于 RTC 推流。 |
ZegoAudioCodecIDLow2 | 不推荐使用,最大码率为 16 kbps。 | 仅可用于 RTC 推流。 |
ZegoAudioCodecIDLow3 | 码率范围 6 kbps ~ 192 kbps;支持双声道;延迟在 200ms 左右;相同码率下(较低码率),音质明显好于 [Normal] 与 [Normal2];CPU 开销较低。与 Web SDK 互通时不需要服务端云转码;转推 CDN 时需要服务端转码。 | 仅可用于 RTC 推流。 |
通过 mixMode 参数可以修改多路音频流混音模式,目前支持如下多路音频流混音模式:
枚举值 | 说明 | 适用场景 |
---|---|---|
ZegoAudioMixModeRaw | 默认模式,无特殊行为。 | 对音频无特殊需求的场景。 |
ZegoAudioMixModeFocused | 音频聚焦模式,可在多路音频流中突出某路流的声音。 | 需要突出某路流的声音的场景。 |
通过 ZegoMixerOutput 设置自动混流输出列表,用户可以从列表中的输出目标拉取混流。
// 混流输出目标,URL 或者流 ID
NSArray<ZegoMixerOutput *> *outputArray = @[[[ZegoMixerOutput alloc] initWithTarget:@"output-stream"]];
[task setOutputList:outputArray];
可通过设置 enableSoundLevel 参数选择是否开启自动混流的声浪回调通知,开启后(取值为 “True”)用户拉混流时可通过 onAutoMixerSoundLevelUpdate 回调收到每条单流的声浪信息。
task.enableSoundLevel = YES;
完成了 ZegoAutoMixerTask 自动混流任务对象的配置后,调用 startAutoMixerTask 接口开始该自动混流任务,并在 ZegoMixerStartCallback 回调中接收开始自动混流任务结果。
[[ZegoExpressEngine sharedEngine] startAutoMixerTask:task callback:^(int errorCode, NSDictionary * _Nullable extendedData) {
if (errorCode == 0) {
// 开始自动混流任务成功
}
}];
调用 stopAutoMixerTask 接口停止自动混流。
在同一个房间内开启下一个自动混流任务前,请先调用 stopAutoMixerTask 接口结束上一次自动混流任务,以免造成当一个主播已经开启下一个自动混流任务与其他主播混流时,观众依然在一直拉上一个自动混流任务的输出流的情况。若用户未主动结束当前自动混流任务,该任务将在房间关闭后自动结束。
// 传入之前创建的混流任务对象
[[ZegoExpressEngine sharedEngine] stopAutoMixerTask:self.autoMixerTask callback:^(int errorCode) {
if(errorCode == 0) {
//停止自动混流任务成功
}
}];
通过 ZEGO 服务端的配置实现每个房间都自动混音频流,详情请联系 ZEGO 技术支持。
能将混流推到第三方 CDN 吗?如何转推多路 CDN?
若需要将混流推到第三方 CDN,可在 ZegoMixerOutput 的 “target” 参数填写 CDN 的 URL。
填写的 URL 格式需要为 RTMP 格式:“rtmp://xxxxxxxx”。
推多路 CDN 就创建 N 个输出流对象 ZegoMixerOutput 放入 ZegoMixerTask 中的 “outputList” 输出列表中。
如何设置混流中每条流的布局?
ZegoMixerInput 的 “layout” 参数使用示例:
那么这条流在最终的输出混流中的位置如下所示:
混流输入对象 “ZegoMixerInput” 的 “CGRect” 布局的比例与这条流本身的分辨率不相符时,画面将如何裁剪?
SDK 会做等比缩放。假设一条输入流的分辨率为 “720 × 1280”,即比例为 “9:16”,同时这条流的 ZegoMixerInput 的 “layout” 参数为 “CGRectMake(0, 0, 100, 100);”,即比例为 “1:1” 时,画面将会显示这条流的中间部分,即上下部分被裁剪掉。
参与连麦的主播们想让各自的观众看到自己的视频在混流后的画面布局中位于大窗口,如何混流?
主播们各自布局再各自发起混流。
例如:主播 A 设置自己推的流 A 画面布局的宽高大于拉主播 B 的流 B 的布局宽高,然后发起一个混流任务输出一个流 “A_Mix”;主播 B 设置自己推的流 B 画面布局的宽高大于拉主播 A 的流 A 的布局宽高,然后发起混流输出一个流 “B_Mix”。
即总共需要发起两个混流任务。
混流的两种方式:“单主播开始直播后就马上开始混流”和“当第二主播加入连麦的时候才开始混流”,这两种方式有什么区别?优劣势是什么?
从单主播直播开始就启动混流的优点是实现简单,缺点是会多一些额外的混单流时间的 CDN 成本开销。
从单主播直播开始仅推流,等第二路主播加入连麦时才启动混流。优点是节约成本;缺点是开发实现上会复杂一些,观众端需要先拉取单主播流,主播们连麦开启混流后需要停止拉单主播流,然后改为拉取混流。而上述从头开始混流的方式,观众端不需要做从拉单主播流再到拉混流的一个切换。
混流是否支持圆形或者方形的画面?
不支持圆形,方形可通过布局实现。
在推纯音频混流并设置了背景图时,遇到背景图无法正常进行展示,如何处理?
在这种情况下,客户需要根据自身业务需求,正确设置输出布局的宽和高,并联系 ZEGO 技术支持配置开启补黑帧。
联系我们
文档反馈