直播项目之 将SDK提供的C 头文件,封装成C++类 管理

版权声明:www.gudianxiaoshuo.com (古典小说网) 今日头条号: 古典古韵古典小说、讨厌编程 https://blog.csdn.net/shuilan0066/article/details/88390554

一:封装前准备

以网易直播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, &param);
	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, &param) != 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 &param, 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, &param);
		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, &param) != 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(&param);
		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(&param);
		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;

	
}

有时间再整理

猜你喜欢

转载自blog.csdn.net/shuilan0066/article/details/88390554