旁路推流画面布局
更新时间: 2024/08/05 15:02:55
配置旁路推流任务时,您可以自定义房间画面的各路视频布局方式,例如调整整体画布大小和颜色、各路视频的图像大小、位置等。本文档为您介绍视频布局的设置方式和推荐设置。
限制说明
- 用户窗口边界不能超出整体画布。
- 视频互动的画面布局中,最多 7 人参与;纯语音互动最多 21 人。
设置画面布局
您可以选择通过客户端的旁路推流相关接口的 layout
参数或者服务端 RESTful API 设置旁路推流的画面布局,服务端设置旁路推流画面布局的方法请参考旁路推流画面布局。移动客户端受限于网络状况,为保证直播的稳定性,建议通过服务端实现旁路推流。本文档为您展示通过客户端设置旁路推流画面布局的实现方式。
背景画布
通过 NERtcLiveStreamLayout
类的 width
等字段设置混流视频的背景画布属性,包括背景画布的高度、宽度和颜色。
相关字段的具体说明如下:
参数名称 |
类型 |
描述 |
---|---|---|
width | int | 整体画布的宽度,单位为 px。 取值范围为 0 ~ 1920,若设置为奇数值,会自动向下取偶。 |
height | int | 整体画布的高度,单位为 px。 取值范围为 0 ~ 1920。若设置为奇数值,会自动向下取偶。 |
backgroundColor |
int |
画面背景颜色,默认为 0,即黑色。支持以下格式的颜色码:
|
用户画面
通过 NERtcLiveStreamLayout
类的 userTranscodingList
参数设置混流视频中每个参与者对应的画面属性。
参数名称 |
类型 |
描述 |
---|---|---|
uid | long | 将指定uid对应用户的视频流拉入直播。 如果添加多个 users,则 uid 不能重复。 |
x | int | 通过 x 和 y 指定画布坐标中的一个点,该点将作为用户图像的左上角。 x 参数用于设置画布的横轴坐标值。 取值范围为 0 ~ 1920,若设置为奇数值,会自动向下取偶。 |
y | int | 通过 x 和 y 指定画布坐标中的一个点,该点将作为用户图像的左上角。 y 参数用于设置画布的纵轴坐标值。 取值范围为 0 ~ 1920,若设置为奇数值,会自动向下取偶。 |
zOrder |
int |
直播视频上用户视频帧的图层编号。取值范围为 0 ~ 100。
|
width | int | 该用户图像在画布中的宽度。 取值范围为 0 ~ 1920,若设置为奇数值,会自动向下取偶。 |
height | int | 该用户图像在画布中的高度。 取值范围为 0 ~ 1920,若设置为奇数值,会自动向下取偶。 |
adaption |
int |
用于设置占位图片和指定区域的适应属性。可设置为:
|
audioPush |
boolean |
是否在直播中混流该用户的对应音频流。可设置为:
|
videoPush |
boolean |
是否在直播中向观看者播放该用户的对应视频流。可设置为:
|
占位图片
通过 NERtcLiveStreamLayout
类的 backgroundImgList
参数设置混流视频中的占位图片属性,包括占位图片的位置、URL 等。
- 支持设置最多 6 张图片。
- 您可以通过
NERtcLiveConfig
的interruptedPlaceImage
字段选择是否开启此功能;若设置为关闭,则此设置无效。 - 原
backgroundImg
字段仍保留,仅可设置一张占位图,若您同时配置了两个字段,则以backgroundImgList
配置为准。
参数名称 | 类型 | 描述 |
---|---|---|
url | String | 占位图片的 URL。 |
x | int | 通过 x 和 y 指定画布坐标中的一个点,该点将作为占位图片的左上角。 x 参数用于设置画布的横轴坐标值。 取值范围为 0 ~ 1920,若设置为奇数值,会自动向下取偶。 |
y | int | 通过 x 和 y 指定画布坐标中的一个点,该点将作为占位图片的左上角。 y 参数用于设置画布的纵轴坐标值。 取值范围为 0 ~ 1920,若设置为奇数值,会自动向下取偶。 |
width | int | 该占位图片在画布中的宽度。 取值范围为 0 ~ 1920,若设置为奇数值,会自动向下取偶。 |
height | int | 该占位图片在画布中的高度。 取值范围为 0 ~ 1920,若设置为奇数值,会自动向下取偶。 |
zOrder |
int |
该占位图的图层编号。取值范围为 0 ~ 100。
|
布局示例
本节为您介绍如何通过客户端 addLiveStreamTask
接口的 layout
参数设置旁路推流画面布局。
请求示例
在本示例中,音视频房间中有两位主播/连麦者,其推流画面布局为横向左右均分布局,原始画布为深灰色,其中一人掉线后显示占位图片。
示例代码如下:
// 加入房间前设置直播模式
NERtcEx.getInstance().setChannelProfile(1);
// 加入房间后添加推流任务
NERtcLiveStreamTaskInfo liveTask1 = new NERtcLiveStreamTaskInfo();
liveTask1.taskId = String.valueOf(Math.abs(rtmp://test.url.hashCode()));
liveTask1.url = rtmp://test.url;
liveTask1.serverRecordEnabled = false;
liveTask1.liveMode = enableVideo? NERtcLiveStreamTaskInfo.NERtcLiveStreamMode.kNERtcLsModeVideo;
//设置整体布局
NERtcLiveStreamLayout layout = new NERtcLiveStreamLayout();
layout.userTranscodingList = new ArrayList<>();
layout.backgroundImgList = new ArrayList<>();
layout.width = 720;
layout.height = 640;
layout.backgroundColor = Color.parseColor(#8421504);
liveTask1.layout = layout;
// 设置左边成员布局
NERtcLiveStreamUserTranscoding user1 = new NERtcLiveStreamUserTranscoding();
user1.uid = 66601; //
user1.audioPush = true;
user1.videoPush = enableVideo;
if (user1.videoPush) {
user1.adaption = NERtcLiveStreamUserTranscoding.NERtcLiveStreamVideoScaleMode.kNERtcLsModeVideoScaleCropFill;
user1.x = 0;
user1.y = 0;
user1.width = 360;
user1.height = 640;
user1.zOrder = 1;
}
layout.userTranscodingList.add(user1);
// 设置右边成员布局
NERtcLiveStreamUserTranscoding user2 = new NERtcLiveStreamUserTranscoding();
user2.uid = 66602;
user2.audioPush = true;
user2.videoPush = enableVideo;
if (user2.videoPush) {
user2.adaption = NERtcLiveStreamUserTranscoding.NERtcLiveStreamVideoScaleMode.kNERtcLsModeVideoScaleCropFill;
user2.x = 360;
user2.y = 0;
user2.width = 360;
user2.height = 640;
user2.zOrder = 1;
}
layout.userTranscodingList.add(user2);
//设置音视频流编码参数
NERtcLiveConfig config = new NERtcLiveConfig();
config.interruptedPlaceImage = true;
config.singleVideoPassThrough = true;
config.audioBitrate = 64 kbps;
config.sampleRate = NERtcLiveStreamAudioSampleRate48000;
config.audioCodecProfile= NERtcLiveStreamAudioCodecProfileLCAAC;
config.channels = 2;
layout.config = config;
//设置占位图片布局
NERtcLiveStreamImageInfo image1 = new NERtcLiveStreamImageInfo();
image1.url = www.163.com/test.jpg;
image1.x = 360;
image1.y = 0;
image1.width = 360;
image1.height = 640;
image1.zOrder = 0;
layout.backgroundImgList.add(image1);
// 调用 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);
}
}
};
效果展示
-
原始画布:
-
增加占位图片:
-
正常显示画面:
-
右侧用户断线:
-
左侧用户断线:
常用布局设置
互动直播支持自定义配置视频布局,您也可以参考以下典型的常用布局配置,优化您的自定义布局。
双人横向平铺
-
显示效果
-
layout
参数配置示例
//设置整体布局
NERtcLiveStreamLayout layout = new NERtcLiveStreamLayout();
layout.userTranscodingList = new ArrayList<>();
layout.backgroundImgList = new ArrayList<>();
layout.width = 640;
layout.height = 360;
layout.backgroundColor = Color.parseColor(#0x808080);
liveTask1.layout = layout;
// 设置左边成员布局
NERtcLiveStreamUserTranscoding user1 = new NERtcLiveStreamUserTranscoding();
user1.uid = 66601; //
user1.audioPush = true;
user1.videoPush = enableVideo;
if (user1.videoPush) {
user1.adaption = NERtcLiveStreamUserTranscoding.NERtcLiveStreamVideoScaleMode.kNERtcLsModeVideoScaleCropFill;
user1.x = 0;
user1.y = 0;
user1.width = 320;
user1.height = 360;
user1.zOrder = 1;
}
layout.userTranscodingList.add(user1);
// 设置右边成员布局
NERtcLiveStreamUserTranscoding user2 = new NERtcLiveStreamUserTranscoding();
user2.uid = 66602;
user2.audioPush = true;
user2.videoPush = enableVideo;
if (user2.videoPush) {
user2.adaption = NERtcLiveStreamUserTranscoding.NERtcLiveStreamVideoScaleMode.kNERtcLsModeVideoScaleCropFill;
user2.x = 320;
user2.y = 0;
user2.width = 320;
user2.height = 360;
user2.zOrder = 1;
}
layout.userTranscodingList.add(user2);
三人纵向平铺
-
显示效果
-
layout
参数配置示例
//设置整体布局
NERtcLiveStreamLayout layout = new NERtcLiveStreamLayout();
layout.userTranscodingList = new ArrayList<>();
layout.backgroundImgList = new ArrayList<>();
layout.width = 360;
layout.height = 640;
layout.backgroundColor = Color.parseColor(#0x808080);
liveTask1.layout = layout;
// 设置上方成员布局
NERtcLiveStreamUserTranscoding user1 = new NERtcLiveStreamUserTranscoding();
user1.uid = 123; //
user1.audioPush = true;
user1.videoPush = enableVideo;
if (user1.videoPush) {
user1.adaption = NERtcLiveStreamUserTranscoding.NERtcLiveStreamVideoScaleMode.kNERtcLsModeVideoScaleCropFill;
user1.x = 0;
user1.y = 0;
user1.width = 360;
user1.height = 320;
user1.zOrder = 1;
}
layout.userTranscodingList.add(user1);
// 设置左下成员布局
NERtcLiveStreamUserTranscoding user2 = new NERtcLiveStreamUserTranscoding();
user2.uid = 456;
user2.audioPush = true;
user2.videoPush = enableVideo;
if (user2.videoPush) {
user2.adaption = NERtcLiveStreamUserTranscoding.NERtcLiveStreamVideoScaleMode.kNERtcLsModeVideoScaleCropFill;
user2.x = 0;
user2.y = 320;
user2.width = 180;
user2.height = 320;
user2.zOrder = 1;
}
layout.userTranscodingList.add(user2);
// 设置右下成员布局
NERtcLiveStreamUserTranscoding user3 = new NERtcLiveStreamUserTranscoding();
user3.uid = 789;
user3.audioPush = true;
user3.videoPush = enableVideo;
if (user3.videoPush) {
user3.adaption = NERtcLiveStreamUserTranscoding.NERtcLiveStreamVideoScaleMode.kNERtcLsModeVideoScaleCropFill;
user3.x = 180;
user3.y = 320;
user3.width = 180;
user3.height = 320;
user3.zOrder = 1;
}
layout.userTranscodingList.add(user3);
1 + N 悬浮布局
-
显示效果
-
layout
参数配置示例
//设置整体布局
NERtcLiveStreamLayout layout = new NERtcLiveStreamLayout();
layout.userTranscodingList = new ArrayList<>();
layout.backgroundImgList = new ArrayList<>();
layout.width = 360;
layout.height = 640;
layout.backgroundColor = Color.parseColor(#0x808080);
liveTask1.layout = layout;
// 设置上方成员布局
NERtcLiveStreamUserTranscoding user1 = new NERtcLiveStreamUserTranscoding();
user1.uid = 123; //
user1.audioPush = true;
user1.videoPush = enableVideo;
if (user1.videoPush) {
user1.adaption = NERtcLiveStreamUserTranscoding.NERtcLiveStreamVideoScaleMode.kNERtcLsModeVideoScaleCropFill;
user1.x = 0;
user1.y = 0;
user1.width = 360;
user1.height = 640;
user1.zOrder = 1;
}
layout.userTranscodingList.add(user1);
// 设置左下成员布局
NERtcLiveStreamUserTranscoding user2 = new NERtcLiveStreamUserTranscoding();
user2.uid = 456;
user2.audioPush = true;
user2.videoPush = enableVideo;
if (user2.videoPush) {
user2.adaption = NERtcLiveStreamUserTranscoding.NERtcLiveStreamVideoScaleMode.kNERtcLsModeVideoScaleCropFill;
user2.x = 45;
user2.y = 390;
user2.width = 110;
user2.height = 213;
user2.zOrder = 1;
}
layout.userTranscodingList.add(user2);
// 设置右下成员布局
NERtcLiveStreamUserTranscoding user3 = new NERtcLiveStreamUserTranscoding();
user3.uid = 789;
user3.audioPush = true;
user3.videoPush = enableVideo;
if (user3.videoPush) {
user3.adaption = NERtcLiveStreamUserTranscoding.NERtcLiveStreamVideoScaleMode.kNERtcLsModeVideoScaleCropFill;
user3.x = 185;
user3.y = 390;
user3.width = 110;
user3.height = 213;
user3.zOrder = 1;
}
layout.userTranscodingList.add(user3);