互动主播中,主播创建房间,然后第一个加入房间,再加入房间时,设置合成连麦布局
一 设置布局时机
1) 主播创建房间
bool ChatroomForm::StartLiveStream()
{
bool ret = false;
if (!livestreaming_)
{
if (master_) //主播创建房间
{
nim::VChat::Opt2Callback cb = nbase::Bind(&ChatroomForm::CreateRoomCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
nim_comp::VideoManager::GetInstance()->CreateRoom(GetRoomName(), "", cb);
std::string info;
info = "roomname:";
info.append(GetRoomName());
WriteSuccessFuc("主播已经调用creatRoom",info);
ret = true;
}
else
{
QLOG_APP(L"StartLiveStream...");
nim::NIMVideoChatMode mode = nim::kNIMVideoChatModeVideo;
if (((!master_) && is_audio_interact_) || living_model_ == kAudio)
mode = nim::kNIMVideoChatModeAudio;
nim::VChat::Opt2Callback cb = nbase::Bind(&ChatroomForm::JoinRoomCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
if (nim_comp::VideoManager::GetInstance()->JoinRoom(mode, GetRoomName(), GetRoomSessionId(), "", true, cb))
{
nim_comp::VideoManager::GetInstance()->SetChatRoomCb(nbase::Bind(&ChatroomForm::RoomConnectCallback, this, std::placeholders::_1),
nbase::Bind(&ChatroomForm::RoomPeopleChangeCallback, this, std::placeholders::_1, std::placeholders::_2), nbase::Bind(&ChatroomForm::LsInfoCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6));
ret = true;
std::string info;
info = "roomname:";
info.append(GetRoomName());
WriteSuccessFuc("观众已经调用JoinRoom,并成功", info);
QLOG_APP(L"JoinRoom...");
QLOG_APP(nbase::UTF8ToUTF16(GetRoomName()));
}
else{
std::string info;
info = "roomname:";
info.append(GetRoomName());
WriteSuccessFuc("观众已经调用JoinRoom,但失败", info);
}
}
}
return ret;
}
2)在创建房间回调后,加入房间
void ChatroomForm::CreateRoomCallback(int code, __int64 channel_id, const std::string& json_extension)
{
std::string info;
info = "roomname:";
info.append(GetRoomName());
info.append(" channel_id:");
info.append(std::to_string(channel_id));
info.append(" code:");
info.append(std::to_string(code));
WriteSuccessFuc("CreateRoomCallback 触发,主播已创建Room", info);
QLOG_ERR(L"CreateRoomCallback code:{0}") << code;
nim::VChat::Opt2Callback cb = nbase::Bind(&ChatroomForm::JoinRoomCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
nim::NIMVideoChatMode mode = nim::kNIMVideoChatModeVideo;
if (living_model_ == kAudio)
{
std::string def_device;
int no = 0;
nim_comp::VideoManager::GetInstance()->GetDefaultDevicePath(no, def_device, nim::kNIMDeviceTypeAudioIn);
nim_comp::VideoManager::GetInstance()->StartDevice(nim::kNIMDeviceTypeAudioIn, def_device, kDeviceSessionTypeChatRoom);
mode = nim::kNIMVideoChatModeAudio;
}
std::wstring dir = QPath::GetAppPath();
//测试的时候,给观众传入一个拉流地址
std::wstring iniPath = dir + L"set.ini";
if (PathFileExists(iniPath.c_str()))
{
TCHAR szPath[266];
GetPrivateProfileStringW(_T("Layout"), _T("layout"), NULL, szPath, 267, iniPath.c_str());
if (_tcslen(szPath) > 0)
{
m_nLayoutType = (nim::NIMVChatVideoSplitMode)_ttoi(szPath);
}
}
//主播加入房间,设置布局
if (nim_comp::VideoManager::GetInstance()->JoinRoom(mode, GetRoomName(), GetRoomSessionId(), rtmp_url_, true, cb,m_nLayoutType,pFontPage->BDoubleVideo()))
{
std::string info;
info = "roomname:";
info.append(GetRoomName());
info.append(" Session_id:");
info.append(GetRoomSessionId());
info.append(" code:");
info.append(std::to_string(code));
WriteSuccessFuc("主播成功调用JoinRoom", info);
pFontPage->SendResumeLiveMsg();
nim_comp::VideoManager::GetInstance()->SetChatRoomCb(nbase::Bind(&ChatroomForm::RoomConnectCallback, this, std::placeholders::_1),
nbase::Bind(&ChatroomForm::RoomPeopleChangeCallback, this, std::placeholders::_1, std::placeholders::_2), nbase::Bind(&ChatroomForm::LsInfoCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6));
}
else
{
std::string info;
info = "roomname:";
info.append(GetRoomName());
info.append(" Session_id:");
info.append(GetRoomSessionId());
info.append(" code:");
info.append(std::to_string(code));
WriteErrorFuc("主播JoinRoom失败", info);
StartLiveStreamRet(false,code);
}
}
3)在加入房间时,设置布局
bool VideoManager::JoinRoom(nim::NIMVideoChatMode mode, const std::string& room_name, const std::string& session_id, const std::string &rtmp_url, bool bypass_rtmp, nim::VChat::Opt2Callback cb, int nLayout, bool bDoubleMode)
{
Json::FastWriter fs;
Json::Value value;
value[nim::kNIMVChatSessionId] = session_id;
bool live = false;
if (!rtmp_url.empty())
{
value[nim::kNIMVChatRtmpUrl] = rtmp_url;
live = true;
}
if (bypass_rtmp)
{
value[nim::kNIMVChatBypassRtmp] = 1;
live = true;
}
if (live)
{
value[nim::kNIMVChatCustomVideo] = 1;
}
{
//kNIMVChatVideoFrameRateNormal = 0, /**< 视频通话帧率默认值,最大取每秒15帧 */
// kNIMVChatVideoFrameRate5 = 1, /**< 视频通话帧率 最大取每秒5帧 */
// kNIMVChatVideoFrameRate10 = 2, /**< 视频通话帧率 最大取每秒10帧 */
// kNIMVChatVideoFrameRate15 = 3, /**< 视频通话帧率 最大取每秒15帧 */
// kNIMVChatVideoFrameRate20 = 4, /**< 视频通话帧率 最大取每秒20帧 */
// kNIMVChatVideoFrameRate25 = 5, /**< 视频通话帧率 最大取每秒25帧 */
value[nim::kNIMVChatVideoFrameRate] = nim::kNIMVChatVideoFrameRate20;
value[nim::kNIMVChatVideoQuality] = nim::kNIMVChatVideoQuality720p;//分辨率
}
{
value[nim::kNIMVChatAudioHighRate] = 1; //语音高清模式
value[nim::kNIMVChatVideoRecord] = 1; //需要服务器录制视频
value["rtmp_record"] = 1; //互动直播录制开关
}
if (nLayout>=0)
{
value[nim::kNIMVChatSplitMode] = nLayout; //自定义布局
if (nLayout == nim::kNIMVChatSplitCustomLayout)
{
std::wstring selfLayoutStr;
std::wstring dir = QPath::GetAppPath();
//测试的时候,给观众传入一个拉流地址
std::wstring iniPath = dir + L"set.ini";
if (PathFileExists(iniPath.c_str()))
{
TCHAR szPath[2000];
std::wstring nSelfLayoutStr;
GetPrivateProfileStringW(_T("Layout"), _T("nSelfLayout"), NULL, szPath, 2000, iniPath.c_str());
if (_tcslen(szPath) > 0)
{
nSelfLayoutStr = szPath; //0,1,2,3
}
std::wstring selfLayoutItem = L"selfLayout";
selfLayoutItem.append(nSelfLayoutStr);
if (bDoubleMode)
{
selfLayoutItem = L"selfLayout6";
}
//
//selfLayout0 selfLayout1
GetPrivateProfileStringW(_T("Layout"), selfLayoutItem.c_str(), NULL, szPath, 2000, iniPath.c_str());
if (_tcslen(szPath) > 0)
{
selfLayoutStr = szPath;
}
}
value[nim::kNIMVChatCustomLayout] = nbase::UTF16ToUTF8(selfLayoutStr);
}
}
std::string json_value = fs.write(value);
return nim::VChat::JoinRoom(mode, room_name, json_value, cb);
}
bool VChat::JoinRoom(NIMVideoChatMode mode, const std::string& room_name, const std::string& json_extension, Opt2Callback cb)
{
Opt2Callback* cb_pointer = new Opt2Callback(cb);
return NIM_SDK_GET_FUNC(nim_vchat_join_room)(mode, room_name.c_str(), json_extension.c_str(), OnOpt2Callback, cb_pointer);
}
布局读取:
我将布局,写在了ini配置文件中
二 布局设置
布局设置网址 https://netease.im/paramCheck
参数说明:
1) version 版本, 默认即可,布局参数校验 规则的版本号
2) 主播是否作为底画面 set_host_as_main
主播作为底画面
主播不作为主画面
此时,可进行底色设置
3) 展示方式
adaption 裁剪充满控件方式,还是原图缩放方式 1 裁剪 0 缩放
4) x y, w h 横纵坐标 画面宽度、高度 采用万分比
5) 连麦者总体属性,还有两个 一是 连麦者数量 二是连麦者位置自适应
n_host_area_number 连麦者数量
special_show_mode 语译为 特定的显示模式 连麦者位置是否自适应
这个参数有点坑,默认是true
true时, 特定显示模式, 此时连麦者位置是固定的,不会自适应,房间内的连麦者不会自适应改变自己的位置
false时,非特定显示模式,连麦者位置自适应, 当有连麦者退出房间时,房间内的剩余连麦者自适应改变位置
设置好了,生成一json串,
{"version":0,"set_host_as_main":true,"host_area":{"adaption":1},"special_show_mode":true,"n_host_area_number":4,"n_host_area_0":{"position_x":7000,"position_y":300,"width_rate":2100,"height_rate":1000,"adaption":1},"n_host_area_1":{"position_x":7000,"position_y":1500,"width_rate":2100,"height_rate":1000,"adaption":1},"n_host_area_2":{"position_x":7000,"position_y":2700,"width_rate":2100,"height_rate":1000,"adaption":1},"n_host_area_3":{"position_x":7000,"position_y":3900,"width_rate":2100,"height_rate":1000,"adaption":1}}
可通过网站 http://tool.oschina.net/codeformat/json,对JSON串,进行格式化处理,方便显示
{
"version": 0,
"set_host_as_main": true,
"host_area": {
"adaption": 1
},
"special_show_mode": true,
"n_host_area_number": 4,
"n_host_area_0": {
"position_x": 7000,
"position_y": 300,
"width_rate": 2100,
"height_rate": 1000,
"adaption": 1
},
"n_host_area_1": {
"position_x": 7000,
"position_y": 1500,
"width_rate": 2100,
"height_rate": 1000,
"adaption": 1
},
"n_host_area_2": {
"position_x": 7000,
"position_y": 2700,
"width_rate": 2100,
"height_rate": 1000,
"adaption": 1
},
"n_host_area_3": {
"position_x": 7000,
"position_y": 3900,
"width_rate": 2100,
"height_rate": 1000,
"adaption": 1
}
}
设计过的布局如下:
底部
1)平铺底部
{
"version": 0,
"set_host_as_main": true,
"host_area": {
"adaption": 1
},
"special_show_mode": true,
"n_host_area_number": 4,
"n_host_area_0": {
"position_x": 0,
"position_y": 7000,
"width_rate": 2500,
"height_rate": 3000,
"adaption": 1
},
"n_host_area_1": {
"position_x": 2500,
"position_y": 7000,
"width_rate": 2500,
"height_rate": 3000,
"adaption": 1
},
"n_host_area_2": {
"position_x": 5000,
"position_y": 7000,
"width_rate": 2500,
"height_rate": 3000,
"adaption": 1
},
"n_host_area_3": {
"position_x": 7500,
"position_y": 7000,
"width_rate": 2500,
"height_rate": 3000,
"adaption": 1
}
}
1) 小布局
高 1000 宽 1250 (高/宽==5/6)
{
"version": 0,
"set_host_as_main": true,
"host_area": {
"adaption": 1
},
"special_show_mode": false,
"n_host_area_number": 4,
"n_host_area_0": {
"position_x": 2500,
"position_y": 9000,
"width_rate": 1250,
"height_rate": 1000,
"adaption": 1
},
"n_host_area_1": {
"position_x": 3750,
"position_y": 9000,
"width_rate": 1250,
"height_rate": 1000,
"adaption": 1
},
"n_host_area_2": {
"position_x": 5000,
"position_y": 9000,
"width_rate": 1250,
"height_rate": 1000,
"adaption": 1
},
"n_host_area_3": {
"position_x": 6250,
"position_y": 9000,
"width_rate": 1250,
"height_rate": 1000,
"adaption": 1
}
}
高 1500 宽 1250 (高/宽==6/5)
{
"version": 0,
"set_host_as_main": true,
"host_area": {
"adaption": 1
},
"special_show_mode": false,
"n_host_area_number": 4,
"n_host_area_0": {
"position_x": 2500,
"position_y": 8500,
"width_rate": 1250,
"height_rate": 1500,
"adaption": 1
},
"n_host_area_1": {
"position_x": 3750,
"position_y": 8500,
"width_rate": 1250,
"height_rate": 1500,
"adaption": 1
},
"n_host_area_2": {
"position_x": 5000,
"position_y": 8500,
"width_rate": 1250,
"height_rate": 1500,
"adaption": 1
},
"n_host_area_3": {
"position_x": 6250,
"position_y": 8500,
"width_rate": 1250,
"height_rate": 1500,
"adaption": 1
}
}
隐藏其它连麦者
{
"version": 0,
"set_host_as_main": true,
"host_area": {
"adaption": 1
},
"special_show_mode": false,
"n_host_area_number": 4,
"n_host_area_0": {
"position_x": 0,
"position_y": 9999,
"width_rate": 1,
"height_rate": 1,
"adaption": 1
},
"n_host_area_1": {
"position_x": 0,
"position_y": 9999,
"width_rate": 1,
"height_rate": 1,
"adaption": 1
},
"n_host_area_2": {
"position_x": 0,
"position_y": 9999,
"width_rate": 1,
"height_rate": 1,
"adaption": 1
},
"n_host_area_3": {
"position_x": 0,
"position_y": 9999,
"width_rate": 1,
"height_rate": 1,
"adaption": 1
}
}
参考资料:
http://faq.yunxin.163.com/kb/main/#/item/KB0261
JSON格式化工具: http://tool.oschina.net/codeformat/json
JSON 压缩工具: https://www.sojson.com/yasuo.html