发布音视频流到直播 CDN

本文将介绍如何将 TRTC 房间中的音视频流发布(也称作“转推”)到直播CDN上,用于兼容常规的直播播放器进行观看。

适用场景

由于 TRTC 采用 UDP 协议进行传输音视频数据,而标准直播 CDN 则采用的 RTMP\HLS\FLV 等协议进行数据传输,所以需要将 TRTC 中的音视频数据旁路到直播 CDN 中,才能让观众通过直播 CDN 进行观看。
给 TRTC 对接 CDN 观看,一般被用于解决如下两类问题:
问题一:超高并发观看 TRTC 的低延时观看能力,单房间支持的最大人数上限为10万人。CDN 观看虽然延迟要高一些,但支持10万人以上的并发观看,且 CDN 的计费价格更加便宜。
问题二:移动端网页播放 TRTC 虽然支持 WebRTC 协议接入,但主要用于 Chrome 桌面版浏览器,移动端浏览器的兼容性非常不理想,尤其是 Android 手机浏览器对 WebRTC 的支持普遍都很差。所以如果希望通过 Web 页面在移动端分享直播内容,还是推荐使用 HLS(m3u8) 播放协议,这也就需要借助直播 CDN 的能力来支持 HLS 协议。

旁路转推控制方案

TRTC 提供几种发布音视频流到直播 CDN(即“旁路转推”)的控制方案,分别是通过 终端 SDK API 发起、通过 服务端 Restful API 发起 和通过 自动旁路发起,几种方案具体说明如下:

方案一:终端 SDK 发起转推

步骤1:发布当前用户的音视频流到直播 CDN


功能介绍
您可以使用 TRTCCloud 提供的接口 startPublishMediaStream(以IOS为例) 将房间中当前用户的音视频流发布到直播 CDN 上(“发布到 CDN” 也被常称为“转推到CDN”)。 在这个过程中,TRTC 的云端服务器不会对音视频数据进行二次的转码加工,而是直接将音视频数据直接导入到直播 CDN 服务器上,因此整个过程所产生的费用是最低的。 但房间中的每一个音视频用户都需要有一条 CDN 上的直播流与之对应,因此如果房间中有多个用户的音视频流,就需要观众端使用多个直播播放器进行观看,且多条 CDN 直播流之间的播放进度可能相差很大。
操作指引
您可以按照如下指引将房间中当前用户的音视频流发布到直播 CDN 上。
1. 创建 TRTCPublishTarget 对象,并指定 TRTCPublishTarget 对象中的 mode 参数为TRTCPublishBigStreamToCdn或者TRTCPublishSubStreamToCdn,其中前者是指发布当前用户的主路画面(一般是摄像头),后者是指发布当前用户的辅路画面(一般是屏幕分享)。
2. 指定 TRTCPublishTarget 对象中的 cdnUrlList 参数为一个或者多个 CDN 推流地址(标准的 CDN 推流地址一般以 rtmp:// 作为 URL 的前缀)。如果您指定的 URL是腾讯云的直播 CDN 推流地址(可前往 云直播控制台 > 地址生成器 生成),需要将 isInternalLine 设置为 true,否则请将其设置为 false
3. 因为该模式下不涉及转码服务,所以请在调用接口时保持 TRTCStreamEncoderParamTRTCStreamMixingConfig 两个参数为空。
4. 调用 startPublishMediaStream 接口,并通过 onStartPublishMediaStream 监听本地 API 调用是否成功,如果成功 onStartPublishMediaStream 中的 taskId 会返回一个不为空字符串。
5. 如果您需要停止发布动作,只需要调用 stopPublishMediaStream 并传入之前通过 onStartPublishMediaStream 获得 taskId 即可。
参考代码
如下这段代码的功能是发布当前用户的音视频流到直播 CDN:
java
Objective-C
C++
Web
Dart
// 发布当前用户的音视频流到直播CDN
TRTCCloudDef.TRTCPublishTarget target = new TRTCCloudDef.TRTCPublishTarget();
target.mode = TRTC_PublishBigStream_ToCdn;
TRTCCloudDef.TRTCPublishCdnUrl cdnUrl= new TRTCCloudDef.TRTCPublishCdnUrl();
cdnUrl.rtmpUrl = "rtmp://tencent/live/bestnews";
cdnUrl.isInternalLine = true;
target.cdnUrlList.add(cdnUrl);
mTRTCCloud.startPublishMediaStream(target, null, null);
// 发布当前用户的音视频流到直播CDN
TRTCPublishTarget* target = [[TRTCPublishTarget alloc] init];
target.mode = TRTCPublishBigStreamToCdn;
TRTCPublishCdnUrl* cdnUrl = [[TRTCPublishCdnUrl alloc] init];
cdnUrl.rtmpUrl = @"rtmp://tencent/live/bestnews";
cdnUrl.isInternalLine = YES;
NSMutableArray* cdnUrlList = [NSMutableArray new];
[cdnUrlList addObject:cdnUrl];
target.cdnUrlList = cdnUrlList;
[_trtcCloud startPublishMediaStream:target encoderParam:nil mixingConfig:nil];
// 发布当前用户的音视频流到直播CDN
TRTCPublishTarget target;
target.mode = TRTCPublishMode::TRTCPublishBigStreamToCdn;
TRTCPublishCdnUrl* cdn_url_list = new TRTCPublishCdnUrl[1];
cdn_url_list[0].rtmpUrl = "rtmp://tencent/live/bestnews";
cdn_url_list[0].isInternalLine = true;
target.cdnUrlList = cdn_url_list;
target.cdnUrlListSize = 1;
trtc->startPublishMediaStream(&target, nullptr, nullptr);
delete[] cdn_url_list;
const options = {
target: {
publishMode: PublishMode.PublishMainStreamToCDN
}
}
try {
await trtc.startPlugin('CDNStreaming', options);
} catch (error) {
console.error('CDNStreaming start failed', error);
}
TRTCPublishTarget target = TRTCPublishTarget();
target.mode = TRTCPublishMode.TRTCPublishBigStreamToCdn;
TRTCPublishCdnUrl cdnUrlEntity = new TRTCPublishCdnUrl();
cdnUrlEntity.rtmpUrl = "rtmp://tencent/live/bestnews";
cdnUrlEntity.isInternalLine = true;
target.cdnUrlList.add(cdnUrlEntity);

trtcCloud.startPublishMediaStream(target: target);
注意:
Web 端类名稍有差异,其他使用方式一致,详细信息请参见 CDNStreaming Plugin
Web 4.x 版本转推请参见: Client.startMixTranscode()

步骤2:发布混合后的音视频流到直播 CDN




功能介绍
如果您希望将 TRTC 房间中多个用户的音视频流混合成一路,并将混合后的音视频流发布到直播 CDN 上,可以调用 startPublishMediaStream 接口并同时指定 TRTCStreamEncoderParamTRTCStreamMixingConfig 两个参数对混流和转码的细节进行控制。
由于 TRTC 中的多路音视频流需要先在云端进行解码,然后按照您指定的混流参数(TRTCStreamMixingConfig)进行混合,最终再通过您指定的参数(TRTCStreamEncoderParam)进行二次编码,最终才能合成一路音视频流并发布到直播 CDN 上,因此该模式下的转推服务需要额外的 转码费用
操作指引
您可以按照如下指引将房间中多个用户的音视频流混合并发布到直播 CDN 上。
1. 创建 TRTCPublishTarget 对象,并指定 TRTCPublishTarget 中的 mode 参数为 TRTCPublishMixStreamToCdn
2. 指定 TRTCPublishTarget 对象中的 cdnUrlList 参数为一个或者多个 CDN 推流地址(标准的 CDN 推流地址一般以 rtmp:// 作为 URL 的前缀)。如果您指定的 url 是腾讯云的直播 CDN 推流地址,需要将 isInternalLine 设置为 true,否则请将其设置为 false。
3. 通过参数 TRTCStreamEncoderParam 设置转码后的音视频流的编码参数:
视频编码参数:需要您指定混合后画面的分辨率、帧率、码率和编码的 GOP 大小,其中 GOP 推荐设置为 3s 即可,FPS 推荐设置为 15,码率和分辨率有一定的映射关系,如下表格列出了几种常用的分辨率以及其对应的推荐码率。
视频宽度
视频高度
视频帧率
视频GOP
视频码率
640
360
15
3
800kbps
960
540
15
3
1200kbps
1280
720
15
3
1500kbps
1920
1080
15
3
2500kbps
音频编码参数:需要您指定混合后音频的编码格式、编码码率、采样率和声道数。这一步首先需要您先确认一下您在调用 startLocalAudio 时第二个参数 AudioQuality 所指定的音质类型,然后根据您指定的音质类型填写此处的参数。
TRTC音频质量类型
音频采样率
音频声道数
音频码率
TRTCAudioQualitySpeech
48000
1
50kbps
TRTCAudioQualityDefault
48000
1
50kbps
TRTCAudioQualityMusic
48000
2
60kbps
4. 通过参数 TRTCStreamMixingConfig 设置音频混流参数和画面排版模式:
音频混流参数(audioMixUserList):默认情况下填空值即可,代表会混合房间中的所有音频,如果您只希望混合画面中某几个用户的声音,才需要指定该参数。
画面布局参数(videoLayoutList):画面布局是由一个数组所定义的,数组中的每一个 TRTCVideoLayout 对象都代表了一块区域的位置、大小、背景颜色等等。如果您指定了 TRTCVideoLayout 中的 fixedVideoUser 字段,意味着这个 layout 对象所定义的区域被固定用来显示某个用户的画面。您也可以将 fixedVideoUser 设置为 null,这意味着您只是指定了该区域会有视频画面,但画面中的用户具体是谁是不确定的,交由 TRTC 混流服务器根据一定的规则来决定。
案例:
案例一:四个用户的画面被混合在了同一个画面中,我们还使用了一张图片作为背景画布
layout1:定义了用户 jerry 的摄像头画面的大小和位置,画面大小为 640x480,位置在画面的中上部。
layout2、 layout3、 layout4:均没有指定具体的用户 Id,因此 TRTC 会根据一定的规则选择房间中 3 路用户的画面安置在这三个位置。

案例二:四位用户的摄像头画面和一路屏幕分享的画面被混合在了同一个画面中
layout1:定义了用户 jerry 的屏幕分享画面的大小和位置,画面大小为 1280x720,填充模式为可能有黑边的 Fit 模式,背景填充色为黑色,位置在画面的左侧。
layout2:定义了用户 jerry 的摄像头画面的大小和位置,画面大小为 300x200,填充模式为 Fill 模式,位置在画面的右上角。
layout3、layout4、layout5:均没有指定具体的用户 Id,因此 TRTC 会根据一定的规则选择房间中 3 路用户的画面安置在这三个位置。

5. 调用 startPublishMediaStream 接口,并通过 onStartPublishMediaStream 监听本地 API 调用是否成功,如果成功 onStartPublishMediaStream 中的 taskId 会返回一个不为空字符串。
6. 如果您需要变更混流参数,比如想要调整多个画面的排版模式,您只需要通过第六步中的 taskId 调用 updatePublishMediaStream API 并传递新的 TRTCStreamMixingConfig 参数即可。TRTCStreamEncoderParam 不建议您在转推过程中进行变更,这会影响到 CDN 播放器的稳定性。
7. 如果您需要停止发布动作,只需要调用 stopPublishMediaStream 并传入之前通过 onStartPublishMediaStream 获得 taskId 即可。
参考代码
如下这段代码的功能是将房间中多个用户的音视频流混合并发布到直播 CDN 上:
java
Objective-C
C++
Dart
// 指定发布模式为 TRTC_PublishMixedStream_ToCdn
TRTCCloudDef.TRTCPublishTarget target = new TRTCCloudDef.TRTCPublishTarget();
target.mode = TRTC_PublishMixedStream_ToCdn;
// 指定发布的 CDN 推流地址
TRTCCloudDef.TRTCPublishCdnUrl cdnUrl= new TRTCCloudDef.TRTCPublishCdnUrl();
cdnUrl.rtmpUrl = "rtmp://tencent/live/bestnews";
cdnUrl.isInternalLine = true;
target.cdnUrlList.add(cdnUrl);
// 指定发布模式为 TRTCPublishMixStreamToCdn
TRTCPublishTarget* target = [[TRTCPublishTarget alloc] init];
target.mode = TRTCPublishMixStreamToCdn;
// 指定发布的 CDN 推流地址
TRTCPublishCdnUrl* cdnUrl = [[TRTCPublishCdnUrl alloc] init];
cdnUrl.rtmpUrl = @"rtmp://tencent/live/bestnews";
cdnUrl.isInternalLine = YES;
NSMutableArray* cdnUrlList = [NSMutableArray new];
[cdnUrlList addObject:cdnUrl];
target.cdnUrlList = cdnUrlList;
// 设置混合后的音视频流的二次编码参数
TRTCStreamEncoderParam* encoderParam = [[TRTCStreamEncoderParam alloc] init];
encoderParam.videoEncodedWidth = 1280;
encoderParam.videoEncodedHeight = 720;
encoderParam.videoEncodedFPS = 15;
encoderParam.videoEncodedGOP = 3;
encoderParam.videoEncodedKbps = 1000;
encoderParam.audioEncodedSampleRate = 48000;
encoderParam.audioEncodedChannelNum = 1;
encoderParam.audioEncodedKbps = 50;
encoderParam.audioEncodedCodecType = 0;
// 设置画面的布局参数
TRTCStreamMixingConfig* config = [[TRTCStreamMixingConfig alloc] init];
NSMutableArray* videoLayoutList = [NSMutableArray new];
TRTCVideoLayout* layout1 = [[TRTCVideoLayout alloc] init];
layout1.zOrder = 0;
layout1.rect = CGRectMake(0, 0, 720, 1280);
layout1.fixedVideoStreamType = TRTCVideoStreamTypeSub;
layout1.fixedVideoUser.intRoomId = 1234;
layout1.fixedVideoUser.userId = @"mike";
TRTCVideoLayout* layout2 = [[TRTCVideoLayout alloc] init];
layout2.zOrder = 0;
layout2.rect = CGRectMake(1300, 0, 300, 200);
layout2.fixedVideoStreamType = TRTCVideoStreamTypeBig;
layout2.fixedVideoUser.intRoomId = 1234;
layout2.fixedVideoUser.userId = @"mike";
TRTCVideoLayout* layout3 = [[TRTCVideoLayout alloc] init];
layout3.zOrder = 0;
layout3.rect = CGRectMake(1300, 220, 300, 200);
layout3.fixedVideoStreamType = TRTCVideoStreamTypeSub;
layout3.fixedVideoUser = nil;
[videoLayoutList addObject:layout1];
[videoLayoutList addObject:layout2];
[videoLayoutList addObject:layout3];
config.videoLayoutList = videoLayoutList;
config.audioMixUserList = nil;
// 发起混流
[_trtcCloud startPublishMediaStream:target encoderParam:encoderParam mixingConfig:config];
// 指定发布模式为 TRTCPublishMixStreamToCdn
TRTCPublishTarget target;
target.mode = TRTCPublishMode::TRTCPublishMixStreamToCdn;
// 指定发布的 CDN 推流地址
TRTCPublishCdnUrl* cdn_url = new TRTCPublishCdnUrl[1];
cdn_url[0].rtmpUrl = "rtmp://tencent/live/bestnews";
cdn_url[0].isInternalLine = true;
target.cdnUrlList = cdn_url;
target.cdnUrlListSize = 1;
// 设置混合后的音视频流的二次编码参数
TRTCStreamEncoderParam encoder_param;
encoder_param.videoEncodedWidth = 1280;
encoder_param.videoEncodedHeight = 720;
encoder_param.videoEncodedFPS = 15;
encoder_param.videoEncodedGOP = 3;
encoder_param.videoEncodedKbps = 1000;
encoder_param.audioEncodedSampleRate = 48000;
encoder_param.audioEncodedChannelNum = 1;
encoder_param.audioEncodedKbps = 50;
encoder_param.audioEncodedCodecType = 0;
// 设置画面的布局参数
TRTCStreamMixingConfig config;
TRTCVideoLayout* video_layout_list = new TRTCVideoLayout[3];
TRTCUser* fixedVideoUser0 = new TRTCUser();
fixedVideoUser0->intRoomId = 1234;
fixedVideoUser0->userId = "mike";
video_layout_list[0].zOrder = 0;
video_layout_list[0].rect.left = 0;
video_layout_list[0].rect.top = 0;
video_layout_list[0].rect.right = 720;
video_layout_list[0].rect.bottom = 1280;
video_layout_list[0].fixedVideoStreamType =
TRTCVideoStreamType::TRTCVideoStreamTypeSub;
video_layout_list[0].fixedVideoUser = fixedVideoUser0;
TRTCUser* fixedVideoUser1 = new TRTCUser();
fixedVideoUser1->intRoomId = 1234;
fixedVideoUser1->userId = "mike";
video_layout_list[1].zOrder = 0;
video_layout_list[1].rect.left = 1300;
video_layout_list[1].rect.top = 0;
video_layout_list[1].rect.right = 300;
video_layout_list[1].rect.bottom = 200;
video_layout_list[1].fixedVideoStreamType =
TRTCVideoStreamType::TRTCVideoStreamTypeBig;
video_layout_list[1].fixedVideoUser = fixedVideoUser1;
video_layout_list[2].zOrder = 0;
video_layout_list[2].rect.left = 1300;
video_layout_list[2].rect.top = 220;
video_layout_list[2].rect.right = 300;
video_layout_list[2].rect.bottom = 200;
video_layout_list[2].fixedVideoStreamType =
TRTCVideoStreamType::TRTCVideoStreamTypeSub;
video_layout_list[2].fixedVideoUser = nullptr;
config.videoLayoutList = video_layout_list;
config.videoLayoutListSize = 3;
config.audioMixUserList = nullptr;
// 发起混流
trtc->startPublishMediaStream(&target, &encoder_param, &config);
delete fixedVideoUser0;
delete fixedVideoUser1;
delete[] video_layout_list;
TRTCPublishTarget target = TRTCPublishTarget();
target.mode = TRTCPublishMode.TRTCPublishMixStreamToCdn;
TRTCPublishCdnUrl cdnUrlEntity = new TRTCPublishCdnUrl();
cdnUrlEntity.rtmpUrl = "rtmp://tencent/live/bestnews";
cdnUrlEntity.isInternalLine = true;
target.cdnUrlList.add(cdnUrlEntity);

TRTCStreamMixingConfig config = TRTCStreamMixingConfig();
TRTCUser selfUser = TRTCUser();
selfUser.userId = localUserId;
selfUser.intRoomId = localRoomId;

TRTCVideoLayout selfVideoLayout = TRTCVideoLayout();
selfVideoLayout.fixedVideoStreamType = TRTCVideoStreamType.TRTCVideoStreamTypeBig;
selfVideoLayout.rect = Rect(originX: 0, originY: 0, sizeWidth: 1080, sizeHeight: 1920);
selfVideoLayout.zOrder = 0;
selfVideoLayout.fixedVideoUser = selfUser;
selfVideoLayout.fillMode = TRTCVideoFillMode.TRTCVideoFillMode_Fit;
config.videoLayoutList.add(selfVideoLayout);TRTCUser remoteUser = TRTCUser();

remoteUser.userId = remoteUserId;
remoteUser.intRoomId = remoteRoomId;
TRTCVideoLayout remoteVideoLayout = TRTCVideoLayout();
remoteVideoLayout.fixedVideoStreamType = TRTCVideoStreamType.TRTCVideoStreamTypeBig;
remoteVideoLayout.rect = Rect(originX: 100, originY: 50, sizeWidth: 216, sizeHeight: 384);
remoteVideoLayout.zOrder = 1;
remoteVideoLayout.fixedVideoUser = remoteUser;
remoteVideoLayout.fillMode = TRTCVideoFillMode.TRTCVideoFillMode_Fit;
config.videoLayoutList.add(remoteVideoLayout);

TRTCStreamEncoderParam param = TRTCStreamEncoderParam();
param.videoEncodedWidth = 1080;
param.videoEncodedHeight = 1920;
param.videoEncodedKbps = 5000;
param.videoEncodedFPS = 30;
param.videoEncodedGOP = 3;
param.audioEncodedSampleRate = 48000;
param.audioEncodedChannelNum = 2;
param.audioEncodedKbps = 128;
param.audioEncodedCodecType = 2;

trtcCloud.startPublishMediaStream(target: target, config: config, params: param);

方案二:Restful API 发起转推

下文将介绍如何使用 REST API 将 TRTC 房间中的音视频流发布(也称作“转推”)到直播 CDN 上或者回推 TRTC 房间,用于兼容常规的直播播放器进行观看。

功能支持

使用 Restful API 发起转推任务时,可以实现下面功能:
将单路音视频流转推到直播 CDN 和 TRTC 房间。
将多路音视频流混成一路新的流转推到直播 CDN 和 TRTC 房间。
支持输出纯音频和音视频。
支持自定义布局和动态模板。
支持设置背景图、占位图和水印图片。
支持对输入视频和图片进行裁剪和缩放。
支持在混合的音视频流中增加 SEI 信息。

原理解析

云端混流包含进房、拉流、解码、混合、编码、转推六个过程:
进房:通过您指定的机器人信息,MCU 会创建伴生机器人实例进房。
拉流:根据您指定的混流布局参数,MCU 机器人会拉取相关用户的音视频流。
解码:MCU 会将多路音视频流进行解码,包括视频解码和音频解码。
混合:MCU 会根据您指定的混流布局参数,将多路画面混合在一起。同时,MCU 也会将解码后的多路音频信号进行混音处理。
编码:MCU 会将混合后的画面和声音,根据您配置的输出编码参数进行二次编码,并封装成一路音视频流。
转推:MCU 会将编码封装的音视频数据,分发给您配置的直播 CDN。




启动转推任务

由您的服务器调用 REST API StartPublishCdnStream 可以启动云端转推任务。发起方法如下:
1. 设置基本参数(必需)
您需要指定发起转推任务的基本信息,比如您的应用 ID(sdkappid)、主房间信息(RoomId)、主房间类型(RoomIdType)、是否转码(WithTranscoding)。您可以通过设置 WithTranscoding,决定是否转码,若 WithTranscoding 设置为true,则为混流转推,若设置为false,则为旁路转推。
字段名称
描述
必选
SdkAppId
TRTC 的 SdkAppId
RoomId
主房间信息 RoomId
RoomIdType
主房间信息 RoomType
WithTranscoding
是否转码
2. 设置机器人参数(必需)
您需要指定进房机器人的 AgentParams 参数,MCU 会根据您指定的参数创建实例进房。
字段名称
描述
必选
AgentParams.UserId
转推服务在 TRTC 房间使用的 UserId,请勿与房间内正常用户使用的 UserId 一致
AgentParams.UserSig
转推服务加入TRTC房间的用户签名
AgentParams.MaxIdleTime
空闲等待时间
3. 设置音频参数(混流输出音频流时必需,单流转推时无需填写)
如果您需要混流输出音频流,您需要指定 AudioParams 参数。MCU 会根据您配置的 AudioEncode,MCU 会输出对应格式的音频流。您可以配置 SubscribeAudioList 指定混流哪些用户的音频。
字段名称
描述
必选
AudioParams.AudioEncode
混流-音频输出编码参数
AudioParams.SubscribeAudioList
混流-音频用户白名单
具体含义可以参见 McuAudioParams 中参数介绍。
4. 设置视频参数(混流输出视频流时,单流转推时无需填写)
如果您需要混流输出视频流,您需要指定 VideoParams 参数。MCU 会根据您配置的 VideoEncode,输出对应格式的视频流。
您可以配置 LayoutParams,指定您需要的画面布局。
您可以配置 BackGroundColor,指定您需要的画布背景色。
您可以配置 BackgroundImageUrl,指定您需要的画布背景图。
您可以配置 WaterMarkList,指定您需要的水印布局。
字段名称
描述
必选
VideoParams.VideoEncode
混流-视频输出编码参数
VideoParams.LayoutParams
混流-布局参数
VideoParams.BackGroundColor
混流-画布背景颜色
VideoParams.BackgroundImageUrl
混流-画布背景图 URL
VideoParams.WaterMarkList
混流-水印参数
具体含义可以参见 McuVideoParams 中参数介绍。
混流布局类型介绍
VideoParams.LayoutParams.MixLayoutMode 存在四种布局模式,您可以根据您的需求选择一种布局。
动态布局(1:悬浮布局,2:屏幕分享布局,3:九宫格布局),静态布局(4:自定义布局(默认))。
悬浮布局
第一个进入房间的用户的视频画面会铺满整个屏幕,其他用户的视频画面从左下角依次水平排列,显示为小画面。最多4行,每行最多4个,小画面悬浮于大画面之上。最多支持1个大画面和15个小画面。如果用户只发送音频,仍然会占用画面位置。
屏幕分享布局
适合视频会议和在线教育场景的布局。屏幕分享(或者主讲的摄像头)始终占据屏幕左侧的大画面位置,其他用户依次垂直排列于右侧。需要通过 VideoParams.LayoutParams.MaxVideoUser 参数来指定左侧主画面的内容。最多两列,每列最多8个小画面。最多支持1个大画面和15个小画面。如果用户只发送音频,仍然会占用画面位置。
九宫格布局
所有用户的视频画面大小一致,平分整个屏幕,人数越多,每个画面的尺寸越小。最多支持16个画面,如果用户只发送音频,仍然会占用画面位置。
自定义布局(默认)
适用于需要自定义排布各路画面位置的场景,您可以通过 VideoParams.LayoutParams 中的 MixLayoutList 参数(这是一个数组),预先设置各路画面的位置。您可以不指定 MixLayoutList 参数中的 UserId 参数,排版引擎会根据进房的先后顺序,将进房的用户依次分配到 MixLayoutList 数组中指定的各个位置上。
如果 MixLayoutList 数组中的某一个被指定了 UserId 参数,则排版引擎会预先给指定的用户预留好他/她在画面中的位置。如果用户只上行音频,不上行视频,该用户依然会占用画面位置。
当 MixLayoutList 数组中预设的位置被用完后,排版引擎将不再混合其他用户的画面和声音。
具体含义可以参见 McuAudioParams 中参数介绍。
5. 设置单流旁路用户信息(单流旁路转推,必需)
如果您需要单流旁路转推,此时 WithTranscoding 您应该设置成 false,同时您需要指定 SingleSubscribeParams 参数 MCU 会将您指定用户的音视频流分发到您指定的直播 CDN。
字段名称
描述
必选
SingleSubscribeParams.UserMediaStream.UserInfo
TRTC 用户参数
SingleSubscribeParams.UserMediaStream.StreamType
主辅路流类型
具体含义可以参见 McuAudioParams 中参数介绍。
6. 设置转推 CDN 参数(转推 CDN 时填写 )
您需要指定分发 CDN 的 PublishCdnParams 参数 ,MCU 会将编码后的音视频流转发到您设置的 CDN 地址。
字段名称
描述
必选
PublishCdnParams.N.PublishCdnUrl
CDN 转推 URL
PublishCdnParams.N.IsTencentCdn
是否是腾讯云 CDN,0为转推非腾讯云 CDN,1为转推腾讯 CDN,不携带该参数默认为1。
注意:
1:为避免误产生转推费用,该参数建议明确填写,转推非腾讯云 CDN 时会产生转推费用,详情参见接口文档说明
2:国内站默认只支持转推腾讯云 CDN,如您有转推第三方 CDN 需求,请联系腾讯云技术支持
7. 设置回推 TRTC 房间参数(回推 TRTC 房间时填写)
您需要指定回推 TRTC 房间 的 FeedBackRoomParams 参数, MCU 会将编码后的音视频流转发到您设置的 TRTC 房间。
字段名称
描述
必选
FeedBackRoomParams.N.RoomId
回推房间的RoomId
FeedBackRoomParams.N.RoomIdType
回推房间的类型,0为整形房间号,1为字符串房间号
FeedBackRoomParams.N.UserId
回推房间使用的UserId
注意:
这个UserId不能与其他TRTC或者转推服务等已经使用的UserId重复,建议可以把房间ID作为UserId的标识的一部分。
FeedBackRoomParams.N.UserSig
回推房间UserId对应的用户签名,具体计算方法请参考TRTC计算 UserSig 的方案。
8. 设置 SEI 参数(非必需)
您需要指定 音量布局 SEI 或 透传 SEI 参数,MCU 会在输出视频流中插入对应的 SEI 信息。
字段名称
描述
必选
McuSeiParams.LayoutVolume
音量布局 SEI,内容是固定的 JSON 结构,具体见下面说明。
McuSeiParams.PassThrough
透传 SEI。
插入的音量布局SEI示例如下,其中 app_data 为传入的透传数据,canvas 为输出画布的宽高,regions 为布局信息,布局的 volume 为混流用户的音量(范围为0-100),值越大表示音量越大,ts为服务器本地秒级时间戳,ver可以忽略。
{
"app_data": "test",
"canvas": {
"w": 1280,
"h": 720
},
"regions": [
{
"uid": "test1",
"zorder": 1,
"volume": 60,
"x": 0,
"y": 0,
"w": 640,
"h": 360
},
{
"uid": "test2",
"zorder": 1,
"volume": 80,
"x": 640,
"y": 0,
"w": 640,
"h": 360
}
],
"ver": "1.0",
"ts": 1648544726
}

更新转推任务

由您的服务器调用 REST API UpdatePublishCdnStream 可以更新云端转推任务。此时,您需要使用 StartPublishCdnStream 返回的 TaskId,发起更新转推。对于此 API 使用方法,您可以参见 启动转推任务

停止转推任务

由您的服务器调用 REST APIStopPublishCdnStream 可以更新云端转推任务。此时,您需要使用StartPublishCdnStream返回的TaskId,发起停止转推。
字段名称
描述
SdkAppId
TRTC 的 SdkAppId
TaskId
转推任务唯一的 String Id

方案三:自动旁路发起转推

除了上面通过接口触发旁路的方式,TRTC 还提供了无需手动发起的自动旁路方案。使用自动旁路方案时,TRTC 房内的主播上行音视频后,会自动发起旁路任务,将主播的单路流推到 CDN ,主播退房后旁路任务自动结束。

前提条件

需开通腾讯 云直播 服务,云直播播放必须配置播放域名,具体操作请参见 添加自有域名
1. 
登录
云直播控制台
2. 在左侧导航栏选择域名管理,您会看到在您的域名列表新增了一个推流域名,格式为 xxxxx.livepush.myqcloud.com,其中 xxxxx 是一个数字,叫做 bizid。
3. 单击添加域名,输入您已经备案过的播放域名,选择域名类型为播放域名,选择加速 区域,单击确定即可。
4. 域名添加成功后,系统会为您自动分配一个 CNAME 域名(以.liveplay.myqcloud.com为后缀)。CNAME 域名不能直接访问,您需要在域名服务提供商处完成 CNAME 配置,配置生效后,即可享受云直播服务。具体操作请参见 CNAME 配置



注意:
不需要添加推流域名,在 步骤1 中开启旁路直播功能后,腾讯云会默认在您的云直播控制台中增加一个格式为 xxxxx.livepush.myqcloud.com 的推流域名,该域名为腾讯云直播服务和 TRTC 服务之间约定的一个默认推流域名。

全局自动旁路

旁路转推配置开启全局自动旁路后,TRTC 房间内的主播,只要上行音视频就会触发自动旁路。



开启全局自动旁路后,也可以通过前面所述的进房参数指定推到直播的自定义流 ID ,如果没有指定,系统会生成一个缺省的流 ID,生成规则如下:
拼装流 ID 用到的字段
SDKAppID:您可以在 控制台 > 应用管理 > 应用信息中查找到。
bizid:您可以在 控制台 > 应用管理 > 应用信息中查找到。
roomId:由您在 enterRoom 函数的参数 TRTCParams 中指定。
userId:由您在 enterRoom 函数的参数 TRTCParams 中指定。
streamType: 摄像头画面为 main,屏幕分享为 aux (WebRTC 由于同时只支持一路上行,因此 WebRTC 上屏幕分享的流类型是 main)。
拼装流 ID 的计算规则
拼装
2020年01月09日及此后新建的应用
2020年01月09日前创建且使用过的应用
拼装规则
streamId = urlencode(sdkAppId_roomId_userId_streamType)
streamId = bizid_MD5(roomId_userId_streamType)
计算样例
例如:sdkAppId = 12345678,roomId = 12345,userId = userA,用户当前使用了摄像头。那么:streamId = 12345678_12345_userA_main
例如:bizid = 1234,roomId = 12345,userId = userA,用户当前使用了摄像头。那么:streamId = 1234_MD5(12345_userA_main) = 1234_8D0261436C375BB0DEA901D86D7D70E8

实现拉流播放与优化

给 SDK 配置 License 授权

实时音视频(TRTC)SDK 提供了功能全面性能强大的直播播放能力,可轻松配合云直播实现 CDN 直播观看功能。若您使用移动端(iOS&Android)10.1及其之后版本的实时音视频(TRTC)SDK 实现 CDN 直播观看,则须配置 License 授权,否则可跳过本步骤。
1. 获取 License 授权:
若您已获得相关 License 授权,需在 云直播控制台 获取 License URL 和 License Key。

若您暂未获得 License 授权,需先参考 新增与续期 License 进行申请。
2. 在您的 App 调用 SDK 相关功能之前(建议在 Application / - [AppDelegate application:didFinishLaunchingWithOptions:]中)进行如下设置:
Android
iOS
public class MApplication extends Application {

@Override
public void onCreate() {
super.onCreate();
String licenceURL = ""; // 获取到的 licence url
String licenceKey = ""; // 获取到的 licence key
V2TXLivePremier.setLicence(this, licenceURL, licenceKey);
V2TXLivePremier.setObserver(new V2TXLivePremierObserver() {
@Override
public void onLicenceLoaded(int result, String reason) {
Log.i(TAG, "onLicenceLoaded: result:" + result + ", reason:" + reason);
}
});
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSString * const licenceURL = @"<获取到的licenseUrl>";
NSString * const licenceKey = @"<获取到的key>";

// V2TXLivePremier 位于 "V2TXLivePremier.h" 头文件中
[V2TXLivePremier setLicence:licenceURL key:licenceKey];
[V2TXLivePremier setObserver:self];
NSLog(@"SDK Version = %@", [V2TXLivePremier getSDKVersionStr]);
return YES;
}

#pragma mark - V2TXLivePremierObserver
- (void)onLicenceLoaded:(int)result Reason:(NSString *)reason {
NSLog(@"onLicenceLoaded: result:%d reason:%@", result, reason);
}
@end

注意:
License 中配置的 packageName/BundleId 必须和应用本身一致,否则会播放失败。

获取播放地址并对接播放

当您完成的推流操作后,即可得到直播的播放地址,播放地址的标准格式为:
http://播放域名/AppName(默认live)/StreamName(流ID).flv
您的播放域名、AppName和StreamName可前往 云直播控制台 中流管理进行查看:



您可以得到三路播放地址:
rtmp 协议的播放地址:rtmp://example.myhost.com/AppName_example/StreamName_example
flv 协议的播放地址:http://example.myhost.com/AppName_example/StreamName_example.flv
hls 协议的播放地址:http://example.myhost.com/AppName_example/StreamName_example.m3u8

优化播放延时

开启旁路直播后的 http - flv 地址,由于经过了直播 CDN 的扩散和分发,观看时延肯定要比直接在 TRTC 直播间里的通话时延要高。 按照目前腾讯云的直播 CDN 技术,如果配合 V2TXLivePlayer 播放器,可以达到下表中的延时标准:
旁路流类型
V2TXLivePlayer 的播放模式
平均延时
实测效果
独立画面
极速模式(推荐)
2s - 3s
下图中左侧对比图(橙色)
混合画面
极速模式(推荐)
4s - 5s
下图中右侧对比图(蓝色)
如果您在实测中延时比上表中的更大,可以按照如下指引优化延时:
使用 TRTC SDK 自带的 V2TXLivePlayer 普通的 ijkplayer 或者 ffmpeg 基于 ffmpeg 的内核包装出的播放器,缺乏延时调控的能力,如果使用该类播放器播放上述直播流地址,时延一般不可控。V2TXLivePlayer 有一个自研的播放引擎,具备延时调控的能力。
设置 V2TXLivePlayer 的播放模式为极速模式 可以通过设置 V2TXLivePlayer 的参数来实现极速模式,以 iOS 为例:
//自动模式
[_txLivePlayer setCacheParams:1 maxTime:5];
//极速模式
[_txLivePlayer setCacheParams:1 maxTime:1];
//流畅模式
[_txLivePlayer setCacheParams:5 maxTime:5];

//设置完成之后再启动播放

相关费用

使用 CDN 直播观看需要云直播服务资源和终端 SDK 直播播放能力的配合,可能会产生以下费用。

实时音视频费用

混流转码费用:如果使用 发布混合后的音视频流到直播 CDN 的方式,将会产生混流费用,费用说明请见 云端混流转码计费说明。如使用 发布当前用户的音视频流(单流)到直播 CDN 将不会产生此部分费用。
旁路转推费用:费用详情请见 转推计费说明
音视频时长费用:音视频时长费用将根据房间内用户订阅实际产生的音视频费用正常收取,详情请见 音视频时长计费说明

其他云服务费用

CDN 直播观看需要使用云直播的资源进行直播分发。云直播的费用主要包括基础服务费用和增值服务费用:基础服务主要是直播推流/播放产生的消耗;增值服务是直播过程中,使用增值服务产生的消耗。
注意:
本文中的价格为示例,仅供参考。最终价格与计费策略请以 云直播 的计费说明为准。
基础服务费用: 将 TRTC 的内容旁路到云直播 CDN 观看时,云直播会收取观众观看产生的下行流量/带宽费用,可以根据实际需要选择适合自己的计费方式,默认采用流量计费,详情请参见 云直播 > 标准直播 > 流量带宽 计费说明。
增值服务费用: 如果您使用了云直播的转码、录制、云导播等功能,会产生对应额外的增值服务费用。增值服务可按需使用进行付费。

费用的节约

基于客户端 SDK API 混流方案下,要停止后端混流任务,需要满足如下条件之一:
发起混流任务(即调用 startPublishMediaStream)的主播退出了房间
调用 stopPublishMediaStream 主动停止混流
在其他情况下,TRTC 云端都将会尽力持续保持混流状态。因此,为避免产生预期之外的混流费用,请在您不需要混流的时候尽早通过上述方法结束云端混流。

常见问题

1、能否监听 CDN 流的当前状态,状态异常时该如何处理?

可通过监听 onCdnStreamStateChanged 回调获取最新的后台任务状态更新回调。更多细节可参考 API 文档。

2、如何从单路转推切换到混流转推,是否需要手动停止再重新创建转推任务?

可以从单路推流任务直接切换到混流任务,只需对单路推流任务 taskid 发起 updatePublishMediaStream 推流任务变更即可。但是为了确保推流链接稳定,从单路推流切换到混流推流无法切换到纯音频或者纯视频模式。单路推流默认是音视频模式,切换后的混流也需要是音视频模式。

3、如何实现纯视频混流?

配置混流设置时,TRTCStreamEncodeParam 里音频相关参数不要设置且 TRTCStreamMixingConfigaudioMixUserList 设置为空。

4、可以给混流画面添加水印吗?

可以,可通过 TRTCStreamMixingConfigwatermarkList 进行设置。更多细节可参考 API 文档。

5、可以混流屏幕分享内容么,我们是做教培的,需要混流转推老师的屏幕分享画面?

可以的。建议您使用辅路推送屏幕分享画面,然后发起将老师的摄像头画面与屏幕分享画面配置到同一个混流任务内。设置混流辅路时只需配置 TRTCVideoLayoutfixedVideoStreamTypeTRTCVideoStreamTypeSub 即可。

6、预设排版模式下,音频流的混合时怎么确定的?

使用预设排版模式的时候,混流里的音频将会从当前房间内所有上行音频里选取最多16路音频进行混合。