写点什么

基于 DSS 框架开发一个 GB28181 协议插件

用户头像
Changing Lin
关注
发布于: 2021 年 06 月 02 日
基于DSS框架开发一个GB28181协议插件

1.项目概要:

本文主要介绍GB28181协议概念、应用场景,以及如何基于DSS框架开发一个插件。
复制代码

2.背景和需求

公司的机场线项目,由于项目存在某些不可控背景因素,以及无线网络的特点,导致使用传统的海康GB28181系列的设备,无法保证在网络中的观看监控需求,可能出现卡顿、马赛克、绿屏等问题。因此,决定开发一款轻量级、支持接入GB28181协议的上下游设备、具有抗丢包能力,适合无线网络的视频传输和远程播放需求。
复制代码

2.1 GB28181 协议简介

GB/T28181-2011 《安全防范视频监控联网系统信息传输、交换、控制技术要求》是由公安部科技信息化局提出,由全国安全防范报警系统标准化技术委员会(SAC/TC100)归口,公安部一所等多家单位共同起草的一部国家标准。GB/T28181-2011已经于2016年07月12日已经被GB/T28181-2016所取代。GB28181协议主要分为信令协议和媒体流传输协议两部分,信令协议是通过SIP协议完成,媒体流传输是走RTP+PS流、RTCP完成。
复制代码

2.2 GB28181 协议结构

GB28181 联网协议结构如下图所示:


联网系统在进行视音频传输及控制时应建立两个传输通道: 会话通道和媒体流通道。会话通道用于在设备之间建立会话并传输系统控制命令,会话协议采用SIP协议(RFC3261); 媒体流通道用于传输视音频数据, 经过压缩编码的视音频流采用流媒体协议RTP/RTCP传输。会话协议实现的功能主要包括:注册、心跳保活、目录查询、实时视频点播、录像查询、录像回放/下载、报警事件上报、网络校时、事件订阅等;
复制代码

2.3 方案选型:


2.4 需求分析:

搜集市面上的GB28181协议的软件,发现基本可定制性不高,无法满足二次开发的需求。DSS流媒体服务是具有支持在互联网传输流媒体,多平台,可定制性高的特点,同时,也支持标准RTP和RTSP协议,是一个适合二次开发的平台。因此,经过技术预研分析,结合过往项目经验,我们决定设计方案如下:
复制代码



2.5 开发目标

在DSS平台开发一个SipModule插件,用来实现GB28181协议,支持信令级联、媒体级联等基础功能。
复制代码

3.实现原理

根据国标GB28181联网协议结构显示,分为信令和媒体两部分基础模块。
复制代码

3.1 SIP 信令:

  • sip 协议使用比较成熟的 exosip2 库,下载下来,编译通过后即可使用

  • exosip2 开源库:http://www.antisip.com/doc/exosip2/

  • eXosip2 has support for:



* registrations. (REGISTER) * call initiation and modification. (INVITE, re-INVITE) * other methods within calls (INFO, OPTIONS, UPDATE) * call transfer. (REFER) * reliability for provisionnal response. (PRACK) * sip event package. (SUBSCRIBE/NOTIFY) * event state publication. (PUBLISH) * instant messaging. (MESSAGE) * ...
复制代码


  • 编译方法:


./configuremake
复制代码


  • 编译结果:(把编译生成的库和头文件放到一个目录下,后面需要用到)


.├── include│   ├── eXosip2│   │   ├── Makefile│   │   ├── Makefile.am│   │   ├── Makefile.in│   │   ├── eX_call.h│   │   ├── eX_message.h│   │   ├── eX_options.h│   │   ├── eX_publish.h│   │   ├── eX_refer.h│   │   ├── eX_register.h│   │   ├── eX_setup.h│   │   ├── eX_subscribe.h│   │   └── eXosip.h│   ├── osip2│   │   ├── osip.h│   │   ├── osip_condv.h│   │   ├── osip_dialog.h│   │   ├── osip_fifo.h│   │   ├── osip_mt.h│   │   └── osip_time.h│   └── osipparser2│       ├── headers│       │   ├── osip_accept.h│       │   ├── osip_accept_encoding.h│       │   ├── osip_accept_language.h│       │   ├── osip_alert_info.h│       │   ├── osip_allow.h│       │   ├── osip_authentication_info.h│       │   ├── osip_authorization.h│       │   ├── osip_call_id.h│       │   ├── osip_call_info.h│       │   ├── osip_contact.h│       │   ├── osip_content_disposition.h│       │   ├── osip_content_encoding.h│       │   ├── osip_content_length.h│       │   ├── osip_content_type.h│       │   ├── osip_cseq.h│       │   ├── osip_error_info.h│       │   ├── osip_from.h│       │   ├── osip_header.h│       │   ├── osip_mime_version.h│       │   ├── osip_proxy_authenticate.h│       │   ├── osip_proxy_authentication_info.h│       │   ├── osip_proxy_authorization.h│       │   ├── osip_record_route.h│       │   ├── osip_route.h│       │   ├── osip_to.h│       │   ├── osip_via.h│       │   └── osip_www_authenticate.h│       ├── osip_body.h│       ├── osip_const.h│       ├── osip_headers.h│       ├── osip_list.h│       ├── osip_md5.h│       ├── osip_message.h│       ├── osip_parser.h│       ├── osip_port.h│       ├── osip_uri.h│       └── sdp_message.h├── libeXosip2.a├── libosip2.a└── libosipparser2.a
5 directories, 58 files
复制代码




  • 新建 SIP_SERVER 类,用于实现 SIP 代理服务,支持下游设备的接入


class SIP_SERVER:public SIP_BASIC, public OSThread{    public :        void notifyDeviceStatuChange(char* req_xml_body);        void PushDeviceCatalog(char *rsp_xml_body, char* targetSN, int sumNum, char* deviceId, bool isLive = true);        int RegisterAction(osip_message_t *reg);        int RegisterHeart(eXosip_event_t* je, int expires, osip_message_t* reg, char *strAuth );        int RegisterWithAuthentication(osip_message_t* reg, eXosip_event_t* je );        int eXosipInitialize(); // 初始化 eXosip 底层库,开始监听SIP端口        void setserverport(int port);        int getserverport();        void GenerateRadom();        char * GetNonce();        int    Init_Server();        void ResponseDeviceInfo(char* rsp_xml_body);        int  ProcessRegister();            void ProcessNotify(osip_message_t *request);        void ResponseDeviceStatus(char* rsp_xml_body);        int ProcessInvite(sdp_message_t *sdp, osip_message_t *request);        int ResponseInvite(int index);        void ResponseDeviceBoot(char* rsp_xml_body);        void ResponseCatalog(char *rsp_xml_body, char* targetSN, int sumNum, char* deviceId, bool isLive = true);        void ProcessKeepAlive();        void ProcessDirectKeepAlive();        void SendKeepAlive();        void VideoFileQuery(char* rsp_xml_body);        void Catalog(char* rsp_xml_body, int size, int sn, char* deviceId);        void SendCatalog(char* req_xml_body, int index);        void PTZ_Control_left(char* rsp_xml_body);        int Call_Build_Initial_Invite(int index, const char *rtpHost, int rtpRecvPort); // 给IPC发送邀请信息,开始实时视频点播        int Get_Ipc_Num();        SIP_IPC* getIpcByIndex(int index);
/* 设备注册时 需要对应的deviceID 均匹配 */ int Is_device_Register(char * DeviceID); char* GetHost() const {return fHost;} // eXosip_event_t *inviteEvent = NULL; // 保存邀请会话信息 // RTPSession* fRTPSession = NULL; // 用于接收 某一路RTP流
void InitSuperiorPlatformSipConfig(char* serial, char* realm, char* host, UInt16 port, char* account, char* password, UInt16 expires, UInt16 keepAliveTime); UInt16 GetKeepAliveTime(){return fSuperiorPlatformSipKeepAliveTime;} void onTimeoutCallback(char* sn); // 超时回调
void setMode(Bool16 value){fDirectMode = value;}
SIP_SERVER(char* serial, char* realm, char* host, UInt16 port, char* devicePassword, int ipcKeepAliveTimeout): OSThread() { fListenPort = port; fSerial = serial; fRealm = realm; fHost = host; fDevicePassword = devicePassword; fKeepAliveTimeout = ipcKeepAliveTimeout; fIpcNum = 0;
memset(fLocalSip, 0, sizeof(fLocalSip));
qtss_sprintf(fLocalSip, "sip:%s@%s:%d", serial, host, port); // qtss_printf("%s SIP_SERVER mLocalSip = %s\n", TAG, fLocalSip);
for(int loop = 0; loop < MAX_IPC_NUM; loop++) { fIpcList[loop] = NULL; } this->GenerateRadom();
fDeviceList = new DeviceList(0); } ~SIP_SERVER();
private : int fListenPort = 0; // 监听的SIP端口
char* fSerial; // 服务自身序列号 char* fRealm; // 服务自身所在域 char* fHost; // 服务自身所在IP UInt16 fPort; char* fDevicePassword; // 下级接入的密码 int fKeepAliveTimeout;
char* fSuperiorPlatformSipSerial; // 上级平台序列号 char* fSuperiorPlatformSipRealm; // 上级平台所在域 char* fSuperiorPlatformSipHost; // 上级平台所在IP UInt16 fSuperiorPlatformSipPort; // 上级平台SIP端口 char* fSuperiorPlatformSipAccount; // 登陆上级平台时的账号 char* fSuperiorPlatformSipPassword; // 登陆上级平台时的密码 UInt16 fSuperiorPlatformSipRegisterExpires; // 登陆上级平台超时时间 UInt16 fSuperiorPlatformSipKeepAliveTime; // 登陆上级平台保活时间 UInt16 fSuperiorPlatformSipSN = 1; // 登陆上级平台SIP信令序号
char fLocalSip[MAX_STRING_LEN] = {0}; char fLocalSipForSuperiorPlatform[MAX_STRING_LEN] = {0}; char fSuperiorPlatformSipProxy[MAX_STRING_LEN] = {0};
char fNonce[32] = {0}; SIP_IPC* fIpcList[MAX_IPC_NUM]; int fIpcNum = 0;
virtual void Entry(); // 自身服务线程处理入口,用于监听是否有下级设备接入,并处理注册和心跳信息
KeepAliveTask* fKeepAliveIdleTask = NULL;
eXosip_event_t *fSuperiorPlatformInviteEvent = NULL; DeviceList* fDeviceList = NULL; // 维护下级IPC设备列表
复制代码


  • SIP_SERVER::Entry 线程处理入口


void SIP_SERVER::Entry(){    eXosip_event_t *event;    osip_message_t *reg = NULL;    int register_id = RegisterAction(reg); // 向上级平台注册    while (1)    {        event = eXosip_event_wait(0, 50);        eXosip_lock();        eXosip_automatic_action();  //部分non-200消息自动重发,SIP会话中Retry很常见        eXosip_automatic_refresh(); /*Refresh REGISTER and SUBSCRIBE before the expiration delay*/        eXosip_unlock();
if (NULL == event) continue;
/* 增加赋值操作 */ this->setEvent(event);
if (EXOSIP_CALL_INVITE == event->type) { if (MSG_IS_INVITE(event->request)) { this->PrintMsg(REQUEST); } } else if (EXOSIP_MESSAGE_NEW == event->type) { if (MSG_IS_REGISTER(event->request)) // 收到下级IPC或者平台的注册信息 { ProcessRegister(); } else if(MSG_IS_SUBSCRIBE(event->request)) { qtss_printf("%sMSG_IS_SUBSCRIBE\n", TAG); Answer200(); } else if (MSG_IS_NOTIFY(event->request)) // 收到下级平台的设备信息通知 { printf("%sMSG_IS_NOTIFY\n", TAG); PrintMsg(REQUEST); this->Answer200(); } else if (MSG_IS_MESSAGE(event->request)) { osip_body_t *req_body = NULL; osip_message_get_body(event->request, 0, &req_body); char CmdType[99] = {0}, TeleBoot[99] = {0}, rsp_xml_body[MAX_LEN] = {0}; char Sn[100] = {0}; char DeviceID[100] = {0}; this->get_str(req_body->body, "<CmdType>", false, "</CmdType>", false, CmdType);
this->get_str(req_body->body, "<SN>", false, "</SN>", false, Sn); this->get_str(req_body->body, "<DeviceID>", false, "</DeviceID>", false, DeviceID);
TRACE("%sCmdType~%s, SN~%s,DeviceID~%s\n", TAG, CmdType, Sn, DeviceID);
if (strcmp(DeviceID, fSuperiorPlatformSipAccount) == 0) { // 应答上级设备 this->Answer200(); if (strcmp(CmdType, "Catalog") == 0) { int sum = Get_Ipc_Num(); TRACE("%sCatalog CmdType %d\n", TAG, sum);
char req_xml_body[MAX_LEN] = {0}; Catalog(req_xml_body, MAX_LEN, atoi(Sn), getIpcByIndex(0)->GetUsr()); SendCatalog(req_xml_body, 0); // 透传给下一级平台
continue; } else { TRACE("%s unsupport superior platform CmdType is %s \n", CmdType); } continue; }
/* 已经注册的信息才返回,否则不返回 */ int index = Is_device_Register(DeviceID); if (index != -1) // 应答下级设备 { this->Answer200(); getIpcByIndex(index)->RefreshTimeout();
if (strcmp(CmdType, "Keepalive") == 0) { TRACE("%sCmdType is Keepalive\n", TAG); } else if (strcmp(CmdType, "Alarm") == 0) { TRACE("%s %s happen!\n", TAG, CmdType); }else if (strcmp(CmdType, "Catalog") == 0) { // 返回给上一级平台 eXosip_lock(); osip_message_t *rsp_msg = NULL; // int ret = StrReplace(req_body->body, DeviceID, fSerial); // qtss_printf("%sStrReplace = %d\n", TAG, ret); // ret = StrReplace(req_body->body, "3402", "3502"); qtss_printf("%sreq_body->body = %s\n", TAG, req_body->body); eXosip_message_build_request(&rsp_msg, "MESSAGE", fSuperiorPlatformSipProxy, fLocalSipForSuperiorPlatform, NULL); osip_message_set_body(rsp_msg, req_body->body, strlen(req_body->body)); osip_message_set_content_type(rsp_msg, "Application/MANSCDP+xml"); eXosip_message_send_request(rsp_msg); eXosip_unlock(); } else { TRACE("%sunsupport ipc CmdType is %s \n", TAG, CmdType); PrintMsg(REQUEST);
} } else { TRACE("%sdevice[%s] not register! @@@@@@@@@@@@@@@\n", TAG, DeviceID); } } } else if (EXOSIP_CALL_ACK == event->type) { this->PrintMsg(REQUEST); TRACE("%sEXOSIP_CALL_ACK: %s\n", TAG, event->textinfo); } else if (EXOSIP_CALL_ANSWERED == event->type) { TRACE("%sEXOSIP_CALL_ANSWERED: %s\n", TAG, event->textinfo); this->PrintMsg(RESPONSE); osip_message_t *ack; eXosip_call_build_ack(event->did, &ack); eXosip_call_send_ack(event->did, ack); } else if (EXOSIP_MESSAGE_ANSWERED == event->type) { TRACE("%sEXOSIP_MESSAGE_ANSWERED: %s\n", TAG, event->textinfo); // this->PrintMsg(RESPONSE); osip_message_t *ack; eXosip_call_build_ack(event->did, &ack); eXosip_call_send_ack(event->did, ack); } else if (EXOSIP_CALL_RINGING == event->type) { TRACE("%sEXOSIP_CALL_RINGING:call_id is %d,dialog_id is %d \n", TAG, event->cid, event->did); //this->PrintMsg(RESPONSE); } else if (EXOSIP_CALL_CLOSED == event->type) { // TRACE("%s the other side closed!\n", TAG); this->PrintMsg(RESPONSE); } else if (EXOSIP_CALL_PROCEEDING == event->type) { TRACE("%s PROCEEDING:call_id is %d,dialog_id is %d \n", TAG, event->cid, event->did); this->PrintMsg(RESPONSE); } else if (EXOSIP_REGISTRATION_FAILURE == event->type) { if (NULL == event->response) { TRACE("%s EXOSIP_REGISTRATION_FAILURE, and event->response is NULL\n", TAG); continue; } TRACE("%s EXOSIP_REGISTRATION_FAILURE %d\r\n", TAG, event->response->status_code); // PrintMsg(RESPONSE); if ((NULL != event->response) && (401 == event->response->status_code)){ // 由于提前输入了验证信息,在消息为401处,用eXosip_automatic_action()自动处理,因此此处我们不直接处理 // eXosip_default_action(event); // int success = RegisterWithAuthentication(reg, event); } else { } } else if (EXOSIP_REGISTRATION_SUCCESS == event->type) { TRACE("%sEXOSIP_REGISTRATION_SUCCESS %d\r\n", TAG, event->response->status_code); register_id = event->rid;
if (NULL != fKeepAliveIdleTask) { fKeepAliveIdleTask->CancelTimeout(); fKeepAliveIdleTask = NULL; }
fKeepAliveIdleTask = new KeepAliveTask(); fKeepAliveIdleTask->SetSipServer((void *)this); fKeepAliveIdleTask->Signal(Task::kStartEvent); } else { TRACE("%sother event is %s %d\n", TAG, event->textinfo, event->type); PrintMsg(RESPONSE); } }}
复制代码

3.2 媒体级联:

媒体流传输是走RTP+PS流、RTCP完成,经过抓包对比分析,海康IPC的媒体流是RTP+PS流,并没有RTCP包的通信,并且,底层是UDP协议。因此,理论上只需要SIP插件支持接收或发送UDP包,就可以收到IPC发送出来的媒体流,并进行转发给上一级。DSS本身自带UDPSocket类,封装了简洁好用的API,可方便的给上层调用。
复制代码


#ifndef __UDPSOCKET_H__#define __UDPSOCKET_H__
#ifndef __Win32__#include <sys/socket.h>#include <sys/uio.h>#endif
#include "Socket.h"#include "UDPDemuxer.h"

class UDPSocket : public Socket{ public: //Another socket type flag (in addition to the ones defined in Socket.h). //The value of this can't conflict with those! enum { kWantsDemuxer = 0x0100 //UInt32 }; UDPSocket(Task* inTask, UInt32 inSocketType); virtual ~UDPSocket() { if (fDemuxer != NULL) delete fDemuxer; }
//Open OS_Error Open() { return Socket::Open(SOCK_DGRAM); }
OS_Error JoinMulticast(UInt32 inRemoteAddr); OS_Error LeaveMulticast(UInt32 inRemoteAddr); OS_Error SetTtl(UInt16 timeToLive); OS_Error SetMulticastInterface(UInt32 inLocalAddr);
//returns an ERRNO OS_Error SendTo(UInt32 inRemoteAddr, UInt16 inRemotePort, void* inBuffer, UInt32 inLength); OS_Error RecvFrom(UInt32* outRemoteAddr, UInt16* outRemotePort, void* ioBuffer, UInt32 inBufLen, UInt32* outRecvLen); //A UDP socket may or may not have a demuxer associated with it. The demuxer //is a data structure so the socket can associate incoming data with the proper //task to process that data (based on source IP addr & port) UDPDemuxer* GetDemuxer() { return fDemuxer; } private: UDPDemuxer* fDemuxer; struct sockaddr_in fMsgAddr;};#endif // __UDPSOCKET_H__
复制代码


  • 封装一个 SIP_IPC 类,用于描述一个 IPC 信息


/** * 创建一个Udp socket用于接收IPC发送的RTP包 **/QTSS_Error SIP_IPC::createUDPSocket(int port){    if(NULL != this->fTask){        delete this->fTask;    }    this->fTask = new IpcTask(this);
this->fUDPSocket = new UDPSocket(this->fTask, Socket::kNonBlockingSocketType); // 非阻塞型UDPSocket int theErr = this->fUDPSocket->Open(); if (theErr != OS_NoErr){ qtss_printf("%s SIP_IPC::createUDPSocket %d Open fail!\n", TAG, port); return theErr; } theErr = this->fUDPSocket->Bind(INADDR_ANY, port); if (theErr != OS_NoErr){ qtss_printf("%s SIP_IPC::createUDPSocket %d Bind fail!\n", TAG, port); return theErr; }
this->fUDPSocket->SetSocketRcvBufSize(MAX_BUFFER_SIZE);
// Start the connection process going this->fTask->Signal(Task::kStartEvent); return OS_NoErr;}
QTSS_Error SIP_IPC::destroyUDPSocket(){ if (this->fUDPSocket != NULL) { OS_Error theErr = OS_NoErr; UInt32 theRemoteAddr = 0; UInt32 theLength = 0; UInt16 theRemotePort = 0; char thePacketBuf[2048]; while (theErr == OS_NoErr) { // Get a packet from one of the UDP sockets. theErr = this->fUDPSocket->RecvFrom(&theRemoteAddr, &theRemotePort, &thePacketBuf[0], 2048, &theLength); }
delete this->fUDPSocket; this->fUDPSocket = NULL; // Todo: must after delete fUDPSocket if(NULL != this->fTask){ this->fTask->Signal(Task::kKillEvent); this->fTask = NULL; } return OS_NoErr; } qtss_printf("%s fUDPSocket is NULL, not allow to release\n", TAG); return OS_BadURLFormat;}/** * 定时从UdpSocket中接收并读取数据 **/OS_Error SIP_IPC::ReadMediaData(){ // For iterating over the array of UDP sockets OS_Error theErr = OS_NoErr; UInt32 theTrackID = 0; UInt32 theLength = 0; char* thePacket = NULL; UInt32 theRemoteAddr = 0; UInt16 theRemotePort = 0; char thePacketBuf[MAX_LEN];
while (isListening()) { // Get a packet from one of the UDP sockets. theErr = fUDPSocket->RecvFrom(&theRemoteAddr, &theRemotePort, &thePacketBuf[0], MAX_LEN, &theLength); if ((theErr != OS_NoErr) || (theLength == 0)) { if(!isListening()) return theErr; fUDPSocket->RequestEvent(EV_RE); // request async event break; } thePacket = &thePacketBuf[0]; // We have a valid packet. Invoke the packet handler function // qtss_printf("%s We have a valid packet %d. Invoke the packet handler function!\n", TAG, theLength); ProcessRTPPacket(thePacket, theLength, theTrackID); } return theErr;}
复制代码

3.3 DSS 开发插件方法:

  • 新建 SIPModule 目录:root\APIModules\SipModule

  • 把所有插件相关的源码都放到该目录下,并相关文件、目录添加到编译脚本

  • 修改编译脚本:root\Makefile.POSIX


CCFLAGS += -IAPIModules/SipModuleCCFLAGS += -IAPIModules/SipModule/libsCCFLAGS += -IAPIModules/SipModule/libs/eXosip2/includeCCFLAGS += -IAPIModules/SipModule/libs/iconv/includeCCFLAGS += -IAPIModules/SipModule/libs/jsoncpp/include
LINKOPTS += -LAPIModules/SipModule/libs/hiredisLINKOPTS += -LAPIModules/SipModule/libs/eXosip2LINKOPTS += -LAPIModules/SipModule/libs/iconvLINKOPTS += -LAPIModules/SipModule/libs/jsoncpp
CPPFILES = Server.tproj/GenerateXMLPrefs.cpp \ ... APIModules/SipModule/GB28181Session.cpp \ APIModules/SipModule/IpcTask.cpp \ APIModules/SipModule/RtpToES.cpp \ APIModules/SipModule/SipModule.cpp \ APIModules/SipModule/algorithm.cpp \ APIModules/SipModule/sipbasic.cpp \ APIModules/SipModule/sipipc.cpp \ APIModules/SipModule/sipserver.cpp \ ...
复制代码


  • 修改配置文件:root\streamingserver.xml


    <MODULE NAME="SIPModule" >        <PREF NAME="serial" >34020000002000000001</PREF>        <PREF NAME="realm" >3402000000</PREF>        <PREF NAME="host" >192.168.1.20</PREF>        <PREF NAME="port" TYPE="UInt16" >7100</PREF>        <PREF NAME="device_password" >12345678</PREF>        <PREF NAME="ack_timeout" TYPE="UInt16" >10</PREF>        <PREF NAME="keepalive_timeout" TYPE="UInt16" >300</PREF>        <PREF NAME="forbid_ip_list" >192.168.1.5;192.168.1.6</PREF>        <PREF NAME="superior_platform_sip_serial" >36020000002000000001</PREF>        <PREF NAME="superior_platform_sip_realm" >3602000000</PREF>        <PREF NAME="superior_platform_sip_host" >192.168.1.10</PREF>        <PREF NAME="superior_platform_sip_port" TYPE="UInt16" >5060</PREF>        <PREF NAME="superior_platform_sip_account" >36020000001110000108</PREF>        <PREF NAME="superior_platform_sip_password" >12345678</PREF>        <PREF NAME="superior_platform_sip_register_expires" TYPE="UInt16" >3600</PREF>        <PREF NAME="superior_platform_sip_keep_alive_time" TYPE="UInt16" >60</PREF>    </MODULE>
复制代码


  • 新建 APIModules/SipModule/SipModule.h



/* * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2008 Apple Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ * *//* File: SipModule.h Contains: A module for SIP stacks.*/
#ifndef _SIP_MODULE_#define _SIP_MODULE_
#include "QTSS.h"
#define KEY_SERIAL "serial"#define KEY_REALM "realm"#define KEY_HOST "host"#define KEY_PORT "port"#define KEY_DEVICE_PASSWORD "device_password"#define KEY_ACK_TIMEOUT "ack_timeout"#define KEY_KEEPALIVE_TIMEOUT "keepalive_timeout"
#define KEY_FORBID_IP_LIST "forbid_ip_list"#define KEY_SUPERIOR_PLATFORM_SIP_SERIAL "superior_platform_sip_serial"#define KEY_SUPERIOR_PLATFORM_SIP_REALM "superior_platform_sip_realm"#define KEY_SUPERIOR_PLATFORM_SIP_HOST "superior_platform_sip_host"#define KEY_SUPERIOR_PLATFORM_SIP_PORT "superior_platform_sip_port"#define KEY_SUPERIOR_PLATFORM_SIP_ACCOUNT "superior_platform_sip_account"#define KEY_SUPERIOR_PLATFORM_SIP_PASSWORD "superior_platform_sip_password"#define KEY_SUPERIOR_PLATFORM_SIP_REGISTER_EXPIRES "superior_platform_sip_register_expires"#define KEY_SUPERIOR_PLATFORM_SIP_KEEP_ALIVE_TIME "superior_platform_sip_keep_alive_time"
extern "C"{ EXPORT QTSS_Error SipModule_Main(void* inPrivateArgs);}#endif
复制代码


  • 新建 APIModules/SipModule/SipModule.cpp


#include "SipModule.h"

#include "OSArrayObjectDeleter.h"#include "StringParser.h"#include "StrPtrLen.h"#include "QTSSModuleUtils.h"#include "ReflectorSession.h"#include "ReflectorStream.h"#include "RTPSessionOutput.h"
#include "redis.h"#include "osip2/osip.h"#include "iconv.h"#include "sipserver.h"
#include "../../HTTPUtilitiesLib/HTTPClientResponseStream.h"
#if __cplusplus >= 201103L #warning “Should use –std=c++11 option for compile#endif
static QTSS_ModulePrefsObject sPrefs = NULL;
static char* sDefaultSerial = "34020000002000000001";static char* sDefaultRealm = "3402000000";static char* sDefaultHost = "192.168.1.20";static UInt16 sDefaultPort = 7100;static char* sDefaultDevicePassword = "12345678";static UInt16 sDefaultAckTimeout = 10;static UInt16 sDefaultKeepAliveTimeout = 300;
static char* sSerial = NULL;static char* sRealm = NULL;static char* sHost = NULL;static UInt16 sPort = 0;static char* sDevicePassword = NULL;static UInt16 sAckTimeout = 10;static UInt16 sKeepAliveTimeout = 300;static char* sForbidIpList = NULL;
static char* sSuperiorPlatformSipSerial = NULL;static char* sDefaultSuperiorPlatformSipSerial = "36020000002000000001";
static char* sSuperiorPlatformSipRealm = NULL;static char* sDefaultSuperiorPlatformSipRealm = "3602000000";
static char* sSuperiorPlatformSipHost = NULL;static char* sDefaultSuperiorPlatformSipHost = "192.168.1.10";
static UInt16 sSuperiorPlatformSipPort = 5060;static UInt16 sDefaultSuperiorPlatformSipPort = 5060;
static char* sSuperiorPlatformSipAccount = NULL;static char* sDefaultSuperiorPlatformSipAccount = "36020000001110000108";
static char* sSuperiorPlatformSipPassword = NULL;static char* sDefaultSuperiorPlatformSipPassword = "12345678";
static UInt16 sSuperiorPlatformSipRegisterExpires = 3600;static UInt16 sDefaultSuperiorPlatformSipRegisterExpires = 3600;static UInt16 sSuperiorPlatformSipKeepAliveTime = 60;static UInt16 sDefaultSuperiorPlatformSipKeepAliveTime = 60;

static Bool16 sRefGB28181Enabled = false;static Bool16 sDefaultRefGB28181Enabled = false;

static QTSS_Error SipModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);static QTSS_Error Register(QTSS_Register_Params *inParams);static QTSS_Error Initialize(QTSS_Initialize_Params *inParams);static QTSS_Error RereadPrefs();static QTSS_Error GetSIPService(SIP_InviteIpc_Params *inparams);static QTSS_Error SIPInviteIpc(SIP_InviteIpc_Params *inparams);static QTSS_Error SIPByeIpc(SIP_InviteIpc_Params *inparams);SIP_SERVER *sipServer;
QTSS_Error SipModule_Main(void *inPrivateArgs){ return _stublibrary_main(inPrivateArgs, SipModuleDispatch);}
QTSS_Error SipModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams){ switch (inRole) { case QTSS_Register_Role: return Register(&inParams->regParams); case QTSS_Initialize_Role: return Initialize(&inParams->initParams); case QTSS_RereadPrefs_Role: return RereadPrefs(); case SIP_GET_Service_Role: return GetSIPService(&inParams->inviteIpcParams); case SIP_Invite_Ipc_Role: return SIPInviteIpc(&inParams->inviteIpcParams); case SIP_Bye_Ipc_Role: return SIPByeIpc(&inParams->inviteIpcParams); } return QTSS_NoErr;}
QTSS_Error Initialize(QTSS_Initialize_Params *inParams){ // Setup module utils QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
StrPtrLen theSplitterModule("QTSSSplitterModule"); QTSS_ModulePrefsObject theSplitterPrefs = QTSSModuleUtils::GetModulePrefsObject(QTSSModuleUtils::GetModuleObjectByName(theSplitterModule)); QTSSModuleUtils::GetAttribute(theSplitterPrefs, "reflect_gb28181_enabled", qtssAttrDataTypeBool16, &sRefGB28181Enabled, &sDefaultRefGB28181Enabled, sizeof(sRefGB28181Enabled)); qtss_printf("%sSipModule reflect_gb28181_enabled = %d\n", TAG, sRefGB28181Enabled);
sPrefs = QTSSModuleUtils::GetModulePrefsObject(inParams->inModule);
sSerial = QTSSModuleUtils::GetStringAttribute(sPrefs, KEY_SERIAL, sDefaultSerial); sRealm = QTSSModuleUtils::GetStringAttribute(sPrefs, KEY_REALM, sDefaultRealm); sHost = QTSSModuleUtils::GetStringAttribute(sPrefs, KEY_HOST, sDefaultHost); QTSSModuleUtils::GetAttribute(sPrefs, KEY_PORT, qtssAttrDataTypeUInt16, &sPort, &sDefaultPort, sizeof(sPort));
sDevicePassword = QTSSModuleUtils::GetStringAttribute(sPrefs, KEY_DEVICE_PASSWORD, sDefaultDevicePassword); QTSSModuleUtils::GetAttribute(sPrefs, KEY_ACK_TIMEOUT, qtssAttrDataTypeUInt16, &sAckTimeout, &sDefaultAckTimeout, sizeof(sAckTimeout)); QTSSModuleUtils::GetAttribute(sPrefs, KEY_KEEPALIVE_TIMEOUT, qtssAttrDataTypeUInt16, &sKeepAliveTimeout, &sDefaultKeepAliveTimeout, sizeof(sKeepAliveTimeout));
// qtss_printf("%s Initialize: %s %s %s %d %s %d %d\n", TAG, sSerial, sRealm, sHost, sPort, // sDevicePassword, sAckTimeout, sKeepAliveTimeout);
sSuperiorPlatformSipSerial = QTSSModuleUtils::GetStringAttribute(sPrefs, KEY_SUPERIOR_PLATFORM_SIP_SERIAL, sDefaultSuperiorPlatformSipSerial); sSuperiorPlatformSipRealm = QTSSModuleUtils::GetStringAttribute(sPrefs, KEY_SUPERIOR_PLATFORM_SIP_REALM, sDefaultSuperiorPlatformSipRealm); sSuperiorPlatformSipHost = QTSSModuleUtils::GetStringAttribute(sPrefs, KEY_SUPERIOR_PLATFORM_SIP_HOST, sDefaultSuperiorPlatformSipHost); QTSSModuleUtils::GetAttribute(sPrefs, KEY_SUPERIOR_PLATFORM_SIP_PORT, qtssAttrDataTypeUInt16, &sSuperiorPlatformSipPort, &sDefaultSuperiorPlatformSipPort, sizeof(sSuperiorPlatformSipPort)); sSuperiorPlatformSipAccount = QTSSModuleUtils::GetStringAttribute(sPrefs, KEY_SUPERIOR_PLATFORM_SIP_ACCOUNT, sDefaultSuperiorPlatformSipAccount); sSuperiorPlatformSipPassword = QTSSModuleUtils::GetStringAttribute(sPrefs, KEY_SUPERIOR_PLATFORM_SIP_PASSWORD, sDefaultSuperiorPlatformSipPassword); QTSSModuleUtils::GetAttribute(sPrefs, KEY_SUPERIOR_PLATFORM_SIP_REGISTER_EXPIRES, qtssAttrDataTypeUInt16, &sSuperiorPlatformSipRegisterExpires, &sDefaultSuperiorPlatformSipRegisterExpires, sizeof(sSuperiorPlatformSipRegisterExpires)); QTSSModuleUtils::GetAttribute(sPrefs, KEY_SUPERIOR_PLATFORM_SIP_KEEP_ALIVE_TIME, qtssAttrDataTypeUInt16, &sSuperiorPlatformSipKeepAliveTime, &sDefaultSuperiorPlatformSipKeepAliveTime, sizeof(sSuperiorPlatformSipKeepAliveTime)); qtss_printf("%s Initialize: %s %s %s %d %s %s %d %d\n", TAG, sSuperiorPlatformSipSerial, sSuperiorPlatformSipRealm, sSuperiorPlatformSipHost, sSuperiorPlatformSipPort, sSuperiorPlatformSipAccount, sSuperiorPlatformSipPassword, sSuperiorPlatformSipRegisterExpires, sSuperiorPlatformSipKeepAliveTime);
Redis *r = new Redis(); if (!r->connect("127.0.0.1", 6379)) { qtss_printf("%sRedis connect error!\n", TAG); } else { qtss_printf("%sconnect success %d %d!!\n", TAG, QTSS_MAX_REQUEST_BUFFER_SIZE, QTSS_MAX_RESPONSE_BUFFER_SIZE); } delete r;
sipServer = new SIP_SERVER(sSerial, sRealm, sHost, sPort, sDevicePassword, sKeepAliveTimeout); sipServer->InitSuperiorPlatformSipConfig(sSuperiorPlatformSipSerial, sSuperiorPlatformSipRealm, sSuperiorPlatformSipHost, sSuperiorPlatformSipPort, sSuperiorPlatformSipAccount, sSuperiorPlatformSipPassword, sSuperiorPlatformSipRegisterExpires, sSuperiorPlatformSipKeepAliveTime); sipServer->setMode(!sRefGB28181Enabled); int ret = sipServer->Init_Server(); // qtss_printf("cclin: ret = %d\n", ret); sipServer->Start();
return QTSS_NoErr;}
QTSS_Error Register(QTSS_Register_Params *inParams){ // Do role & attribute setup (void)QTSS_AddRole(QTSS_Initialize_Role); (void)QTSS_AddRole(QTSS_RereadPrefs_Role); (void)QTSS_AddRole(SIP_GET_Service_Role); (void)QTSS_AddRole(SIP_Invite_Ipc_Role); (void)QTSS_AddRole(SIP_Bye_Ipc_Role);
// Tell the server our name! static char *sModuleName = "SipModule"; ::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;}
QTSS_Error RereadPrefs(){ qtss_printf("SipModule.cpp RereadPrefs\n"); return QTSS_NoErr;}
QTSS_Error GetSIPService(SIP_InviteIpc_Params *inparams){ inparams->outSipServer = sipServer; return QTSS_NoErr;}
QTSS_Error SIPInviteIpc(SIP_InviteIpc_Params *inparams){ int index = sipServer->Is_device_Register(inparams->inDevice);
qtss_printf("SipModule.cpp SIPInviteIpc %s %d %d\n", inparams->inDevice, inparams->inPort, index);
if (index != -1) { SIP_IPC* sipIpc = sipServer->getIpcByIndex(index); if (sipIpc->isListening()) { sipIpc->destroyUDPSocket(); if(NULL != sipIpc->currentEvent){ eXosip_lock(); int result = eXosip_call_terminate(sipIpc->currentEvent->cid, sipIpc->currentEvent->did); qtss_printf("%sHang Up! %d\n", TAG, result); eXosip_unlock(); sipIpc->setEvent(NULL); } }
int theErr = sipIpc->createUDPSocket(inparams->inPort); qtss_printf("%s createUDPSocket %d\n", TAG, theErr); theErr = sipServer->Call_Build_Initial_Invite(index, sipServer->GetHost(), inparams->inPort); qtss_printf("%sCall_Build_Initial_Invite %d\n", TAG, theErr); inparams->outIsReady = theErr >= 0; } else { inparams->outIsReady = false; } return QTSS_NoErr;}
QTSS_Error SIPByeIpc(SIP_InviteIpc_Params *inparams){
int index = sipServer->Is_device_Register(inparams->inDevice);
qtss_printf("SipModule.cpp SIPByeIpc %s %d %d\n", inparams->inDevice, inparams->inPort, index); if (index != -1) { SIP_IPC* sipIpc = sipServer->getIpcByIndex(index); if (sipIpc->isListening()) { sipIpc->destroyUDPSocket(); } eXosip_lock(); if(NULL == sipIpc->currentEvent){ inparams->outIsReady = false; return QTSS_NoErr; } int result = eXosip_call_terminate(sipIpc->currentEvent->cid, sipIpc->currentEvent->did); qtss_printf("Hang Up! %d\n", result); eXosip_unlock(); sipIpc->setEvent(NULL); inparams->outIsReady = result == 0; } else { inparams->outIsReady = false; } return QTSS_NoErr;}
复制代码


  • 修改 root\Server.tproj\QTSServer.cpp



#include "SipModule.h"void QTSServer::LoadCompiledInModules(){ QTSSModule *sipModule = new QTSSModule("SIPModule"); (void)sipModule->SetupModule(&sCallbacks, &SipModule_Main); (void)AddModule(sipModule); ...}
复制代码

4.使用方法

  • 编译工程:


./Buildit    // 编译dss可执行文件
复制代码


  • 启动服务


./DarwinStreamingServer -c streamingserver.xml -d    // 添加 -c 可以指定当前目录下的配置文件,-d 表示以调试模式运行
复制代码


  • 打开 root\Logs\Error.log


[Version] Built on: May 28 2021, 17:53:20WARNING: No module folder exists.INFO: Module Loaded...SIPModule [static] // 看到这句打印,意味着我们开发的插件,已经正确被调用,开始工作了INFO: Module Loaded...QTSSFileModule [static]        INFO: Module Loaded...QTSSReflectorModule [static]   INFO: Module Loaded...QTSSRelayModule [static]       INFO: Module Loaded...QTSSSplitterModule [static]    INFO: Module Loaded...QTSSAccessLogModule [static]   INFO: Module Loaded...QTSSFlowControlModule [static] INFO: Module Loaded...QTSSPosixFileSysModule [static]INFO: Module Loaded...QTSSAdminModule [static]       INFO: Module Loaded...QTSSMP3StreamingModule [static]INFO: Module Loaded...QTSSAccessModule [static]      [cclin] SipModule reflect_gb28181_enabled = 0 // 从配置文件streamingserver.xml中读取配置信息[cclin]  Initialize: 36020000002000000001 3602000000 192.168.1.10 5060 36020000001110000108 12345678 3600 60[cclin] redisConnect error: Connection refused // 由于redis服务并未开启,因此无法连接[cclin] Redis connect error! // 忽略redis连接失败日志[cclin] reflect_gb28181_enabled = 0
WARNING: No users file found at /Library/QuickTimeStreaming/Config/qtusers.WARNING: No groups file found at /Library/QuickTimeStreaming/Config/qtgroups.Streaming Server done starting upHello world!!![cclin] eXosip_register_send_register no authorization success! // 由于未打开上级平台,打印认证失败了[cclin] EXOSIP_REGISTRATION_FAILURE, and event->response is NULL
复制代码

5.插件启动顺序:

SipModule_Main->

SipModuleDispatch->

QTSS_Register_Role->Register // 所有的插件启动时都会执行一次的规则;在里面会注册其他的规则 QTSS_Initialize_Role->Initialize // 插件初始化时的入口,也是在里面开启 SipServer 服务,开始监听下级 IPC 的注册,同时,向上级注册以及维持心跳

6.成果展示


7.参考文献

https://www.pianshen.com/article/1556871516/

https://blog.csdn.net/xiejiashu/article/details/80560550

https://github.com/GB28181/EasyDarwinGo

https://github.com/GB28181/GB28181.Solution

https://github.com/usecpp/GB28181Platform

https://github.com/inrg/gb28181_2016

https://github.com/qinpengit/Gb28181_Platform2016_Test

发布于: 2021 年 06 月 02 日阅读数: 155
用户头像

Changing Lin

关注

获得机遇的手段远超于固有常规之上~ 2020.04.29 加入

我能做的,就是调整好自己的精神状态,以最佳的面貌去面对那些未曾经历过得事情,对生活充满热情和希望。

评论

发布
暂无评论
基于DSS框架开发一个GB28181协议插件