国标 GB28181 平台 视频对接

国标 GB28181 平台 视频流对接

     今天抽空写下以GB28181的方式获取摄像机视频流以备后用,同时也希望能帮助到正着手开发GB28181对接视频的同学,这块的资料实在不多。

今天讲的内容不涉及到平台对接,平台对接下次有时间再讲,平台对接相对更麻烦点。通过GB28181获取摄像机视频流,首先需要摄像机支持GB28181

,如何知道摄像机是否支持GB28181协议呢?请看下图:

                                                            图1.摄像机28181协议配置图

图1 展示了海康摄像机配置GB28181页面,其他厂家摄像机GB28181配置页面(我遇到的)基本跟海康配置的页面相同。

下面介绍下各配置项基本意义:

   本地端口:默认为5060,SIP服务发送命令给摄像机时需要知道摄像机GB28181端口号,要不向哪发?

SIP服务器ID:说简单就是 服务器的标识,只不过这个标识有一定的要求,具体请参见28181-2001标准安全防范视频监控联网系统信息传输交换控制技术要求.pdf

                    当然也可以参考新点的文档,新旧文档这部分差异不大。文档在从群里下载。

SIP服务域:实际就是SIP服务器ID前10位。

SIP服务器地址:SIP服务所在机器的IP地址(如果存在多网卡建议将不用的网卡禁用掉)。

SIP服务器端口:SIP服务Port,其他SIP服务发送命令到此端口与之通信。

其他的配置默认即可。

   GB28181配置好以后,需要启动摄像机GB28181服务。

启动摄像机GB28181的方法是勾选“启用”选项,启动成功后,摄像机会向SIP Server发送注册消息,通过抓包可以看到具体的注册消息内容:

                            图2 摄像机发送注册消息图

看下注册消息的具体内容:

                                       图3 具体注册消息图

重要是Cantact信息,包含了摄像机GB28181 SIP ID 以及IP地址和端口号,这样与摄像机通信的SIP服务就知道往哪里回应答消息。

     摄像机端基本介绍了完了(摄像机端相当于SIP Client),下面 介绍CG28181 服务端也即 SIP Server,这正是我们要实现的。

实现CG28181服务端可以借助于现有的开源库 PJSIP,自己实现开发量还是很大的,具体的实现步骤如下:

一. 将PJSIP运行起来,毕竟人家是一个服务。只有运行以后才能接收客户端发来的消息。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

bool Init(std::string concat, int logLevel)

{

    this->concat = concat;

    pj_log_set_level(logLevel);

    auto status = pj_init();

    status = pjlib_util_init();

    pj_caching_pool_init(&cachingPool, &pj_pool_factory_default_policy, 0);

    status = pjsip_endpt_create(&cachingPool.factory, nullptr, &endPoint);

                 

    status = pjsip_tsx_layer_init_module(endPoint);

    status = pjsip_ua_init_module(endPoint, nullptr);

    pool = pj_pool_create(&cachingPool.factory, "proxyapp", 4000, 4000, nullptr);

    auto pjStr =StrToPjstr(GetAddr());

    pj_sockaddr_in pjAddr;

    pjAddr.sin_family = pj_AF_INET();

    pj_inet_aton(&pjStr, &pjAddr.sin_addr);

    auto port = GetPort();

    pjAddr.sin_port = pj_htons(static_cast<pj_uint16_t>(GetPort()));

    status = pjsip_udp_transport_start(endPoint, &pjAddr, nullptr, 1, nullptr);

      if (status != PJ_SUCCESS) return status;

      auto realm = StrToPjstr(GetLocalDomain());

      return pjsip_auth_srv_init(pool, &authentication, &realm, lookup, 0) == PJ_SUCCESS ? true false;

         

}

  以上是PJSip初始化的代码,需要将服务将要监听的端口传给PJSIP,这样服务就在监听的端口接收SIP 消息了。

二. 应答注册消息

     摄像机端发送来Register消息后,如果服务端不应答,摄像机端会一直发送直到收到服务端应答为止。如果服务器端重新运行,需要手动再次

开启摄像机,如果等摄像机自己再次发送注册消息可能是一个小时以后,我们当然不希望那么久。

服务端应答注册消息代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

bool OnReceive(pjsip_rx_data* rdata) override

{

    if(rdata->msg_info.cseq->method.id == PJSIP_REGISTER_METHOD)

    {

      auto expires = static_cast<pjsip_expires_hdr*>(pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, nullptr));

      auto authHdr = static_cast<pjsip_authorization_hdr*>(pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION, nullptr));

      if(expires && expires->ivalue > 0 )

      {

        if(authHdr)

        {

          cout <<"receive register info"<<endl;

          response(rdata, PJSIP_SC_OK, DateHead);

          QureryDeviceInfo(rdata);

        }

        else

        {

          response(rdata, PJSIP_SC_UNAUTHORIZED, AuthenHead);

        }

        return true;

      }

    }

    return false;

}

  

1

OnReceive 是服务端接收注册消息以后的响应方法,也就是说要将OnReceive作为入参传给PJSIP,完成此项功能在初始化<br>PJSIP Moudle时。至于PJSIP moudle,这里不多解释,想要知道细节的话,可以查看PJSIP文档,文档群里有,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

bool  Init(std::string concat, int loglevel)

{

  bool ret = false;

  if(!mainModule)

 {

    ret = context.Init(concat,loglevel);

    if(!ret) return ret;

    static struct pjsip_module moudle =

    {

      nullptrnullptr,

      "MainModule", 10 },

      -1,

     PJSIP_MOD_PRIORITY_APPLICATION,

     nullptr,

     nullptr,

     nullptr,

     nullptr,

    nullptr,

    &CGSipMedia::OnReceive,

    nullptr,

    nullptr,

    nullptr,

    };

    mainModule = &moudle;

    pjsip_inv_callback callback;

    pj_bzero(&callback, sizeof(callback));

    callback.on_state_changed = &onStateChanged;

    callback.on_new_session = &onNewSession;

    callback.on_tsx_state_changed = &onTsxStateChanged;

    callback.on_rx_offer = &onRxOffer;

    callback.on_rx_reinvite = &onRxReinvite;

    callback.on_create_offer = &onCreateOffer;

    callback.on_send_ack = &onSendAck;

    ret = context.RegisterCallback(&callback);

    if(!ret ) return ret;

    context.InitModule();

    ret  = context.RegisterModule(mainModule);

    if(!ret ) return ret;

    CGSipModule::GetInstance().Init();

    ret = context.CreateWorkThread(&proc,workthread,nullptr,"proxy");

    }

    return ret;

    }

  OnReceive方法内Resonse方法实现了发送响应数据到客户端(摄像机):

复制代码

 void Response(pjsip_rx_data* rdata, int st_code,int headType) 
 {
    std::lock_guard<mutex> lk(lock);
     pjsip_tx_data* tdata;
    pjsip_endpt_create_response(endPoint, rdata, st_code, nullptr, &tdata);
     auto date = DateTimeFormatter::format(LocalDateTime(), "%Y-%m-%dT%H:%M:%S");
     pj_str_t c;
     pj_str_t key;
     pjsip_hdr *hdr;
     switch(headType)
      {
           case DateHead:                                                        
             key = pj_str("Date");
             hdr = reinterpret_cast<pjsip_hdr*>(pjsip_date_hdr_create(pool, &key, pj_cstr(&c, date.c_str())));
             pjsip_msg_add_hdr(tdata->msg, hdr);
             break;
           case AuthenHead:
             pjsip_auth_srv_challenge(&authentication, nullptr, nullptr, nullptr, PJ_FALSE, tdata);
             break;
              default:
               break;
       }
      pjsip_response_addr addr;
      pjsip_get_response_addr(pool, rdata, &addr);
      pjsip_endpt_send_response(endPoint, &addr, tdata, nullptr, nullptr);
   }

复制代码

   实际也就是利用发PJSIP发送一些字符串给客户端。具体发送了些什么,可以抓个包看下。

                                                                                                                                               图4 SIP服务应答注册消息

SIP 服务实际回了“200 OK” 给摄像机端。看下具体的消息内容:

                                  图5  “200 OK” 具体内容

      SIP服务端响应注册命令后,发送Invite请求,请求catalog信息,也就是设备基本信息,具体的方法上面已

给出,具体的内容是:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

void QueryDeviveInfo(GBDevice *device, const string& scheme = "Catalog")

{

  char szQuerInfo[200] = { 0 };

  pj_ansi_snprintf(szQuerInfo, 200,

   "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"

   "<Query>\n"

  "<CmdType>%s</CmdType>\n"

  "<SN>17430</SN>\n"

  "<DeviceID>%s</DeviceID>\n"

  "</Query>\n", scheme.c_str(), device->GetUser()

  );

  pjsip_tx_data *tdata;

  const pjsip_method method = { PJSIP_OTHER_METHOD,{ "MESSAGE", 7 } };

  auto text = StrToPjstr(string(szQuerInfo));

  pjsip_endpt_create_request(endPoint, &method, &StrToPjstr(device->GetSipIpUrl()), &StrToPjstr(concat), &StrToPjstr(device->GetSipCodecUrl()),&StrToPjstr(concat), nullptr, -1, &text, &tdata);

  tdata->msg->body->content_type.type = pj_str("Application");

  tdata->msg->body->content_type.subtype = pj_str("MANSCDP+xml");

  pjsip_endpt_send_request(endPoint, tdata, -1, nullptrnullptr);<br>}

 SIP服务端 发送了请求catalog  消息,摄像机端收到消息发送其自身的catalog消息,SIP 服务端将在OnReceive中收到具体的catalog消息。取catalog消息的方法如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

bool OnReceive(pjsip_rx_data* rdata) override

{

  if (rdata->msg_info.cseq->method.id == PJSIP_OTHER_METHOD)

  {

    CGXmlParser xmlParser(context.GetMessageBody(rdata));

    CGDynamicStruct dynamicStruct;

    dynamicStruct.Set(xmlParser.GetXml());

    auto cmd = xmlParser.GetXml()->firstChild()->nodeName();

    auto cmdType = dynamicStruct.Get<std::string>("CmdType");

    if (cmdType != "Catalog"return false;

             

    auto DeviceID = dynamicStruct.Get<std::string>("DeviceID");

                 

    Vector deviceList = dynamicStruct.Get<Vector>("DeviceList");

    for (auto& x : deviceList)

    {

      CGCatalogInfo devinfo;

    try

    {

      devinfo.PlatformAddr = rdata->pkt_info.src_name;

      devinfo.PlatformPort = rdata->pkt_info.src_port;

      devinfo.Address = x["Address"].convert<string>();

      devinfo.Name = WstringToString(x["Name"].convert<wstring>());

      devinfo.Manufacturer = x["Manufacturer"].convert<string>();

      devinfo.Model = x["Model"].convert<string>();

      devinfo.Owner = x["Owner"].convert<string>();

      devinfo.Civilcode = x["CivilCode"].convert<string>();

      devinfo.Registerway = x["RegisterWay"].convert<int>();

      devinfo.Secrecy = x["Secrecy"].convert<int>();

      //devinfo.IPAddress = x["IPAddress"].convert<string>();

      devinfo.DeviceID = x["DeviceID"].convert<string>();

      devinfo.Status= x["Status"].convert<string>();

    }

    catch (...)

    {

        //continue;

    }

    if(callback)

    {

        callback(user, &devinfo);

    }

    //SipControlModule::GetInstance().CatalogCallBack(devinfo);

    }

         

    response(rdata, PJSIP_SC_OK,NoHead);

    return true;

  SIP服务取都摄像机的信息后就可以发送请求视频信息了,请求视频最为关键的是SDP,下面看下SDP信息如何填写:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

static string createSDP(MediaContext& mediaContext)

{

    char str[500] = { 0 };

    pj_ansi_snprintf(str, 500,

    "v=0\n"

    "o=%s 0 0 IN IP4 %s\n"

    "s=Play\n"

    "c=IN IP4 %s\n"

    "t=0 0\n"

    "m=video %d RTP/AVP 96 98 97\n"

    "a=recvonly\n"

    "a=rtpmap:96 PS/90000\n"

    "a=rtpmap:98 H264/90000\n"

    "a=rtpmap:97 MPEG4/90000\n"

    "y=0100000001\n",

    mediaContext.GetDeviceId().c_str(),

    mediaContext.GetRecvAddress().c_str(),

    mediaContext.GetRecvAddress().c_str(),

    mediaContext.GetRecvPort()

            );

    return str;

}

  发送请求视频命令到摄像机端当然也是通过PJSIP API实现代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

bool Invite(pjsip_dialog *dlg, MediaContext mediaContext, string sdp)

{

    pjsip_inv_session *inv;

    if (PJ_SUCCESS != pjsip_inv_create_uac(dlg, nullptr, 0, &inv)) return false;

    pjsip_tx_data *tdata;

    if (PJ_SUCCESS != pjsip_inv_invite(inv, &tdata)) return false;

    pjsip_media_type type;

    type.type = pj_str("application");

    type.subtype = pj_str("sdp");

    auto text = pj_str(const_cast<char *>(sdp.c_str()));

    try

    {

        tdata->msg->body = pjsip_msg_body_create(pool, &type.type, &type.subtype, &text);

        auto hName = pj_str("Subject");

        auto subjectUrl = mediaContext.GetDeviceId() + ":" + SiralNum + "," + GetInstance().GetCode() + ":" + SiralNum;

        auto hValue = pj_str(const_cast<char*>(subjectUrl.c_str()));

        auto hdr = pjsip_generic_string_hdr_create(pool, &hName, &hValue);

        pjsip_msg_add_hdr(tdata->msg, reinterpret_cast<pjsip_hdr*>(hdr));

        pjsip_inv_send_msg(inv, tdata);

    }

    catch (...)

    {

    }

    return true;

}

  代码就不解释了,要想知道到底发了什么还是抓个包看看,无论你用什么方法只要抓包的数据是正确定说明发送成功了。

                                                图6 服务端发送invite视频消息

摄像机端收到Invite请求后,会将视频数据以rtp的方式推送到指定的端口,端口在invite消息指定。

这样在指定的地址(ip + port)就可以拿到数据了。

最后提供一个测试demo,demo的作用是可以让大家抓包,看看双方都发了些什么。

demo运行界面如下:

                                                                             图6 demo运行初始界面

1.运行demo后,首先配置好配置,如果不知道可以默认,但IP地址需要修改,端口不能被占用。

2.完成配置各配置项以后点击获取视频源按钮 等待摄像机端注册。

3.摄像机端开启28181功能:具体的方法可以是:平台选择方式下拉框先选择一个非28181方式,点击保存,再选择28181方式并点击保存。

4.摄像机端成功开启28181功能以后,视频源下拉框中会显示摄像机的名称信息。

5.选中视频源下拉框中出现的选项并点击播放按钮,正常情况下会可以播放从摄像机端过来的视频流。

   成功接入视频源并播放的运行界面如下。

                                                                                                图7 demo成功运行以后的界面

Demo 可以在群里下载。

如需交流,可以加QQ群1038388075,766718184,或者微信:18355123565

demo 下载地址:GB28181Demo

  • 30
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: GB28181监控平台对接是指将视频监控设备与GB28181协议的监控平台进行连接,实现视频监控设备的有效监控与管理。该过程主要包括设备接入、设备管理以及视频流传输等步骤。 设备接入是指将设备接入GB28181协议的监控平台,通过设备接入,监控平台可以实时获取设备状态、录像及告警等信息。 设备管理是指监控平台通过对接已接入的设备进行统一管理,包括设备配置、设备状态监控、设备告警管理等。通过设备管理,监控平台可以更好地监管设备,及时处理异常情况,确保监控系统的稳定性。 视频流传输是指通过网络将设备捕获到的视频传输到监控平台上进行实时监控和录像存储。在视频流传输的过程中,需要考虑视频流效率和带宽占用的问题,以保证视频传输的稳定性和高效性。 总之,GB28181监控平台对接是保证监控系统稳定可靠、高效运行的重要步骤。通过将设备接入、设备管理以及视频流传输等工作有机地结合起来,可以确保监控系统的安全性和高效性,为用户提供更好的监控保障。 ### 回答2: GB28181是国家标准的IP视频监控系统,在GB28181标准下,不同的设备可以方便快捷地实现互联互通,从而构建起统一的视频监控平台GB28181监控平台对接是指将不同品牌、不同类型的监控设备接入到同一个监控平台中,从而实现设备管理、视频预览、视频录像、报警管理、智能分析等监控管理功能。为了实现GB28181监控平台对接,一般需要进行以下几个步骤: 1. 搭建监控服务器:搭建一台专门用于管理和控制多个监控设备的服务器,通过该服务器对设备进行集中管理和控制。 2. 配置监控设备:对接监控平台监控设备进行网络和协议参数的配置,使其能够与监控服务器进行通信。 3. 安装监控客户端:监控客户端是一种用于管理和控制监控设备的软件,用户可以通过监控客户端实现对监控设备的实时预览、录像回放、报警处理等操作。 4. 实现对接:在监控客户端中配置监控设备的IP地址和端口号等协议参数,然后对接入的设备进行连接测试,确认监控设备已经成功接入到监控平台中。 综上所述,GB28181监控平台对接需要进行一系列的操作和配置,需要专业的技术人员进行实施。在实际应用过程中,需要根据具体的监控设备和监控系统进行相应的调整和优化,以确保监控系统运行稳定、可靠。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值