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

前言

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

基于jrtplib实现tcp被动和主动收流介绍:https://blog.csdn.net/www_dong/article/details/134451387

本文主要介绍下级平台或设备发流功能,用于对接特定的SIP服务器或上级平台。

UDP发流

流程图

在这里插入图片描述

发送端流程

  • 初始化rtp参数;
  • 裸流数据做PS复用;
  • 组RTP包发送;

设计

  1. 初始化rtp参数
int CUdp::InitRtp_()
{RTPSessionParams sessionParams;sessionParams.SetMinimumRTCPTransmissionInterval(10);sessionParams.SetOwnTimestampUnit(1.0 / 90000.0);sessionParams.SetAcceptOwnPackets(true);sessionParams.SetMaximumPacketSize(1450);RTPUDPv4TransmissionParams transParams;transParams.SetRTPSendBuffer(2*1024*1024);transParams.SetBindIP(m_ip);transParams.SetPortbase((uint16_t)m_port);if (0 != Create(sessionParams, &transParams)){return -1;}SetDefaultPayloadType((uint8_t)m_payload);SetDefaultTimestampIncrement(3600);SetDefaultMark(true);RTPIPv4Address addr(ntohl(inet_addr(m_ip), (uint16_t)m_port);if(0 != AddDestination(addr)){return -1;}return 0;
}
  1. 流数据复用为PS
// 使用ireader开源库进行ps复用
// 初始化
CData2PS::CData2PS()
{struct ps_muxer_func_t func;func.alloc = Alloc;func.free = Free;func.write = Packet;m_ps = ps_muxer_create(&func, this);// TODO codecid待补充m_ps_stream = ps_muxer_add_stream(m_ps, PSI_STREAM_H264, nullptr, 0);
}// 塞数据
int CData2PS::InputData(void* data, int len)
{if (!m_ps)return -1;uint64_t clock = time64_now();if (0 == m_ps_clock)m_ps_clock = clock;return ps_muxer_input(m_ps, m_ps_stream, 0, (clock - m_ps_clock) * 90, (clock - m_ps_clock) * 90, data, len);
}
  1. 发送rtp包
// 调用jrtplib中SendPacket(data, len);接口发送数据// 以下为SendPacket部分源码
// 主要流程:
// 1. 构建packet
// 2. 发送rtp数据
int RTPSession::SendPacket(const void *data,size_t len,uint8_t pt,bool mark,uint32_t timestampinc)
{int status;if (!created)return ERR_RTP_SESSION_NOTCREATED;BUILDER_LOCKif ((status = packetbuilder.BuildPacket(data,len,pt,mark,timestampinc)) < 0){BUILDER_UNLOCKreturn status;}if ((status = SendRTPData(packetbuilder.GetPacket(),packetbuilder.GetPacketLength())) < 0){BUILDER_UNLOCKreturn status;}BUILDER_UNLOCKSOURCES_LOCKsources.SentRTPPacket();SOURCES_UNLOCKPACKSENT_LOCKsentpackets = true;PACKSENT_UNLOCKreturn 0;
}// 构建包
int RTPPacketBuilder::PrivateBuildPacket(const void *data,size_t len,uint8_t pt,bool mark,uint32_t timestampinc,bool gotextension,uint16_t hdrextID,const void *hdrextdata,size_t numhdrextwords)
{RTPPacket p(pt,data,len,seqnr,timestamp,ssrc,mark,numcsrcs,csrcs,gotextension,hdrextID,(uint16_t)numhdrextwords,hdrextdata,buffer,maxpacksize,GetMemoryManager());int status = p.GetCreationError();if (status < 0)return status;packetlength = p.GetPacketLength();if (numpackets == 0) // first packet{lastwallclocktime = RTPTime::CurrentTime();lastrtptimestamp = timestamp;prevrtptimestamp = timestamp;}else if (timestamp != prevrtptimestamp){lastwallclocktime = RTPTime::CurrentTime();lastrtptimestamp = timestamp;prevrtptimestamp = timestamp;}numpayloadbytes += (uint32_t)p.GetPayloadLength();numpackets++;timestamp += timestampinc;seqnr++;return 0;
}// 发送包
int RTPSession::SendRTPData(const void *data, size_t len)
{if (!m_changeOutgoingData)return rtptrans->SendRTPData(data, len);void *pSendData = 0;size_t sendLen = 0;int status = 0;status = OnChangeRTPOrRTCPData(data, len, true, &pSendData, &sendLen);if (status < 0)return status;if (pSendData){status = rtptrans->SendRTPData(pSendData, sendLen);OnSentRTPOrRTCPData(pSendData, sendLen, true);}return status;
}// 底层实现
int RTPUDPv4Transmitter::SendRTPData(const void *data,size_t len)	
{if (!init)return ERR_RTP_UDPV4TRANS_NOTINIT;MAINMUTEX_LOCKif (!created){MAINMUTEX_UNLOCKreturn ERR_RTP_UDPV4TRANS_NOTCREATED;}if (len > maxpacksize){MAINMUTEX_UNLOCKreturn ERR_RTP_UDPV4TRANS_SPECIFIEDSIZETOOBIG;}destinations.GotoFirstElement();while (destinations.HasCurrentElement()){// 调用sendto函数实现udp包的发送sendto(rtpsock,(const char *)data,len,0,(const struct sockaddr *)destinations.GetCurrentElement().GetRTPSockAddr(),sizeof(struct sockaddr_in));destinations.GotoNextElement();}MAINMUTEX_UNLOCKreturn 0;
}

tcp passive发流

流程图

在这里插入图片描述

发送端流程:

  • 上级平台或sip服务器以主动方式连接,对于下级平台或者设备(数据发送端)为被动方式;
  • 下级平台或者设备(数据发送端)启动端口监听;
  • 接收上级平台或sip服务器tcp连接请求;
  • 向上级平台或sip服务器发送流数据;

设计

  1. 创建socket、bind、listen,启动数据接收线程;
// TcpServer为封装的socket类int CGBTcpServer::Start()
{if (0 != m_localPort || m_tcpServer.get())return 0;int ret = -1;do {m_tcpServer = std::make_shared<TcpServer>(nullptr, this);if (!m_tcpServer.get())break;ret = m_tcpServer->TcpCreate();if (0 != ret)break;ret = m_tcpServer->TcpBind(m_localPort);if (0 != ret)break;ret = m_tcpServer->TcpListen(5);if (0 != ret)break;m_thread = std::thread(TCPData2PSThread, this);return 0;} while (0);Stop();return ret;
}
  1. 在线程内等待连接,连接成功后接收数据并回调至应用层处理
void CGBTcpServer::TCPData2PSWorker()
{if (!m_pspacker)m_pspacker = new(std::nothrow) CData2PS(PSTCPDataCB, this);bool bAccept = false;while (m_running){if (!bAccept){if (0 == m_tcpServer->TcpAccept()){bAccept = true;if (0 != InitRtp_()){break;}}continue;}std::this_thread::sleep_for(std::chrono::seconds(1));}
}
  1. 初始化rtp参数
int CGBTcpServer::InitRtp_()
{const int packetSize = 45678;RTPSessionParams sessionparams;sessionparams.SetProbationType(RTPSources::NoProbation);sessionparams.SetOwnTimestampUnit(1.0 / packetSize);sessionparams.SetMaximumPacketSize(packetSize + 64);m_rtpTcpTransmitter = new RTPTCPTransmitter(nullptr);m_rtpTcpTransmitter->Init(true);m_rtpTcpTransmitter->Create(65535, 0);int status = Create(sessionparams, m_rtpTcpTransmitter);if (status < 0){return status;}status = AddDestination(RTPTCPAddress(m_tcpServer->GetClientSocket()));if (0 != status)return status;SetDefaultPayloadType(96);SetDefaultMark(false);SetDefaultTimestampIncrement(160);return 0;
}
  1. 将数据复用为PS;
  2. tcp方式发包
// 调用jrtplib中SendPacket(data, len);接口发送数据// 以下为tcp方式SendPacket部分源码
int RTPTCPTransmitter::SendRTPData(const void *data,size_t len)	
{return SendRTPRTCPData(data, len);
}int RTPTCPTransmitter::SendRTPRTCPData(const void *data, size_t len)
{if (!m_init)return ERR_RTP_TCPTRANS_NOTINIT;MAINMUTEX_LOCKif (!m_created){MAINMUTEX_UNLOCKreturn ERR_RTP_TCPTRANS_NOTCREATED;}// #define RTPTCPTRANS_MAXPACKSIZE							65535if (len > RTPTCPTRANS_MAXPACKSIZE){MAINMUTEX_UNLOCKreturn ERR_RTP_TCPTRANS_SPECIFIEDSIZETOOBIG;}std::map<SocketType, SocketData>::iterator it = m_destSockets.begin();std::map<SocketType, SocketData>::iterator end = m_destSockets.end();vector<SocketType> errSockets;int flags = 0;
#ifdef RTP_HAVE_MSG_NOSIGNALflags = MSG_NOSIGNAL;
#endif // RTP_HAVE_MSG_NOSIGNALwhile (it != end){uint8_t lengthBytes[2] = { (uint8_t)((len >> 8)&0xff), (uint8_t)(len&0xff) };SocketType sock = it->first;// 调用send接口发送数据// 1. 先发送2字节头(固定格式)// 2. 再发送数据if (send(sock,(const char *)lengthBytes,2,flags) < 0 ||send(sock,(const char *)data,len,flags) < 0)errSockets.push_back(sock);++it;}MAINMUTEX_UNLOCKif (errSockets.size() != 0){for (size_t i = 0 ; i < errSockets.size() ; i++)OnSendError(errSockets[i]);}// Don't return an error code to avoid the poll thread exiting// due to one closed connection for examplereturn 0;
}

tcp active发流

流程图

在这里插入图片描述

发送端流程:

  • 上级平台或sip服务器启动tcp监听连接,对于下级平台或者设备(数据发送端)为主动方式;
  • 下级平台或者设备(数据发送端)发起tcp连接;
  • 接收上级平台或sip服务器tcp响应;
  • 向上级平台或sip服务器发送流数据;

设计

  1. 创建socket、connect、初始化rtp,启动数据接收线程
// TcpClient为封装的客户端socket类int CGBTcpClient::Start()
{if (0 != m_localPort || m_tcpClient.get())return 0;int ret = -1;do{m_tcpClient = std::make_shared<TcpClient>(nullptr, this);if (!m_tcpClient.get() || 0 != m_tcpClient->TcpCreate())break;ret = m_tcpClient->TcpConnectByTime(m_localIP.c_str(), m_localPort, 5);if (0 != ret)break;ret = InitRtp_();if (0 != ret)break;m_thread = std::thread(RTPPackerThread, this);return 0;} while (0);Stop();return ret;
}
  1. 初始化rtp参数
int CGBTcpClient::InitRtp_()
{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_tcpClient->GetClientSocket())))return -1;return 0;
}
  1. 视音频数据复用为PS
  2. 发送数据,同tcp passive发流

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

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

相关文章

生活如果真能像队列一样的话

生活如果真能像队列一样&#xff0c;那该多好啊。 —————————————————————————————————————————— 背包&#xff0c;队列 可以先看他们的API&#xff1a;都含有一个无参构造函数&#xff0c;添加单个元素的方法&#xff0c;测试集合…

基于材料生成算法优化概率神经网络PNN的分类预测 - 附代码

基于材料生成算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于材料生成算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于材料生成优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

ROSNS3(一)

https://github.com/malintha/rosns3 第一步&#xff1a;clone和构建rosns3客户端 第二步&#xff1a;运行 最详细的ubuntu 安装 docker教程 - 知乎 1. unable to find source space /home/muta/src 解决方法&#xff1a; 将副将将碰到的bug&#xff0c;解决方法_#include &…

Python从零开始快速搭建一个语音对话机器人

文章目录 01-初心缘由02-准备工作03-语音机器人的搭建思路04-语音生成音频文件05-音频文件转文字STT06-与图灵机器人对话07-文字转语音08-语音对话机器人的完整代码09-结束语10-有问必答关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学…

SSH连接远程服务器报错:WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED 解决方法

一.错误描述 报错信息里提示了路径信息/root/.ssh/known_hosts:20 二.解决方案 方法一 输入以下指令&#xff1a; ssh-keygen -R XXX&#xff08;需要连接远程服务器的ip&#xff09; 按照我的例子ip:10.165.7.136&#xff0c;会返回以下信息: 重新尝试连接&#xff1a; 输…

规划类3d全景线上云展馆帮助企业轻松拓展海外市场

科技3D线上云展馆作为一种基于VR虚拟现实和互联网技术的新一代展览平台。可以在线上虚拟空间中模拟真实的展馆&#xff0c;让观众无需亲自到场&#xff0c;即可获得沉浸式的参观体验。通过这个展馆&#xff0c;您可以充分、全面、立体展示您的产品、服务以及各种创意作品&#…

2023年【安全生产监管人员】考试题及安全生产监管人员找解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 安全生产监管人员考试题参考答案及安全生产监管人员考试试题解析是安全生产模拟考试一点通题库老师及安全生产监管人员操作证已考过的学员汇总&#xff0c;相对有效帮助安全生产监管人员找解析学员顺利通过考试。 1、…

数据结构-栈的实现

1.栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原则。 压栈&…

Matlab群体智能优化算法之海象优化算法(WO)

文章目录 一、灵感来源二、算法的初始化三、GTO的数学模型Phase1&#xff1a;危险信号和安全信号Phase2&#xff1a;迁移&#xff08;探索&#xff09;Phase3&#xff1a;繁殖&#xff08;开发&#xff09; 四、流程图五、伪代码六、算法复杂度七、WO搜索示意图八、实验分析和结…

FreeRTOS列表和列表项

FreeRTOS内核调度使用了大量的列表&#xff08;list&#xff09;和列表项&#xff08;listitem&#xff09;数据结构。它的源码中涉及到很多列表的操作&#xff0c;对于FreeRTOS来说&#xff0c;列表就是它最基础的一部分&#xff0c;列表被用作FreeRTOS调度器使用&#xff0c;…

力扣.面试题 04.06. 后继者(java 树的中序遍历)

Problem: 面试题 04.06. 后继者 文章目录 题目描述思路解题方法复杂度Code 题目描述 设计一个算法&#xff0c;找出二叉搜索树中指定节点的“下一个”节点&#xff08;也即中序后继&#xff09;。 如果指定节点没有对应的“下一个”节点&#xff0c;则返回null。 思路 由于题…

Python开发运维:Celery连接Redis

目录 一、理论 1.Celery 二、实验 1.Windows11安装Redis 2.Python3.8环境中配置Celery 三、问题 1.Celery命令报错 2.执行Celery命令报错 3.Win11启动Celery报ValueErro错误 一、理论 1.Celery (1) 概念 Celery是一个基于python开发的分布式系统&#xff0c;它是简单…

JSP内置对象

一、request对象 1、访问请求参数 2、在作用域中管理属性 3、获取Cookie 4、解决中文乱码 5、获取客户端信息 6、显示国际化信息 是一个javax.servlet.http.HttpServletRequest对象 request封装了用户浏览器提交的信息&#xff0c;因此可以调用相应的方法可以获取这些封…

优先经验回放(prioritized experience replay)

prioritized experience replay 思路 优先经验回放出自ICLR 2016的论文《prioritized experience replay》。 prioritized experience replay的作者们认为&#xff0c;按照一定的优先级来对经验回放池中的样本采样&#xff0c;相比于随机均匀的从经验回放池中采样的效率更高&…

UML建模图文详解教程——类图

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl本文参考资料&#xff1a;《UML面向对象分析、建模与设计&#xff08;第2版&#xff09;》吕云翔&#xff0c;赵天宇 著 类图概述 类图用来描述系统内各种实体的类型以及不同…

Unsupervised MVS论文笔记

Unsupervised MVS论文笔记 摘要1 引言2 相关工作3 实现方法 Tejas Khot and Shubham Agrawal and Shubham Tulsiani and Christoph Mertz and Simon Lucey and Martial Hebert. Tejas Khot and Shubham Agrawal and Shubham Tulsiani and Christoph Mertz and Simon Lucey and …

JAVA小游戏拼图

第一步是创建项目 项目名自拟 第二部创建个包名 来规范class 然后是创建类 创建一个代码类 和一个运行类 代码如下&#xff1a; package heima; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import …

10、信息打点——APP小程序篇抓包封包XP框架反编译资产提取

APP信息搜集思路 外在——抓包封包——资产安全测试 抓包&#xff08;Fiddle&茶杯&burp&#xff09;封包&#xff08;封包监听工具&#xff09;&#xff0c;提取资源信息 资产收集——资源提取——ICO、MAD、hash——FOFA等网络测绘进行资产搜集 外在——功能逻辑 内在…

国际版Amazon Lightsail的功能解析

Amazon Lightsail是一项易于使用的云服务,可为您提供部署应用程序或网站所需的一切,从而实现经济高效且易于理解的月度计划。它是部署简单的工作负载、网站或开始使用亚马逊云科技的理想选择。 作为 AWS 免费套餐的一部分&#xff0c;可以免费开始使用 Amazon Lightsail。注册…