基于EasyDarwin框架实现EasyNVR H5无插件直播流媒体服务器方案
在之前的一篇博客《web无插件播放RTSP摄像机方案,拒绝插件,拥抱H5!》中,描述了实现一套H5无插件直播方案的各个组件的参考建议,又在博客《EasyNVR H5流媒体服务器方案架构设计之视频能力平台》中对整体的架构设计思路做了总结,做高内聚、低耦合的视频能力平台,不涉足业务部分!那么今天,我们来公开一下EasyNVR到底是如何实现这些功能的;
一、程序框架
熟悉EasyDarwin的同学都知道,EasyDarwin作为老牌的功能框架,在很多领域是非常能够满足开发者需求的,尤其是在安防领域,综合其架构和性能,性价比极高,尤其是在网络、多线程线程池、模块化设计等方面,熟悉之后,可以衍生出很多功能类型的服务,这里将要描述的EasyNVR就是其框架产物之一:EasyNVR based on EasyDarwin,底层网络事件模型完全沿用EasyDarwin,只对其HTTPSession、Module部分做二次开发,包括HTTP协议、Json的协议栈都完全复用EasyDarwin的HTTPProtocol类和EasyProtocol框架,非常方便;
二、模块设计
EasyNVR一共设计了两个Module:EasyNVRModule和EasyCMSModule:
EasyNVRModule的主要工作就是做EasyNVR通道管理的功能,进行摄像机源与推流的衔接,EasyNVRModule总管理多个EasyNVRChannel,一路EasyNVRChannel就是一个设备源,EasyNVRChannel继承自Darwin框架的Task类,这样每一个EasyNVRChannel就可以独立进行Task、Timeout、Event等多种功能了,这样就能非常方便地实现:设备在线监测、设备定时快照、设备直播流获取等多种功能,每一个EasyNVRChannel的所有输出都共享一个RTSPSource源,这样就能保证一路流媒体数据输入,多种协议(JPEG、RTMP、HLS)输出了!
EasyCMSModule是EasyNVR为了扩展云平台对接功能扩展的,与具体平台进行协议交互用到的,这个部分可以完全参考EasyDarwin的EasyCMSModule:https://github.com/EasyDarwin/EasyDarwin/tree/master/EasyDarwin/APIModules,对外的对接方法根据协议而定,这里就不多描述了;
三、接口调用
在博客《基于EasyNVR二次开发实现自己的摄像机IPC/NVR无插件化直播解决方案》中,我们大概描述了EasyNVR是如何进行接口设计的,在代码实现上,我们直接复用了EasyDarwin的HTTPSession功能,实现基于http+json的接口协议,例如获取广场视频列表部分,我们在读取到请求后,直接从EasyNVRModule获取列表清单,反馈给客户端:
QTSS_Error HTTPSession::execNetMsgCSGetChannelsRESTful(const char* queryString)
{
if(QTSServerInterface::GetServer()->GetPrefs()->liveStreamingAuth())
{
if (!isAuthenticate())
return EASY_ERROR_CLIENT_UNAUTHORIZED;
}
QTSS_Error theErr;
QTSS_RoleParams params;
params.easyNVRChannelsParams.inChannels = NULL;
theErr = EasyNVRUtil::CallDispatch(Easy_NVRGetChannels_Role, QTSSModule::kGetChannelsRole, params);
EasyProtocolACK rsp(MSG_SC_SERVER_GET_CHANNELS_ACK);
EasyJsonValue header, body;
header[EASY_TAG_VERSION] = EASY_PROTOCOL_VERSION;
header[EASY_TAG_CSEQ] = 1;
header[EASY_TAG_ERROR_NUM] = EASY_ERROR_SUCCESS_OK;
header[EASY_TAG_ERROR_STRING] = EasyProtocol::GetErrorString(EASY_ERROR_SUCCESS_OK);
if (theErr == QTSS_NoErr)
{
std::map<int, EasyNVRChannel*>* pChannels = static_cast<std::map<int, EasyNVRChannel*>*>(params.easyNVRChannelsParams.inChannels);
if (!pChannels) return QTSS_BadArgument;
Json::Value* proot = rsp.GetRoot();
int i = 0;
std::map<int, EasyNVRChannel*>::iterator it = (*pChannels).begin();
while (it != (*pChannels).end())
{
CameraInfo* item = it->second->GetChannelInfo();
if (item->enable != 0)
{
Json::Value value;
value[EASY_TAG_CHANNEL] = item->id;
//value[EASY_TAG_ENABLE] = item->enable;
value[EASY_TAG_ONLINE] = item->online;
value[EASY_TAG_NAME] = item->name;
value[EASY_TAG_SNAP_URL] = item->snap.empty() ? "" : item->snap;
value[EASY_TAG_ERROR_STRING] = item->lasterror.empty() ? "" : item->lasterror;
(*proot)[EASY_TAG_ROOT][EASY_TAG_BODY][EASY_TAG_CHANNELS].append(value);
++i;
}
++it;
}
body[EASY_TAG_CHANNEL_COUNT] = EasyUtil::Int2String(i);
}
rsp.SetHead(header);
rsp.SetBody(body);
string msg = rsp.GetMsg();
this->SendHTTPPacket(msg, false, false);
return QTSS_NoErr;
}
诸如此类,按照这种方式实现每一个http接口功能即可实现完整一套的http接口功能,对外进行能力输出!
四、配套组件
在《web无插件播放RTSP摄像机方案,拒绝插件,拥抱H5!》中描述了很多组件:live555、ffmpeg、librtmp、faac等等,基于这些组件可以非常好地帮助您实现这些功能,但是这些组件都会存在不成熟或者不易使用的问题,EasyDarwin社区的开发者也实现了一系列此类功能的功能组件,例如:
EasyRTSPClient:类似于live555的RTSPClient功能组件,进行RTSP拉流;
EasyAACEncoder:AAC转码,将各种类型的安防音频格式转码成标准H5需要的AAC格式,开源项目地址:https://github.com/EasyDarwin/EasyAACEncoder
EasyRTMP:RTMP推流工具,能非常好地实现推流,重连,数据缓冲,过滤等等功能;
五、细节亮点
- 按需直播
EasyNVR独特设计了一套按需直播的方案,也就是可以配置EasyNVR的通道只有在有人看的时候,才会从摄像机设备源取流到EasyNVR流媒体服务器进行直播,当没有客户端观看的时候,直接切断与源的连接,只做常态的设备状态监测和快照功能,降低带宽和服务器的压力;
具体实现:客户端页面需要不断向EasyNVR发送心跳包,保持具体某一个通道的流状态,当超时时间内某通道没有获得心跳包,那么就认为该通道无人观看,即停止从设备拉流!
- token认证
EasyNVR实现了一套authToken验证机制,客户端调用登陆接口后,客户端会获取到一个token,在本次操作的过程中,只要在http cookies中携带token,即可操作EasyNVR其余接口,如果不携带token或者携带的token已经过期,EasyNVR会返回401,这样客户端需要重新调用EasyNVR登陆接口获取新的token!
- 占位设计
我们经常在做服务器时会遇到内外网映射的问题,当外网客户端请求一个通过端口映射出来的内网服务器时,内网服务器往往不知道其所在的公网的IP地址,如果每次都是通过配置IP的方式,一方面是很麻烦,另一方面,很多网络路由规则会限制内网的客户端不能请求该内网所在的公网的IP,也就是内网的客户端不能请求自己的公网IP的对外服务,内网的只能通过内网路由,而且内网请求内网IP就获取到的是内网的直播流地址也是很合理的需求,于是,我们设计了一套占位方案,比如客户端请求一个RTMP的直播地址,我们返回给客户端的地址是:rtmp://{host}:10935/hls/channel_1,那么客户端再根据自己请求到EasyNVR的host IP,替换{host}占位符,就实现了,内网用内网的IP,公网用公网的IP了!
关于EasyNVR
EasyNVR能够通过简单的网络摄像机通道配置,将传统监控行业里面的高清网络摄像机IP Camera、NVR等具有RTSP协议输出的设备接入到EasyNVR,EasyNVR能够将这些视频源的音视频数据进行拉取,转换为RTMP/HLS,进行全平台终端H5直播(Web、Android、iOS),并且EasyNVR能够将视频源的直播数据对接到第三方CDN网络,实现互联网级别的直播分发;
详细说明:http://www.easydarwin.org/easynvr/
获取更多信息
Copyright © EasyDarwin.org 2012-2017