如何接入IPC的GB28181平台

通常工业级的IPC一般支持onvif,GB28181以及各厂家私有协议,接下来介绍如何接入通过国内最主流的GB28181协议对接IPC。对于GB28181协议内容细节不多介绍,他是国家公安部定义的安防设备互通的协议,细节详见《GBT28181-2016 公共安全视频监控联网系统信息传输、交换、控制技术要求.pdf》。目前城市街道,公共场所,社区等各个安防设备基本都是通过GB28181在协议互通。如IPC,NVR,媒体网关等。本文以大华IPC为例子,直接上代码,演示如何通过GB28181协议将视频流拉下来。

一.配置IPC

 IPC配置如上所述,主要关注SIP服务器相关参数,也就是你的代码将来部署的参数。在这种场景下,IPC扮演UAC(客户端代理)角色,你的代码扮演是UAS(服务器端代理)角色。  SIP服务器编码:44011300002000000001 该编码是服务器根据公安部GBT28181编码标准自定义的 编码规则由中心编码(8位) 、行业编码(2位) 、类型编码(3位)和序号(7 位)四个码段共20位十进制数字字符构成,即系统编码 =中心编码 + 行业编码 + 类型编码 + 序号。 详见《GBT28181-2016 公共安全视频监控联网系统信息传输、交换、控制技术要求.pdf》附录D统一编码规则。44011300002000000001 其中44011300为广东省广州市番禺区,00为社会治安路面接入,200为SIP 服务器。0000001 为序列号。

 SIP服务器IP:即UAS的IP地址  设备编码:即IPC的编码。该编码也是根据GB28181编码的,其中132代表IPC,其他与服务器编码意义雷同  本地SIP 端口:默认采用5060  SIP域:即SIP服务器编码的前10bit。   注册密码:无或者任意,因为服务器目前还没有做密码认证。如果需要可以根据《GBT28181-2016 公共安全视频监控联网系统    信息传输、交换、控制技术要求.pdf》附录H的数字摘要信令认证过程和方法加以认证。   通道编码:跟设备编号一致即可   其他选项暂时默认即可 二 接入方案  因为GB28181信令是基于SIP协议的一个应用,本文采用eXosip开源方案作为GB28181的协议栈完成接入。  1.配置IPC后,IPC就会不断向服务器UAS发注册信息。

 2.完成注册后,ICP就会停止向服务器发注册消息。不过注册消息有效期过了以后会再次注册。注册有效期在配置页面默认设了3600s.

 我们有个线程专门处理SIP消息。处理注册消息接口为IPCRegister

void DealSipThread()
 {
     while (1)
     {
         eXosip_event_t* je = NULL;

        je = eXosip_event_wait(m_context, 0, 200);   // 等待一个eXosip事件,超时时间秒数使用第一个参数,微秒使用第二个参数
         eXosip_lock(m_context);
         eXosip_default_action(m_context, je);
         eXosip_automatic_action(m_context);
         eXosip_unlock(m_context);
         if (je)
         {
             switch (je->type)
             {
                 case EXOSIP_MESSAGE_NEW:
                 {
                         if (MSG_IS_REGISTER(je->request))
                         {
                             // ipc注册消息
                             printf("regist is comming\n");
                             IPCRegister(je);
                         }
                         else if (MSG_IS_MESSAGE(je->request))
                         {
                             // ipc心跳消息
                             osip_message_t* answer = NULL;
                             eXosip_lock(m_context);
                             eXosip_message_build_answer(m_context, je->tid, 200, &answer);
                             eXosip_message_send_answer(m_context, je->tid, 200, answer);   // 回复200 OK
                             printf("[INTest] get ipc heart beat [did %d] [cid %d] [tid %d]\n", je->did, je->cid, je->tid);
                             eXosip_unlock(m_context);
                         }
                         break;
                 }
             case EXOSIP_CALL_ANSWERED:
             {
                     // ipc收到invite之后会返回200 OK,并给IPC返回一个ACK
                     osip_message_t* ack = NULL;
                     eXosip_call_build_ack(m_context, je->did, &ack);
                     eXosip_call_send_ack(m_context, je->did, ack);

                    osip_via_t* via = NULL;
                     osip_message_get_via(je->request, 0, &via);

                    printf("[INTest] recv 200 OK after invite, send ack [ipcIp %s] [ipcId %s]\n", via->host, je->request->from->url->username);

                    // 记录此次会话的dialog id、call id、transction id
                     int ipcCnt;
                     for (ipcCnt = 0; ipcCnt < INTEST_MAX_IPC_NUM; ipcCnt++)
                     {
                         if (!strcmp(m_ipcInfo[ipcCnt].ipcId, je->request->req_uri->username))
                         {
                             m_ipcInfo[ipcCnt].invdid = je->did;
                             m_ipcInfo[ipcCnt].invcid = je->cid;
                             m_ipcInfo[ipcCnt].invtid = je->tid;
                             printf("[INTest] recv 200 OK after invite, save info [did %d] [cid %d] [tid %d]\n",
                                 m_ipcInfo[ipcCnt].invdid, m_ipcInfo[ipcCnt].invcid, m_ipcInfo[ipcCnt].invtid);
                         }
                     }
                     if (ipcCnt == INTEST_MAX_IPC_NUM)
                     {
                         printf("[INTest] recv 200 OK after invite, save ipc info failed [ipcId %s]\n", je->request->from->url->username);
                     }
                     break;
             }
 
         }
     }
 
   }
 }

// ipc注册
 void IPCRegister(eXosip_event_t* je)
 {
     osip_authorization_t* auth = NULL;
     osip_message_get_authorization(je->request, 0, &auth);

    // 摄像机第一次发来未鉴权注册信息,返回401
     if (NULL == auth)
     {
         AnswerRegister(je, 401);
     }
     else
     {
         // 摄像机第二次发来鉴权注册信息,返回200 OK,暂时没有做认证,有需求可以做认证开发
         AnswerRegister(je, 200);

        // 获取鉴权摄像机的信息
         osip_via_t* via = NULL;
         osip_message_get_via(je->request, 0, &via);
         if (via)
         {
             // 记录注册上来的摄像机终端设备,并发送catalog请求设备信息
             eXosip_lock(m_context);

            // 先判断该ipc是否已注册
             int ipcCnt;
             for (ipcCnt = 0; ipcCnt < m_ipcCurrCount; ipcCnt++)
             {
                 if (!strcmp(m_ipcInfo[ipcCnt].ipcId, je->request->from->url->username))
                     break;
             }

            if (ipcCnt == m_ipcCurrCount)
             {
                 memcpy(m_ipcInfo[m_ipcCurrCount].ipcId, je->request->from->url->username, strlen(je->request->from->url->username) + 1);
                 memcpy(m_ipcInfo[m_ipcCurrCount].ipcIp, via->host, strlen(via->host) + 1);
                 m_ipcInfo[m_ipcCurrCount].bWork = true;

                printf("[INTest] ipc register [sn %d] [id %s] [ip %s]\n", m_ipcCurrCount, m_ipcInfo[m_ipcCurrCount].ipcId, m_ipcInfo[m_ipcCurrCount].ipcIp);
                 m_ipcCurrCount++;

                //GetIPCInfo(via->host, je->request->from->url->username);
             }

            eXosip_unlock(m_context);
         }
     }
 }

3.完成IPC注册后,我们向IPC发一个invite消息。invite指定媒体接收端口(6000)和IP地址(即媒体服务器地址),IPC给server回复了200OK,server再向IPC回复一个ACK,完成3次握手后,IPC就向server发RTP流

 3.国标RTP流是PS流。我们开始用PS播放器来接收。

  // 向ipc发送invite
 int SendInviteToIPC(int ipcSn)
 {
     osip_message_t* invite = NULL;
     char cmd[4096] = { 0 };
     char ipcCall[128] = { 0 };
     int iRet;
     eXosip_lock(m_context);
     sprintf(ipcCall, "sip:%s@%s:%d", m_ipcInfo[ipcSn].ipcId, m_ipcInfo[ipcSn].ipcIp, _sip_ipc_port_);   // 构建ipc基本信息,通道编号,设备编号要一致,否则返回404
     printf("[INTest] create ipcCall:  %s", ipcCall);
     iRet = eXosip_call_build_initial_invite(m_context, &invite, ipcCall, m_serverCall, NULL, "This is a call for camera conversation");
     if (iRet)
     {
         printf("[INTest] eXosip_call_build_initial_invite failed [error %d]\n", iRet);
         eXosip_unlock(m_context);
         return iRet;
     }
     // 构建向ipc申请发流信息
     sprintf(cmd,
         "v=0\r\n"
         "o=%s 0 0 IN IP4 %s\r\n"
         "s=Play\r\n"
         "c=IN IP4 %s\r\n"
         "t=0 0\r\n"
         "m=video %d RTP/AVP 96 98 97\r\n"
         "a=recvonly\r\n"
         "a=rtpmap:96 PS/90000\r\n"
         "a=rtpmap:98 H264/90000\r\n"
         "a=rtpmap:97 MPEG4/90000\r\n"
         "m=audio %d RTP/AVP 97\r\n"
         "a=rtpmap:97 mpeg4-generic/44100/2\r\n"
         "a=control:trackID=1\r\n"
         "a=mpeg4-esid:3\r\n"
         "a=fmtp:97 streamtype=5;profile-level-id=15;mode=AAC-hbr;config=1210;SizeLength=13;IndexLength=3;IndexDeltaLength=3;Profile=1;\r\n"
         "y=0100000001\r\n",
         m_realm,
         GetLocalIp(),
         GetLocalIp(),
         m_ipcInfo[ipcSn].recvPort,
         m_ipcInfo[ipcSn].recvPort
         );
     osip_message_set_body(invite, cmd, strlen(cmd));
     osip_message_set_content_type(invite, "application/sdp");
     eXosip_call_send_initial_invite(m_context, invite);
     eXosip_unlock(m_context);
     printf("[INTest] send invite to IPC %s succeed [recvPort %d]\n", m_ipcInfo[ipcSn].ipcId, m_ipcInfo[ipcSn].recvPort);
     return 0;
 }

PS播放器这个时候就在信令调试过程就派上用场了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值