对于一个音视频通话,你可以将其中的多路音视频流合为一路,并将合并得到的音视频流推送到指定的推流地址(通常是 CDN 地址)。你可以在应用服务端和应用客户端启动转推直播,本文介绍如何通过调用客户端 API,在 RTC 服务端发起和完成转推直播任务。

关于如何调用 Open API,在服务端完成转推直播,参见 通过 OpenAPI 使用转推直播功能。
alt

前提条件

你已经集成 RTC SDK,实现了基本的音视频通话。

调用时序

api flow

示例代码

开启任务

开启合流方法,需要在收到 onRoomStateChanged 回调,进入 RTC 房间成功后执行。


 - (void)startLiveTranscoding {    
    // 获取合流转推视频流布局
    NSArray *regions = [self getRegionWithUserId:rtcUid
                                       rtcRoomId:rtcRoomId];
    // 设置转推直播参数
    self.mixedStreamConfig = [ByteRTCMixedStreamConfig defaultMixedStreamConfig];
    // 设置参与合流的单路流
    self.mixedStreamConfig.layoutConfig.regions = regions;
    // 设置房间id
    self.mixedStreamConfig.roomID = rtcRoomId;
    // 参与合流的媒体流所属用户的 ID
    self.mixedStreamConfig.userID = [LocalUserComponents userModel].uid;
    // rtmp 推流地址
    self.mixedStreamConfig.pushURL = pushUrl;
    // 设置为服务器合流
    self.mixedStreamConfig.expectedMixingType = ByteRTCMixedStreamByServer;
    // 示例参数,具体参数可以根据业务修改。
    // 合流音频采样率,单位 Hz
    self.mixedStreamConfig.audioConfig.sampleRate = 44100;
    // 合流音频声道数
    self.mixedStreamConfig.audioConfig.channels = 2;
    // 合流视频帧率
    self.mixedStreamConfig.videoConfig.fps = frameRate;
    // 合流视频码率
    self.mixedStreamConfig.videoConfig.bitrate = maxKbps;
    // 合流视频分辨率
    self.mixedStreamConfig.videoConfig.width = videoSize.width;
    self.mixedStreamConfig.videoConfig.height = videoSize.height;
    
    // 设置合流SEI
    self.mixedStreamConfig.layoutConfig.userConfigExtraInfo = @"json";

    // 开始合流转推
    [self.rtcEngineKit startPushMixedStreamToCDN:@""
                                     mixedConfig:self.mixedStreamConfig
                                        observer:self];
}
// 设置参与合流的单路流
- (NSArray *)getRegionWithUserId:(NSString *)userId
                       rtcRoomId:(NSString *)rtcRoomId {
    NSInteger audienceIndex = 0;
    NSMutableArray *list = [[NSMutableArray alloc] init];
    ByteRTCMixedStreamLayoutRegionConfig *region = [[ByteRTCMixedStreamLayoutRegionConfig alloc] init];
    region.userID = userId;
    region.roomID = rtcRoomId;
    region.isLocalUser = [userModel.uid isEqualToString:[LocalUserComponents userModel].uid] ? YES : NO;
    region.renderMode = ByteRTCMixedStreamRenderModeHidden;
    region.locationX = 0.0;
    region.locationY = 0.0;
    region.widthProportion = 1.0;
    region.heightProportion = 1.0;
    region.zOrder = 1;
    region.alpha = 1.0;
    [list addObject:region];
    
    return [list copy];
}
}
objectivec
/**
 * 开启合流转推
 * @param roomId 房间id
 * @param userId 用户id
 * @param liveUrl rtmp 推流地址
 */
private void startLiveTranscoding(String roomId, String userId, String liveUrl,String taskId) {
    MixedStreamConfig config = MixedStreamConfig.defaultMixedStreamConfig();
    // 设置房间id
    config.setRoomID(roomId);
    // 设置用户id
    config.setUserID(userId);
    // 设置推流的直播地址
    config.setPushURL(liveUrl);
    // 设置合流模式,0 代表服务端合流
    config.setExpectedMixingType(ByteRTCStreamMixingType.STREAM_MIXING_BY_SERVER);


    //设置合流视频参数,具体参数根据情况而定
    MixedStreamVideoConfig videoConfig = config.getVideoConfig();
    videoConfig.setWidth(mHostConfig.width);
    videoConfig.setHeight(mHostConfig.height);
    videoConfig.setFps(mHostConfig.frameRate);
    videoConfig.setBitrate(mHostConfig.bitRate);
    config.setVideoConfig(videoConfig);

    //设置合流音频参数,具体参数根据情况而定
    MixedStreamAudioConfig audioConfig = config.getAudioConfig();
    audioConfig.setSampleRate(44100);
    audioConfig.setChannels(2);
    config.setAudioConfig(audioConfig);
    
    //设置合流视频布局参数
    MixedStreamLayoutRegionConfig localRegionConfig = new MixedStreamLayoutRegionConfig();
    localRegionConfig.setUserID(userId);
    localRegionConfig.setIsLocalUser(true);
    localRegionConfig.setRoomID(roomId);
    localRegionConfig.setLocationX(0);
    localRegionConfig.setLocationY(0);
    localRegionConfig.setWidthProportion(1);
    localRegionConfig.setHeightProportion(1);
    localRegionConfig.setAlpha(1);
    localRegionConfig.setZOrder(0);
    localRegionConfig.setRenderMode(MixedStreamRenderMode.MIXED_STREAM_RENDER_MODE_HIDDEN);
    
    
    MixedStreamLayoutRegionConfig[] regions = new MixedStreamLayoutRegionConfig[]{localRegionConfig};
    MixedStreamLayoutConfig layoutConfig = new MixedStreamLayoutConfig();
    layoutConfig.setRegions(regions);
    JsonObject json = new JsonObject();
    json.addProperty(KEY_SEI_KEY_SOURCE, KEY_SEI_VALUE_SOURCE_NONE);
    //设置SEI信息
    layoutConfig.setUserConfigExtraInfo(json.toString());
    config.setLayout(layoutConfig);
    //开始合流任务
    mRTCVideo.startPushMixedStreamToCDN(taskId, config, mMixedStreamObserver);
   }
java
更新任务

在收到远端用户视频流后,才可以更新合流布局。
在转推直播进行时,部分设置可以更新,详见 API 文档。

结束任务

在音视频互动中,你可以随时启动或停止转推直播。


/**
 * 停止合流转推或者退出房间的时候调用
 */
- (void)stopPushStreamToCDN {
    [self.rtcEngineKit stopPushStreamToCDN:@""];
    self.transcodingSetting = nil;
}
objectivec

/**
 * 停止合流转推或者退出房间的时候调用
 */
public void stopPushStreamToCDN() {
    if (mRTCVideo != null) {
        mRTCVideo.stopPushStreamToCDN("");
    }
    mLiveTranscoding = null;
}
java
常见问题

1. 如何在转推直播流中使用 SEI

SEI 是视频编码格式中的补充增强信息,用于传输一些用户自定义的信息。
使用场景:SEI 和视频编码帧一起打包发送,因此可以达到和视频帧发送和解析同步的效果。比如在直播答题场景中,在 SEI 中打包题目信息,每个人听到主播讲题时,同时看到对应的题目,不会因为不同延时导致题目出现的时间与讲解不匹配。

2. 如何设置 task_id

当 APP 需要开启多个视频合流时,可以通过 task_id 来区分多个合流 ID。如果同时只有一个合流视频数据可以使用空字符串代替。
startPushMixedStreamToCDN 和 stopPushStreamToCDN 的 task_id 需要成对出现。 如果 task_id 不同,会导致合流不会关闭。

3. 如何处理发起端意外掉线后重新登录

在开启转推任务后,如果因为进行刷新页面等操作,造成应用端进程异常终止,则转推任务会在空闲时间超过设定值后自动停止,默认空闲超时时间为180s。
重启客户端重新登录后,需先调用 stopPushStreamToCDN 结束上一个任务,再开启新的转推任务,以免造成多个任务同时操作一个推流地址,导致新的转推任务失败。

4. 错误码

转推直播过程中返回的错误码详见各端 API 文档。

AndroidiOSMacWindowsElectronWeb
错误码ByteRTCTranscoderErrorCodeByteRTCStreamMixingErrorCodeByteRTCStreamMixingErrorCodeStreamMixingErrorCodeStreamMixingErrorCodeStreamMixingEventErrorCode

客户端 API 参考
Android
iOS
Mac
Windows
Web