旁路推流
更新时间: 2024/08/05 15:02:55
NERTC SDK 支持云端音视频混流和 RTMP 旁路推流,可以将实时音视频流转为标准直播流,并将其从网易云信实时音视频云推送到第三方 CDN(Content Delivery Network)或网易云信直播服务,这一过程称为旁路推流。
推流到 CDN 后,基于 CDN 的大规模内容分发,观众可通过 URL 拉流地址使用播放器或 Web 端浏览器直接在线观看直播,也可以加入音视频房间进行实时连麦。当房间中有多个主播时,需要将多个直播流组合成单流,并设置直播流的推流布局,在直播画布中显示主播们的实时画面。
您可以选择通过客户端或者服务端实现旁路推流,服务端推流方法请参考旁路推流。移动客户端受限于网络状况,为保证直播的稳定性,建议通过服务端实现旁路推流。本文档为您展示通过客户端进行旁路推流的实现方式。
功能描述
在您的项目中实现音视频通话后,可以将房间画面进行旁路推流。网易云信可以将您的媒体流在云端进行混流、转码、合图等操作,生成一路视频流,并将其推送到网易云信直播服务中进行大规模内容分发,以便观众直播观看。
- 房间内首个终端加入自动触发转码,房间内无终端时停止转码。
- 直播流支持 RTMP 协议。
前提条件
- 主播端推流之前,需要通过
setChannelProfile
接口设置房间模式为直播模式。 - 发起推流任务之前,请确认您已实现音视频通话的相关流程。详细信息请参考接入流程。
注意事项
- 同一个音视频房间(即同一个 channelId)可以创建 6 个不同的推流任务。
- 客户端 SDK 添加推流任务时,默认将创建推流任务的用户 UID 设置为主播角色。互动直播场景中,仅支持解析主播端的 SEI 自定义数据。
- 自 V4.4.0 版本起,初始化之前无需通过
setParameters
接口打开推流开关。加入房间并创建推流任务后,房间中所有成员的音视频流均可以进行混流并推流至 CDN。 - 移动客户端互动直播受限于网络状况,为保证直播的稳定性,推荐通过服务端实现旁路推流。
实现方式
在项目中实现音视频通话后,可以调用 addLiveStreamTask
接口创建推流任务,并配置推流画面布局,将房间画面进行旁路推流。
创建推流任务相关参数说明如下:
- 通过
taskInfo
参数设置推流任务信息,包括推流任务基本信息、推流布局、 音视频流配置等。 - 通过
addLiveTaskCallback
参数设置操作结果回调。
配置推流任务信息
创建推流任务时,需要配置任务 ID、推流地址等推流参数信息,还可以开启直播视频录制。
录制的视频默认存储在点播服务中,您可以通过点播的相关接口查看并下载视频文件。详细信息请参考点播媒资管理。
推流任务信息相关参数说明如下:
参数名称 | 描述 |
---|---|
taskId | 自定义的推流任务 ID。请保证此 ID 唯一。字母数字下划线组成的 64 位以内的字符串。 |
url | 推流地址,例如 rtmp://test.url 。此处的推流地址可设置为网易云信直播产品中服务端 API 创建直播频道的返回参数 pushUrl 。 |
serverRecordEnabled | 旁路推流是否需要进行音视频录制。 |
liveMode |
直播推流模式。可设置为:
|
配置推流布局
通过 layout
参数可以设置互动直播的画面布局,即自定义房间画面的各路视频布局方式,例如调整整体画布大小和颜色、各路视频的图像大小、位置等。
布局参数的配置方式及典型配置示例请参考旁路推流画面布局。
参数名称 | 描述 |
---|---|
width | 整体画布的宽度,单位为 px。 取值范围为 0 ~ 1920,若设置为奇数值,会自动向下取偶。 |
height | 整体画布的高度,单位为 px。 取值范围为 0 ~ 1920。若设置为奇数值,会自动向下取偶。 |
backgroundColor |
画面背景颜色,默认为 0,即黑色。支持以下格式的颜色码:
|
backgroundImgList | 设置直播成员离线后的占位图片。字段相关具体说明请参考 NERtcLiveStreamImageInfo 。
|
userTranscodingList | 设置直播成员的画面布局。字段相关具体说明请参考 NERtcLiveStreamUserTranscoding 。 |
音视频流配置
config
参数用于配置音视频流编码参数等设置。
参数名称 | 描述 |
---|---|
interruptedPlaceImage | 参数设置为 true 表示开启主播占位图模式,开启后,通过 layout 的 backgroundImgList 字段设置的占位图列表才可生效。 |
singleVideoPassThrough | 参数设置为 true 表示开启视频透传,开启后,如果房间中只有一路视频流输入,则不对输入视频流进行转码,不遵循转码布局,直接推流 CDN。视频透传默认为关闭状态。 |
audioBitrate | 音频推流码率。单位为 kbps,取值范围为 10 ~ 192。语音场景建议设置为 64 及以上码率,音乐场景建议设置为 128 及以上码率。 |
sampleRate | 音频推流采样率。默认值为 NERtcLiveStreamAudioSampleRate48000 ,即 48K。 |
channels | 音频推流声道数,默认为 2,即双声道。 |
codecProfile | 音频编码规格,默认为 1,即 LC-AAC 规格,表示基本音频编码规格。 |
API 参考
方法 |
功能 |
---|---|
NERtcEx#addLiveStreamTask() |
添加房间推流任务。 |
NERtcEx#updateLiveStreamTask() |
更新修改房间推流任务。 |
NERtcEx#removeLiveStreamTask() |
删除房间推流任务。 |
事件 |
描述 |
---|---|
onAddLiveStreamTask |
通知添加直播任务结果。 |
onUpdateLiveStreamTask |
通知更新直播任务结果。 |
onDeleteLiveStreamTask |
通知删除直播任务结果。 |
onLiveStreamState |
通知直播推流状态。 |
示例代码
java// 加入房间前设置直播模式
// 0 - COMMUNICATION(通信模式), 1 - LIVE_BROADCASTING(直播模式)
NERtcEx.getInstance().setChannelProfile(1);
// 加入房间后添加推流任务
// 初始化推流任务
NERtcLiveStreamTaskInfo liveTask1 = new NERtcLiveStreamTaskInfo();
//taskID 可选字母、数字,下划线,不超过64位
liveTask1.taskId = String.valueOf(Math.abs(pushUlr.hashCode()));
// 设置推互动直播推流地址,一个推流任务对应一个推流房间
liveTask1.url = pushUlr;
// 设置是否进行互动直播录制,请注意与音视频通话录制区分。
liveTask1.serverRecordEnabled = false;
// 设置推音视频流还是纯音频流
liveTask1.liveMode = enableVideo? NERtcLiveStreamTaskInfo.NERtcLiveStreamMode.kNERtcLsModeVideo : NERtcLiveStreamTaskInfo.NERtcLiveStreamMode.kNERtcLsModeAudio;
//设置整体布局
NERtcLiveStreamLayout layout = new NERtcLiveStreamLayout();
layout.userTranscodingList = new ArrayList<>(); //设置成员数组
layout.backgroundImgList = new ArrayList<>(); //设置占位图数组
layout.width = 720;//整体布局宽度
layout.height = 1280;//整体布局高度
layout.backgroundColor = Color.parseColor("#3399ff"); // 整体背景色
liveTask1.layout = layout;
// 设置直播成员布局
NERtcLiveStreamUserTranscoding user1 = new NERtcLiveStreamUserTranscoding();
user1.uid = uid1; // 用户id
user1.audioPush = true; // 推流是否发布user1 的音频
user1.videoPush = enableVideo; // 推流是否发布user1的视频
if (user1.videoPush) {// 如果发布视频,需要设置一下视频布局参数
// user1 视频的缩放模式, 详情参考NERtcLiveStreamUserTranscoding 的API 文档
user1.adaption = NERtcLiveStreamUserTranscoding.NERtcLiveStreamVideoScaleMode.kNERtcLsModeVideoScaleCropFill;
user1.x = 10; // user1 的视频布局x偏移,相对整体布局的左上角
user1.y = 10; // user1 的视频布局y偏移,相对整体布局的左上角
user1.width = 180; // user1 的视频布局宽度
user1.height = 320; // user1 的视频布局高度
user1.zOrder = 1; //user1 的视频帧的图层编号
}
layout.userTranscodingList.add(user1);
...
// 设置第n位直播成员布局
NERtcLiveStreamUserTranscoding usern = new NERtcLiveStreamUserTranscoding();
usern.uid = uidn;
usern.audioPush = true;
usern.videoPush = enableVideo;
if (usern.videoPush) {
usern.adaption = NERtcLiveStreamUserTranscoding.NERtcLiveStreamVideoScaleMode.kNERtcLsModeVideoScaleCropFill;
usern.x = user1.x + user1.width + 10;
usern.y = user1.y + user1.height + 10;
usern.width = 320;
usern.height = 640;
usern.zOrder = 1;
}
layout.userTranscodingList.add(usern);
//设置音视频流编码参数
NERtcLiveConfig config = new NERtcLiveConfig();
//设置是否开启直播成员离线时的占位图
config.interruptedPlaceImage = false;
//设置是否开启单路视频透传开关
config.singleVideoPassThrough = false;
//设置音频推流码率
config.audioBitrate = 192 kbps;
//设置音频推流采样率
config.sampleRate = NERtcLiveStreamAudioSampleRate48000;
//设置音频编码规格
config.audioCodecProfile= NERtcLiveStreamAudioCodecProfileLCAAC;
//设置音频推流声道数
config.channels = 2;
layout.config = config;
//设置占位图片布局(可选)
NERtcLiveStreamImageInfo image1 = new NERtcLiveStreamImageInfo();
image1.url = url; //image1的URL
image1.x = 10; //image1的布局x偏移,相对整体布局的左上角
image1.y = 10; //image1的布局y偏移,相对整体布局的左上角
image1.width = 180;//image1的宽度
image1.height = 320;//image1的高度
image1.zOrder = 0;//image1的图层编号
layout.backgroundImgList.add(image1);
...
//设置第n张占位图片布局
NERtcLiveStreamImageInfo imagen = new NERtcLiveStreamImageInfo();
imagen.url = url;//不同的占位图需要设置不同的url
imagen.x = image1.x + image1.width + 10;
imagen.y = image1.y + image1.height + 10;
imagen.width = 320;
imagen.height = 640;
imagen.zOrder = 0;//不同占位图可以设置不同的zOrder
layout.backgroundImgList.add(imagen);
// 调用 addLiveStreamTask 接口添加推流任务
int ret = NERtcEx.getInstance().addLiveStreamTask(liveTask1, addLiveTaskCallback);
if (ret != 0) {
showToast("调用添加推流任务接口执行失败 , ret : " + ret);
}
// 添加推流任务的异步callback
private AddLiveTaskCallback addLiveTaskCallback = new AddLiveTaskCallback(){
void onAddLiveStreamTask(String taskId, int errCode){
if (code == RtcCode.LiveCode.OK) {
showToast("添加推流任务成功 : taskId " + taskId);
} else {
showToast("添加推流任务失败 : taskId " + taskId + " , code : " + code);
}
}
};