【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.pswp.cn/web/72319.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/72319.shtml
英文地址,請注明出處:http://en.pswp.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 服務器 …