直播项目之 将SDK提供的C 头文件,封装成C++类 管理
一:封装前准备
以网易直播SDK为例解析,将其封装成一个类,便于管理。
提供了 SDK/include目录: 存放直播推流 SDK API的头文件。
SDK/lib目录: 存放直播推流 SDK 的静态库文件和动态库文件
建立静态库项目:
stdafx.h添加常用头文件
// stdafx.h : 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 特定于项目的包含文件
//
#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的信息
#include <string>
#include <map>
#include <set>
#include <list>
#include <functional>
#include <memory>
// TODO: 在此处引用程序需要的其他头文件
#include "nls_api/nlss_define.h"
#include "nls_api/nlss_type.h"
#include "nls_api/nlss_api.h"
#include "nls_api/nlss_childvideo_api.h"
#include "nls_api/nlss_childaudio_api.h"
//base header
#include "base/callback/callback.h"
#include "base/util/string_util.h"
#include "base/util/unicode.h"
#include "base/util/string_number_conversions.h"
#include "base/memory/deleter.h"
#include "base/memory/singleton.h"
#include "base/win32/platform_string_util.h"
#include "base/win32/path_util.h"
#include "base/win32/win_util.h"
#include "base/thread/thread_manager.h"
#include "base/macros.h"
#include "base/base_types.h"
#include "base/file/file_util.h"
#include "base/time/time.h"
#include "base/framework/task.h"
#include "base/util/at_exit.h"
//shared project
#include "shared/utf8_file_util.h"
#include "shared/tool.h"
#include "shared/log/log.h"
#include "shared/util.h"
#include "shared/closure.h"
#include "shared/threads.h"
二 封装成推流实例类
2.1 新版封装
#pragma once
#include "nls_cpp_def.h"
class NlsInstance : public nbase::SupportWeakCallback
{
public:
NlsInstance(const std::string& work_path, const std::string& cache_path);
~NlsInstance();
bool IsInitialized() { return m_bInitialized; }
bool IsVideoPreviewing() { return m_bVideoPreviewing; }
bool IsLiveStreaming() { return m_bLiveStreaming; }
bool IsVideoLiveStreaming() { return m_bVideoLiveStreaming; }
bool IsAudioLiveStreaming() { return m_bAudioLiveStreaming; }
bool IsRecording() { return m_bRecording; }
SIZE GetOutputSize() { return { m_stVideoParam.iOutWidth, m_stVideoParam.iOutHeight }; }
/**
* 初始化直播
*
* @param output_type: 1——仅音频;2——仅视频;3——音视频
* @param width: 视频输出宽度
* @param height:视频输出高度
* @param url: 推流地址,可为空,稍后单独设置
* @param sync_ts_type: 透传同步时间戳类型:0——不透传;1——透传,从0开始;2——透传,绝对时间
*
* @return NLS_ERRNO: 初始化成功返回true,否则返回false
*/
NLS_ERRNO Initialize(int output_type = 3, int width = 0, int height = 0, const std::string& url = "", int sync_ts_type = 0);
/**
* 反初始化直播,重新初始化之前必须先执行反初始化
*
* @return 无
*/
void Uninitialize();
/**
* 设置视频水印
*
* @param image_path: 水印图片路径
* @param pos_x: 水印在画布上位置的横坐标.
* @param pos_y: 水印在画布上位置的纵坐标.
*
* @return 无
*/
void SetWatermark(const std::string& image_path, int pos_x, int pos_y);
/**
* 设置视频截图的的回调。注意:为减少内存拷贝带来的损耗,此回调的执行默认不在UI线程。
*
* @param cb: 回调闭包
*
* @return 无
*/
void SetVideoDataCB(const VideoDataCallback& cb);
/**
* 设置推流状态的回调
*
* @param cb: 回调闭包
*
* @return 无
*/
void SetStatusCB(const StatusCallback& cb);
/**
* 修改视频输出的宽高,必须在未推流状态下才能修改成功
*
* @param width: 视频输出宽度
* @param height:视频输出高度
*
* @return NLS_ERRNO: 修改成功返回true,否则返回false
*/
NLS_ERRNO SetOutputSize(int width, int height);
/**
* 修改推流地址,必须在未推流状态下才能修改成功
*
* @param push_url:新的推流地址
*
* @return NLS_ERRNO: 修改成功返回true,否则返回false
*/
NLS_ERRNO SetPushUrl(const std::string& push_url);
/**
* 修改同步时间戳类型,必须在未推流状态下才能修改成功
*
* @param sync_ts_type:新的同步时间戳类型
*
* @return NLS_ERRNO: 修改成功返回true,否则返回false
*/
NLS_ERRNO SetSyncTimestampType(int sync_ts_type);
/**
* 开启或关闭视频预览
*
* @param preview:是否预览视频
*
* @return NLS_ERRNO: 开启/关闭成功返回true,否则返回false
*/
NLS_ERRNO SwitchVideoPreview(bool preview);
/**
* 暂停或继续视频预览
*
* @param pause:是否暂停预览
*
* @return 无
*/
void SwitchPauseVideoPreview(bool pause);
/**
* 开始或停止直播推流
*
* @param live_stream:是否开始直播推流
* @param cb:开始/停止直播推流的结果回调
*
* @return 无
*/
void SwitchLiveStream(bool live_stream, const AsyncCallback& cb);
/**
* 暂停或继续视频推流
*
* @param pause:是否暂停视频推流
*
* @return 无
*/
void SwicthPauseVideoLiveStream(bool pause);
/**
* 暂停或继续音频推流
*
* @param pause:是否暂停音频推流
*
* @return 无
*/
void SwicthPauseAudioLiveStream(bool pause);
/**
* 开始或停止录制
*
* @param record:是否开始录制本地文件
* @param flv_or_mp4:若是开始录制,本参数指定录制格式为flv还是mp4
* @param utf8_save_path:若是开始录制,本参数指定录制保存的本地路径
* @param cb:开始/停止直播推流的结果回调
*
* @return 无
*/
void SwitchRecord(bool record, bool flv_or_mp4, const std::string& utf8_save_path, const AsyncCallback& cb);
/**
* 发送透传的自定义数据
*
* @param content: 发送数据:字符串,最大长度 50KB (估计全角字数在1600左右)(需要校验长度)
* @param send_interval: 发送间隔:同一个数据的多次发送之间 间隔的视频帧数,0表示每帧都发,最大50
* @param send_times: 发送总次数:目前暂定最大500
*
* @return NLS_ERRNO: 开始/停止成功返回true,否则返回false
*/
NLS_ERRNO SendCustomData(const std::string& content, int send_interval, int send_times);
/**
* 获取直播推流状态信息
*
* @param video_frame_rate: 视频帧率
* @param video_real_width: 视频分辨率宽度
* @param video_real_height: 视频分辨率高度
* @param total_bitrate: 音视频总码率
* @param net_level: 网络状态等级,见EN_NLSS_NET_LEVEL
*
* @return NLS_ERRNO: 开始/停止成功返回true,否则返回false
*/
NLS_ERRNO GetStasticInfo(int& video_frame_rate, int& video_real_width, int& video_real_height, int& total_bitrate, int& net_level);
#pragma region CHILD_VIDEO
//////////////////////////////////////////////////////////////////////////
/**
* 打开一个摄像头子视频
*
* @param device_path:摄像头设备路径
* @param quality:清晰度,只能是枚举EN_NLSS_VIDEOQUALITY_LVL中的值
*
* @return HANDLE: 返回子视频对象句柄,如果打开失败,就返回NULL
*/
NLS_ERRNO OpenCamera(const std::string& device_path, int quality, HANDLE* child_video_out);
/**
* 打开截屏(全屏或区域)子视频
*
* @param fullscreen:是否截取全屏
* @param clip_rect:如果是区域截屏,需给出截取的屏幕区域
*
* @return HANDLE: 返回子视频对象句柄,如果打开失败,就返回NULL
*/
NLS_ERRNO OpenScreenCapture(bool fullscreen, const RECT& clip_rect, HANDLE* child_video_out);
/**
* 打开应用程序窗口截取子视频
*
* @param hWnd:要截取的窗口句柄
*
* @return HANDLE: 返回子视频对象句柄,如果打开失败,就返回NULL
*/
NLS_ERRNO OpenAppCapture(HWND hWnd, HANDLE* child_video_out);
/**
* 打开自定义数据子视频
*
* @param width:自定义数据的宽度
* @param height:自定义数据的高度
* @param format: 自定义数据的格式,参见EN_NLSS_VIDEOIN_FMT
*
* @return HANDLE: 返回子视频对象句柄,如果打开失败,就返回NULL
*/
NLS_ERRNO OpenCustomChildVideo(int width, int height, int fps, int format, HANDLE* child_video_out);
/**
* 打开DeckLink设备的视频
*
* @param png_path:png图片路径
*
* @return HANDLE: 返回子视频对象句柄,如果打开失败,就返回NULL
*/
NLS_ERRNO OpenImageCapture(const std::string& png_path, HANDLE* child_video_out);
/**
* 打开DeckLink设备的视频
*
* @param device_path:DeckLink设备路径
* @param mode:DeckLink打开模式
*
* @return HANDLE: 返回子视频对象句柄,如果打开失败,就返回NULL
*/
NLS_ERRNO OpenDeckLinkVideo(const std::string& device_path, int mode, HANDLE* child_video_out);
/**
* 关闭子视频
*
* @param child_video:子视频对象的句柄
*
* @return 无
*/
void CloseChildVideo(HANDLE child_video);
/**
* 设置子视频为背景(主画面)
*
* @param child_video:子视频对象的句柄
*
* @return 无
*/
void SetChildVideoAsBackground(HANDLE child_video);
/**
* 调整子视频的绘制次序
*
* @param child_video:子视频对象的句柄
* @param adjust_up:为true表示向上调整一级,为false表示向下调整一级
*
* @return 无
*/
void AdjustChildVideoIndex(HANDLE child_video, bool adjust_up);
/**
* 设置子视频在画布上的绘制区域
*
* @param child_video:子视频对象的句柄
* @param rect:绘制区域
*
* @return 无
*/
void SetChildVideoDisplayRect(HANDLE child_video, const RECT& rect);
/**
* 隐藏或显示子视频
*
* @param child_video:子视频对象的句柄
* @param hide:隐藏或显示
*
* @return 无
*/
void SwitchChildVideoDisplay(HANDLE child_video, bool hide);
/**
* 设置子视频单独预览的回调。注意:为减少内存拷贝带来的损耗,此回调的执行默认不在UI线程。
*
* @param child_video:子视频对象的句柄
* @param cb:回调闭包
*
* @return 无
*/
void SetChildVideoDataCB(HANDLE child_video, const VideoDataCallback& cb);
/**
* 开始或停止预览子视频
*
* @param child_video:子视频对象的句柄
* @param hide:隐藏或显示
*
* @return 无
*/
void SwitchChildVideoPreview(HANDLE child_video, bool preview);
/**
* 发送自定义视频数据
*
* @param child_video:子视频对象的句柄(必须为EN_NLSS_VIDEOIN_RAWDATA类型的子视频)
* @param data:数据首地址
* @param len:数据字节数
*
* @return bool: 发送成功返回true,失败返回false
*/
NLS_ERRNO SendCustomVideoData(HANDLE child_video, unsigned char* data, int size);
#pragma endregion
#pragma region CHILD_AUDIO
//////////////////////////////////////////////////////////////////////////
/**
* 打开麦克风子音频
*
* @param device_path:麦克风设备的路径
* @param samplerate:采样率
* @param channel_num:声道数
*
* @return HANDLE: 返回子音频对象句柄,如果打开失败,就返回NULL
*/
NLS_ERRNO OpenMicrophone(const std::string& device_path, int samplerate, int channel_num, HANDLE* child_audio_out);
/**
* 打开系统声卡采集子音频
*
*
* @return HANDLE: 返回子音频对象句柄,如果打开失败,就返回NULL
*/
NLS_ERRNO OpenSystemSoundCapture(HANDLE* child_audio_out);
/**
* 打开自定义数据子音频
*
* @param samplerate:采样率
* @param channel_num:声道数
*
* @return HANDLE: 返回子音频对象句柄,如果打开失败,就返回NULL
*/
NLS_ERRNO OpenCustomDataChildAudio(int samplerate, int channel_num, HANDLE* child_audio_out);
/**
* 打开DeckLink设备的音频
*
* @param device_path:DeckLink设备的路径
* @param samplerate:采样率
* @param channel_num:声道数
*
* @return HANDLE: 返回子音频对象句柄,如果打开失败,就返回NULL
*/
NLS_ERRNO OpenDeckLinkAudio(const std::string& device_path, int samplerate, int channel_num, HANDLE* child_audio_out);
/**
* 关闭子音频
*
* @param child_video:子音频对象的句柄
*
* @return 无
*/
void CloseChildAudio(HANDLE child_audio);
/**
* 设置子音频的音量
*
* @param child_video:子音频对象的句柄
* @param volume:0表示静音,100表示原始音量
*
* @return 无
*/
void SetChildAudioVolume(HANDLE child_audio, int volume);
/**
* 暂停/继续子音频采集
*
* @param child_video:子音频对象的句柄
* @param pause:true表示暂停,false表示继续
*
* @return 无
*/
void SwitchChildAudioCapture(HANDLE child_audio, bool pause);
/**
* 发送自定义音频数据
*
* @param child_video:子音频对象的句柄(必须为EN_NLSS_AUDIOIN_RAWDATA类型的子音频)
* @param data:数据首地址
* @param len:数据字节数
*
* @return bool: 发送成功返回true,失败返回false
*/
NLS_ERRNO SendCustomAudioData(HANDLE child_audio, unsigned char* data, int size);
#pragma endregion
private:
enum
{
CALLBACK_TAG_STATUS,
CALLBACK_TAG_VIDEO,
CALLBACK_TAG_CHILD_VIDEO,
CALLBACK_TAG_AUDIO,
CALLBACK_TAG_CHILD_AUDIO,
CALLBACK_COUNT
};
static nbase::NLock s_InstListLock[CALLBACK_COUNT]; //几个回调就几个锁
static std::set<NlsInstance*> s_stInstList;
static void MergedVideoSampleCb(_HNLSSERVICE hNLSService, ST_NLSS_VIDEO_SAMPLER *pstSampler);
static void ChildVideoSampleCb(_HNLSSERVICE hNLSService, _HNLSSCHILDSERVICE hNLSSChild, ST_NLSS_VIDEO_SAMPLER *pstSampler);
static void StatusCb(_HNLSSERVICE hNLSService, EN_NLSS_STATUS enStatus, EN_NLSS_ERRCODE enErrCode);
void UIStatusCb(EN_NLSS_STATUS enStatus, EN_NLSS_ERRCODE enErrCode);
void DoSwitchLiveStream(bool live_stream, const AsyncCallback& cb);
void DoSwitchRecord(bool record, bool flv_or_mp4, const std::string& utf8_save_path, const AsyncCallback& cb);
private:
bool m_bInitialized = false;
bool m_bVideoPreviewing = false;
bool m_bVideoPreviewPaused = false;
bool m_bLiveStreaming = false;
bool m_bVideoLiveStreaming = false;
bool m_bAudioLiveStreaming = false;
bool m_bRecording = false;
_HNLSSERVICE m_hNlsInstance = NULL;
EN_NLSS_OUTCONTENT m_enContent = EN_NLSS_OUTCONTENT_AV;
std::string m_strPushUrl;
ST_NLSS_VIDEOOUT_PARAM m_stVideoParam;
ST_NLSS_AUDIOOUT_PARAM m_stAudioParam;
EN_NLSS_SYNC_TS_TYPE m_enSyncTsType = EN_NLSS_SYNC_TS_NONE;
StatusCallback m_StatusCB;
VideoDataCallback m_VideoDataCB;
AudioDataCallback m_AudioDataCB;
std::string m_VideoBuffer;
nbase::NLock m_VideoBufferLock;
std::string m_AudioBuffer;
nbase::NLock m_AudioBufferLock;
std::map<_HNLSSCHILDSERVICE, NlsChildVideo> m_stChildVideoMap;
nbase::NLock m_ChildVideoLock;
std::map<_HNLSSCHILDSERVICE, NlsChildAudio> m_stChildAudioMap;
nbase::NLock m_ChildAudioLock;
};
其中,类成员变量,在CPP中初始化
nbase::NLock NlsInstance::s_InstListLock[CALLBACK_COUNT];
std::set<NlsInstance*> NlsInstance::s_stInstList;
void NlsInstance::StatusCb(_HNLSSERVICE hNLSService, EN_NLSS_STATUS enStatus, EN_NLSS_ERRCODE enErrCode)
{
nbase::NAutoLock auto_lock(&s_InstListLock[CALLBACK_TAG_STATUS]);
for (auto iter = s_stInstList.begin(); iter != s_stInstList.end(); iter++)
{
if ((*iter)->m_hNlsInstance == hNLSService)
{
NlsInstance *inst = (*iter);
if (inst->m_StatusCB)
Post2UI(nbase::Bind(&NlsInstance::UIStatusCb, inst, enStatus, enErrCode));
break;
}
}
}
直播接口调用顺序
Nlss_Create -> Nlss_InitParam -> Nlss_Start -> Nlss_Stop -> Nlss_UninitParam -> Nlss_Destroy的调用顺序是固定的
对应到封装的直播类上为:
1) Nlss_Create
NlsInstance::NlsInstance(const std::string & work_path, const std::string & cache_path)
{
memset(&m_stVideoParam, 0, sizeof(ST_NLSS_VIDEOOUT_PARAM));
memset(&m_stAudioParam, 0, sizeof(ST_NLSS_AUDIOOUT_PARAM));
Nlss_Create(work_path.c_str(), NULL/*cache_path.c_str()*/, &m_hNlsInstance);
for (int i = 0; i < CALLBACK_COUNT; i++)
s_InstListLock[i].Lock();
s_stInstList.insert(this);
for (int i = CALLBACK_COUNT - 1; i >= 0 ; i--)
s_InstListLock[i].Unlock();
}
2) Nlss_InitParam -> Nlss_Start
NLS_ERRNO NlsInstance::Initialize(int output_type /*= 3*/, int width /*= 0*/, int height /*= 0*/, const std::string& url /*= ""*/, int sync_ts_type /*= 0*/)
{
if (m_bInitialized)
return NLS_ERRNO_UNINITIALIZED;
if (output_type < EN_NLSS_OUTCONTENT_AUDIO || output_type > EN_NLSS_OUTCONTENT_AV)
return NLS_ERRNO_PARAM_ERROR;
if ((output_type & EN_NLSS_OUTCONTENT_VIDEO) && (width == 0 || height == 0))
return NLS_ERRNO_PARAM_ERROR;
if (sync_ts_type < EN_NLSS_SYNC_TS_NONE || sync_ts_type > EN_NLSS_SYNC_TS_BASE_ON_MACHINE_START)
return NLS_ERRNO_PARAM_ERROR;
ST_NLSS_PARAM param;
Nlss_GetDefaultParam(m_hNlsInstance, ¶m);
param.enOutContent = (EN_NLSS_OUTCONTENT)output_type;
param.paOutUrl = (char*)url.c_str();
param.stVideoParam.iOutWidth = width;
param.stVideoParam.iOutHeight = height;
param.stVideoParam.iOutFps = 20; //Nlss_GetDefaultParam得到的默认值也是20帧
param.stVideoParam.iOutBitrate = width * height * param.stVideoParam.iOutFps * 6 / 100; //经验公式:width * height * fps * 6 / 100
param.enSyncTsType = (EN_NLSS_SYNC_TS_TYPE)sync_ts_type;
m_enContent = param.enOutContent;
m_strPushUrl = url;
m_stVideoParam = param.stVideoParam;
m_stAudioParam = param.stAudioParam;
m_enSyncTsType = param.enSyncTsType;
if (Nlss_InitParam(m_hNlsInstance, ¶m) != NLSS_OK)
return NLS_ERRNO_SDK_RET_ERROR;
if (Nlss_Start(m_hNlsInstance) != NLSS_OK)
{
Nlss_UninitParam(m_hNlsInstance);
return NLS_ERRNO_SDK_RET_ERROR;
}
Nlss_SetStatusCB(m_hNlsInstance, StatusCb);
Nlss_SetVideoSamplerCB(m_hNlsInstance, MergedVideoSampleCb);
m_bInitialized = true;
return NLS_ERRNO_SUCCESS;
}
3) Nlss_Stop -> Nlss_UninitParam
void NlsInstance::Uninitialize()
{
if (!m_bInitialized)
return;
if (m_bRecording)
SwitchRecord(false, false, "", AsyncCallback());
if (m_bLiveStreaming)
SwitchLiveStream(false, AsyncCallback());
while (m_bLiveStreaming)
Sleep(100);
if (m_bVideoPreviewing)
SwitchVideoPreview(false);
{
nbase::NAutoLock auto_lock1(&m_ChildVideoLock);
for (auto iter = m_stChildVideoMap.begin(); iter != m_stChildVideoMap.end(); iter++)
{
Nlss_ChildVideoStopCapture(iter->first);
Nlss_ChildVideoClose(iter->first);
}
m_stChildVideoMap.clear();
nbase::NAutoLock auto_lock2(&m_ChildAudioLock);
for (auto iter = m_stChildAudioMap.begin(); iter != m_stChildAudioMap.end(); iter++)
{
Nlss_ChildAudioStopCapture(iter->first);
Nlss_ChildAudioClose(iter->first);
}
m_stChildAudioMap.clear();
}
Nlss_SetVideoSamplerCB(m_hNlsInstance, NULL);
Nlss_SetStatusCB(m_hNlsInstance, NULL);
Nlss_Stop(m_hNlsInstance);
Nlss_UninitParam(m_hNlsInstance);
memset(&m_stVideoParam, 0, sizeof(ST_NLSS_VIDEOOUT_PARAM));
memset(&m_stAudioParam, 0, sizeof(ST_NLSS_AUDIOOUT_PARAM));
m_strPushUrl = "";
m_enContent = EN_NLSS_OUTCONTENT_AV;
m_bInitialized = false;
}
4) Nlss_Destroy
NlsInstance::~NlsInstance()
{
Nlss_Destroy(m_hNlsInstance);
for (int i = 0; i < CALLBACK_COUNT; i++)
s_InstListLock[i].Lock();
s_stInstList.erase(this);
for (int i = CALLBACK_COUNT - 1; i >= 0; i--)
s_InstListLock[i].Unlock();
}
2.2 早先版本基础上封装:
考虑到之前用的是早先版本,完全替换新版本代价太大,就在早先版本上 扩展封装处理
struct DeviceInfo
{
std::string device_path_;
std::string friendly_name_;
};
class LssManange
{
public:
//初始化和释放dll,必须先调用init才能使用nim_ls中的其他接口
static bool Init();
static void Exit();
//设备
static bool GetAvailableAppWindNum(int *piAppWindNum);
static bool GetAvailableAppWind(ST_NLSS_INDEVICE_INF **pLSAppWindTitles, int* iMaxNum);
static bool GetFreeDevicesNum(int* video_num, int* audio_num);
static bool GetFreeDeviceInf(NLSS_OUT ST_NLSS_INDEVICE_INF **pstVideoDevices, int*iMaxVideoDevicesNum, NLSS_OUT ST_NLSS_INDEVICE_INF** pstAudioDevices, int *iMaxAudioDevicesNum);
//static void ClearDeviceInfo(ST_NLSS_INDEVICE_INF** devices, int num);
static bool GetPerCameraCaptureinf(ST_NLSS_INDEVICE_INF *pstCamera, NLSS_OUT ST_NLSS_CAMERA_CAPTURE_PARAM *pstCaptureParams, NLSS_OUT int *piNum);
static nim_comp::VideoFrameMng* GetVideoFrameMng();
static nim_comp::VideoFrameMng* GetSoloVideoFrameMng();
static bool IsBlackVideo;
};
class LsSession : public virtual nbase::SupportWeakCallback
{
public:
LsSession();
~LsSession();
bool IsLiveStreaming() { return live_streaming_; }
/**
* 初始化直播
*
* @param output_type: 1——仅音频;2——仅视频;3——音视频
* @param width: 视频输出宽度
* @param height:视频输出高度
* @param url: 推流地址,可为空,稍后单独设置
* @param sync_ts_type: 透传同步时间戳类型:0——不透传;1——透传,从0开始;2——透传,绝对时间
*
* @return NLS_ERRNO: 初始化成功返回true,否则返回false
*/
NLS_ERRNO Initialize(int output_type = 3, int width = 0, int height = 0, int nfps=20, const std::string& url = "", int sync_ts_type = 0);
//typedef std::function<void(_HNLSSERVICE, ST_NLSS_VIDEO_SAMPLER)> VideoCBType;
void ClearSession();
bool Create(const char *paWorkPath);
void GetSDKVersion(NLSS_OUT char *ppaVersion);
void Destroy();
bool GetDefaultParam(NLSS_OUT ST_NLSS_PARAM *pstParam);
bool InitParam(ST_NLSS_PARAM *pstParam);
void SetVideoWaterMark(ST_NLSS_VIDEO_WATER_PARAM *pstWaterParam);
void SetVideoSamplerCB();
void SetVideoSamplerCB(PFN_NLSS_MERGED_VIDEO_SAMPLER_CB cb);
void SetStatusCB(PFN_NLSS_STATUS_NTY pFunStatusNty);
void UninitParam();
bool OnStartVideo();
void OnStopVideo();
bool OnStartVideoPreview();
void OnPauseVideoPreview();
void OnResumeVideoPreview();
void OnStopVideoPreview();
bool OnStartLiveStream(nim_nls::OptCallback cb);
bool OnStopLiveStream(nim_nls::OptCallback cb);
void OnPauseVideoLiveStream();
void OnResumeVideoLiveStream();
void OnPauseAudioLiveStream();
void OnResumeAudioLiveStream();
bool OnSendCustomVideoData(char *pcVideoData, int iLen);
bool OnSendCustomAudioData(char *pcAudioData, int iLen, int iSampleRate);
bool OnGetStaticInfo(NLSS_OUT ST_NLSS_STATS *pstStats);
bool OnStartRecord(char*path);
void OnStopRecord();
void OnSetAudioVolume(int iRatio);
void GetDefaultAudioParam(NLSS_OUT ST_NLSS_AUDIOIN_PARAM *pstParam);
void InitAudioParam(ST_NLSS_AUDIOIN_PARAM* pstParam);
void CloseAudio();
bool OnStartAudio();
////////////////////////////////////////////////////////
bool OnChildVideoOpen(const std::string& accid, ST_NLSS_VIDEOIN_PARAM *pVideoInParam);
void OnChildVideoSetBackLayer(const std::string& accid);
void OnChildVideoSetAdjustLayer(const std::string& accid, bool bAdustUp);
void OnChildVideoSetDisplayRect(const std::string& accid, ST_NLSS_RECTSCREEN_PARAM *pstRect);
void OnChildVideoClose(const std::string& accid);
bool OnChildVideoIsOtherDevice(const std::string& accid);
bool OnChildVideoOpenOtherDeviceConf(const std::string& accid);
void OnChildVideoSwitchDisplay(const std::string& accid, bool bHide);
bool OnChildVideoStartCapture(const std::string& accid);
void OnChildVideoStopCapture(const std::string& accid);
void OnChildVideoSetSoloPreviewCB(const std::string& accid, PFN_NLSS_CHILD_VIDEO_SAMPLER_CB pFunVideoSamplerCB);
void OnChildVideoSwitchSoloPreview(const std::string& accid, bool bOn);
void OnChildVideoPauseLiveStream(const std::string& accid);
void OnChildVideoResumeLiveStream(const std::string& accid);
////////////////////////////////////////////////////////
//new 更新参数
bool UpdateVideoOutParam(ST_NLSS_VIDEOOUT_PARAM* pstVideoParam);
bool UpdateAudioOutParam(ST_NLSS_AUDIOOUT_PARAM* pstAudioParam);
bool UpdatePushUrl(char* pucPushUrl);
//音频管理
void DeleteDeviceInfoArray(ST_NLSS_INDEVICE_INF * array, int num);
ST_NLSS_INDEVICE_INF* NewDeviceInfoArray(int num);
NLS_ERRNO GetMicrophoneList(std::list<StDeviceInfo>& microphone_list);
NLS_ERRNO GetDeckLinkList(std::list<DeckLinkInfo>& decklink_list);
/**
* 打开系统声卡采集子音频
*
*
* @return HANDLE: 返回子音频对象句柄,如果打开失败,就返回NULL
*/
NLS_ERRNO OpenSystemSoundCapture(HANDLE* child_audio_out);
/**
* 打开麦克风子音频
*
* @param device_path:麦克风设备的路径
* @param samplerate:采样率
* @param channel_num:声道数
*
* @return HANDLE: 返回子音频对象句柄,如果打开失败,就返回NULL
*/
NLS_ERRNO OpenMicrophone(const std::string& device_path, int samplerate, int channel_num, HANDLE* child_audio_out);
/**
* 暂停/继续子音频采集
*
* @param child_video:子音频对象的句柄
* @param pause:true表示暂停,false表示继续
*
* @return 无
*/
void SwitchChildAudioCapture(HANDLE child_audio, bool pause);
/**
* 设置子音频的音量
*
* @param child_video:子音频对象的句柄
* @param volume:0表示静音,100表示原始音量
*
* @return 无
*/
void SetChildAudioVolume(HANDLE child_audio, int volume);
/**
* 关闭子音频
*
* @param child_video:子音频对象的句柄
*
* @return 无
*/
void CloseChildAudio(HANDLE child_audio);
void CloseAllAudio();
std::string GetAudioPathByName(std::string name, std::list<StDeviceInfo>& microphone_list);
_HNLSSCHILDSERVICE GetHandleByAudioPath(std::string path);
_HNLSSCHILDSERVICE GetSysHandle();
//
// 0 静音 、100原音
void SetSysVolum(int nVol);
void MuteSysAudio();
void UnMuteSysAudio(int nVol=100);
void SetCurMicVolum(int nVol);
bool SetCurMic(std::string Path);
void MuteOthers(std::string Path);
void MuteAllMics();
bool OpenAndCapture(ST_NLSS_AUDIOIN_PARAM ¶m, NlsChildAudio &childAudio);
private:
void DoStartLiveStream(OptCallback cb);
void DoStopLiveStream(OptCallback cb);
public:
_HNLSSCHILDSERVICE GetChildServiceByAccid(std::string accid);
bool BCaptruing(std::string accid); //是否正在采集
bool BCloseAccid(std::string accid);//accid窗口是否关闭
bool BHideAccid(std::string accid);//accid窗口是否隐藏
private:
nbase::NLock lock_;
_HNLSSERVICE* ls_client_; //推流实例
_HNLSSERVICE m_hNlsInstance = NULL;
bool live_streaming_; //正在推流中
bool is_recording_; //正在记录中
bool preview_; //是否预览中
std::map<std::string, _HNLSSCHILDSERVICE > nlss_child_services_; //子窗口句柄映射
std::map<std::string, bool> m_AccidCaptureMap; //accid是否正在采集的map
std::map<std::string, bool> m_AccidCloseMap; //accid是否关闭
std::map<std::string, bool> m_AccidHideMap; //accid是否关闭
std::string accid; //子窗口标识符
std::string audio_devid_;
ST_NLSS_AUDIOIN_PARAM audio_param_;
_HNLSSCHILDSERVICE audio_handle_;
_HNLSSCHILDSERVICE curMic_handle_; //当前选择的MIC
nbase::NLock m_ChildAudioLock;
nbase::NLock m_ChildVideoLock;
std::map<_HNLSSCHILDSERVICE, NlsChildAudio> m_stChildAudioMap;
};
1)Nlss_Create 创建直播对象
bool LsSession::Create(const char *paWorkPath)
{
ls_client_ = new _HNLSSERVICE;
if (NLSS_OK == NLS_SDK_GET_FUNC(Nlss_Create)(paWorkPath, paWorkPath, pLsClient))
{
ls_client_ = pLsClient;
return true;
}
else
{
delete ls_client_;
ls_client_ = nullptr;
assert(0);
return false;
}
}
2) Nlss_InitParam 初始化直播对象,直播参数ST_NLSS_PARAM生效
bool LsSession::InitParam(ST_NLSS_PARAM *pstParam)
{
if (NLSS_OK == NLS_SDK_GET_FUNC(Nlss_InitParam)(LsClient, pstParam))
return true;
else
return false;
}
NLS_ERRNO LsSession::Initialize(int output_type /*= 3*/, int width /*= 0*/, int height /*= 0*/, int nfps/* = 20*/, const std::string& url /*= ""*/, int sync_ts_type /*= 0*/)
{
if (output_type < EN_NLSS_OUTCONTENT_AUDIO || output_type > EN_NLSS_OUTCONTENT_AV)
return NLS_ERRNO_PARAM_ERROR;
if ((output_type & EN_NLSS_OUTCONTENT_VIDEO) && (width == 0 || height == 0))
return NLS_ERRNO_PARAM_ERROR;
if (sync_ts_type < EN_NLSS_SYNC_TS_NONE || sync_ts_type > EN_NLSS_SYNC_TS_BASE_ON_MACHINE_START)
return NLS_ERRNO_PARAM_ERROR;
ST_NLSS_PARAM param;
NLS_SDK_GET_FUNC(Nlss_GetDefaultParam)(LsClient, ¶m);
param.enOutContent = (EN_NLSS_OUTCONTENT)output_type;
param.paOutUrl = (char*)url.c_str();
param.stVideoParam.iOutWidth = width;
param.stVideoParam.iOutHeight = height;
param.stVideoParam.iOutFps = nfps; //Nlss_GetDefaultParam得到的默认值也是20帧
param.stVideoParam.iOutBitrate = width * height * param.stVideoParam.iOutFps * 6 / 100; //经验公式:width * height * fps * 6 / 100
param.stVideoParam.bQosAutoChangeRatio = true; //自动根据网络情况,调节参数
param.enSyncTsType = (EN_NLSS_SYNC_TS_TYPE)sync_ts_type;
if (NLS_SDK_GET_FUNC(Nlss_InitParam)(LsClient, ¶m) != NLSS_OK)
return NLS_ERRNO_SDK_RET_ERROR;
return NLS_ERRNO_SUCCESS;
}
3) Nlss_Start 开始音视频前处理
bool LsSession::OnStartVideo()
{
bool ret=false;
ret =(NLSS_OK == NLS_SDK_GET_FUNC(Nlss_Start)(LsClient));
return ret;
}
- Nlss_StartVideoPreview:开始视频预览
bool LsSession::OnStartVideoPreview()
{
//nbase::NAutoLock auto_lock(&lock_);
if (ls_client_&&!preview_)
{
if (NLSS_OK == NLS_SDK_GET_FUNC(Nlss_StartVideoPreview)(LsClient))
{
preview_ = true;
return true;
}
}
return false;
}
- Nlss_StopVideoPreview:停止视频预览
void LsSession::OnStopVideoPreview()
{
nbase::NAutoLock auto_lock(&lock_);
if (ls_client_&&preview_)
{
NLS_SDK_GET_FUNC(Nlss_StopVideoPreview)(LsClient);
preview_ = false;
}
}
- Nlss_StartLiveStream:创建推流实例,开始直播推流
bool LsSession::OnStartLiveStream(OptCallback cb)
{
nbase::NAutoLock auto_lock(&lock_);
if (live_streaming_)
{
return false;
}
if (ls_client_)
{
nbase::ThreadManager::PostTask(kThreadLiveStreaming, nbase::Bind(&LsSession::DoStartLiveStream, this, cb));
return true;
}
return false;
}
void LsSession::DoStartLiveStream(nim_nls::OptCallback cb)
{
bool ret = false;
if (NLSS_OK == NLS_SDK_GET_FUNC(Nlss_StartLiveStream)(LsClient))
{
live_streaming_ = true;
ret = true;
}
else {
ret = false;
}
if (cb)
{
Post2UI(nbase::Bind(cb, ret));
}
}
- Nlss_StopLiveStream:停止直播推流,销毁推流实例
bool LsSession::OnStopLiveStream(OptCallback cb)
{
//org_code
nbase::NAutoLock auto_lock(&lock_);
if (ls_client_&& live_streaming_)
{
nbase::ThreadManager::PostTask(kThreadLiveStreaming, nbase::Bind(&LsSession::DoStopLiveStream, this, cb));
return true;
}
return false;
}
void LsSession::DoStopLiveStream(nim_nls::OptCallback cb)
{
nbase::NAutoLock auto_lock(&lock_);
bool ret = true;
if (live_streaming_ && ls_client_)
{
NLS_SDK_GET_FUNC(Nlss_StopLiveStream)(LsClient);
{
live_streaming_ = false;
ret = true;
}
}
else {
ret = false;
}
if (cb)
{
Post2UI(nbase::Bind(cb, ret));
}
}
- Nlss_ChildVideoOpen:打开子视频对象
bool LsSession::OnChildVideoOpen(const std::string& accid, ST_NLSS_VIDEOIN_PARAM *pVideoInParam)
{
nbase::NAutoLock auto_lock(&lock_);
bool ret = false;
if (ls_client_)
{
_HNLSSCHILDSERVICE NewLsChildClient = NLS_SDK_GET_FUNC(Nlss_ChildVideoOpen)(LsClient, pVideoInParam);
if (NewLsChildClient != NULL)
{
if (nlss_child_services_.find(accid) == nlss_child_services_.end())
{
std::pair< std::map< std::string, _HNLSSCHILDSERVICE>::iterator, bool > inert_ret;
inert_ret = nlss_child_services_.insert(std::pair<std::string, _HNLSSCHILDSERVICE >(accid, NewLsChildClient));
if (inert_ret.second)
ret = true;
}
}
}
if (ret == false)
m_errorLog.WriteErrorFuc("OnChildVideoOpen");
if (ret)
{
auto it = m_AccidCloseMap.find(accid);
if (it != m_AccidCloseMap.end())
{
it->second = false;
}
else
{
m_AccidCloseMap[accid] = false;
}
}
return ret;
}
bool LsSession::OnChildVideoOpenOtherDeviceConf(const std::string& accid)
{
bool ret = false;
std::map<std::string, _HNLSSCHILDSERVICE>::const_iterator iter = nlss_child_services_.find(accid);
if (iter != nlss_child_services_.end())
ret = (NLSS_OK == NLS_SDK_GET_FUNC(Nlss_ChildVideoOpenOtherDeviceConf)(iter->second));
if (ret)
{
auto it = m_AccidCloseMap.find(accid);
if (it != m_AccidCloseMap.end())
{
it->second = false;
}
else
{
m_AccidCloseMap[accid] = false;
}
}
return ret;
}
- Nlss_ChildVideoStartCapture:开始采集子视频
bool LsSession::OnChildVideoStartCapture(const std::string& accid)
{
if (BCaptruing(accid))
{
return TRUE;
}
bool ret = false;
std::map<std::string, _HNLSSCHILDSERVICE>::const_iterator iter = nlss_child_services_.find(accid);
if (iter != nlss_child_services_.end())
{
NLSS_RET nReturn = NLS_SDK_GET_FUNC(Nlss_ChildVideoStartCapture)(iter->second);
if (NLSS_OK != nReturn)
m_errorLog.WriteErrorFuc("Nlss_ChildVideoStartCapture", (int)nReturn);
ret = (NLSS_OK == nReturn);
}
if (ret)
{
auto it = m_AccidCaptureMap.find(accid);
if (it != m_AccidCaptureMap.end())
{
it->second = true;
}
else
{
m_AccidCaptureMap[accid] = true;
}
}
return ret;
}
- Nlss_ChildVideoStopCapture:停止采集子视频
void LsSession::OnChildVideoStopCapture(const std::string& accid)
{
if (!BCaptruing(accid))
{
return;
}
std::map<std::string, _HNLSSCHILDSERVICE>::const_iterator iter = nlss_child_services_.find(accid);
if (iter != nlss_child_services_.end())
{
NLS_SDK_GET_FUNC(Nlss_ChildVideoStopCapture)(iter->second);
auto it = m_AccidCaptureMap.find(accid);
if (it != m_AccidCaptureMap.end())
{
it->second = false;
}
else
{
m_AccidCaptureMap[accid] = false;
}
}
}
- Nlss_ChildVideoClose:关闭子视频对象
void LsSession::OnChildVideoClose(const std::string& accid)
{
std::map<std::string, _HNLSSCHILDSERVICE>::const_iterator iter = nlss_child_services_.find(accid);
if (iter != nlss_child_services_.end())
{
NLS_SDK_GET_FUNC(Nlss_ChildVideoClose)(iter->second);
nlss_child_services_.erase(iter);
auto it = m_AccidCloseMap.find(accid);
if (it != m_AccidCloseMap.end())
{
it->second = true;
}
else
{
m_AccidCloseMap[accid] = true;
}
}
}
- Nlss_ChildAudioOpen:打开子音频对象
- Nlss_ChildAudioStartCapture:开始采集子音频
//音频管理
bool LsSession::OpenAndCapture(ST_NLSS_AUDIOIN_PARAM &audio_param_, NlsChildAudio &childAudio){
//打开设备
_HNLSSCHILDSERVICE child_audio = NLS_SDK_GET_FUNC(Nlss_ChildAudioOpen)(LsClient, &audio_param_);
if (child_audio == NULL)
return NLS_ERRNO_SDK_RET_ERROR;
if (NLS_SDK_GET_FUNC(Nlss_ChildAudioStartCapture)(child_audio) != NLSS_OK)
{
NLS_SDK_GET_FUNC(Nlss_ChildAudioClose)(child_audio);
return NLS_ERRNO_SDK_RET_ERROR;
}
childAudio.m_hChildAudio = child_audio;
m_stChildAudioMap[child_audio] = childAudio;
audio_handle_ = child_audio;
return NLS_ERRNO_SUCCESS;
}
- Nlss_ChildAudioStopCapture:停止采集子音频
- Nlss_ChildAudioClose:关闭子音频对象
void LsSession::CloseChildAudio(HANDLE child_audio)
{
nbase::NAutoLock lock(&lock_);
auto iter = m_stChildAudioMap.find((_HNLSSCHILDSERVICE)child_audio);
if (iter == m_stChildAudioMap.end())
return;
m_stChildAudioMap.erase((_HNLSSCHILDSERVICE)child_audio);
Nlss_ChildAudioStopCapture((_HNLSSCHILDSERVICE)child_audio);
Nlss_ChildAudioClose((_HNLSSCHILDSERVICE)child_audio);
}
void LsSession::CloseAllAudio(){
//nbase::NAutoLock auto_lock2(&lock_);
for (auto iter = m_stChildAudioMap.begin(); iter != m_stChildAudioMap.end(); iter++)
{
Nlss_ChildAudioStopCapture(iter->first);
Nlss_ChildAudioClose(iter->first);
}
m_stChildAudioMap.clear();
}
NLS_ERRNO LsSession::OpenMicrophone(const std::string& device_path, int samplerate, int channel_num, HANDLE* child_audio_out)
{
nbase::NAutoLock auto_lock(&lock_);
if (!ls_client_)
return NLS_ERRNO_UNINITIALIZED;
if (child_audio_out == NULL || device_path.empty() || samplerate <= 0 || channel_num < 1 || channel_num > 2)
return NLS_ERRNO_PARAM_ERROR;
m_hNlsInstance = LsClient;
NlsChildAudio stChildAudio;
stChildAudio.m_enInType = EN_NLSS_AUDIOIN_MIC;
stChildAudio.m_strPath = device_path;
DO_CHECK_CHILD_AUDIO_DUP(this, stChildAudio);
ST_NLSS_AUDIOIN_PARAM param;
GetDefaultAudioParam(¶m);
param.enInType = EN_NLSS_AUDIOIN_MIC;
param.paAudioDevicePath = (char*)device_path.c_str();
param.iInSamplerate = samplerate;
param.iInNumOfChannels = channel_num;
param.iInFrameSize = 1024 * channel_num * param.iInBitsPerSample / 8;
OpenAndCapture(param, stChildAudio);
//DO_OPEN_CHILD_AUDIO(this, param, stChildAudio, *child_audio_out);
}
NLS_ERRNO LsSession::OpenSystemSoundCapture(HANDLE* child_audio_out)
{
nbase::NAutoLock auto_lock(&lock_);
if (!ls_client_)
return NLS_ERRNO_UNINITIALIZED;
if (child_audio_out == NULL)
return NLS_ERRNO_PARAM_ERROR;
m_hNlsInstance = LsClient;
NlsChildAudio stChildAudio;
stChildAudio.m_enInType = EN_NLSS_AUDIOIN_SYS;
DO_CHECK_CHILD_AUDIO_DUP(this, stChildAudio);
ST_NLSS_AUDIOIN_PARAM param;
GetDefaultAudioParam(¶m);
param.enInType = EN_NLSS_AUDIOIN_SYS;
OpenAndCapture(param, stChildAudio);
//DO_OPEN_CHILD_AUDIO(this, param, stChildAudio, *child_audio_out);
}
4) Nlss_Stop:停止音视频前处理
void LsSession::OnStopVideo()
{
CloseAudio();
NLS_SDK_GET_FUNC(Nlss_Stop)(LsClient);
nlss_child_services_.clear();
}
5) Nlss_UninitParam :反初始化直播对象,直播参数ST_NLSS_PARAM失效
void LsSession::UninitParam()
{
NLS_SDK_GET_FUNC(Nlss_UninitParam)(LsClient);
}
6)Nlss_Destroy:销毁直播对象
LsSession::~LsSession()
{
NLS_SDK_GET_FUNC(Nlss_Destroy)(LsClient); //销毁实例
delete ls_client_;
ls_client_ = nullptr;
}
调用顺序说明:
ss_Create -> Nlss_InitParam -> Nlss_Start -> Nlss_Stop -> Nlss_UninitParam -> Nlss_Destroy的调用顺序是固定的。
视频预览接口(Nlss_StartVideoPreview / Nlss_StopVideoPreview)
直播推流接口(Nlss_StartLiveStream / Nlss_StopLiveStream)
以及所有的子视频、子音频接口必须在Nlss_Start和Nlss_Stop之间调用,但它们相互之间的调用顺序不限。
功能相对的接口需要成对调用,例如:
- Nlss_Create 和 Nlss_Destroy
- Nlss_InitParam 和 Nlss_UninitParam
- Nlss_Start 和 Nlss_Stop
- Nlss_StartVideoPreview 和 Nlss_StopVideoPreview
- Nlss_StartLiveStream 和 Nlss_StopLiveStream(例外:直播出错时SDK会自动执行Nlss_StopLiveStream,上层不必再调)
- Nlss_ChildVideoOpen 和 Nlss_ChildVideoClose
- Nlss_ChildVideoStartCapture 和 Nlss_ChildVideoStopCapture
- Nlss_ChildAudioOpen 和 Nlss_ChildAudioClose
- Nlss_ChildAudioStartCapture 和 Nlss_ChildAudioStopCapture
三 项目中实际使用:
nim_nls::LsSession m_LiveStreaming; //推流直播实例
1) 创建直播对象
//初始化窗口时
m_LiveStreaming.Create(NULL); //创建直播对象
2) 初始化参数
bool NLSLiveForm::NewInitMediaCapture(){
int content_type = EN_NLSS_OUTCONTENT_AV;
bool have_video_source = true;
bool have_audio_source = true;
ST_NLSS_PARAM stParam;
m_LiveStreaming.GetDefaultParam(&stParam); //获得默认参数
have_video_source = GetVideoOutParam(&stParam.stVideoParam); //获得视频输出参数
auto ret = m_LiveStreaming.Initialize(content_type, stParam.stVideoParam.iOutWidth, stParam.stVideoParam.iOutHeight, m_videoFps, m_strUrl, 0);
if (ret == NLS_ERRNO_SUCCESS && (content_type & EN_NLSS_OUTCONTENT_VIDEO))
;
m_bInited = true;
return ret == NLS_ERRNO_SUCCESS;
}
3) 开始音视频前处理
//Nlss_Start开始音视频前处理
if (!m_LiveStreaming.OnStartVideo())
{
m_errorLog.WriteErrorFuc("!m_LiveStreaming.OnStartVideo() -视频前处理失败");
m_ChildVideoDisplayList.clear();
m_ChildVideoList.clear();
m_ChildVideoInfoList.clear();
m_errMsg = L"LNlss_Start 失败";
ShowMsgBox(this->GetHWND(), m_errMsg, MsgboxCallback(), L"校内外直播间 ", L"确定", L"取消");
return false;
}
4) 添加音视频
//添加所有音频
AddAllMics();
AddSysSound();
SetCurMicVolumAndMuteOrthers();
//插入新视频源
void NLSLiveForm::AddNewVideoSource()
{
if (!m_ChildVideoList.size())
{
m_iVideoAccount = 0;
}
if (m_ChildVideoList.size()<NLSS_CHILD_VIDEO_NUM)
{
ST_NLSS_CHILD_VIDEO_INFO video_info;
ST_NLSS_VIDEOIN_PARAM stChildVInParam;
std::string accid;
if (!GetVideoInParam(&stChildVInParam, m_VideoSourceType, accid))
return;
if (!m_LiveStreaming.OnChildVideoOpen(accid, &stChildVInParam))
{
m_ChildVideoList.remove(accid);
m_ChildVideoDisplayList.remove(accid);
return;
}
{
if (m_ChildVideoList.size()==1)
{
m_LiveStreaming.OnChildVideoSetBackLayer(accid);
SetChildVideoAccidAsBkID(accid);
}
else
{
m_LiveStreaming.OnChildVideoSetDisplayRect(accid, &child_display_rect_[m_iVideoAccount%NLSS_CHILD_VIDEO_NUM]);
}
if (stChildVInParam.enInType == EN_NLSS_VIDEOIN_CAMERA)
{
if (m_LiveStreaming.OnChildVideoIsOtherDevice(accid))
m_LiveStreaming.OnChildVideoOpenOtherDeviceConf(accid);
}
if (!StartChildCapture(accid))//if (!m_LiveStreaming.OnChildVideoStartCapture(accid))
{
m_bForcedReFresh = true; //确保暂停视频、继续视频后重置环境
m_ChildVideoList.remove(accid);
m_ChildVideoDisplayList.remove(accid);
return;
}
RefreshChildVideoList(accid, m_iVideoAccount, m_VideoTypeName, child_margin_rect_[m_iVideoAccount%NLSS_CHILD_VIDEO_NUM], child_video_rect_[m_iVideoAccount%NLSS_CHILD_VIDEO_NUM]);
}
video_info.VideoInParam = stChildVInParam;
video_info.ChildVideoAccid = accid;
video_info.VideoSourceName = m_VideoTypeName;
m_ChildVideoInfoList.push_back(video_info); //将子窗口添加到列表中
m_iVideoAccount ++;
if (m_ChildVideoList.size() == NLSS_CHILD_VIDEO_NUM)
{
full_screen_model_btn_->SetEnabled(false);
screen_shot_model_btn_->SetEnabled(false);
application_model_btn_->SetEnabled(false);
camera_model_btn_->SetEnabled(false);
}
}
}
初始化时,加入全部视频源
void NLSLiveForm::AddAllCameras(std::string defaultAccid){
if (m_iVideoDeviceNum == 0 || m_pVideoDevices == NULL)
{
m_errorLog.WriteErrorFuc("m_iVideoDeviceNum == 0 || m_pVideoDevices == NULL");
//加入全屏桌面
m_VideoSourceType = EN_NLSS_VIDEOIN_FULLSCREEN;
//处于预览中,才能添加其它视频源
PreView();
HideAllChildWindowExcept("");
m_errorLog.WriteSuccessFuc("HideAllChildWindowExcept--完毕");
ShowCameraWndSelect(true);//创建摄像头选择窗口,并隐藏
m_errorLog.WriteSuccessFuc("ShowCameraWndSelect--完毕");
return;
}
ShowCameraWndSelect(true);//创建摄像头选择窗口,并隐藏
CameraSelectForm* pCameraWnd = GetCameraSelectWnd();
std::string vedioPath;
std::string firstCameraPath;
//加入第一个摄像头
//默认预览第一个
m_VideoSourceType = EN_NLSS_VIDEOIN_CAMERA;
vedioPath = m_pVideoDevices[0].paPath;
firstCameraPath = m_pVideoDevices[0].paPath;
m_CameraPathList.push_back(vedioPath);
pCameraWnd->SetCurCamera(GetCurCameraPath(), m_CameraCaptureQ);
//处于预览中,才能添加其它视频源
PreView();
//加入其它摄像头
for (int i = 1; i < m_iVideoDeviceNum;i++)
{
vedioPath = m_pVideoDevices[i].paPath;
m_CameraPathList.push_back(vedioPath);
//设置当前摄像头所能采集的清晰度
pCameraWnd->SetCurCamera(GetCurCameraPath(), m_CameraCaptureQ);
AddNewVideoSource(); //每次加的设备路径 为m_CameraPathList.back() 最后末尾
}
//加入全屏桌面
m_VideoSourceType = EN_NLSS_VIDEOIN_FULLSCREEN;
AddNewVideoSource();
if (defaultAccid.empty() || !IsAccidExist(defaultAccid))
{
defaultAccid = GetAccidFromPath(m_CameraPathList.front());//默认第一个摄像头为背景
}
//将选中的摄像头 添加到PathList最后 m_CameraPathList.push_back(firstCameraPath);
//否则可能造成 选中的 和list中的不一致
std::string curCameraPath=GetPathFromAccid(defaultAccid);
m_CameraPathList.push_back(curCameraPath);
if (m_iBkAccountID != defaultAccid)
SetChildVideoAsBKEx(defaultAccid);
else
HideAllChildWindowExcept(defaultAccid);
//设置背景摄像头 所能支持的清晰度
pCameraWnd->SetCurCamera(GetCurCameraPath(), m_CameraCaptureQ);
}
5)开始预览
if (!m_LiveStreaming.OnStartVideoPreview()) //nlss_startVideoPreview 开始视频预览
{
m_errorLog.WriteErrorFuc("!m_LiveStreaming.OnStartVideoPreview() - 打开视频预览出错");
m_ChildVideoDisplayList.clear();
m_ChildVideoList.clear();
m_ChildVideoInfoList.clear();
m_errMsg = L"OnStartVideoPreview";
ShowMsgBox(this->GetHWND(), m_errMsg, MsgboxCallback(), L"校内外直播间 ", L"确定", L"取消");
return false;
}
6)开始推流
bool NLSLiveForm::StartLiveStream()
{
ST_NLSS_VIDEOIN_PARAM stChildVInParam;
nim_nls::OptCallback cb = [this](bool ret){
if (ret)
{
StartLive_req(); //开始直播
m_bPaused = false;
m_bLiving = true;
if (pClassForm->m_BeginStartTime == "")
pClassForm->m_BeginStartTime = GetCurTime();
AutoJustAudioState_ui();// 根据当前麦克风状态 启用、禁用麦克风
HideAllChildWindowMargin();//隐藏边框
ReallyLiving();
}
else
{
m_errorLog.WriteErrorFuc("推流参数初始化失败");
m_errMsg = L"推流参数初始化失败";
ShowMsgBox(this->GetHWND(), m_errMsg, MsgboxCallback(), L"校内外直播间 ", L"确定", L"取消");
}
};
if (!m_bPreviewing )
{
StopPrew();
if (!StartPrew())
return false;
if (!m_LiveStreaming.OnStartLiveStream(cb))
{
m_errorLog.WriteErrorFuc("func:StartLiveStream 1");
StopAndQuicklyReStart();
}
return true;
}
if (!m_LiveStreaming.OnStartLiveStream(cb))
{
m_errorLog.WriteErrorFuc("func:StartLiveStream 2");
StopAndQuicklyReStart();
}
return true;
}
7)结束推流
void NLSLiveForm::StopLiveStream(bool bNullCB)
{
if (bNullCB){//
nim_nls::OptCallback cb = [this](bool ret){
m_bLiving = false;
};
m_LiveStreaming.OnStopLiveStream(cb);
paint_video_info_timer_.Cancel();
return;
}
nim_nls::OptCallback cb = [this](bool ret)
{
{
m_bLiving = false;
paint_video_info_timer_.Cancel();
SetStartLiveBtnText(L"开始直播");
}
};
m_LiveStreaming.OnStopLiveStream(cb);
while (m_LiveStreaming.IsLiveStreaming())
Sleep(100);
return;
}
在Logout时,StopLiveStream(true), 在课程结束时 TotalEndLive,调用 StopLiveStream(true);
除此,在直播过程中,也可暂停推流,继续推流
//暂停
void NLSLiveForm::PauseStream(){
nim_nls::OptCallback cb = [this](bool ret)
{
if (ret)
{
m_bPaused = true;
m_bLiving = false;
EndRepeatedPain();
SetPauseRestoreText(L"继续直播");
box_pauseInfo->SetVisible(true);
PauseLive_req();
}
else{
SetPauseRestoreText(L"暂停直播");
}
};
m_LiveStreaming.OnStopLiveStream(cb);
SetPauseRestoreText(L"继续直播");
}
8)停止视频前处理,清理参数
void NLSLiveForm::StopPrew(){
if (!m_bPreviewing)
return;
//关闭子窗口采集
for (std::list<std::string>::iterator it = m_ChildVideoList.begin(); it != m_ChildVideoList.end(); it++)
{
m_LiveStreaming.OnChildVideoStopCapture(*it); //停止子窗口采集
m_LiveStreaming.OnChildVideoClose(*it); //关闭子窗口
}
//关闭音频采集
m_LiveStreaming.CloseAllAudio();
if (m_bBlackBackground) //黑屏是背景
{
m_LiveStreaming.OnChildVideoStopCapture(m_BlackBackground); //停止采集黑屏
m_LiveStreaming.OnChildVideoClose(m_BlackBackground); //关闭黑屏窗口
m_bBlackBackground = false;
}
//停止预览
m_LiveStreaming.OnStopVideoPreview();
m_LiveStreaming.OnStopVideo(); //停止视频前处理
m_LiveStreaming.UninitParam(); //清楚init设置的参数
m_iVideoAccount = 0;
child_media_list_->RemoveAll();
m_ChildVideoList.clear();
m_ChildVideoDisplayList.clear();
m_bPreviewing = false;
m_bfirst = true;
}
有时间再整理