【RTSP】客户端(五)H264 265处理逻辑

H264处理逻辑

整体逻辑分析

实现逻辑

  • 解析 RTP 包头:首先检查 RTP 头部的有效负载类型(payloadType)是否匹配
  • 处理扩展头:如果 RTP 包包含扩展头,跳过扩展头部分,获取有效负载
  • 处理分片数据:H264 分片数据通过 FU 指示符和 FU 头部来标识开始、中间和结束部分,分片数据会拼接并在结束时回调给外部
  • 单一封包数据:如果是单一封包(非分片),直接将数据传递给回调函数

代码实现

头文件

// NALU头部数据结构
struct H264NaluHeader {uint8_t type : 5;uint8_t nri : 2;uint8_t f : 1;
};
// 分片数据结构
struct H264FUIndicator {uint8_t type : 5;uint8_t nri : 2;uint8_t f : 1;};struct H264FUHeader {uint8_t type : 5;uint8_t r : 1;uint8_t e : 1; // 结束标志uint8_t s : 1; // 开始标志};class H264Demuxer : public RTPDemuxer {
public:void InputData(const uint8_t* data, size_t size);
private:uint8_t buffer_[4 * 1024 * 1024];  // 用于存储拼接后的数据bool find_start_ = false;  // 是否已经找到一个分片的起始部分size_t pos_buffer_ = 0;    // 当前缓冲区的数据位置
};

源文件

//  总结:H264 数据进行解复用,处理分片(如果有)并将视频数据传递给回调
void H264Demuxer::InputData(const uint8_t* data, size_t size) {//1. 解析RTP头部struct RtpHeader *header = (struct RtpHeader *)data;int payload_type = header->payloadType;if (payload_type != payload_) {return;}//2. 提取有效的负载数据const uint8_t* payload = data + sizeof(struct RtpHeader);size_t payload_len = size - sizeof(struct RtpHeader);if (header->extension) {const uint8_t *extension_data = payload;size_t extension_length = 4 * (extension_data[2] << 8 | extension_data[3]);size_t payload_offset = 4 + extension_length;payload = payload + payload_offset;payload_len = payload_len - payload_offset;}//3. 处理分片FU数据struct H264NaluHeader *h264_header = (struct H264NaluHeader *)payload;if(h264_header->type == 28){//FU指示器struct H264FUIndicator *fu_indicator = (struct H264FUIndicator *)payload;//FU头部struct H264FUHeader *fu_header = (struct H264FUHeader *)&payload[1];// 3.1 进一步处理分片数据,起始分片数据处理if (fu_header->s == 1) {  // start//缓冲区存储:0001+H264NaluHeaderfind_start_ = true;if (pos_buffer_ == 0) {struct H264NaluHeader header;header.f = fu_indicator->f;header.nri = fu_indicator->nri;header.type = fu_header->type;buffer_[0] = 0;buffer_[1] = 0;buffer_[2] = 0;buffer_[3] = 1;memcpy(buffer_ + 4, &header, sizeof(struct H264NaluHeader));pos_buffer_ += 4 + sizeof(struct H264NaluHeader);}memcpy(buffer_ + pos_buffer_, payload + 2, payload_len - 2);pos_buffer_ += payload_len - 2;}else if (fu_header->e == 1) {  // endif (find_start_ == false) {return;}memcpy(buffer_ + pos_buffer_, payload + 2, payload_len - 2);pos_buffer_ += payload_len - 2;// 拼接结束后交给视频处理器处理if (call_back_) {call_back_->OnVideoData(ntohl(header->timestamp),  buffer_, pos_buffer_);}find_start_ = false;pos_buffer_ = 0;}else {  // 中间分片if (!find_start_) {return;}memcpy(buffer_ + pos_buffer_, payload + 2, payload_len - 2);pos_buffer_ += payload_len - 2;}   }
}

H264处理逻辑

整体逻辑分析

  • 解析 RTP 包头:首先检查 RTP 包头,判断是否为我们关心的 H.265 视频数据。
  • 处理 RTP 扩展头:如果 RTP 包中包含扩展头,跳过扩展头,获取有效负载部分。
  • 处理 H.265 分片数据:H.265 视频数据可能被分成多个 RTP 包传输,使用 FU 头部标识分片的开始、中间和结束部分。H265Demuxer 将这些分片数据拼接成完整的视频帧。
  • 单一封包数据:如果数据不是分片,直接将完整的视频帧数据通过回调传递给外部

代码实现

头文件

// H265 NALU头部数据结构
struct H265NaluHeader {uint16_t layer_hi : 1;uint16_t type : 6; // NALU类型uint16_t f : 1;   //  标志位uint16_t tid : 3; //  类型标识符uint16_t layer_low : 5; 
};
// 分片数据结构
struct H265FUHeader {uint8_t type : 6; // NALU类型uint8_t e : 1;   // 结束标志uint8_t s : 1;   // 开始标志
};// H265解复用器
class H265Demuxer : public RTPDemuxer {
public:void InputData(const uint8_t* data, size_t size) override;
private:uint8_t buffer_[4 * 1024 * 1024];  // 用于存储拼接后的数据bool find_start_ = false;  // 是否已经找到一个分片的起始部分size_t pos_buffer_ = 0;    // 当前缓冲区的数据位置
};

源文件

//  总结:H265 数据进行解复用,处理分片(如果有)并将视频数据传递给回调
void H265Demuxer::InputData(const uint8_t* data, size_t size){//1. 解析RTP头部struct RtpHeader *header = (struct RtpHeader *)data;int payload_type = header->payloadType;if (payload_type != payload_) {return;}//2. 提取有效的负载数据const uint8_t* payload = data + sizeof(struct RtpHeader);size_t payload_len = size - sizeof(struct RtpHeader);if (header->extension) {const uint8_t *extension_data = payload;size_t extension_length = 4 * (extension_data[2] << 8 | extension_data[3]);size_t payload_offset = 4 + extension_length;payload = payload + payload_offset;payload_len = payload_len - payload_offset;}//3. 处理分片FU数据struct H265NaluHeader *h265_header = (struct H265NaluHeader *)payload;if(h265_header->type == 49){//FU指示器struct H265FUHeader *fu_header = (struct H265FUHeader *)&payload[2];if (fu_header->s ==1){// 分片开始处理find_start_ = true;if(pos_buffer_ == 0){buffer_[0] = 0;buffer_[1] = 0;buffer_[2] = 0;buffer_[3] = 1;memcpy(buffer_ + 4, &header, sizeof(struct H265NaluHeader));pos_buffer_ += 4 + sizeof(struct H265NaluHeader);}memcpy(buffer_ + pos_buffer_, payload + 3, payload_len - 3);pos_buffer_ += payload_len - 3;}else if(fu_header->e ==1){// 结束分片标志处理if(find_start_ == false){return;}memcpy(buffer_ + pos_buffer_, payload + 3, payload_len - 3);pos_buffer_ += payload_len - 3;if (call_back_) {call_back_->OnVideoData(ntohl(header->timestamp), buffer_, pos_buffer_);}find_start_ = false;pos_buffer_ = 0;}else{// 中间分片处理if (!find_start_) {return;}memcpy(buffer_ + pos_buffer_, payload + 3, payload_len - 3);pos_buffer_ += payload_len - 3;}}else{ // 单一封包buffer_[0] = 0;buffer_[1] = 0;buffer_[2] = 0;buffer_[3] = 1;memcpy(buffer_ + 4, payload, payload_len);if(call_back_){call_back_->OnVideoData(ntohl(header->timestamp),  buffer_, payload_len + 4);}}return;
}

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

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

相关文章

IDEA集成git,项目的克隆,远程仓库中文件的添加删除

目录 一、克隆项目 二、使用IDEA完成文件的上传和删除 1.配置git 2.上传 3.删除&#xff08;通过git bash&#xff09; 一、克隆项目 点击克隆&#xff0c;复制url &#xff0c;如下 打开你想要克隆到哪里&#xff0c;右击&#xff0c;选择 open Git Bash here 这一步之后…

神经网络:定义与核心原理

神经网络&#xff08;Artificial Neural Network, ANN&#xff09;是一种受生物神经系统启发的计算模型&#xff0c;旨在通过模拟神经元之间的连接与信息传递机制&#xff0c;实现复杂的数据处理和模式识别功能。其本质是由大量简单处理单元&#xff08;神经元&#xff09;构成…

将pdf或者word转换成base64格式

废话不多说直接上代码&#xff1a; function fileToBase64(file) {return new Promise((resolve, reject) > {const reader new FileReader();reader.readAsDataURL(file);reader.onload function (event) {const base64Data event.target.result.split(,)[1];resolve(b…

Spring @Bean注解使用场景二

bean:最近在写一篇让Successfactors顾问都能搞明白的sso的逻辑的文章&#xff0c;所以一致在研究IAS的saml2.0的协议&#xff0c;希望用代码去解释SP、idp的一些概念&#xff0c;让顾问了解SSO与saml的关系&#xff0c;在github找代码的时候发现一些代码的调用关系很难理解&…

ubuntu22.04 关于挂在设备为nfts文件格式无法创建软连接的问题

最近遇到情况&#xff0c;解压工程报错&#xff0c;无法创建软连接 但是盘内还有130G空间&#xff0c;明显不是空间问题&#xff0c;查找之后发现是移动硬盘的文件格式是NTFS&#xff0c;在ubuntu上不好兼容&#xff0c;于是报错。 开贴记录解决方案。 1.确定文件格式 使用命…

docker后台运行,便于后期用命令行进入它的终端

在 docker compose up --build -d 命令中&#xff0c;​**-d​&#xff08;或 --detach&#xff09;参数的作用是让容器以后台模式&#xff08;detached mode&#xff09;​**运行。以下是详细解释&#xff1a; ​**-d 参数的作用** ​后台运行容器&#xff1a; 默认情况下&a…

网页制作14-Javascipt时间特效の显示动态日期

<!doctype html> <html> <head> <meta charset"utf-8"> <title>动态日期</title> </head><script>var today new Date();//获取时间var ytoday.getFullYear();//截取年var mtoday.getMonth();//截取月份,返回0~11v…

【BP神经网络】实战

1.参考Python实战&#xff1a;BP神经网络_bp神经网络实战python-CSDN博客 2.实践 &#xff08;1&#xff09;运行环境 anocanda Powershell Prompt&#xff08;anocanda3&#xff09; &#xff08;2&#xff09;创建虚拟环境&#xff0c;解决安装包的版本问题 *打开终端&a…

深度学习多模态人脸情绪识别:从理论到实践

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。https://www.captainbed.cn/north 文章目录 1. 引言2. 技术框架与流程图3. 核心算法解析3.1 视觉特征提取&#xff08;CNN&#xff09;3.2…

ssh通过22端口无法连接服务器问题处理

一&#xff0c;安全组开放22端口 root无法连接服务器&#xff0c;22端口也开放了&#xff0c;可能是防火墙开启了拦截。 二&#xff0c;检测防火墙状态 查看防火墙状态 sudo firewall-cmd --state 关闭防火墙 sudo systemctl stop firewalld 开启防火墙 sudo systemctl sta…

element 的tab怎么动态根据参数值添加一个vue页面

在使用 Element UI 的 Tabs 组件时&#xff0c;动态添加 Vue 组件或页面可以通过操作 tabs 数组来实现。假设你要根据参数值来动态添加一个 Vue 页面&#xff08;这里假设是一个 Vue 组件&#xff09;&#xff0c;你可以按照以下步骤操作&#xff1a; 首先&#xff0c;确保你已…

Docker封装镜像、分发、部署实践:nginx

在实际生产工作中&#xff0c;通常是没法直接访问公网的&#xff0c;但是有经常需要使用Docker部署应用&#xff0c;本文将介绍使用Docker从拉取nginx、打包、分发到加载部署nginx的全流程&#xff01; 1 准备工作 1.1 安装docker 请参考&#xff1a;Docker入门指南&#xff…

LuaJIT 学习(5)—— string.buffer 库

文章目录 Using the String Buffer LibraryBuffer ObjectsBuffer Method Overview Buffer Creation and Managementlocal buf buffer.new([size [,options]]) local buf buffer.new([options])buf buf:reset()buf buf:free() Buffer Writersbuf buf:put([str|num|obj] [,……

vue3:request.js中请求方法,api封装请求,方法请求

方法一 request.js // 封装GET请求 export const get (url, params {}) > {return request.get(url, { params }); }; // 封装POST请求 export const post (url, data {}) > {return request.post(url, data); }; api封装 import { post } from /utils/request; …

Ollama+OpenWebUI本地部署大模型

OllamaOpenWebUI本地部署大模型 前言Ollama使用Ollama安装Ollama修改配置Ollama 拉取远程大模型Ollama 构建本地大模型Ollama 运行本地模型&#xff1a;命令行交互Api调用Web 端调用 总结 前言 Ollama是一个开源项目&#xff0c;用于在本地计算机上运行大型语言模型&#xff0…

【机器学习】基于t-SNE的MNIST数据集可视化探索

一、前言 在机器学习和数据科学领域&#xff0c;高维数据的可视化是一个极具挑战但又至关重要的问题。高维数据难以直观地理解和分析&#xff0c;而有效的可视化方法能够帮助我们发现数据中的潜在结构、模式和关系。本文以经典的MNIST手写数字数据集为例&#xff0c;探讨如何利…

【redis】发布订阅

Redis的发布订阅&#xff08;Pub/Sub&#xff09;是一种基于消息多播的通信机制&#xff0c;它允许消息的**发布者&#xff08;Publisher&#xff09;向特定频道发送消息&#xff0c;而订阅者&#xff08;Subscriber&#xff09;**通过订阅频道或模式来接收消息。 其核心特点如…

C语言零基础入门:嵌入式系统开发之旅

C语言零基础入门&#xff1a;嵌入式系统开发之旅 一、引言 嵌入式系统开发是当今科技领域中一个极具魅力和挑战性的方向。从智能家居设备到汽车电子系统&#xff0c;从智能穿戴设备到工业自动化控制&#xff0c;嵌入式系统无处不在。而C语言&#xff0c;作为嵌入式开发中最常…

K8S学习之基础二十三:k8s的持久化存储之nfs

K8S持久化存储之nfs ​ 在 Kubernetes (k8s) 中使用 NFS&#xff08;Network File System&#xff09;作为存储解决方案是一种常见的方式&#xff0c;特别是在需要共享存储的场景中。以下是关于如何在 Kubernetes 中使用 NFS 存储的详细说明&#xff1a; 1. 准备 NFS 服务器 …