Windows 直播推流 SDK 开发指南

1 开发准备

开始前请确保下述前提条件已准备就绪:

解压后 SDK 包的 libs 文件夹中,包含了 dll 库文件和头文件,文件列表如下:


SDK  
│   lib  
│   ├──base.dll     
│   ├──cos.dll         
│   ├──engine.dll   
│   ├──itrd.dll  
│   ├──libeay32.dll    
│   ├──libfdk-aac-0.dll   
│   ├──libgcc_s_dw2-1.dll    
│   ├──libiconv-2.dll  
│   ├──libopenh264.dll  
│   ├──libx264-120.dll    
│   ├──librtmp.dll  
│   ├──libstdc++-6.dll  
│   ├──livestream.dll  
│   ├──LSMediaCapture.lib  
│   ├──ssleay32.dll   
│   ├──zlib1.dll  
├── include    
│   ├── nlss_api.h    
│   ├── nlss_childvideo_api.h   
│   ├── nlss_define.h    
│   ├── nlss_type.h    
└──

总体接口约定
SDK 提供的API C类型接口,分别提供 win32 的动态库 LSMediaCapture.dll 及其对应的导入库 LSMediaCapture.lib 方便开发者动态加载或者加载 SDK 库文件。
App 开发者只需要引用 SDK 包里 sdk\include 目录下的定义头文件 nlss_api.h nlss_childvideo_api.h nlss_define.h nlss_type.h 即可。
关于API类或者函数的定义,可以查看API 文档。

2 集成 SDK

本文是根据官网的直播推流 Demo 来介绍 SDK 的集成,可在网易视频云官网下载最新的直播推流 Windows Demo,来查看更多的实现细节。

将SDK 相关的dll 文件(lib\)放到App 的运行目录下。SDK 基于vs2013 开发,如果App 没有对应的运行时库文件(msvcp120.dll和msvcr120.dll),请将其放到App 的运行目录下。准备工作完成后,可以选择动态加载或者静态加载的模式调用LSMediaCapture.dll。

2.1 获取可供采集资源列表

2.1.1 获取音视频设备列表(如果想直播麦克或者摄像头)

先通过调用 Nlss_GetFreeDevicesNum获取当前摄像头/麦克风的个数。
根据个数,初始化结构体 ST_NLSS_INDEVICE_INF 数组,并将此数组作为参数传递给Nlss_GetFreeDeviceInf接口,进而获取当前所有摄像头/麦克风对象信息。
下面以 sample.cpp 以及 testSimple函数 分别简单介绍,如何调用这两个接口

NLSS_RET  Nlss_GetFreeDevicesNum(NLSS_OUT int *iVideoDeviceNum, NLSS_OUT int *iAudioDeviceNum);
NLSS_RET  Nlss_GetFreeDeviceInf(NLSS_OUT ST_NLSS_INDEVICE_INF *pstVideoDevices, NLSS_OUT ST_NLSS_INDEVICE_INF* pstAudioDevices);

示例代码

int  m_iVideoDeviceNum = 0;
int  m_iAudioDeviceNum = 0;
ST_NLSS_INDEVICE_INF *m_pVideoDevices;
ST_NLSS_INDEVICE_INF *m_pAudioDevices;

Nlss_GetFreeDevicesNum(&m_iVideoDeviceNum, &m_iAudioDeviceNum);

if (m_iVideoDeviceNum <= 0){
    return;
}
else{
    m_pVideoDevices = new ST_NLSS_INDEVICE_INF[m_iVideoDeviceNum];
    for (int i = 0; i < m_iVideoDeviceNum; i++){
        m_pVideoDevices[i].paPath = new char[1024];
        m_pVideoDevices[i].paFriendlyName = new char[1024];
    }
}

if (m_iAudioDeviceNum <= 0){
    return;
}
else{
    m_pAudioDevices = new ST_NLSS_INDEVICE_INF[m_iAudioDeviceNum];

    for (int i = 0; i < m_iAudioDeviceNum; i++){
        m_pAudioDevices[i].paPath = new char[1024];
        m_pAudioDevices[i].paFriendlyName = new char[1024];
    }
}

Nlss_GetFreeDeviceInf(m_pVideoDevices, m_iVideoDeviceNum, m_pAudioDevices, m_iAudioDeviceNum);

2.1.2 获取此时可采集应用图像的资源列表(如果想直播windows应用窗口)

先通过调用Nlss_GetAvailableAppWindNum 获取当前可采集应用图像的个数。
根据个数,初始化结构体 ST_NLSS_INDEVICE_INF 数组,并将此数组作为参数传递给Nlss_GetAvailableAppWind接口,进而获取当前可采集应用图像信息。
下面以 sample.cpp 以及 testSimple 分别简单介绍,如何调用这两个接口

   NLSS_RET Nlss_GetAvailableAppWindNum(int *piAppWindNum);                                                     
   NLSS_RET Nlss_GetAvailableAppWind(NLSS_OUT ST_NLSS_INDEVICE_INF *pLSAppWindTitles, int iMaxNum);  

示例代码

int  iAppNum = 0;              
ST_NLSS_INDEVICE_INF *m_pappWndTitles;
Nlss_GetAvailableAppWindNum(&iAppNum);
if (iAppNum > 0)
{
    m_pappWndTitles = new ST_NLSS_INDEVICE_INF[iAppNum];
    for (int i = 0; i < iAppNum; i++)
    {
        m_pappWndTitles[i].paPath = new char[1024];
        m_pappWndTitles[i].paFriendlyName = new char[1024];
    }
    Nlss_GetAvailableAppWind(m_pappWndTitles, iAppNum);
}

2.2 初始化音视频直播

SDK的主要功能就是提供音视频的直播功能,包括了音视频的采集,编码,以及发送。
采集编码发送的方式,协议有很多,目前实现的是:采集用Dshow方式获取视频,Dsound方式获取音频,默认支持WIN7平台。
编码采用OpenH264|X264,AAC两种最通用的格式,发送遵照RTMP标准协议;接下来SDK将提供更多格式的音视频直播流。X264带宽更低一些,更平滑,CPU会略高一些;而OpenH264带宽更高,更波动,CPU低一些。

2.2.1 创建|销毁直播对象

如上面所说,使用直播推流服务时,需要首先创建一个直播推流服务句柄,多次推流可以使用同一个句柄。
我们需要创建通过 Nlss_Create 创建它,在不需要使用直播时,通过 Nlss_Destroy 销毁它。

NLSS_RET   Nlss_Create(const char *paWorkPath, const char *paCachePath, NLSS_OUT _HNLSSERVICE *phNLSService);
 void      Nlss_Destroy(_HNLSSERVICE hNLSService);

2.2.2 初始化直播对象

在直播类/结构体创建好之后,需要初始化直播推流各参数,同时需要设置预览回调函数,直播状态初始化接口为:
通过Nlss_GetDefaultParam 获取部分默认参数;
通过Nlss_InitParam(很关键)设置视频推流的输出流参数和音频的输入,输出参数;
通过Nlss_SetVideoWaterMark设置视频是否添加水印。可以设置png图像和添加位置,不调用该函数或者水平参数设为空,不产生水印;
通过Nlss_UninitParam 清除Nlss_InitParam设置的直播参数;

NLSS_RET   Nlss_GetDefaultParam(_HNLSSERVICE hNLSService, NLSS_OUT ST_NLSS_PARAM *pstParam);
NLSS_RET   Nlss_InitParam(_HNLSSERVICE hNLSService, ST_NLSS_PARAM *pstParam);
void       Nlss_SetVideoWaterMark(_HNLSSERVICE hNLSService, ST_NLSS_VIDEO_WATER_PARAM *pstWaterParam);
void       Nlss_UninitParam(_HNLSSERVICE hNLSService);

2.2.3 设置若干回调(预览画面回调,直播状态回调)

Nlss_SetVideoSamplerCB会通过设置的回调函数返回推流之前的rgb32的buffer给上层用于显示;
Nlss_SetStatusCB直播状态的回调函数通知给上层,直播是否出错和错误码,是否直播成功;

void     Nlss_SetVideoSamplerCB(_HNLSSERVICE hNLSService, PFN_NLSS_VIDEOSAMPLER_CB pFunVideoSamplerCB);
void     Nlss_SetStatusCB(_HNLSSERVICE hNLSService, PFN_NLSS_STATUS_NTY pFunStatusNty);

从创建到初始化代码等一系列操作的示例代码(sample.cpp函数testSimple):

_HNLSSERVICE hNLSService = NULL;
Nlss_Create(NULL, NULL, &hNLSService);

Nlss_SetVideoSamplerCB(hNLSService, previewCB);
Nlss_SetStatusCB(hNLSService, statusCB);
ST_NLSS_PARAM stParam;
Nlss_GetDefaultParam(hNLSService, &stParam);
SetVideoOutParam(&stParam.stVideoParam, EN_NLSS_VIDEOQUALITY_HIGH, true);
SetAudioParam(&stParam.stAudioParam, (char *)m_pAudioDevices[0].paPath, EN_NLSS_AUDIOIN_MIC);
initLiveStream(hNLSService, &stParam, paOutUrl);
ST_NLSS_VIDEO_WATER_PARAM stFilterParam;
stFilterParam.pucFilePath = "my_logo.png";
stFilterParam.uiStartx = 10;
stFilterParam.uiStarty = 10;
Nlss_SetVideoWaterMark(m_hNlssService, &stFilterParam);

注:在设置视频的帧率(iOutFps)时,目标码率(iOutBitrate)几个关键值时,为了保障流畅的体验效果。我们的建议值如下:

 iOutWidth;        视频的输出宽.要求是4的倍数
 iOutHeight;       视频的输出高.要求是4的倍数
 iOutFps:         主播生活直播场景设为15-20,共享桌面在线教育场景设为4-8;
 iOutBitrate:     可参考sample.cpp函数getOutBitrate;
 enOutCodec;      视频编码器.可以选择X264,Openh264,建议选择X264

2.3 子视频管理

如果不添加子视频,我们的推流画面会漆黑一片,没有内容。我们可以同时打开并设置叠加显示多个子视频添加到_HNLSSERVICE(直播对象),用于预览和直播业务。

2.3.1 打开|关闭新子视频

使用Nlss_ChildVideoOpen打开摄像头|全屏桌面|区域桌面|APP界面|PNG图片|自定义码流子视频对象
使用Nlss_ChildVideoClose关闭子视频对象

    _HNLSSCHILDSERVICE      Nlss_ChildVideoOpen(_HNLSSERVICE hNLSService, ST_NLSS_VIDEOIN_PARAM *pVideoInParam);
    void               Nlss_ChildVideoClose(_HNLSSCHILDSERVICE hNLSSChild);

2.3.2 设置该子视频显示属性(层级,位置,大小)

使用Nlss_ChildVideoSetDisplayRect设置该子视频窗口在主窗口中的显示位置和大小(位置相同部分会叠加起来),该函数设置后会将采集的图像放缩或者拉升;
使用Nlss_ChildVideoSetBackLayer将该子视频设为最后的背景层,同时将子视频显示位置设为0,0,长宽为iOutWidth,iOutHeight;
使用Nlss_ChildVideoAdjustLayer将该子视频层级调降或者调升一层来显示;
使用Nlss_ChildVideoSwitchDisplay可以临时显示或者隐藏该子视频窗口图像;
最新添加的子视频会在最上层显示,可以通过以上接口去进行显示层级调整;

     void               Nlss_ChildVideoSetBackLayer(_HNLSSCHILDSERVICE hNLSSChild);
     void               Nlss_ChildVideoAdjustLayer(_HNLSSCHILDSERVICE hNLSSChild, bool bAdustUp);
     void               Nlss_ChildVideoSetDisplayRect(_HNLSSCHILDSERVICE hNLSSChild, ST_NLSS_RECTSCREEN_PARAM *pstRect);
     void               Nlss_ChildVideoSwitchDisplay(_HNLSSCHILDSERVICE hNLSSChild, bool bHide);

2.3.3 开始和停止采集子视频(自定义码流不需要如下采集操作)

     NLSS_RET  Nlss_ChildVideoStartCapture( _HNLSSCHILDSERVICE hNLSSChild);
     void     Nlss_ChildVideoStopCapture(_HNLSSCHILDSERVICE hNLSSChild);

如果需要支持采集视频采集卡,在StartCapture之前,先通过Nlss_ChildVideoIsOtherDevice检查是否为其他采集设备(如视频采集卡),然后可以通过Nlss_ChildVideoOpenOtherDeviceConf打开该采集设备配置选项来设置

  bool  Nlss_ChildVideoIsOtherDevice(_HNLSSCHILDSERVICE hNLSSChild);
  NLSS_RET  Nlss_ChildVideoOpenOtherDeviceConf(_HNLSSCHILDSERVICE hNLSSChild);

同时增加3个子视频图像(PNG,区域屏幕,摄像头)的示例代码(参考sample.cpp函数MoveMutliVideo)。如下:

//open child PNG
if (1)
{
    ST_NLSS_VIDEOIN_PARAM stChildVInParam;
    SetVideoInParam(&stChildVInParam, EN_NLSS_VIDEOIN_PNG, (char *)m_pVideoDevices[0].paPath, EN_NLSS_VIDEOQUALITY_MIDDLE);
    hChildVideoService1 = Nlss_ChildVideoOpen(hNLSService, &stChildVInParam);
    Nlss_ChildVideoSetBackLayer(hChildVideoService1);
    Nlss_ChildVideoStartCapture(hChildVideoService1);
}

//open child RECTSCREEN 
if (2)
{
    ST_NLSS_VIDEOIN_PARAM stChildVInParam;
    SetVideoInParam(&stChildVInParam, EN_NLSS_VIDEOIN_RECTSCREEN, (char *)m_pVideoDevices[0].paPath, EN_NLSS_VIDEOQUALITY_MIDDLE);
    hChildVideoService2 = Nlss_ChildVideoOpen(hNLSService, &stChildVInParam);
    ST_NLSS_RECTSCREEN_PARAM stRect = { 200, 400, 300, 530 };
    Nlss_ChildVideoSetDisplayRect(hChildVideoService2, &stRect);
    Nlss_ChildVideoStartCapture(hChildVideoService2);
}

//open child CAMERA
if (3)
{
    ST_NLSS_VIDEOIN_PARAM stChildVInParam;
    SetVideoInParam(&stChildVInParam, EN_NLSS_VIDEOIN_CAMERA, (char *)m_pVideoDevices[0].paPath, EN_NLSS_VIDEOQUALITY_HIGH);
    hChildVideoService3 = Nlss_ChildVideoOpen(hNLSService, &stChildVInParam);
    ST_NLSS_RECTSCREEN_PARAM stRect = { 600, 800, 100, 300 };
    Nlss_ChildVideoSetDisplayRect(hChildVideoService3, &stRect);
    Nlss_ChildVideoStartCapture(hChildVideoService3);
}

2.4 启动|停止处理

调用Nlss_Start启动处理,在各类初始化之后,在预览和直播启动之前;
调用Nlss_Stop停止处理,在预览和直播停止之后,在Nlss_UninitParam之前。

  NLSS_RET  Nlss_Start(_HNLSSERVICE hNLSService)
  void     Nlss_Stop(_HNLSSERVICE hNLSService)

2.5 视频预览操作

2.5.1 所有子视频合成后的图像预览

在2.4之后,就可以开始直播了。一般情况下,我们会先打开视频预览。
先参考2.2.3,确保在初始化时调用Nlss_SetVideoSamplerCB,接受合成后的预览的每帧图像rgb32 buffer。
然后通过如下函数打开/停止,暂停与停止功能的区别是资源是否释放

NLSS_RET  Nlss_StartVideoPreview(_HNLSSERVICE hNLSService);    
void      Nlss_PauseVideoPreview(_HNLSSERVICE hNLSService);
void      Nlss_ResumeVideoPreview(_HNLSSERVICE hNLSService);    
void      Nlss_StopVideoPreview(_HNLSSERVICE hNLSService);

2.5.1 独立某个子视频的图像回调

Nlss_ChildVideoSetSoloPreviewCB设置单独预览的视频流buffer回调函数,可用于预览进行显示 Nlss_ChildVideoSwitchSoloPreview开|关子视频单独预览

void    Nlss_ChildVideoSetSoloPreviewCB(_HNLSSCHILDSERVICE hNLSSChild, PFN_NLSS_VIDEOSAMPLER_CB pFunVideoSamplerCB);
void    Nlss_ChildVideoSwitchSoloPreview(_HNLSSCHILDSERVICE hNLSSChild, bool bOn);

2.6 直播推流操作

2.6.1 直播前和状态介绍

先参考2.2.3,设置直播状态回调。有如下几个状态请关注
直播成功开始状态是 EN_NLSS_STATUS_START,可以及时的显示;
直播过程出错状态是 EN_NLSS_STATUS_ERR(当直播过程中发生头信息发送不出去,或者数据流发送失败时)回调给上层,开发者可以通知给用户即可。

2.6.2 直播控制函数

启动直播,停止直播。

NLSS_RET   Nlss_StartLiveStream(_HNLSSERVICE hNLSService);    
void       Nlss_StopLiveStream(_HNLSSERVICE hNLSService);

当打开的是音视频流推流功能时,在推流过程中,SDK提供单独暂停,恢复音视频中的视频流的直播。

void       Nlss_PauseVideoLiveStream(_HNLSSERVICE hNLSService);    
void       Nlss_ResumeVideoLiveStream(_HNLSSERVICE hNLSService);    

当打开的是音视频流推流功能时,在推流过程中,SDK提供单独暂停,恢复音视频中的音频流的直播。

void       Nlss_PauseAudioLiveStream(_HNLSSERVICE hNLSService);
void       Nlss_ResumeAudioLiveStream(_HNLSSERVICE hNLSService);

还可以单独暂停|恢复某个子视频的直播,即控制视频画面中的一部分。

void       Nlss_ChildVideoPauseLiveStream(_HNLSSCHILDSERVICE hNLSSChild);
void       Nlss_ChildVideoResumeLiveStream(_HNLSSCHILDSERVICE hNLSSChild);

2.6.3 直播统计信息

SDK 为了让调用者对直播流健康状况有个更好的了解,调用者可以定时去获取统计信息。

NLSS_RET   Nlss_GetStaticInfo(_HNLSSERVICE hNLSService, NLSS_OUT ST_NLSS_STATS *pstStats);

2.7 SDK提供裸流直播接口

以上的方式直播推流是黑盒化的,采集都是sdk内部做的。我们也支持开发者或者用户提供裸流(例如:yuv420 或者 pcm 数据),SDK 负责编码和传输;给开发者增加了定制方式。调用方式:

(1)首先初始化在调用Nlss_InitLiveStreamST_NLSS_PARAM 如下音视频输入流参数 stAudioParam.enInType 设为EN_NLSS_AUDIOIN_RAWDATA
(2)通过Nlss_ChildVideoOpen添加子视频,参数设置enInType = EN_NLSS_VIDEOIN_RAWDATA,stInCustomVideo说明输入流的长,宽,格式。设置显示大小,也可以更改显示层级,但是不需要调用采集函数;

(2)在 Nlss_StartLiveStream 之后,就可以调用如下接口进行传输你的音视频流;

NLSS_RET   Nlss_VideoChildSendCustomData(_HNLSSCHILDSERVICE hNLSSChild, char *pcVideoData, int iLen);
NLSS_RET   Nlss_SendCustomAudioData(_HNLSSERVICE hNLSService, char *pcAudioData, int iLen, int iSampleRate);

3 SDK 打包说明

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

4 API说明

有关API的详细说明,可参见SDK包中docs,打开index.html查看,或者打开下面的在线文档。

网易视频云直播推流LiveStreaming Windows SDK API详细文档