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

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

开启合流方法,需要在收到 onRoomStateChanged 回调,进入 RTC 房间成功后执行。
/**
* 开启合流转推
* @param rtcRoomId 房间id
* @param rtcUserId 用户id
* @param pushUrl rtmp 推流地址
*/
- (void)startLiveTranscodingWithPushUrl:(NSString *)pushUrl
rtcUserId:(NSString *)rtcUserId
rtcRoomId:(NSString *)rtcRoomId {
ByteRTCVideoCompositingRegion *region = [[ByteRTCVideoCompositingRegion alloc] init];
region.uid = rtcUserId; // 参与合流的媒体流所属用户的 ID
region.roomId = rtcRoomId;
region.localUser = YES;
region.renderMode = ByteRTCRenderModeHidden;
region.x = 0.0; //
region.y = 0.0;
region.width = 1.0;
region.height = 1.0;
region.zOrder = 1;
region.alpha = 1.0;
ByteRTCLiveTranscoding *transcodingSetting = [ByteRTCLiveTranscoding defaultTranscoding];
transcodingSetting.layout.regions = @[region];
transcodingSetting.roomId = rtcRoomId;
transcodingSetting.userId = rtcUserId;
transcodingSetting.url = pushUrl;
// 设置为服务器合流
transcodingSetting.expectedMixingType = ByteRTCStreamMixingTypeByServer;
// 示例参数,具体参数可以根据业务修改。
// 合流音频采样率,单位 Hz
transcodingSetting.audio.sampleRate = 44100;
// 合流音频声道数
transcodingSetting.audio.channels = 2;
// 合流视频帧率
transcodingSetting.video.fps = 15;
// 合流视频码率
transcodingSetting.video.kBitRate = 1600;
// 合流视频分辨率
transcodingSetting.video.width = 720;
transcodingSetting.video.height = 1280;
self.transcodingSetting = transcodingSetting;
// 开启合流
int result = [self.rtcEngineKit startLiveTranscoding:@""
transcoding:transcodingSetting
observer:self];
if (result != 0) {
// Start live transcoding error
}
}objectivecprivate final ILiveTranscodingObserver mILiveTranscodingObserver = new ILiveTranscodingObserver() {
// ILiveTranscodingObserver相关方法
};
/**
* 开启合流转推
* @param roomId 房间id
* @param userId 用户id
* @param liveUrl rtmp 推流地址
*/
private void startLiveTranscoding(String roomId, String userId, String liveUrl) {
LiveTranscoding liveTranscoding = LiveTranscoding.getDefualtLiveTranscode();
// 设置房间id
liveTranscoding.setRoomId(roomId);
// 设置用户id
liveTranscoding.setUserId(userId);
// 设置推流的直播地址
liveTranscoding.setUrl(liveUrl);
// 设置合流模式,0 代表服务端合流
liveTranscoding.setMixType(ByteRTCStreamMixingType.STREAM_MIXING_BY_SERVER);
// 设置合流视频参数,具体参数根据情况而定
LiveTranscoding.VideoConfig videoConfig = liveTranscoding.getVideo();
videoConfig.setWidth(720);
videoConfig.setHeight(1280);
videoConfig.setFps(15);
videoConfig.setKBitRate(1600);
liveTranscoding.setVideo(videoConfig);
// 设置合流音频参数,具体参数根据情况而定
LiveTranscoding.AudioConfig audioConfig = liveTranscoding.getAudio();
audioConfig.setSampleRate(44100);
audioConfig.setChannels(2);
liveTranscoding.setAudio(audioConfig);
// 设置合流视频布局参数
LiveTranscoding.Layout.Builder layoutBuilder = new LiveTranscoding.Layout.Builder();
LiveTranscoding.Region selfRegion = new LiveTranscoding.Region();
selfRegion.uid(userId);
selfRegion.roomId(roomId);
selfRegion.position(0, 0);
selfRegion.size(1, 1);
selfRegion.alpha(1);
selfRegion.zorder(0);
selfRegion.renderMode(LiveTranscoding.TranscoderRenderMode.RENDER_HIDDEN);
layoutBuilder.addRegion(selfRegion);
liveTranscoding.setLayout(layoutBuilder.builder());
mLiveTranscoding = liveTranscoding;
// 开始服务端合流任务,taskid使用空字符串即可
mRTCVideo.startLiveTranscoding("", liveTranscoding, mILiveTranscodingObserver);
}java在收到远端用户视频流后,才可以更新合流布局。
- (void)rtcRoom:(ByteRTCRoom *)rtcRoom onUserPublishStream:(NSString *)userId type:(ByteRTCMediaStreamType)type {
if (type == ByteRTCMediaStreamTypeBoth ||
type == ByteRTCMediaStreamTypeVideo) {
// 在收到远端用户视频流后,才可以更新合流布局
}
}objectivec
private final IRTCRoomEventHandler mRTCRoomEventHandler = new IRTCRoomEventHandler() {
@Override
public void onUserPublishStream(String uid, MediaStreamType type) {
if (type == MediaStreamType.RTC_MEDIA_STREAM_TYPE_BOTH ||
type == MediaStreamType.RTC_MEDIA_STREAM_TYPE_VIDEO) {
// 在收到远端用户视频流后,才可以更新合流布局
}
}
};java
/**
* @param localUserId 本地用户ID
* @param remoteUserId 远端用户ID
* @param roomId 房间ID
*/
- (void)updateLiveTranscoding:(NSString *)localUserId
remoteUserId:(NSString *)remoteUserId
rtcRoomId:(NSString *)rtcRoomId {
// 当前主播的合流布局
ByteRTCVideoCompositingRegion *localRegion = [[ByteRTCVideoCompositingRegion alloc] init];
localRegion.uid = localUserId;
localRegion.roomId = rtcRoomId;
localRegion.localUser = YES;
localRegion.renderMode = ByteRTCRenderModeHidden;
localRegion.x = 0.0;
localRegion.y = 0.25;
localRegion.width = 0.5;
localRegion.height = 0.5;
localRegion.zOrder = 0;
localRegion.alpha = 1.0;
// 远端主播的合流布局
ByteRTCVideoCompositingRegion *remoteRegion = [[ByteRTCVideoCompositingRegion alloc] init];
remoteRegion.uid = remoteUserId;
remoteRegion.roomId = rtcRoomId;
remoteRegion.localUser = NO;
remoteRegion.renderMode = ByteRTCRenderModeHidden;
remoteRegion.x = 0.5;
remoteRegion.y = 0.25;
remoteRegion.width = 0.5;
remoteRegion.height = 0.5;
remoteRegion.zOrder = 0;
remoteRegion.alpha = 1.0;
self.transcodingSetting.layout.regions = @[localRegion, remoteRegion];
[self.rtcEngineKit updateLiveTranscoding:@""
transcoding:self.transcodingSetting];
}objectivec
/**
* @param localUserId 本地用户ID
* @param remoteUserId 远端用户ID
* @param roomId 房间ID
*/
private void updateLiveTranscoding(String localUserId, String remoteUserId, String roomId) {
LiveTranscoding.Layout.Builder layoutBuilder = new LiveTranscoding.Layout.Builder();
LiveTranscoding.Region selfRegion = new LiveTranscoding.Region();
// 当前主播合流布局
selfRegion.uid(localUserId);
selfRegion.setLocalUser(true);
selfRegion.renderMode(LiveTranscoding.TranscoderRenderMode.RENDER_HIDDEN);
selfRegion.roomId(roomId);
selfRegion.position(0, 0.25);
selfRegion.size(0.5, 0.5);
selfRegion.alpha(1);
selfRegion.zorder(0);
layoutBuilder.addRegion(selfRegion);
// 远端主播的合流布局
LiveTranscoding.Region hostRegion = new LiveTranscoding.Region();
hostRegion.uid(remoteUserId);
hostRegion.setLocalUser(false);
hostRegion.renderMode(LiveTranscoding.TranscoderRenderMode.RENDER_HIDDEN);
hostRegion.roomId(roomId);
hostRegion.position(0.5, 0.25);
hostRegion.size(0.5, 0.5);
hostRegion.alpha(1);
hostRegion.zorder(0);
layoutBuilder.addRegion(hostRegion);
mLiveTranscoding.setLayout(layoutBuilder.builder());
mRTCVideo.updateLiveTranscoding("", mLiveTranscoding);
}java
/**
* 停止合流转推或者退出房间的时候调用
*/
- (void)stopLiveTranscoding {
[self.rtcEngineKit stopLiveTranscoding:@""];
self.transcodingSetting = nil;
}objectivec
/**
* 停止合流转推或者退出房间的时候调用
*/
public void stopLiveTranscoding() {
if (mRTCVideo != null) {
mRTCVideo.stopLiveTranscoding("");
}
mLiveTranscoding = null;
}javaSEI 是视频编码格式中的补充增强信息,用于传输一些用户自定义的信息。
使用场景:因为可以和视频编码帧打包到一起发送,可以和视频帧达到发送和解析同步的效果,比如在直播答题场景中,可以用 SEI 打包题目信息,这样每个人听到主播讲那道题时,可以同步看到题目,不会因为延时不同导致题目出现的时间不一样。
- (void)startLiveTranscodingWithPushUrl:(NSString *)pushUrl
rtcUserId:(NSString *)rtcUserId
rtcRoomId:(NSString *)rtcRoomId {
self.transcodingSetting.layout.appData = @"json";
int result = [self.rtcEngineKit startLiveTranscoding:@""
transcoding:self.transcodingSetting
observer:self];
}
- (void)updateLiveTranscoding:(NSString *)localUserId
remoteUserId:(NSString *)remoteUserId
rtcRoomId:(NSString *)rtcRoomId {
self.transcodingSetting.layout.appData = @"json";
[self.rtcEngineKit updateLiveTranscoding:@""
transcoding:self.transcodingSetting];
}
ObjectC当 APP 需要开启多个视频合流时,可以通过 task_id 来区分多个合流 ID。如果同时只有一个合流视频数据可以使用空字符串代替。startLiveTranscoding 和 stopLiveTranscoding 的 task_id 需要成对出现。 如果 task_id 不同,会导致合流不会关闭。
在开启转推任务后,如果因为进行刷新页面等操作,造成应用端进程异常终止,则转推任务会在空闲时间超过设定值后自动停止,默认空闲超时时间为180s。
重启客户端重新登陆后,需先调用 stopLiveTranscoding 结束上一个任务,再开启新的转推任务,以免造成多个任务同时操作一个推流地址,导致新的转推任务失败。
转推直播过程中返回的错误码详见各端 API 文档。
| Android | iOS | Mac | Windows | Linux | Web | |
|---|---|---|---|---|---|---|
| 错误码 | ByteRTCTranscoderErrorCode | ByteRTCTranscoderErrorCode | ByteRTCTranscoderErrorCode | StreamMixingErrorCode | StreamMixingErrorCode | StreamMixingEventErrorCode |
startLiveTranscoding 开启转推。其中,任务参数为 LiveTranscoding 结构体,使用 getDefualtLiveTranscode 进行参数初始化。updateLiveTranscoding 更改音视频参数和视频布局。startLiveTranscoding:transcoding:observer: 开启转推。其中,任务参数为 ByteRTCLiveTranscoding 结构体,使用 defaultTranscoding 进行参数初始化。updateLiveTranscoding:transcoding: 更改音视频参数和视频布局。startLiveTranscoding:transcoding:observer: 开启转推。其中,任务参数为 ByteRTCLiveTranscoding 结构体,使用 defaultTranscoding 进行参数初始化。updateLiveTranscoding:transcoding: 更改音视频参数和视频布局。startLiveTranscoding开启转推直播。其中,任务参数为 ITranscoderParam 结构体,使用 defaultTranscoding 进行参数初始化。updateLiveTranscoding 更改音视频参数和视频布局。startLiveTranscoding开启转推直播。其中,任务参数为 ITranscoderParam 结构体,使用 defaultTranscoding 进行参数初始化。updateLiveTranscoding 更改音视频参数和视频布局。startLiveTranscoding 开启转推直播。updateLiveTranscoding 更改音视频参数和视频布局。