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;

    // 创建socket
	m_tcpClient = std::make_shared<TcpClient>(TcpDataCB, this);
	if (!m_tcpClient.get() || 0 != m_tcpClient->TcpCreate())
		return -1;

	// connect
	int ret = m_tcpClient->TcpConnectByTime(m_localIP.c_str(), m_localPort, 5);
	if (0 != ret)
		return -1;

    // 初始化rtp session
	if (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;

    // 添加客户端socket
	if (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示例

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32踩坑:LAN8720未接网线,上电后再接网线,网络模块无法正常使用

LAN8720未接网线&#xff0c;上电后再接网线&#xff0c;网络模块无法正常使用 一、问题描述 最近因为做的项目出了BUG&#xff0c;STM32 单片机在未接网线的状态下&#xff0c;上电一段时间后&#xff0c;将网线插入网口后&#xff0c;IP地址ping不通&#xff0c;网络模块无…

股票魔法师第二阶段趋势模板选股公式,寻找上涨趋势

对于股票运行的阶段&#xff0c;不同的股票分析方法有着不同的划分方式。从传统的主力运作分析&#xff0c;可以分为吸筹、洗盘、试盘、拉升、出货五个阶段。在波浪理论中&#xff0c;一个完整的上升或下降周期包含8浪&#xff08;其中5浪是主浪、3浪是调整浪&#xff09;。在缠…

Unity中Shader纹理的多级渐远Mipmap

文章目录 前言一、什么是Mipmap二、Mipmap能带来什么好处1、增加缓存命中率&#xff0c;减少像素抖动感2、可配合质量设置来分级加载&#xff0c;减少不同配置下的内存 二、我们在Shader中实现一下该效果1、我们先布置一个简单的棋盘格&#xff0c;用于测试纹理的多级效果2、我…

vue3 + ts项目(无vite)报错记录

记录项目创建后遇到的报错 1.类型“Window & typeof globalThis”上不存在属性“_CONFIG”。ts(2339) 问题描述&#xff1a; 使用全局 window 上自定义的属性&#xff0c;TypeScript 会报属性不存在 解决&#xff1a;需要将自定义变量扩展到全局 window 上&#xff0c…

MetaCyc、KEGG傻傻分不清?

今天小编从以下三点给大家唠一唠&#xff1a;MetaCyc数据库介绍、与KEGG比较、使用方法。 一、MetaCyc数据库介绍 MetaCyc全称Metabolic Pathways From all Domains of Life&#xff0c;属于BioCyc子数据库&#xff0c;BioCyc是一个专注于代谢通路的高质量数据库。MetaCyc是一个…

完整版指南:企业网络中的VXLAN-BGP-EVPN

随着互联网的发展&#xff0c;数据中心的数量和规模呈爆炸性增长趋势。数据中心业务不断增加&#xff0c;用户需求不断提高。随之而来的问题是数据中心的功能变得越来越复杂&#xff0c;运维管理变得越来越困难。VXLAN-BGP-EVPN的出现为企业网络带来了无限的可能性。 什么是VX…

跟李沐学AI-深度学习课程00-03【预告、课程安排、深度学习介绍、安装】

目录 00 预告 01 课程安排 02 深度学习介绍 03 安装 本地安装 04 数据操作数据预处理 数据操作 数据类型 创建数组 访问元素 数据操作实现 入门 运算符 广播机制 索引和切片 节省内存 转换为其他Python对象 数据预处理实现 读取数据集 处理缺失值 转换为张…

vue下载xlsx表格

vue下载xlsx表格 // 导入依赖库 import XLSX from xlsx; import FileSaver from file-saver; methods:{btn(){let date new Date()let Y date.getFullYear() -let M (date.getMonth() 1 < 10 ? 0 (date.getMonth() 1) : date.getMonth() 1) -let D (date.getDat…

基于springboot实现私人健身与教练预约管理系统项目【项目源码+论文说明】

基于springboot实现私人健身与教练预约管理系统演示 摘要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应…

几款开源视频编辑软件的比较

软件特点OpenShot跨平台、免费开源、易于上手、功能丰富Shotcut跨平台、免费开源、支持多种格式、性能强大kdenlive跨平台、开源、功能强大、支持多种滤镜Avidemux跨平台、免费开源、小巧简洁、功能实用 OpenShot 是一款免费开源的视频编辑软件&#xff0c;支持 Windows、macO…

Axure RP 9下载教程,轻松入手!

Axurerp9是产品经理必备的专业快速原型设计工具。Axurerp9可以快速高效地创建产品原型图&#xff0c;绘制APP和网页的原型图、框架图、结构图等。但Axurerp9下载在用户体验上的缺陷也很明显&#xff0c;其设置交互方式相对繁琐&#xff0c;可视化不足、条件判断、变量、中继器等…

操作系统 day11(进程调度时机、切换、调度方式)

进程调度的时机 这里的主动放弃指的是—内中断&#xff08;异常、例外&#xff09;&#xff0c;中断信号来自CPU内部。而被动放弃指的是—外中断&#xff08;中断&#xff09;&#xff0c;中断信号来自CPU外部 如果该进程还没退出临界区&#xff08;还没解锁&#xff09;就进行…

【2023高交会成绩单出炉】Gooxi斩获“AIC年度标杆应用奖”大奖

第25届中国国际高新技术成果交易会&#xff08;简称“高交会”&#xff09;在深圳盛大开幕&#xff0c;作为高交会人工智能板块的重点活动之一&#xff0c;11.16日的第八届人工智能领袖大会的AIC年度奖项评选环节备受关注&#xff0c;Gooxi从多家入围企业中脱颖而出&#xff0c…

Linux输入设备应用编程(键盘,按键)

目录 一 输入设备编程介绍 1.1 什么是输入设备呢&#xff1f; 1.2 什么是输入设备的应用编程&#xff1f; 1.3 input子系统 1.4 数据读取流程 1.5 应用程序如何解析数据 1.5.1 按键类事件&#xff1a; 1.5.2 相对位移事件 1.5.3 绝对位移事件 二 读取 struct input_e…

MyBatis整合Spring Boot扫描Mapper相关配置

MyBatis是一款 Java 平台的优秀数据库映射框架&#xff0c;支持 XML 定义或注解&#xff0c;免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。 针对 Spring 提供 Mapper 扫描注解&#xff1a; 集成 Spring Boot 时&#xff0c;可以通过 MapperScan 注解&#xff0…

【论文阅读】2736. 最大和查询-2023.11.17

题目&#xff1a; 2736. 最大和查询 给你两个长度为 n 、下标从 0 开始的整数数组 nums1 和 nums2 &#xff0c;另给你一个下标从 1 开始的二维数组 queries &#xff0c;其中 queries[i] [xi, yi] 。 对于第 i 个查询&#xff0c;在所有满足 nums1[j] > xi 且 nums2[j]…

大数据-之LibrA数据库系统告警处理(ALM-12050 网络写吞吐率超过阈值)

告警解释 系统每30秒周期性检测网络写吞吐率&#xff0c;并把实际吞吐率和阈值&#xff08;系统默认阈值80%&#xff09;进行比较&#xff0c;当检测到网络写吞吐率连续多次&#xff08;默认值为5&#xff09;超过阈值时产生该告警。 用户可通过“系统设置 > 阈值配置 >…

2023年09月 Scratch(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

Scratch等级考试(1~4级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 下列哪项内容是不可以修改的?( ) A:角色名称 B:造型名称 C:舞台名称 D:背景名称 答案:C 第2题 要给“古诗朗诵”作品录制配音,可以使用下列哪个按钮?( ) A: B: C:…

图像分类系列(二) VGGNet学习详细记录

经典神经网络论文超详细解读&#xff08;二&#xff09;——VGGNet学习笔记&#xff08;翻译&#xff0b;精读&#xff09; 前言 上一篇我们介绍了经典神经网络的开山力作——AlexNet&#xff1a;经典神经网络论文超详细解读&#xff08;一&#xff09;——AlexNet学习笔记&a…