GB28181设备对接视频流的流程

搭建CG28181 服务端,也即 SIP Server,这正是我们要实现的。实现CG28181服务端可以借助于现有的开源库 PJSIP,具体的实现步骤如下:

1、启动GB28181服务端,接收客户端消息请求

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 消息了。

2、应答注册消息

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

服务端应答注册消息代码如下:

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;
}

 OnReceive 是服务端接收注册消息以后的响应方法,也就是说要将OnReceive作为入参传给PJSIP,完成此项功能在初始化<br>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方法实现了发送响应数据到客户端(设备):

  接到PJSIP发送一些字符串给客户端:

void  Response(pjsip_rc_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(poll, &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);}

SIP服务端响应注册命令后,发送Invite请求,请求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<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信息如何填写:

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实现,实现代码如下:

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;}

 设备端收到Invite请求后,会将视频数据以rtp的方式推送到指定的端口,端口在invite消息指定。这样在指定的地址(ip + port)就可以拿到数据了。

交流联系:

微信:

LiveMedia视频汇聚平台www.houhangkeji.com

QQ技术交流群:698793654

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/176791.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode 189.轮转数组(三种方法解决)

文章目录 题目暴力求解空间换时间三段逆置总结 题目 LeetCode 189.轮转数组 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 输入: nums [1,2,3,4,5,6,7], k 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5…

Nginx 使用笔记大全(唯一入口)

Linux服务器因为Nginx日志access.log文件过大项目无法访问 项目处于运行状态下无法访问&#xff0c;第一步查看磁盘状态 1、查看磁盘状态 df -h 2、查找100M以上的文件 find / -size 100M |xargs ls -lh 3、删除文件 rm -rf /usr/local/nginx/logs/access.log 4、配置nginx.…

keil和proteus联动要点

一、keil与proteus如何进行联动&#xff1f; 1.先安装vdmagdi.exe&#xff0c;这是驱动 2.要保证keil工程编译通过&#xff0c;左上角红色图标进行编译&#xff0c;黑色框图标进行链接。 3.生成hex文件 先点击这个图标 按照顺序点击&#xff0c;生成HEX文件。 4.在打开的prot…

Redis 连接不上 WRONGPASS invalid username-password pair

1.我的RedisDesktopManager 可以连接 但是 Springboot远程使用Redis就是连不上 2.我的密码是 abc123.. 多了英文的 ..符号 在Springboot过不了&#xff0c;所以Redis密码尽量字母数字&#xff0c;不要其他符号

Visual Studio 2019 C# 断点调试代码内存窗口显示无法计算表达式的解决问题

查看如下界面&#xff0c;发现右下角内存1窗口显示无法计算表达式&#xff1a; 按照如下步骤操作即可&#xff1a; 如果s1局部变量此时有值&#xff0c;但是内存窗口还是无法计算表达式我们可以

2023.11.15 hive sql之函数标准,字符串,日期,数学函数

目录 一.函数分类标准 二.查看官方函数,与简单演示 三.3种类型函数演示 四.字符串函数 1.常见字符串函数 2.索引函数 解析函数 五.日期函数 1.获取当前时间 2.获取日期相关 3.周,季度等计算 4.时间戳 六.数学函数 一.函数分类标准 目前hive三大标准 UDF:&#xff08…

3分钟带你了解前端缓存-HTTP缓存

前情提要 前端缓存分为下面三大类&#xff0c;本文主要讲解HTTP缓存~ 1. HTTP缓存 强缓存协商缓存 2. 浏览器缓存 本地小容量缓存本地大容量缓存 3. 应用程序缓存 HTML5应用程序缓存 缓存作用 减少了冗余的数据传输减少服务器的负担提高了网站的性能加快加载网页速度 …

图论17-有向图的强联通分量-Kosaraju算法

文章目录 1 概念2 Kosaraju算法2.1 在图类中设计反图2.2 强连通分量的判断和普通联通分量的区别2.3 代码实现 1 概念 2 Kosaraju算法 对原图的反图进行DFS的后序遍历。 2.1 在图类中设计反图 // 重写图的构造函数public Graph(TreeSet<Integer>[] adj, boolean dire…

建筑楼宇智慧能源管理系统,轻松解决能源管理问题

随着科技的进步与人们节能减排意识的不断增强&#xff0c;建筑楼宇是当下节能减排的重要工具。通过能源管理平台解决能效管理、降低用能成本、一体化管控、精细化管理和服务提供有力支撑。 建筑楼宇智慧能源管理系统是一种利用先进手段&#xff0c;采用微服务架构&#xff0c;…

计算机毕业设计选题推荐-个人记账理财微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

【联邦学习+区块链】TORR: A Lightweight Blockchain for Decentralized Federated Learning

文章目录 I.CONTRIBUTIONII. ASSUMPTIONS AND THREAT MODELA. AssumptionsB. Threat Model III. SYSTEM DESIGNA. Design OverviewB. Block DesignC. InitializationD. Role SelectionE. Storage ProtocolF. Aggregation ProtocolG. Proof of ReliabilityH. Blockchain Consens…