定制旅游网站建设方案,wordpress mysql版本,做英文网站费用多少,wordpress响应式今天抽空写下以GB28181的方式获取摄像机视频流以备后用#xff0c;同时也希望能帮助到正着手开发GB28181对接视频的同学#xff0c;这块的资料实在不多。今天讲的内容不涉及到平台对接#xff0c;平台对接下次有时间再讲#xff0c;平台对接相对更麻烦点。通过GB28181获取摄…今天抽空写下以GB28181的方式获取摄像机视频流以备后用同时也希望能帮助到正着手开发GB28181对接视频的同学这块的资料实在不多。今天讲的内容不涉及到平台对接平台对接下次有时间再讲平台对接相对更麻烦点。通过GB28181获取摄像机视频流首先需要摄像机支持GB28181如何知道摄像机是否支持GB28181协议呢请看下图图1.摄像机28181协议配置图图1 展示了海康摄像机配置GB28181页面其他厂家摄像机GB28181配置页面(我遇到的)基本跟海康配置的页面相同。下面介绍下各配置项基本意义本地端口默认为5060SIP服务发送命令给摄像机时需要知道摄像机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运行起来毕竟人家是一个服务。只有运行以后才能接收客户端发来的消息。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(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消息后如果服务端不应答摄像机端会一直发送直到收到服务端应答为止。如果服务器端重新运行需要手动再次开启摄像机如果等摄像机自己再次发送注册消息可能是一个小时以后我们当然不希望那么久。服务端应答注册消息代码bool OnReceive(pjsip_rx_data* rdata) override{if(rdata-msg_info.cseq-method.id PJSIP_REGISTER_METHOD){auto expires static_cast(pjsip_msg_find_hdr(rdata-msg_info.msg, PJSIP_H_EXPIRES, nullptr));auto authHdr static_cast(pjsip_msg_find_hdr(rdata-msg_info.msg, PJSIP_H_AUTHORIZATION, nullptr));if(expires expires-ivalue 0 ){if(authHdr){cout response(rdata, PJSIP_SC_OK, DateHead);QureryDeviceInfo(rdata);}else{response(rdata, PJSIP_SC_UNAUTHORIZED, AuthenHead);}return true;}}return false;}OnReceive 是服务端接收注册消息以后的响应方法也就是说要将OnReceive作为入参传给PJSIP,完成此项功能在初始化PJSIP Moudle时。至于PJSIP moudle这里不多解释想要知道细节的话可以查看PJSIP文档文档群里有代码如下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 {nullptr, nullptr,{ 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,intheadType){std::lock_guard 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){caseDateHead:key pj_str(Date);hdr reinterpret_cast(pjsip_date_hdr_create(pool, key, pj_cstr(c, date.c_str())));pjsip_msg_add_hdr(tdata-msg, hdr);break;caseAuthenHead: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信息也就是设备基本信息具体的方法上面已给出具体的内容是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\n%s\n17430\n%s\n\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(MANSCDPxml);pjsip_endpt_send_request(endPoint, tdata, -1, nullptr, nullptr);}SIP服务端 发送了请求catalog 消息摄像机端收到消息发送其自身的catalog消息SIP 服务端将在OnReceive中收到具体的catalog消息。取catalog消息的方法如下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:string(CmdType);if (cmdType ! Catalog) return false;auto DeviceID dynamicStruct.Get:string(DeviceID);Vector deviceList dynamicStruct.Get(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();devinfo.Name WstringToString(x[Name].convert());devinfo.Manufacturer x[Manufacturer].convert();devinfo.Model x[Model].convert();devinfo.Owner x[Owner].convert();devinfo.Civilcode x[CivilCode].convert();devinfo.Registerway x[RegisterWay].convert();devinfo.Secrecy x[Secrecy].convert();//devinfo.IPAddress x[IPAddress].convert();devinfo.DeviceID x[DeviceID].convert();devinfo.Status x[Status].convert();}catch (...){//continue;}if(callback){callback(user, devinfo);}//SipControlModule::GetInstance().CatalogCallBack(devinfo);}response(rdata, PJSIP_SC_OK,NoHead);return true;SIP服务取都摄像机的信息后就可以发送请求视频信息了请求视频最为关键的是SDP下面看下SDP信息如何填写:static string createSDP(MediaContext mediaContext){char str[500] { 0 };pj_ansi_snprintf(str, 500,v0\no%s 0 0 IN IP4 %s\nsPlay\ncIN IP4 %s\nt0 0\nmvideo %d RTP/AVP 96 98 97\narecvonly\nartpmap:96 PS/90000\nartpmap:98 H264/90000\nartpmap:97 MPEG4/90000\ny0100000001\n,mediaContext.GetDeviceId().c_str(),mediaContext.GetRecvAddress().c_str(),mediaContext.GetRecvAddress().c_str(),mediaContext.GetRecvPort());return str;}发送请求视频命令到摄像机端当然也是通过PJSIP API实现代码如下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(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(subjectUrl.c_str()));auto hdr pjsip_generic_string_hdr_create(pool, hName, hValue);pjsip_msg_add_hdr(tdata-msg, reinterpret_cast(hdr));pjsip_inv_send_msg(inv, tdata);}catch (...){}return true;}代码就不解释了要想知道到底发了什么还是抓个包看看无论你用什么方法只要抓包的数据是正确定说明发送成功了。图6 服务端发送invite视频消息摄像机端收到Invite请求后会将视频数据以rtp的方式推送到指定的端口端口在invite消息指定。这样在指定的地址(ip port)就可以拿到数据了。最后提供一个测试demodemo的作用是可以让大家抓包看看双方都发了些什么。demo运行界面如下图6 demo运行初始界面1.运行demo后首先配置好配置如果不知道可以默认但IP地址需要修改端口不能被占用。2.完成配置各配置项以后点击获取视频源按钮 等待摄像机端注册。3.摄像机端开启28181功能具体的方法可以是平台选择方式下拉框先选择一个非28181方式点击保存再选择28181方式并点击保存。4.摄像机端成功开启28181功能以后视频源下拉框中会显示摄像机的名称信息。5.选中视频源下拉框中出现的选项并点击播放按钮正常情况下会可以播放从摄像机端过来的视频流。成功接入视频源并播放的运行界面如下。图7 demo成功运行以后的界面Demo 可以在群里下载。如需交流可以加QQ群1038388075766718184或者QQ350197870