实现互动直播
更新时间: 2024/08/05 15:02:55
网易云信互动直播产品的基本功能包括音视频通话和连麦直播,当您成功初始化 SDK 之后,您可以简单体验本产品的基本业务流程,例如主播加入房间、观众CDN拉流、连麦者上下麦、结束直播等。本文档为您展示互动直播提供的基本业务流程。
前提条件
请确认您已完成以下操作:
主播加入房间
步骤一 引用头文件
在您的工程中对应的文件里添加如下代码引入头文件:
cpp #include "nrtc_engine.h"
步骤二 初始化
在调用 SDK 其他接口前,需要先完成初始化。具体步骤如下。
- 调用
createNERtcEngine
方法创建一个 NERtcEngine 实例。 - 调用
initialize
方法完成初始化。
setParameters
方法打开推流开关。加入房间并创建推流任务后,房间中所有成员的音视频流均可以进行混流并推流至 CDN。
示例代码如下:
cpp // 创建 RTC 引擎对象并返回指针。
nertc::IRtcEngineEx *rtc_engine_ = (IRtcEngineEx *)createNERtcEngine();
// 设置已开通音视频功能的云信应用的AppKey。
rtc_engine_context_.app_key = app_key_.c_str();
// 设置日志目录的完整路径,采用UTF-8 编码。可选。
rtc_engine_context_.log_dir_path = log_dir_path_.c_str();
// 设置日志级别,默认级别为 kNERtcLogLevelInfo。
rtc_engine_context_.log_level = log_level;
// 指定 SDK 输出日志文件的大小上限,单位为 KB。如果设置为 0,则默认为 20 M。
rtc_engine_context_.log_file_max_size_KBytes = log_file_max_size_KBytes;
// 设置SDK向应用发送回调事件的通知。
rtc_engine_context_.event_handler = this;
// 初始化 NERTC SDK 服务。
if (kNERtcNoError != rtc_engine_->initialize(rtc_engine_context_))
{
//TODO
}
步骤三 设置本地视图
初始化成功后,可以设置本地视图,来预览本地图像。您可以在加入房间之前预览,或在加入房间后预览。
-
加入房间前预览。
- 调用
setupLocalVideoCanvas
与startVideoPreview(type)
方法,在加入房间前设置本地视图,预览本地图像。
示例代码如下:
cpp
NERtcVideoCanvas canvas; canvas.cb = nullptr; canvas.user_data = nullptr; canvas.window = window; //设置视频缩放模式。 canvas.scaling_mode = mode; //设置本地视频画布 rtc_engine_->setupLocalVideoCanvas(&canvas); //以开启本地视频主流预览为例 rtc_engine_->startVideoPreview(kNERTCVideoStreamMain);
- 若要结束预览,或者准备加入房间时,调用
stopVideoPreview(type)
方法停止预览。
stopVideoPreview(streamType)
的streamType
参数请与startVideoPreview(streamType)
的保持一致,即同为主流或辅流的开启和停止预览。 - 调用
-
加入房间后预览。
在成功加入房间后,调用
enableLocalVideo(type)
方法进行视频的采集发送与预览。示例代码如下:
cpp
NERtcVideoCanvas canvas; canvas.cb = nullptr; canvas.user_data = nullptr; canvas.window = window; //设置视频缩放模式。 canvas.scaling_mode = mode; //设置本地视频画布 rtc_engine_->setupLocalVideoCanvas(&canvas); bool enabled = true; //以开启本地视频主流采集并发送为例 rtc_engine_->enableLocalVideo(enabled,kNERTCVideoStreamMain);
步骤四 设置直播模式
在互动直播的场景中,建议在加入房间前,调用 setChannelProfile
方法设置房间模式为直播模式。当前默认为通信模式。
示例代码如下:
cpp//kNERtcChannelProfileCommunication为通话模式,
//kNERtcChannelProfileLiveBroadcasting为直播模式
NERtcChannelProfileType channelProfileType = kNERtcChannelProfileLiveBroadcasting;
rtc_engine_->setChannelProfile(channelProfileType);
步骤五 加入房间
加入房间前,请确保已完成初始化相关事项。若您的业务中涉及呼叫邀请等机制,建议通过信令实现。
调用 joinChannel
方法加入房间。
示例代码如下:
cpp rtc_engine_->joinChannel(token, channel_name, uid);
重要参数说明
参数 | 说明 |
---|---|
token | 安全认证签名(NERTC Token)。
|
channel_name | 房间名称,长度为 1 ~ 64 字节。目前支持以下 89 个字符:a-z, A-Z, 0-9, space, !#$%&()+-:;≤.,>? @[]^_{|}~"。 设置相同房间名称的用户会进入同一个通话房间。 |
uid | 用户的唯一标识 id,为数字串,房间内每个用户的 uid 必须是唯一的。 |
SDK 发起加入房间请求后,服务器会进行响应,您可以通过初始化时设置的 rtc_engine_context_.event_handler
的 onJoinChannel
回调监听加入房间的结果,同时该回调会抛出当前通话房间的 channelId 与加入房间总耗时(毫秒)。
步骤六 推流任务管理
在成功加入房间后,需要通过 NERtcLiveStreamTaskInfo
设置推流任务,将通话房间内的多媒体数据推至 CDN。典型的业务场景里是由主播进行设置推流任务。推流任务也可以通过服务端 API 进行管理,请根据您的业务需求选择合适的方式。
增加推流任务
音视频房间中默认没有推流任务,您需要在启动直播前调用 addLiveStreamTask
方法增加推流任务。增加推流任务的结果将通过 onAddLiveStreamTask
回调返回给 SDK。
示例代码(增加推流任务)
cpp// 添加推流任务的异步callback,更多错误码请参考onAddLiveStreamTask的API文档
void onAddLiveStreamTask(const char* task_id, const char* url, int error_code){
if (code == kNERtcNoError) {
showToast("添加推流任务成功 : taskId " + taskId);
} else {
showToast("添加推流任务失败 : taskId " + taskId + " , code : " + code);
}
};
//推流任务数据结构
NERtcLiveStreamTaskInfo ls_task_info_;
//请务必保证一个URL地址只能设置一个推流任务,请使用上层自己维护的task_id、url
strncpy(ls_task_info_.task_id, task_id.c_str(), kNERtcMaxTaskIDLength);
strncpy(ls_task_info_.stream_url, url.c_str(), kNERtcMaxURILength);
ls_task_info_.ls_mode = kNERtcLsModeVideo;
ls_task_info_.server_record_enabled = false;
//设置音视频编码参数
ls_task_info_.config.interrupted_place_image = false;//设置是否开启推流成员离线时的占位图
ls_task_info_.config.single_video_passthrough = false;//设置是否开启单路视频透传开关
ls_task_info_.config.audio_bitrate = 192;//设置音频推流码率
ls_task_info_.config.sampleRate = kNERtcLiveStreamAudioSampleRate48000;//设置音频推流采样率
ls_task_info_.config.audioCodecProfile = NERtcLiveStreamAudioCodecProfileLCAAC;//设置音频编码规格
ls_task_info_.config.channels = 2;//设置音频推流声道数
//设置整体布局
ls_task_info_.layout.background_color = 0x3399ff; // 整体背景色
ls_task_info_.layout.width = 1280;//整体布局宽度
ls_task_info_.layout.height = 640;//整体布局高度
//设置直播成员布局
ls_task_info_.layout.user_count = 2;
ls_task_info_.layout.users = new NERtcLiveStreamUserTranscoding[2];
ls_task_info_.layout.users[0].uid = uid;
ls_task_info_.layout.users[1].uid = remote_uid;
for (int i = 0; i < ls_task_info_.layout.user_count; i++)
{
ls_task_info_.layout.users[i].adaption = kNERtcLsModeVideoScaleFit;
ls_task_info_.layout.users[i].video_push = true;// 推流是否发布user的视频
ls_task_info_.layout.users[i].x = 640 * i; // user 的视频布局x偏移,相对整体布局的左上角
ls_task_info_.layout.users[i].y = 0; // user 的视频布局y偏移,相对整体布局的左上角
ls_task_info_.layout.users[i].width = 640; // user 的视频布局宽度
ls_task_info_.layout.users[i].height = 640; //user 的视频布局高度
ls_task_info_.layout.users[i].audio_push = true;// 推流是否发布user的音频
ls_task_info_.layout.users[i].zorder = 1;// user 的视频帧的图层编号
}
//设置占位图布局
ls_task_info_.layout.bg_image_count = 2;
ls_task_info_.layout.bg_image = new NERtcLiveStreamImageInfo[2];
memset(ls_task_info_.layout.bg_image, 0, sizeof(NERtcLiveStreamImageInfo) * 2);
for (int i = 0; i < 2; i++)
{
memcpy(ls_task_info_.layout.bg_image[i].url, url.data(), url.size());
ls_task_info_.layout.bg_image[i].x = 0 + 640 * i;
ls_task_info_.layout.bg_image[i].y = 0;
ls_task_info_.layout.bg_image[i].width = 640;
ls_task_info_.layout.bg_image[i].height = 480;
ls_task_info_.layout.bg_image[i].zorder = 0;
}
// 如果需要,可以配置多个用户,配置的用户不用已经在房间中,如果用户不在房间中,那么会有相应的视频占位
// 之后用户以配置了的uid进入房间,开启推流开关即可推流
int ret = addLiveStreamTask(ls_task_info_);
if (ret != 0) {
showToast("调用添加推流任务失败 , ret : " + ret);
}
更新推流任务
当音视频通话房间内有人员进出或其他情况时,可以调用 updateLiveStreamTask
方法更新推流任务。更新推流任务的结果将通过 onUpdateLiveStreamTask
回调返回给 SDK。更新推流任务时,会覆盖之前对于这条推流任务的所有配置。
示例代码(更新推流任务)
cpp // 更新推流任务的异步callback , 更多错误码请参考onUpdateLiveStreamTask API文档
void onUpdateLiveStreamTask(const char* task_id, const char* url, int error_code) {
if (code == kNERtcNoError) {
showToast("更新推流任务成功 : taskId " + taskId);
} else {
showToast("更新推流任务失败 : taskId " + taskId + " , code : " + code);
}
};
NERtcLiveStreamTaskInfo updateLiveTask1 = preLiveTask;
// todo 重新配置相关配置 , 具体的配置与添加推流任务时一模一样 ,唯一不能修改的就是 taskId
int ret = updateLiveStreamTask(taskInfo);
if (ret != 0) {
showToast("调用更新推流任务失败 , ret : " + ret);
}
删除推流任务
当本场互动直播准备结束时,可以调用 removeLiveStreamTask
方法主动删除推流任务。删除推流任务的结果将通过 onRemoveLiveStreamTask
回调返回给 SDK。
示例代码(删除推流任务)
cpp // 删除推流任务的异步callback,更多错误码请参考onRemoveLiveStreamTask API文档
void onRemoveLiveStreamTask(const char* task_id, int error_code){
if (code == kNERtcNoError) {
showToast("删除推流任务成功 : taskId " + taskId);
} else {
showToast("删除推流任务失败 : taskId " + taskId + " , code : " + code);
}
};
int ret = removeLiveStreamTask(preLiveTask.taskId);
if (ret != 0) {
showToast("调用删除推流任务失败 : ret : " + ret);
}
推流任务相关错误码
在增加、更新与删除推流任务时,如果发生错误,callback 回调通过错误码形式反馈错误原因,错误码如下:
错误码 | 含义 |
---|---|
kNERtcNoError = 0 | 成功 |
kNERtcErrLsTaskRequestInvalid = 1301 | task请求无效,被后续操作覆盖 |
kNERtcErrLsTaskIsInvaild = 1400 | task参数格式错误 |
kNERtcErrLsTaskRoomExited = 1401 | 已经退出房间 |
kNERtcErrLsTaskNumLimit = 1402 | 推流任务超出上限 |
kNERtcErrLsTaskDuplicateId = 1403 | 推流ID重复 |
kNERtcErrLsTaskNotFound = 1404 | taskId任务不存在,或房间不存在 |
kNERtcErrLsTaskRequestErr = 1417 | 请求失败 |
kNERtcErrLsTaskInternalServerErr = 1500 | 服务器内部错误 |
kNERtcErrLsTaskInvalidLayout = 1501 | 布局参数错误 |
kNERtcErrLsTaskUserPicErr = 1502 | 用户图片错误 |
互动直播推流状态
主播/连麦者参与互动直播的过程中,可以通过 onLiveStreamState
回调来监听推流状态。
常见的推流状态如下:
状态码 | 含义 |
---|---|
kNERtcLsStatePushing = 505 | 推流中 |
kNERtcLsStatePushFail = 506 | 互动直播推流失败 |
kNERtcLsStatePushStopped = 511 | 推流结束 |
kNERtcLsStateImageError = 512 | 背景图片设置出错 |
步骤七 设置远端视图
互动直播过程中,除了要显示本地的视频画面,通常也要显示参与互动的其他连麦者/主播的远端视频画面。
-
监听远端用户进出频道。
IRtcEngineEventHandler
通过以下回调获取相关信息:-
onUserJoined
:监听远端用户加入通话房间的事件,并抛出对方的 uid。当本端加入房间后,也会通过此回调抛出通话房间内已有的其他用户。 -
onUserVideoStart
:监听远端用户发布视频流的事件,回调中携带对方的 uid 与发布的视频分辨率。
-
-
设置远端视频画布。
在监听到远端用户加入房间或发布视频流后,本端可以调用
setupRemoteVideoCanvas
方法设置远端用户视频画布,用于显示其视频画面。示例代码如下:
cpp
NERtcVideoCanvas canvas; canvas.cb = nullptr; canvas.user_data = nullptr; canvas.window = window; // 设置视频缩放模式。 canvas.scaling_mode = mode; rtc_engine_->setupRemoteVideoCanvas(uid, &canvas);
-
监听远端视频流发布。
当房间中的其他用户发布视频流时,本端会触发
onUserVideoStart
回调。 -
订阅远端视频流。
在监听到远端用户发布视频流后,本端可以调用
subscribeRemoteVideoStream
方法对其发起视频流的订阅,来将对方的视频流渲染到视频画布上。示例代码如下:
cpp
//订阅指定用户的 kNERtcRemoteVideoStreamTypeHigh 类型的视频流 void NRTCEngine::subscribeRemoteUserVideoStream(nertc::uid_t uid) { int ret_temp = rtc_engine_->subscribeRemoteVideoStream(uid, kNERtcRemoteVideoStreamTypeHigh, true); if (ret_temp) { qDebug("[ERROR] can not subscribe remote video stream! ERROR CODE: %d", ret_temp); } }
-
监听远端用户离开房间或关闭视频功能。
-
onUserLeft
:用户离开房间回调。 -
onUserVideoStop
:远端用户关闭视频功能回调。
步骤八 音频流
本地音频的采集发布和远端音频订阅播放默认启动,正常情况下无需开发者主动干预。
观众进行 CDN 拉流
当通话房间内有主播/连麦者发布多媒体流,且正确设置了推流任务时,通话房间外的观众可以通过 CDN 直播拉流地址进行拉流播放,云信同时提供播放器 SDK 供您使用,详细内容请参考直播-播放器 SDK
连麦者上下麦
步骤一 上麦
若正在进行 CDN 拉流播放的普通观众要上麦参与互动时,必须先停止 CDN 拉流,并释放播放器相关资源。然后按照主播加入房间流程,进行初始化、设置本地视图、设置直播模式、设置推流开关、加入通话房间、设置远端视图。
- 由于有新连麦者的加入,需要更新推流任务将新连麦者设置到推流布局中。操作步骤请参考更新推流任务。更新推流的任务操作,可以由主播执行,也可以由连麦者执行,也可以通过服务端API完成。
- NERTC SDK 支持在直播场景下设置用户角色管理,角色包括主播和观众,默认以主播角色加入房间。调用
setChannelProfile
方法将通话设置为直播场景之后,可以调用setClientRole
方法切换用户角色。
步骤二 下麦
若连麦者互动结束,需要下麦时,可以更新推流任务,在推流布局中剔除该连麦者。同时,连麦者退出通话房间,清理相关资源,重新进行播放器拉流或直接离开直播间。
通话房间内的其他用户可以通过 onUserLeave
回调来监听其他连麦者下麦。
结束互动直播
需要结束该场互动直播时,可以先删除推流任务,然后主播与连麦者退出通话房间,观众结束 CDN 拉流。
步骤一 退出通话房间
调用 leaveChannel
方法退出通话房间。
示例代码如下:
cpp rtc_engine_->leaveChannel();
真正退出房间后,SDK 会走入初始化时设置的 rtc_engine_context_.event_handler 回调事件通知中的 onLeaveChannel
。
步骤二 销毁实例
当确定短期内不再使用音视频通话实例时,可以调用 release
方法释放对应的对象资源。
示例代码如下:
cpp // 同步销毁 IRtcEngine 对象
rtc_engine_->release(true);
// 销毁 RTC 引擎对象
destroyNERtcEngine((void*&)rtc_engine_);
rtc_engine_ = nullptr;
建议您在调用 release
和 destroyNERtcEngine
方法彻底销毁 NERtc 实例后再卸载库,否则可能会导致异常崩溃。