实现互动直播
更新时间: 2024/08/05 15:02:55
网易云信互动直播产品的基本功能包括音视频通话和连麦直播,当您成功初始化 SDK 之后,您可以简单体验本产品的基本业务流程,例如主播加入房间、观众 CDN 拉流、连麦者上下麦、结束直播等。本文档为您展示互动直播提供的基本业务流程。
前提条件
请确认您已完成以下操作:
- 创建应用并获取 App Key。
- 开通互动直播 2.0 服务。
- 集成 SDK(Android),其中需要添加必要的设备权限。
主播加入房间
步骤一 导入类
在您的工程中对应的 Activity 文件里添加如下代码先导入以下重要类:
javaimport com.netease.lava.nertc.sdk.NERtcCallbackEx;
import com.netease.lava.nertc.sdk.NERtcConstants;
import com.netease.lava.nertc.sdk.NERtcEx;
import com.netease.lava.nertc.sdk.NERtcParameters;
import com.netease.lava.nertc.sdk.video.NERtcRemoteVideoStreamType;
import com.netease.lava.nertc.sdk.video.NERtcVideoView;
步骤二 初始化
默认情况下,请先调用 init
方法完成初始化。
示例代码如下:
java//初始化
private void initializeSDK() {
try {
NERtcEx.getInstance().init(getApplicationContext(),Config.APP_KEY,callback,null);
} catch (Exception e) {
showToast("SDK初始化失败");
finish();
...
return;
}
...
}
请在初始化方法中传入原型为 NERtcCallback / NERtcCallbackEx 的 callback。
步骤三 设置本地视图
初始化成功后,可以设置本地视图,来预览本地图像。您可以在加入房间之前预览,或在加入房间后预览。
-
加入房间前预览。
- 调用
setupLocalVideoCanvas
与startVideoPreview(streamType)
方法,在加入房间前设置本地视图,预览本地图像。
示例代码如下:
java
//以开启本地视频主流预览为例 NERtcVideoView localView = (NERtcVideoView)findViewById(R.id.local_view); NERtcEx.getInstance().setupLocalVideoCanvas(localView); NERtcEx.getInstance().startVideoPreview(kNERtcVideoStreamTypeMain);
- 若要结束预览,或者准备加入房间时,调用
stopVideoPreview(streamType)
停止预览。
stopVideoPreview(streamType)
的streamType
参数请与startVideoPreview(streamType)
的保持一致,即同为主流或辅流的开启和停止预览。 - 调用
-
加入房间后预览。
在成功加入房间后,调用
enableLocalVideo(streamType)
方法进行视频的采集发送与预览。示例代码如下:
java
//以开启本地视频主流采集并发送为例 NERtcEx.getInstance().enableLocalVideo(true,kNERtcVideoStreamTypeMain); //设置本地预览画布 NERtcVideoView localView = (NERtcVideoView)findViewById(R.id.local_view); NERtcEx.getInstance().setupLocalVideoCanvas(localView);
步骤四 设置直播模式
在互动直播的场景中,建议在加入房间前,调用 setChannelProfile
方法设置房间模式为直播模式。当前默认为通信模式。
示例代码如下:
java// 0 - COMMUNICATION(通信模式), 1 - LIVE_BROADCASTING(直播模式)
NERtcEx.getInstance().setChannelProfile(1);
步骤五 加入房间
加入房间前,请确保已完成初始化相关事项。若您的业务中涉及呼叫邀请等机制,建议通过信令实现。
调用 joinChannel
方法加入房间。
调用 joinChannel 之后,NERTC SDK 会通过 Android 的 AudioManager.setMode()
方法调整音频模式(audio mode),此后请勿修改 SDK 调整过的音频模式,否则会导致音频路由错误等问题。
示例代码如下:
java NERtcEx.getInstance().joinChannel(token,channelName,uid);
参数说明:
参数 | 说明 |
---|---|
token | 安全认证签名(NERTC Token)。
|
channelName | 房间名称,长度为 1 ~ 64 字节。目前支持以下 89 个字符:a-z, A-Z, 0-9, space, !#$%&()+-:;≤.,>? @[]^_{|}~"。 设置相同房间名称的用户会进入同一个通话房间。 |
uid | 用户的唯一标识 id,为数字串,房间内每个用户的 uid 必须是唯一的。 |
SDK 发起加入房间请求后,服务器会进行响应,您可以通过 NERtcCallback
的 onJoinChannel
回调监听加入房间的结果,同时该回调会抛出当前通话房间的 channelId 与加入房间总耗时(毫秒)。
步骤六 推流任务管理
在成功加入房间后,需要通过 NERtcLiveStreamTaskInfo
设置推流任务来将通话房间内的多媒体数据推至 CDN。典型的业务场景里是由主播进行设置推流任务。推流任务也可以通过服务端 API 进行管理,请根据您的业务需求选择合适的方式。
增加推流任务
音视频房间中默认没有推流任务,您需要在启动直播前调用 addLiveStreamTask
方法增加推流任务。
示例代码(增加推流任务)
java //初始化推流任务
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;
liveTask1.config = config;
//设置占位图片布局(可选)
//原 backgroundImg 字段仍保留,仅可设置一张占位图,若您同时配置了两个字段,则以 backgroundImgList 配置为准
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);
}
}
};
更新推流任务
当音视频通话房间内有人员进出或其他情况时,可以调用 updateLiveStreamTask
方法更新推流任务。更新推流任务时,会覆盖之前对于这条推流任务的所有配置。
示例代码(更新推流任务)
java // 初始化新推流任务。注意:其中的taskid为原推流任务id。
NERtcLiveStreamTaskInfo updateLiveTask1 = preLiveTask;
...
// 更新推流任务
int ret = NERtcEx.getInstance().updateLiveStreamTask(updateLiveTask1 , updateLiveTaskCallback);
if (ret != 0) {
showToast("调用更新推流任务接口执行失败 , ret : " + ret);
}
// 更新推流任务的异步callback
private UpdateLiveTaskCallback updateLiveTaskCallback = new UpdateLiveTaskCallback() {
@Override
public void onUpdateLiveStreamTask(String taskId, int errCode) {
if (code == RtcCode.LiveCode.OK) {
showToast("更新推流任务成功 : taskId " + taskId);
} else {
showToast("更新推流任务失败 : taskId " + taskId + " , code : " + code);
}
}
};
删除推流任务
当本场互动直播准备结束时,可以调用 removeLiveStreamTask
方法主动删除推流任务。
示例代码(删除推流任务)
java int ret = NERtcEx.getInstance().removeLiveStreamTask(preLiveTask.taskId,deleteLiveTaskCallback );
if (ret != 0) {
showToast("调用删除推流任务接口执行失败 : ret : " + ret);
}
// 删除推流任务的异步callback
private DeleteLiveTaskCallback deleteLiveTaskCallback = new DeleteLiveTaskCallback() {
@Override
public void onDeleteLiveStreamTask(String taskId, int errCode) {
if (code == RtcCode.LiveCode.OK) {
showToast("删除推流任务成功 : taskId " + taskId);
} else {
showToast("删除推流任务失败 : taskId " + taskId + " , code : " + code);
}
}
};
推流任务相关错误码
增加、更新与删除推流任务时,如果发生错误,callback 回调通过错误码形式反馈错误原因。错误码 RtcCode.LiveCode 如下:
错误码 | 含义 |
---|---|
OK = 0 | 成功 |
TASK_REQUEST_INVALID = 1301 | task请求无效,被后续操作覆盖 |
TASK_IS_INVALID = 1400 | task参数格式错误 |
TASK_ROOM_EXITED = 1401 | 已经退出房间 |
TASK_NUM_LIMIT = 1402 | 推流任务超出上限 |
TASK_DUPLICATE_ID = 1403 | 推流ID重复 |
TASK_NOT_FOUND = 1404 | taskId任务不存在,或房间不存在 |
TASK_REQUEST_ERR = 1417 | 请求失败 |
TASK_INTERNAL_SERVER_ERR = 1500 | 服务器内部错误 |
TASK_INVALID_LAYOUT = 1501 | 布局参数错误 |
TASK_USER_PIC_ERR = 1502 | 用户图片错误 |
互动直播推流状态
主播或连麦者参与互动直播的过程中,可以通过 NERtcCallback 的 onLiveStreamState
回调来监听推流状态。
常见的推流状态如下:
状态码 | 含义 |
---|---|
NERtcConstants.LiveStreamState.STATE_PUSHING = 505 | 推流中 |
NERtcConstants.LiveStreamState.STATE_PUSH_FAIL = 506 | 互动直播推流失败 |
NERtcConstants.LiveStreamState.STATE_PUSH_STOPPED = 511 | 推流结束 |
NERtcConstants.LiveStreamState.STATE_IMAGE_ERROR = 512 | 背景图片设置出错 |
步骤七 设置远端视图
互动直播过程中,除了要显示本地的视频画面,通常也要显示参与互动的其他连麦者/主播的远端视频画面。
-
监听远端用户进出频道。
NERtcCallback 通过以下回调获取相关信息:
-
onUserJoined
:监听远端用户加入通话房间的事件,并抛出对方的 uid。当本端加入房间后,也会通过此回调抛出通话房间内已有的其他用户。 -
onUserVideoStart
:监听远端用户发布视频流的事件,回调中携带对方的 uid 与发布的视频分辨率。
-
-
设置远端视频画布。
在监听到远端用户加入房间或发布视频流后,本方可以调用
setupRemoteVideoCanvas
方法设置远端用户视频画布,用于显示其视频画面。示例代码如下:
java
NERtcVideoView remoteView = (NERtcVideoView)findViewById(R.id.remote_view); NERtcEx.getInstance().setupRemoteVideoCanvas(remoteView,uid);
-
监听远端视频流发布。
当房间中的其他用户发布视频流时,本端会触发
onUserVideoStart
回调。 -
订阅远端视频流。
在监听到远端用户发布视频流后,本端可以调用
subscribeRemoteVideoStream
方法对其发起视频流的订阅,来将对方的视频流渲染到视频画布上。示例代码如下:
java
NERtcEx.getInstance().subscribeRemoteVideoStream(uid, streamType, subscribe);
-
监听远端用户离开房间或关闭视频功能。
-
onUserLeave
:用户离开房间回调。 -
onUserVideoStop
:远端用户关闭视频功能回调。
步骤八 音频流
本地音频的采集发布和远端音频订阅播放是默认启动的,正常情况下无需开发者主动干预。
观众进行 CDN 拉流
当通话房间内有主播/连麦者发布多媒体流,且正确设置了推流任务时,通话房间外的观众可以通过 CDN 直播拉流地址进行拉流播放。云信同时提供播放器 SDK 供您使用,详细内容请参考直播-播放器 SDK。
连麦者上下麦
步骤一 上麦
若正在进行 CDN 拉流播放的普通观众要上麦参与互动时,必须先停止 CDN 拉流,并释放播放器相关资源。然后按照主播加入房间流程,进行初始化、设置本地视图、设置直播模式、设置推流开关、加入通话房间、设置远端视图。
- 由于有新连麦者的加入,需要更新推流任务将新连麦者设置到推流布局中。操作步骤请参考更新推流任务。更新推流的任务操作,可以由主播执行,也可以由连麦者执行,也可以通过服务端API完成。
- NERTC SDK 支持直播场景下的用户角色管理,角色包括主播和观众,默认以主播角色加入房间。调用 setChannelProfile 将通话设置为直播场景之后,可以调用
setClientRole
方法切换用户角色。
步骤二 下麦
若连麦者互动结束,需要下麦时,可以更新推流任务,在推流布局中剔除该连麦者。同时,连麦者退出通话房间,清理相关资源,重新进行播放器拉流或直接离开直播间。
通话房间内的其他用户可以通过 NERtcCallback 的 onUserLeave
回调来监听其他连麦者下麦。
结束互动直播
需要结束该场互动直播时,可以先删除推流任务,然后主播与连麦者退出通话房间,观众结束 CDN 拉流。
步骤一 退出通话房间
调用 leaveChannel
方法退出通话房间。
示例代码如下:
javaNERtcEx.getInstance().leaveChannel();
NERtcCallback 提供 onLeaveChannel
回调来监听当前用户退出房间的结果。
步骤二 销毁实例
当确定 App 短期内不再使用音视频通话实例时,可以调用 release
方法释放对应的对象资源。
示例代码如下:
java// 销毁实例
NERtcEx.getInstance().release();