互动直播 PC Demo 源码导读

概述

互动直播是网易云通信的一款针对目前市场比较热门的直播解决方案。在方案中结合了网易云通信 IM 能力的聊天室模型和网易云通信音视频能力的多人会议模型。.Demo使用Visual Studio 2013 Update5开发(必须使用Update5版本)。

Demo源码中目录结构中的C++封装层文件夹名字尾部携带_vs2010的为vs2010创建的工程项目,对应的不携带_vs2010的为vs2013创建的工程项目,即Demo目前加载的工程,该工程依赖Demo工程。

Demo的主要功能由Demo工程本身以及UI组件工程共同完成,Demo工程和UI组件工程具有相同的目录结构。UI组件位于tool_kits\ui_component\ui_kit目录,UI组件的相关文档详见:云信UI组件

Demo源码版本历史

Demo源码版本历史

界面开发资料

网易云信Duilib使用说明

代码托管地址

网易云信Duilib使用说明

控件属性

控件属性

界面布局介绍

网易云信 DuiLib 布局功能指南

会话多窗口实现介绍

从3.0.0版本开始,Demo对会话窗口的实现进行了升级,方便开发者快速开发类似QQ,微信等多窗口模式。

会话窗口多窗口合并及分开功能及升级说明

CEF开发指南

从3.4.0版本开始,Demo增加了Cef控件,方便开发者开发Web相关的功能模块。

CEF开发指南

Duillib高分屏(高DPI)支持

从3.5.0版本开始,Duilib增加了对高分屏的支持,方便在用户设置了DPI后保持软件界面的清晰效果。

Duillib高分屏(高DPI)支持

目录结构

打包说明

开发者在打包自己的应用时,应确保将以下云信SDK相关文件打包进去。

其他的文件及目录是应用程序相关的,开发者根据自己程序的使用情况选择是否打包。

功能点指引

SDK C++封装层

因为SDK所有接口都是C接口,为了方便使用C++的同学使用,我们提供了nim_cpp_sdknim_chatroom_cpp_sdk静态库。静态库位于libs\nim_sdk_desktop\nim_cpp_sdklibs\nim_sdk_desktop\nim_chatroom_cpp_sdklibs\nim_sdk_desktop\nim_player_cpp_sdk目录,它将C接口SDK封装为C++代码,demo和UI组件都直接使用nim_cpp_sdknim_chatroom_cpp_sdknim_player_cpp_sdk静态库的C++封装层代码。开发者可以直接在解决方案中导入nim_cpp_sdk工程、nim_chatroom_cpp_sdknim_player_cpp_sdk工程。

封装层提供的包装文件如下:

SDK 初始化

SDK的初始化在main.cpp中InitNim方法进行。

示例:

//sdk能力参数(必填)
//string(db key必填,目前只支持最多32个字符的加密密钥!建议使用32个字符)
config.database_encrypt_key_ = "Netease"; 

std::string app_key = GetConfigValueAppKey();
// 载入云信sdk,初始化安装目录和用户目录
bool ret = nim::Client::Init(app_key, "NIM_LIVE", "", config); 
assert(ret);
//聊天室初始化
ret = nim_chatroom::ChatRoom::Init("","");
assert(ret);

nim_ui::InitManager::GetInstance()->InitUiKit();
nim_chatroom::ChatroomCallback::InitChatroomCallback();

界面开发

云信PC demo以及UI组件的界面开发都依赖云信DuiLib库,关于云信DuiLib库的使用方法和注意事项,请参考:云信Duilib

登录

登录相关界面代码在gui/login目录下,登录相关的控制逻辑已经封装到了UI组件的,逻辑代码在callback\login\login_callback.cpp文件中。登录界面可以直接调用UI组件的登录函数,示例如下:

std::string username = "123456789"std::string password = "123456789";
nim_ui::LoginManager::GetInstance()->DoLogin(username, password);

主窗体功能

Demo登录进去之后有两种模式可供用户选择:观众模式和主播模式。Demo提供网络检测功能,可直观的观测到当前网络状态,示例如下:

void LoginForm::NetDetect()
{
    nim::VChat::NetDetect(nbase::Bind(&LoginForm::NetDetectCb, this, std::placeholders::_1, std::placeholders::_2));
}
void LoginForm::NetDetectCb(int code, nim::NetDetectCbInfo info)
{
    if (code == 200)
    {
        nim_chatroom::BypassLiveFontPage::SetNetDetectInfo(info);
    }
}

观众模式

当以观众模式进行互动时,采用播放器SDK拉流,直接看到主播的直播画面。当与主播成功连麦后,停止拉流,进入音视频房间,和主播互动。互动模式有两种:音频互动和视频互动。当观众向主播申请连麦时,观众将自己加入到连麦队列中,加入成功后发送自定义通知给主播。示例如下:

value_info["nick"] = nbase::UTF16ToUTF8(nick);
value_info["avatar"] = "avatar_default";
string info = fw.write(value_info);
Json::Value value;
value["style"] = kAudio;
value["state"] = NTESLiveMicStateConnecting;
value["info"] = info;
string ext = fw.write(value);
RequestPushMicLink(nbase::Int64ToString(room_id_), uid, ext,kAudio);

主播模式

当以主播模式进行互动时,主播开启直播后,能正常收到观众的连麦请求,主播可以同意观众的连麦请求。主播同意连麦后,发送自定义通知给用户id,如果此时已有连麦者,应该断开已有的连接,然后再更新连麦者的状态。示例如下:

if ((!bypassingaccout_.empty())&&bypassingaccout_ != bypass_inact_account_)
{
    RequestPopMicLink(nbase::Int64ToString(room_id_), bypassingaccout_);
    SendSysNotifyMsg(kDisConnectingMIC, bypassingaccout_);
}
QLOG_APP(L"BypassAgreeMenuItemClick-bypass_inact_account:{0}") << bypass_inact_account_;
SendSysNotifyMsg(kConnectingMIC, bypass_inact_account_);
bypassingaccout_ = bypass_inact_account_;
string nick = nbase::UTF16ToUTF8(nim_ui::UserManager::GetInstance()->GetUserNameW(bypass_inact_account_));
UpdateBypassMembersState(bypass_inact_account_, NTESLiveMicStateConnecting, bypass_inact_type_, kUpdate, nick);

互动直播的逻辑说明

由于聊天室和多人会议都不是直接针对直播的方案模型,所以需要在应用上层补充一些控制指令来保证直播业务逻辑。 控制指令分为两套:

观众端推拉流切换逻辑说明

       void ChatroomForm::SwitchPullStreamAndInteract(bool pull_stream)
         {
             if (master_)
                  {
                    assert(false);
                    return;
                 }

            if (!pull_stream) //切换为互动
                {
                    if (livestreaming_) //已经在互动
                {
                assert(false);
                return;

            //停止拉流
            pull_stream_stopped_cb_.reset(new nbase::WeakCallback<StdClosure>(ToWeakCallback([this]()
                {
                    //开始互动
                    StartLiveStream();
                    pull_stream_stopped_cb_.reset();
                })));
            nim_comp::PlayerManager* player_manager = nim_comp::PlayerManager::GetInstance();
            if (!player_manager->StopPlay()) //当前没在拉流
                {
                    (*pull_stream_stopped_cb_.get())();
                }
            else //切换为拉流
            {
                interact_stopped_cb_.reset(new nbase::WeakCallback<StdClosure>(ToWeakCallback([this]()
                {
                    //开始拉流
                    nim_comp::PlayerManager* player_manager = nim_comp::PlayerManager::GetInstance();
                    player_manager->StartPlay(nbase::UTF16ToUTF8(rtmp_pull_url_), creater_id_);
                    video_show_ctrl_->SetAccount(creater_id_);
                    interact_stopped_cb_.reset();
                })));
                if (livestreaming_) //如果正在互动,停止互动
                        StopLiveStream();
                else if (nim_comp::PlayerManager::GetInstance()->GetPlayerState() =EN_PLAYER_STATE::EN_PLAYER_STOPPING)
                    {
                        pull_stream_stopped_cb_.reset(new nbase::WeakCallback<StdClosure>(ToWeakCallback([this]()
                            {
                                //开始拉流
                                nim_comp::PlayerManager* player_manager = nim_comp::PlayerManager::GetInstance();
                                player_manager->StartPlay(nbase::UTF16ToUTF8(rtmp_pull_url_), creater_id_);
                                video_show_ctrl_->SetAccount(creater_id_);
                                pull_stream_stopped_cb_.reset();
                            })));
                    }
                else
                    (*interact_stopped_cb_.get())();
                 }
             }
            }

点对点系统通知

*进入麦序队列

参数 说明
type 自定义系统通知类型:加入连麦队列通知 kJoinQueue
roomid 房间ID 聊天室ID
style 网络通话类型 InactionType枚举
info 进入聊天队列用户信息 {"nick" : "","avatar" : ""} 字典

*退出麦序队列

参数 说明
type 自定义系统通知类型:退出连麦队列通知 kExitQueue
roomid 房间ID 聊天室ID

*主播同意连麦

参数 说明
type 自定义系统通知类型:主播同意连麦通知 kConnectingMIC
roomid 房间ID 聊天室ID
style 连麦者连麦方式 InactionType枚举

*连麦者拒绝连麦

当连麦者受到主播同意连麦通知时,会检查自身的连麦状态,如果连麦状态过期则需要发送一条拒绝消息告诉主播

参数 说明
type 自定义系统通知类型:连麦者拒绝连麦通知 kRejectConnecting
roomid 房间ID 聊天室ID

*主播强制连麦者断开

参数 说明
type 自定义系统通知类型:主播强制连麦者断开 kDisConnectingMIC
roomid 房间ID 聊天室ID

聊天室广播消息

*连麦者已连麦

参数 说明
uid 连麦者的 accid
nick 连麦者的昵称
avatar 连麦者的头像
style 连麦者的连麦方式

*连麦者已断开

参数 说明
uid 连麦者的 accid

其他说明