GB28181学习(十六)——基于jrtplib实现tcp被动和主动收流

前言

GB/T28181-2022实时流的传输方式介绍:https://blog.csdn.net/www_dong/article/details/134255185

tcp passive收流

流程图

在这里插入图片描述

注意:

  • m字段指定传输方式为TCP/RTP/AVP;
  • sdp信息中增加"a=setup:passive";
  • SIP服务器启动端口监听,设备发起tcp连接请求;

设计

  1. 创建socket、bind、listen,启动数据接收线程;
// TcpServer为封装的socket类int CGBTcpServerStreamReceiver::Start()
{if (m_localIP.empty() || m_localPort <= 0)return -1;if (m_tcpServer.get())return 0;m_tcpServer = std::make_shared<TcpServer>(TcpDataCB, this);if (!m_tcpServer.get())return -1;if (0 != m_tcpServer->TcpCreate() || 0 != m_tcpServer->TcpBind(m_localPort) || 0 != m_tcpServer->TcpListen(5))return -1;m_thread = std::thread(TcpDataThread, this);return 0;
}
  1. 在线程内等待连接,连接成功后接收数据并回调至应用层处理
void CGBTcpServerStreamReceiver::TcpDataWorker()
{bool bAccept = false;uint8_t payload;while (m_running){if (!bAccept){// 等待设备连接if (0 == m_tcpServer->TcpAccept()){bAccept = true;// 连接成功后,初始化rtp参数if (0 != InitRtpSession_()){break;}}continue;}Poll();BeginDataAccess();// 开始接收数据if (GotoFirstSourceWithData()){do{RTPPacket* packet = nullptr;while (nullptr != (packet = GetNextPacket())){payload = packet->GetPayloadType();if (0 == payload){DeletePacket(packet);continue;}struct rtp_packet_tcp data;data.mark = packet->HasMarker();data.pts = packet->GetTimestamp();data.seq = packet->GetSequenceNumber();data.data = packet->GetPayloadData();data.len = (int)packet->GetPayloadLength();m_payload = payload;if (m_lastSeq < 0){m_lastSeq = data.seq - 1;}if (m_lastSeq = data.seq - 1){PackData_(data.data, data.len);}DeletePacket(packet);}} while (GotoNextSourceWithData());}EndDataAccess();Sleep(30);}Destroy();
}
  1. 初始化rtp参数
int CGBTcpServerStreamReceiver::InitRtpSession_()
{const int packSize = 45678;RTPSessionParams sessionParams;sessionParams.SetProbationType(RTPSources::NoProbation);sessionParams.SetOwnTimestampUnit(90000.0 / 25.0);sessionParams.SetMaximumPacketSize(packSize + 64);m_rtpTcpTransmitter = new RTPTCPTransmitter(nullptr);m_rtpTcpTransmitter->Init(true);m_rtpTcpTransmitter->Create(65535, 0);if (0 != Create(sessionParams, m_rtpTcpTransmitter))return -1;if (0 != AddDestination(RTPTCPAddress(m_tcpServer->GetClientSocket())))return -1;return 0;
}

注意:RTP over TCP模式比RTP over UDP模式多了两字节的长度字段。

tcp active收流

流程图

在这里插入图片描述

注意:

  • m字段指定传输方式为TCP/RTP/AVP;
  • sdp信息中增加"a=setup:active";
  • 设备返回200 OK,报文的SDP信息中包含tcp监听端口;
  • SIP服务器根据设备监听端口发起TCP连接请求;

设计

  1. 创建socket、connect、初始化rtp,启动数据接收线程
// TcpClient为封装的客户端socket类int CGBTcpClientStreamReceiver::Start(int streamType)
{if (m_localIP.empty() || m_localPort <= 0)return -1;if (m_tcpClient.get())return 0;// 创建socketm_tcpClient = std::make_shared<TcpClient>(TcpDataCB, this);if (!m_tcpClient.get() || 0 != m_tcpClient->TcpCreate())return -1;// connectint ret = m_tcpClient->TcpConnectByTime(m_localIP.c_str(), m_localPort, 5);if (0 != ret)return -1;// 初始化rtp sessionif (0 != InitRtpSession_())return -1;// 启动接收线程m_thread = std::thread(TcpDataThread, this);
}
  1. 初始化rtp参数
int CGBTcpClientStreamReceiver::InitRtpSession_()
{const int packSize = 45678;RTPSessionParams sessionParams;sessionParams.SetProbationType(RTPSources::NoProbation);sessionParams.SetOwnTimestampUnit(90000.0 / 25.0);sessionParams.SetMaximumPacketSize(packSize + 64);m_rtpTcpTransmitter = new RTPTCPTransmitter(nullptr);m_rtpTcpTransmitter->Init(true);m_rtpTcpTransmitter->Create(65535, 0);if (0 != Create(sessionParams, m_rtpTcpTransmitter))return -1;// 添加客户端socketif (0 != AddDestination(RTPTCPAddress(m_tcpClient->GetClientSocket())))return -1;return 0;
}
  1. 在线程内等待连接,连接成功后接收数据并回调至应用层处理

同tcp passive收流流程。

rtp解包工具

基于qt+ireader库实现tcp解包(ps封装+264载荷)

解包流程

  1. 打开文件,创建解码器
void RtpUnpackDlg::StartRtpUnpack()
{if (m_thread.joinable())return;QString filePath = ui.le_filePath->text();if (filePath.isEmpty()){QMessageBox::critical(this, QString::fromLocal8Bit("错误"), QString::fromLocal8Bit("文件路径为空"), QMessageBox::Ok);return;}m_rtpPayloadParam.payload = 100;m_rtpPayloadParam.encoding = "PS";m_rtpPayloadParam.frtp = fopen(filePath.toStdString().c_str(), "rb");if (!m_rtpPayloadParam.frtp){QMessageBox::critical(this, QString::fromLocal8Bit("警告"), QString::fromLocal8Bit("打开输入文件失败"), QMessageBox::Ok);return;}m_rtpPayloadParam.fout = fopen(outFilePath.toStdString().c_str(), "wb");if (!m_rtpPayloadParam.fout){QMessageBox::critical(this, QString::fromLocal8Bit("警告"), QString::fromLocal8Bit("打开输出文件失败"), QMessageBox::Ok);return;}struct rtp_payload_t handler;handler.alloc = RtpPacketAlloc;handler.free = RtpPacketFree;handler.packet = RtpDecodePacket;m_rtpPayloadParam.decoder = rtp_payload_decode_create(100, "PS", &handler, this);m_thread = std::thread(DataReadThread, this);
}
  1. 读数据
void RtpUnpackDlg::RtpDataReadWorker()
{while (m_running){// 先读两个字节的头unsigned char s2[2];if (2 != fread(s2, 1, 2, m_rtpPayloadParam.frtp))break;m_rtpPayloadParam.size = (s2[0] << 8) | s2[1];assert(ctx.size < sizeof(ctx.packet));if (m_rtpPayloadParam.size != (int)fread(m_rtpPayloadParam.packet, 1, m_rtpPayloadParam.size, m_rtpPayloadParam.frtp))break;// 塞数据if (m_rtpPayloadParam.packet[1] < RTCP_FIR || m_rtpPayloadParam.packet[1] > RTCP_LIMIT)rtp_payload_decode_input(m_rtpPayloadParam.decoder, m_rtpPayloadParam.packet, m_rtpPayloadParam.size);}fclose(m_rtpPayloadParam.frtp);fclose(m_rtpPayloadParam.fout);
}
  1. 解包
int RtpUnpackDlg::DecodePacket(const void* packet, int bytes, uint32_t timestamp, int flags)
{static const unsigned char start_code[4] = { 0, 0, 0, 1 };static unsigned char buffer[2 * 1024 * 1024] = {0, };size_t size = 0;if (0 == strcmp("H264", m_rtpPayloadParam.encoding) || 0 == strcmp("H265", m_rtpPayloadParam.encoding)|| 0 == strcmp("PS", m_rtpPayloadParam.encoding)){memcpy(buffer, start_code, sizeof(start_code));size += sizeof(start_code);}memcpy(buffer + size, packet, bytes);size += bytes;fwrite(buffer, 1, size, m_rtpPayloadParam.fout);// 新增界面播放功能if (m_playWidget)m_playWidget->AddData(CODEC_VIDEO_H264, (void*)buffer, size);return 0;
}

界面示例

在这里插入图片描述

参考:https://github.com/ireader中的demo示例

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

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

相关文章

AWS实战(一)-创建S3 存储桶

1&#xff09;登录AWS账号&#xff0c;选择服务—>存储—>S3。 2&#xff09;查看存储桶列表 3&#xff09;点击"创建存储桶"创建bucket。 4&#xff09;设置跨域 点击编辑&#xff0c;修改跨域设置即可。

Arduino驱动DHT22温湿度传感器(温湿度传感器)

目录 1、传感器特性 2、控制器和传感器连线图 3、驱动程序 DHT22数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。 传感器包括一个电容式感湿元件和一个NTC…

关于Ultra HDR Image的那些事

一、什么是Ultra HDR Image 2023年10月初&#xff0c;google正式发布了Android 14。该版本中引入了一个新的功能Ultra HDR Image&#xff0c;被誉为”图像技术的未来”。之前Android版本各手机厂商或许有自己的HDR图片技术&#xff0c;本文这里重点分析下Android14上google的实…

在test用户下创建test1表并插入数据,然后将tes1t表的查询权限授予test2用户

文章目录 1、以 test 用户登录2、创建 test1 表3、插入数据4、查看数据5、授予权限创建用户test2以 test 用户登录并授予权限&#xff1a;使用test2用户登录查询&#xff0c;测试结果 1、以 test 用户登录 首先&#xff0c;您需要以 test 用户登录到数据库 sqlplus test/1232…

[工业自动化-24]:西门子S7-15xxx编程 - 软件编程 - HMI编程的变量与变量映射

目录 一、HMI变量与连接 1.1 概述 1.2 HMI变量的类型&#xff1a;PLC变量与自主变量 二、HMI变量映射 2.1 场景映射方式 2.2 映射示例 2.3 映射步骤 2.4 如何为HMI添加映射关系 一、HMI变量与连接 1.1 概述 HMI只是显示设备&#xff0c;并非控制设备&#xff0c;因此…

腾讯云4核8G服务器配置价格表,轻量和CVM标准型S5实例

腾讯云4核8G服务器S5和轻量应用服务器优惠价格表&#xff0c;轻量应用服务器和CVM云服务器均有活动&#xff0c;云服务器CVM标准型S5实例4核8G配置价格15个月1437.3元&#xff0c;5年6490.44元&#xff0c;轻量应用服务器4核8G12M带宽一年446元、529元15个月&#xff0c;腾讯云…

Docker Golang 开发环境搭建指南

Docker Golang 开发环境搭建指南 概述 在 Golang 开发中&#xff0c;搭建合适的开发环境是非常重要的。然而&#xff0c;由于 Golang 的跨平台特性&#xff0c;不同操作系统之间的配置差异可能会导致环境搭建过程变得复杂。为了简化这个过程并保持开发环境的一致性&#xff0…

Linux下向Github仓库推送

文章目录 Git 与 Github安装git在github下创建项目下载项目到本地Git三板斧第一板斧 git add第二板斧 git commit第三板斧 git push Git 与 Github Git是目前从开发人员到设计人员的版本控制技术。gitee是国内社交代码托管平台。这是一个你可以玩和实验的地方。在这里你可以找…

YoloV8改进策略:聚焦线性注意力重构YoloV8

文章目录 摘要论文《FLatten Transformer:使用聚焦线性注意力的ViT》1、简介2、相关工作2.1 Vision Transformer2.2 线性注意力3. 预备知识3.1. 视觉Transformer和自注意力3.2. 线性注意力4. 聚焦线性注意力4.1.聚焦能力4.2. 特征多样性4.3. 聚焦线性注意力模块5. 实验5.1. Ima…

注解方式优雅的实现 Redisson 分布式锁

1前言 日常开发中&#xff0c;难免遇到一些并发的场景&#xff0c;为了保证接口执行的一致性&#xff0c;通常采用加锁的方式&#xff0c;因为服务是分布式部署模式&#xff0c;本地锁Reentrantlock和Synchnorized这些就先放到一边了&#xff0c;Redis的setnx锁存在无法抱保证…

levelDB之基础数据结构-Slice

Slice是levelDB中用于操作字符串的数据结构&#xff0c;以字节为单位。 定义与实现 namespace leveldb {class LEVELDB_EXPORT Slice {public:// Create an empty slice.Slice() : data_(""), size_(0) {}// Create a slice that refers to d[0,n-1].Slice(const c…

创建具有负载平衡和集群的可扩展 Node.js 应用程序

创建具有负载平衡和集群的可扩展 Node.js 应用程序 负载平衡是提高应用程序性能、可扩展性和可用性的一项重要技术。当客户端向负载均衡器发出请求时&#xff0c;负载均衡器根据预定义的规则将请求分发到不同的实例。 可以使用cluster集群模块或 PM2 等工具根据负载均衡器的流…

[PHP]关联和操作MySQL数据库然后将数据库部署到ECS

在Mac电脑上使用VS Code进行PHP开发并关联操作MySQL数据库&#xff0c;然后将数据库部署到ECS。 1.安装PHP和MySQL 确保你的Mac上已经安装了PHP和MySQL。你可以使用Homebrew来安装它们&#xff1a; $ brew install php $ brew install mysql 安装mysql完成后记住这一句: …

在.net 6版本以上的web api中添加像.net 5一样的Startup.cs

Program.cs中&#xff1a; 第一步&#xff1a;在var builder WebApplication.CreateBuilder(args);的后面添加上&#xff1a; var startup new Startup(builder.Configuration); startup.ConfigureServices(builder.Services); 第二步&#xff1a;在var app builder.Build()…

城市网吧视频智能监控方案,实现视频远程集中监控

网吧环境较为复杂&#xff0c;电脑设备众多且人员流动性大&#xff0c;极易发生人员或消防事故&#xff0c;亟需改变&#xff0c;TSINGSEE青犀AI智能网吧视频监管方案可以帮助实现对网吧环境和用户活动的实时监控和管理。 1、视频监控系统 在网吧内部布置高清摄像头&#xff0…

设计模式 -- 适配器模式(Adapter Pattern)

适配器模式&#xff1a;属于结构型模式&#xff0c;结合了两个独立接口的功能&#xff0c;作为 两个不兼容的接口之间的桥梁 。 介绍 意图&#xff1a;将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。主要…

【Shell脚本入门】

Shell中的特殊符号 1.$ 美元符号&#xff0c;用来表示变量的值。 如变量NAME的值为Mike&#xff0c;则使用$NAME就可以得到“Mike”这个值。2.# 井号&#xff0c;除了做为超级用户的提示符之外&#xff0c;还可以在脚本中做为注释的开头字母&#xff0c;每一行语句中&#xff…

WPF中可视化树和逻辑树的区别是什么

在WPF中&#xff0c;用户界面元素被组织成树形结构。这种结构主要分为两种&#xff1a;逻辑树&#xff08;Logical Tree&#xff09;和可视化树&#xff08;Visual Tree&#xff09;。它们在设计上各有特点和用途。 逻辑树&#xff08;Logical Tree&#xff09; 逻辑树是WPF中…

反转字符串中的单词

给你一个字符串 s &#xff0c;请你反转字符串中 单词 的顺序。 单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。 返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。 注意&#xff1a;输入字符串 s中可能会存在前导空格、尾随空格…

笔记54:门控循环单元 GRU

本地笔记地址&#xff1a;D:\work_file\DeepLearning_Learning\03_个人笔记\3.循环神经网络\第9章&#xff1a;动手学深度学习~现代循环神经网络 a a a a a a a