GB28181 —— 5、C++编写GB28181设备端,完成将USB摄像头视频实时转发至GB28181服务并可播放(附源码)

被测试的USB摄像头

在这里插入图片描述

效果

源码说明

     主要功能模拟设备端,完成注册、注销、心跳等,同时当服务端下发指令播放视频时 设备端实时读取USB摄像头视频并通过OpenCV处理后实时转ps格式后封包rtp进行推送给服务端播放。

源码
/****@remark:   pes头的封装,里面的具体数据的填写已经占位,可以参考标准*@param :   pData      [in] 填充ps头数据的地址*           stream_id  [in] 码流类型*           paylaod_len[in] 负载长度*           pts        [in] 时间戳*           dts        [in]*@return:   0 success, others failed
*/
int gb28181_make_pes_header(char *pData, int stream_id, int payload_len, int64_t pts, int64_t dts)
{bits_buffer_t  	bitsBuffer;bitsBuffer.i_size = PES_HDR_LEN;bitsBuffer.i_data = 0;bitsBuffer.i_mask = 0x80;bitsBuffer.p_data = (unsigned char *)(pData);memset(bitsBuffer.p_data, 0, PES_HDR_LEN);bits_write(&bitsBuffer, 24, 0x000001);	//*start code*//*bits_write(&bitsBuffer, 8, (stream_id));	//*streamID*//*bits_write(&bitsBuffer, 16, (payload_len)+13);	//*packet_len*//* //指出pes分组中数据长度和该字节后的长度和bits_write(&bitsBuffer, 2, 2);		//*'10'*//*bits_write(&bitsBuffer, 2, 0);		//*scrambling_control*//*bits_write(&bitsBuffer, 1, 0);		//*priority*//*bits_write(&bitsBuffer, 1, 0);		//*data_alignment_indicator*//*bits_write(&bitsBuffer, 1, 0);		//*copyright*//*bits_write(&bitsBuffer, 1, 0);		//*original_or_copy*//*bits_write(&bitsBuffer, 1, 1);		//*PTS_flag*//*bits_write(&bitsBuffer, 1, 1);		//*DTS_flag*//*bits_write(&bitsBuffer, 1, 0);		//*ESCR_flag*//*bits_write(&bitsBuffer, 1, 0);		//*ES_rate_flag*//*bits_write(&bitsBuffer, 1, 0);		//*DSM_trick_mode_flag*//*bits_write(&bitsBuffer, 1, 0);		//*additional_copy_info_flag*//*bits_write(&bitsBuffer, 1, 0);		//*PES_CRC_flag*//*bits_write(&bitsBuffer, 1, 0);		//*PES_extension_flag*//*bits_write(&bitsBuffer, 8, 10);		//*header_data_length*//*// 指出包含在 PES 分组标题中的可选字段和任何填充字节所占用的总字节数。该字段之前//的字节指出了有无可选字段。//*PTS,DTS  PTS DTS均为1的情况*//bits_write(&bitsBuffer, 4, 3);                    //*'0011'*//*bits_write(&bitsBuffer, 3, ((pts) >> 30) & 0x07);     //*PTS[32..30]*//*bits_write(&bitsBuffer, 1, 1);bits_write(&bitsBuffer, 15, ((pts) >> 15) & 0x7FFF);    //*PTS[29..15]*//*bits_write(&bitsBuffer, 1, 1);bits_write(&bitsBuffer, 15, (pts) & 0x7FFF);          //*PTS[14..0]*//*bits_write(&bitsBuffer, 1, 1);bits_write(&bitsBuffer, 4, 1);                    //*'0001'*//*bits_write(&bitsBuffer, 3, ((dts) >> 30) & 0x07);     //*DTS[32..30]*//*bits_write(&bitsBuffer, 1, 1);bits_write(&bitsBuffer, 15, ((dts) >> 15) & 0x7FFF);    //*DTS[29..15]*//*bits_write(&bitsBuffer, 1, 1);bits_write(&bitsBuffer, 15, (dts) & 0x7FFF);          //*DTS[14..0]*//*bits_write(&bitsBuffer, 1, 1);return 0;
}/*** RTP头封装* @param pData buffer地址* @param seqNum 序号* @param timestamp 时间戳* @param ssrc 标识* @return*/
int gb28181_make_rtp_header(char *pData, int seqNum, int64_t timestamp, int ssrc, int isEnd)
{bits_buffer_t  	bitsBuffer;bitsBuffer.i_size = RTP_HDR_LEN;bitsBuffer.i_data = 0;bitsBuffer.i_mask = 0x80;bitsBuffer.p_data = (unsigned char *)(pData);memset(bitsBuffer.p_data, 0, RTP_HDR_LEN);bits_write(&bitsBuffer, 2, 2);	    /*协议版本*/bits_write(&bitsBuffer, 1, 0);		/*P*/bits_write(&bitsBuffer, 1, 0);		/*X*/bits_write(&bitsBuffer, 4, 0);		/*CSRC个数*/bits_write(&bitsBuffer, 1, isEnd);			/*一帧是否结束*/bits_write(&bitsBuffer, 7, 96); 		/*载荷的数据类型*/bits_write(&bitsBuffer, 16, seqNum); 			/*序列号,第几个*/bits_write(&bitsBuffer, 32, timestamp);		/*时间戳,第一个 */bits_write(&bitsBuffer, 32, ssrc);			/*同步信源(SSRC)标识符*/return 0;
}/****@remark:   sys头的封装,里面的具体数据的填写已经占位,可以参考标准*@param :   pData  [in] 填充ps头数据的地址*@return:   0 success, others failed
*/
int32_t __gb28181_make_sys_header(char *pData, int32_t audioCnt, int32_t videoCnt, int32_t audioStreamID, int32_t videoStreamID)
{bits_buffer_t  	bitsBuffer;bitsBuffer.i_size = SYS_HDR_LEN;	// 18bitsBuffer.i_data = 0;bitsBuffer.i_mask = 0x80;bitsBuffer.p_data = (unsigned char *)(pData);memset(bitsBuffer.p_data, 0, SYS_HDR_LEN);/*system header*/bits_write(&bitsBuffer, 32, 0x000001BB);	/*start code*/bits_write(&bitsBuffer, 16, SYS_HDR_LEN - SYS_BASE_HEAD_LEN); /*header_length 表示此字段后面的长度*/bits_write(&bitsBuffer, 1, 1);            /*marker_bit*/// bits_write(&bitsBuffer, 22, 3967);		/*rate_bound*/bits_write(&bitsBuffer, 22, 26234);	// 抓包读取bits_write(&bitsBuffer, 1, 1);            /*marker_bit*/bits_write(&bitsBuffer, 6, audioCnt);     /*audio_bound >= audio_stream_cnt*/bits_write(&bitsBuffer, 1, 0);            /*fixed_flag,1为固定比特率,0为可变比特率 */// bits_write(&bitsBuffer, 1, 1);        	/* CSPS_flag=1表示满足ps标准 */bits_write(&bitsBuffer, 1, 0);				// 抓包CSPS_flag=0bits_write(&bitsBuffer, 1, 1);        	/*system_audio_lock_flag*/bits_write(&bitsBuffer, 1, 1);        	/*system_video_lock_flag*/bits_write(&bitsBuffer, 1, 1);        	/*marker_bit*/bits_write(&bitsBuffer, 5, videoCnt);        	/*video_bound >= video_stream_cnt*/bits_write(&bitsBuffer, 1, 0);        	/*dif from mpeg1,if CSPS_flag=0,inavailable*/bits_write(&bitsBuffer, 7, 0x7F);     	/*reserver*//*video stream bound*/bits_write(&bitsBuffer, 8, videoStreamID);         /*stream_id*/bits_write(&bitsBuffer, 2, 3);        	/*marker_bit */bits_write(&bitsBuffer, 1, 1);        	/*PSTD_buffer_bound_scale*/// bits_write(&bitsBuffer, 13, 2048);     	/*PSTD_buffer_size_bound*/bits_write(&bitsBuffer, 13, 232);	// 抓包为232/*audio stream bound*/bits_write(&bitsBuffer, 8, audioStreamID);         /*stream_id*/bits_write(&bitsBuffer, 2, 3);        	/*marker_bit */bits_write(&bitsBuffer, 1, 0);            /*PSTD_buffer_bound_scale*/// bits_write(&bitsBuffer, 13, 512);          /*PSTD_buffer_size_bound*/bits_write(&bitsBuffer, 13, 40);	// 抓包为40return 0;
}// 推流线程
void Push_rtp_Stream()
{std::cout << "\n................ 进入rtp线程 ................" << std::endl;//std::this_thread::sleep_for(std::chrono::seconds(1));// 套接字初始化if (!BindSocket()) { std::cout << "\n!!!绑定套接字失败,无法发送数据!!!" << std::endl; return; }char ps_header[PS_HDR_LEN] = { 0 };char ps_system_header[SYS_HDR_LEN] = { 0 };char ps_map_header[PSM_HDR_LEN] = { 0 };char pes_header[PES_HDR_LEN] = { 0 };char rtp_header[RTP_HDR_LEN] = { 0 };int time_base = 90000;int fps = 20;int send_packet_interval = 1000 / fps;int interval = time_base / fps;long pts = 0;//char frame[1024 * 128];char *frame = new char[1024 * 1024]; memset(frame, 0, 1024 * 1024);
// 	int single_packet_max_length = 1400;
// 	char rtp_packet[RTP_HDR_LEN + 1400];int single_packet_max_length = 10000;char *rtp_packet = new char[RTP_HDR_LEN + 10000]; memset(frame, 0, RTP_HDR_LEN + 10000);// int ssrc = 0xffffffff;int rtp_seq = 0;//while (PushThreadFlag){for (auto i = 0; i < nalu_vector.size(); i++){auto nalu = nalu_vector.at(i);NaluType  type = nalu->type;int length = nalu->length;char * packet = nalu->packet;int index = 0;if (NALU_TYPE_IDR == type){gb28181_make_ps_header(ps_header, pts);memcpy(frame, ps_header, PS_HDR_LEN);index += PS_HDR_LEN;gb28181_make_sys_header(ps_system_header, 0x3f);memcpy(frame + index, ps_system_header, SYS_HDR_LEN);index += SYS_HDR_LEN;gb28181_make_psm_header(ps_map_header);memcpy(frame + index, ps_map_header, PSM_HDR_LEN);index += PSM_HDR_LEN;}else{gb28181_make_ps_header(ps_header, pts);memcpy(frame, ps_header, PS_HDR_LEN);index += PS_HDR_LEN;}//封装pesgb28181_make_pes_header(pes_header, 0xe0, length, pts, pts);memcpy(frame + index, pes_header, PES_HDR_LEN);index += PES_HDR_LEN;memcpy(frame + index, packet, length);index += length;//组包rtpint rtp_packet_count = ((index - 1) / single_packet_max_length) + 1;for (int i = 0; i < rtp_packet_count; i++) {gb28181_make_rtp_header(rtp_header, rtp_seq, pts, atoi(ssrc.c_str()), i == (rtp_packet_count - 1));int writed_count = single_packet_max_length;if ((i + 1)*single_packet_max_length > index){writed_count = index - (i* single_packet_max_length);}//添加包长字节int rtp_start_index = 0;unsigned short rtp_packet_length = RTP_HDR_LEN + writed_count;if (rtp_protocol == "TCP/RTP/AVP") {unsigned char packt_length_ary[2];packt_length_ary[0] = (rtp_packet_length >> 8) & 0xff;packt_length_ary[1] = rtp_packet_length & 0xff;memcpy(rtp_packet, packt_length_ary, 2);rtp_start_index = 2;}memcpy(rtp_packet + rtp_start_index, rtp_header, RTP_HDR_LEN);memcpy(rtp_packet + +rtp_start_index + RTP_HDR_LEN, frame + (i* single_packet_max_length), writed_count);rtp_seq++;if (PushThreadFlag){send_network_packet(rtp_packet, rtp_start_index + rtp_packet_length);}else{if (nalu != nullptr) {delete nalu;nalu = nullptr;}return;}}pts += interval;std::this_thread::sleep_for(std::chrono::milliseconds(send_packet_interval));}}std::cout << "Push_rtp_Stream - 视频文件发送结束..." << std::endl;
}// 注册:参数true注册、false注销
bool RegisterToServer(const bool rFlag = true)
{// 分配eXosip上下文sip_context = eXosip_malloc();if (!sip_context) { std::cout << "eXosip_malloc fail!" << std::endl; return false; }std::cout << "完成 - 分配eXosip上下文" << std::endl;// 初始化eXosip上下文eXosip_lock(sip_context);if(eXosip_init(sip_context) != OSIP_SUCCESS) { std::cout << "eXosip_init fail!" << std::endl; eXosip_unlock(sip_context); return false; }eXosip_unlock(sip_context);std::cout << "完成 - 初始化eXosip上下文" << std::endl;/*eXosip_guess_localip描述:监听套接字参数:参数2: 17代表用户数据报协议-udp、6代表tcp参数5: 2代表IP协议系列*/eXosip_lock(sip_context);if (eXosip_listen_addr(sip_context, 6, nullptr, GbParameter.device.Port, 2, 0) != OSIP_SUCCESS) { std::cout << "eXosip_listen_addr fail!" << std::endl; eXosip_unlock(sip_context); return false; }eXosip_unlock(sip_context);std::cout << "完成 - 套接字监听" << std::endl;// 清除eXosip中存储的所有身份验证凭据eXosip_lock(sip_context);if (eXosip_clear_authentication_info(sip_context) != OSIP_SUCCESS) { std::cout << "eXosip_clear_authentication_info fail!" << std::endl; eXosip_unlock(sip_context); return false; }eXosip_unlock(sip_context);std::cout << "完成 - 清除eXosip中存储的所有身份验证凭据" << std::endl;/*eXosip_guess_localip描述:查找当前网络(具有默认路由的接口)参数:参数2: 2代表IP协议系列*/eXosip_lock(sip_context);if(eXosip_guess_localip(sip_context, 2, local_ip, 64) != OSIP_SUCCESS) { std::cout << "eXosip_guess_localip fail!" << std::endl; eXosip_unlock(sip_context); return false; }eXosip_unlock(sip_context);std::cout << "完成 - 查找当前网络:" << local_ip << std::endl;// 组合sip字符串from_sip = "sip:" + GbParameter.device.Id + "@" + local_ip + ":" + std::to_string(GbParameter.device.Port);std::string contact = "sip:" + GbParameter.device.Id + "@" + local_ip + ":" + std::to_string(GbParameter.device.Port);proxy_sip = "sip:" + GbParameter.server.Id + "@" + GbParameter.server.Ip + ":" + std::to_string(GbParameter.server.Port);// 生成初始REGISTER请求osip_message_t *register_message = nullptr;eXosip_lock(sip_context);int register_id = eXosip_register_build_initial_register(sip_context,from_sip.c_str(),proxy_sip.c_str(),contact.c_str(),rFlag?3600:0,	// 0为注销[当为0时,设备是注销;当大于0时(Expires最小值为3600),设备是注册]&register_message);eXosip_unlock(sip_context);if (!register_message) { std::cout << "eXosip_register_build_initial_register fail!" << std::endl; return false; }std::cout << "完成 - 生成初始REGISTER请求:\n\t" << "from_sip:" << from_sip << "\n\t"<< "proxy_sip:" << proxy_sip << "\n\t"<< "contact:" << contact << "\n\t"<< "register_id:" << register_id << "\n\t" << std::endl;// 发送对现有注册的REGISTER请求eXosip_lock(sip_context);if (eXosip_register_send_register(sip_context, register_id, register_message) != OSIP_SUCCESS) { std::cout << "eXosip_register_send_register fail!" << std::endl; eXosip_unlock(sip_context); return false; }eXosip_unlock(sip_context);std::cout << (rFlag ? "完成 - 发送注册REGISTER请求" : "完成 - 发送注销REGISTER请求......................") << std::endl;// 开启线程定时发送心跳static bool HeartbeatWorkFlag = true;if (HeartbeatWorkFlag){HeartbeatWorkFlag = false;std::thread heartbeat_task_thread(HeartbeatWork);heartbeat_task_thread.detach();}return true;
}

下载完整源码

关注

笔者 - jxd

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

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

相关文章

开发环境配置本地hosts修改域名

一、找到hosts文件的位置&#xff1a; 方法一&#xff1a;windows系统下&#xff0c;直接 WinR 键输入&#xff1a; C:\WINDOWS\system32\drivers\etc 如图所示&#xff1a;输入完后点击确认 方法二&#xff1a;直接按照路径 C:\WINDOWS\system32\drivers\etc 去找 如图所示…

Opencv | 图像基础知识

目录 一. 图像基础知识1. 颜色空间1.1 RGB颜色空间1.2 HSV颜色空间1.3 CMY(K)颜色空间 (了解) 2. 颜色图2.1 RGB三通道彩色图2.1.1 RGB图片数据格式 2.2 单通道灰度图 一. 图像基础知识 1. 颜色空间 1.1 RGB颜色空间 加法混色&#xff0c;彩色显示器 3通道&#xff1a;Red通道…

ASP.NET-Global.asax使用详解

本文介绍了如何使用Global.asax文件来增强ASP.NET Web应用程序的功能。首先&#xff0c;介绍了Global.asax文件的作用和基本功能。接着&#xff0c;详细探讨了在Global.asax中实现定时任务、应用程序级别的错误处理、应用程序启动和结束时执行特定逻辑等功能。随后&#xff0c;…

QGIS编译(跨平台编译)056:PDAL编译(Windows、Linux、MacOS环境下编译)

点击查看专栏目录 文章目录 1、PDAL介绍2、PDAL下载3、Windows下编译4、linux下编译5、MacOS下编译1、PDAL介绍 PDAL(Point Data Abstraction Library)是一个开源的地理空间数据处理库,它专注于点云数据的获取、处理和分析。PDAL 提供了丰富的工具和库,用于处理激光扫描仪、…

ping 通ip,ping 不通域名

在linux 系统中&#xff0c;ping 通ip,ping 不通对应的域名时&#xff0c;可直接修改系统配置文件 vi /etc/hosts 加入 ip 域名

JavaScript 使用 Promise 实现 sleep 休眠

以下为代码实现&#xff0c;该代码实现了每隔1秒打印一次当前时间&#xff0c;总共打印5次的功能 for(let i 1; i < 5; i){console.log(new Date().toString())await new Promise(resolve>setTimeout(resolve,1000)) }实现休眠的核心代码为: await new Promise(resolv…

uniapp使用Canvas给图片加水印把临时文件上传到服务器

生成的临时路径是没有完整的路径没办法上传到服务器 16:37:40.993 添加水印后的路径, _doc/uniapp_temp_1710923708347/canvas/17109238597881.png 16:37:41.041 添加水印后的完整路径, file://storage/emulated/0/Android/data/com.jingruan.zjd/apps/__UNI__BE4B000/doc/…

Qt笔记 事件处理_鼠标事件

什么是事件&#xff1f; 点击鼠标左键&#xff0c;双击鼠标左键&#xff0c;鼠标来回移动&#xff0c;按下键盘按钮&#xff0c;这些都是事件。 那么事件的响应机制是什么样的呢&#xff1f; 首先main函数中有一个QApplication&#xff0c;其作用是创建一个应用程序对象&…

【论文阅读】Masked Autoencoders Are Scalable Vision Learners

Masked Autoencoders Are Scalable Vision Learners 引用&#xff1a; He K, Chen X, Xie S, et al. Masked autoencoders are scalable vision learners[C]//Proceedings of the IEEE/CVF conference on computer vision and pattern recognition. 2022: 16000-16009. 论文链…

UE4_官方动画内容示例1.3_ 运动混合空间(Locomotion BlendSpace)

如何使用运动&#xff08;Locomotion&#xff09;混合空间将Actor在不同方向上及不同速度的运动混合起来。&#xff08;例如&#xff0c;展示了一个混合了以不同速度向后、前、左和右走路/跑步动作的Actor&#xff09;。 一、相关知识点&#xff1a; 混合空间是允许根据多个输…

联想笔记本的声音键没有反应怎么办?

如果我的联想笔记本电脑上的声音按钮没有响应&#xff0c;该怎么办&#xff1f; 如果我的联想笔记本电脑上的声音按钮没有响应&#xff0c;该怎么办&#xff1f; 按下按钮后我无法控制声音。 我该怎么办&#xff1f; 以下是我为您整理的关于联想笔记本声音按键无反应的相关资料…

Android 10.0 app获取当前已连接wifi列表ssid和密码功能实现

1.前言 在10.0的系统定制化开发中,在一些关于wifi的定制中,有产品需求app中要求获取当前连接wifi和密码功能,在系统原生wifi中 是禁止获取wifi连接的密码的,所以就需要对wifi模块进行一部分的修改,来满足app中获取wifi的ssid和密码功能,接下来就来 实现这个功能 如图:…

金融知识分享系列之:支撑阻力

金融知识分享系列之&#xff1a;支撑阻力 一、支撑阻力原理二、支撑阻力作用1.识别市场资金的预期2.作为入场和平仓的重要参考 三、寻找支撑阻力四、延伸思考五、支撑阻力总结 一、支撑阻力原理 支撑阻力核心要素&#xff1a; 锚定效应订单驱动 支撑阻力原理&#xff1a; 市…

爬虫系列-CSS基础语法

&#x1f308;个人主页&#xff1a;会编程的果子君 &#x1f4ab;个人格言:“成为自己未来的主人~” CSS全称层叠样式表 &#xff0c;主要用来定义页面内容展示效果的一门语言&#xff0c;HTML&#xff1a;页面骨架&#xff0c;素颜CSS&#xff1a;页面效果美化&#xff1a…

01分布式搜索引擎ES

分布式搜索引擎ES 1.初识elasticsearch1.1.了解ES1.2.倒排索引1.3.es的一些概念 2.索引库操作2.1.mapping映射属性2.2.索引库的CRUD 3.文档操作3.1.新增文档3.2.查询文档3.3.删除文档3.4.修改文档3.5.总结 4.RestAPI4.0.导入Demo工程4.1.创建索引库4.2.删除索引库4.3.判断索引库…

免费PDF转换和编辑工具 PDFgear 2.1.4

PDFgear是一款功能强大的 PDF 阅读及转换软件。 它支持多种文件格式的转换和编辑&#xff0c;同时还提供了丰富的功能模块&#xff0c;如签名、表单填写等&#xff0c;方便用户进行多样化的操作。 该软件界面简洁美观&#xff0c;操作简单易懂&#xff0c;适合不同层次的用户…

java static

1、static概念 &#xff08;1&#xff09;static 关键字是静态的意思&#xff0c;可以修饰成员方法&#xff0c;成员变量 &#xff08;2&#xff09;static 修饰的特点 ① 被类的所有对象共享&#xff0c;这也是我们判断是否使用静态关键字的条件。 ② 可以通过类名调用&…

机器学习-06-回归算法

总结 本系列是机器学习课程的系列课程&#xff0c;主要介绍机器学习中回归算法&#xff0c;包括线性回归&#xff0c;岭回归&#xff0c;逻辑回归等部分。 参考 fit_transform,fit,transform区别和作用详解&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&am…

ActiViz三维场景的基本要素——相机

文章目录 前言一、基本属性和方法二、相机操作三、高级功能四、 示例代码和应用五、总结前言 在ActiViz中,vtkCamera是一个非常重要的类,用于定义观察场景的视角和位置。作为三维可视化的核心组件之一,vtkCamera决定了用户在三维场景中所看到的图像内容和视角,因此它对于呈…

基于Java中的SSM框架实现在线通用旅游平台网站系统项目【项目源码+论文说明】计算机毕业设计

基于Java中的SSM框架实现在线通用旅游平台网站系统演示 摘要 近几年来&#xff0c;计算机网络的发展得到了飞速的提升&#xff0c;由此展开的一系列行业大洗牌也由此开始。早些年只是人们只是对于计算机和互联网有了些基础的认识&#xff0c;现在它正在悄悄的改变着我们生活的…