音視頻學習(六十四):avc1 hvc1和hev1

基礎概念

縮寫編碼標準FourCC說明
AVC/H.264Advanced Video Codingavc1最常用的 H.264 編碼標識符,兼容 MP4/MOV/FMP4 等容器。
HEVC/H.265High Efficiency Video Codinghvc1HEVC 視頻流在 MP4/FMP4 中常用標識符,要求存儲 NALU 的 VPS/SPS/PPS(類似 H.264 SPS/PPS)信息在 track header 內。
HEVC/H.265High Efficiency Video Codinghev1另一個 HEVC 標識符,與 hvc1 區別在于封裝方式:hev1 視頻幀中只保留原始 NALU,SPS/PPS/VPS 信息不一定放在 track header 內,而是隨幀存儲。

小結:FourCC 是用于 MP4/FMP4/QuickTime 等容器標識視頻編碼類型的 4 字節字符碼。

avc1(H.264)

  • 用途:用于 MP4/FMP4/MOV 文件中標識 H.264 視頻流。
  • NALU 類型
    • SPS (Sequence Parameter Set)
    • PPS (Picture Parameter Set)
    • IDR/I幀、P幀、B幀
  • 封裝特點
    • avcC box 中存儲 SPS/PPS 信息(解碼初始化參數)。
    • 視頻幀按長度前綴或 Annex-B 封裝。
  • 兼容性
    • 幾乎所有現代播放器和瀏覽器均支持。
    • 廣泛用于 Web 視頻(MP4/HLS/DASH)。

hvc1 與 hev1 (H.265)

特性hvc1hev1
SPS/PPS/VPS存儲在 track header(init segment)中可隨幀存儲,也可在 init segment
主要用途WebM/MP4/FMP4 等 MP4 封裝MP4/FMP4、部分硬件加速播放器
播放兼容性現代硬件/軟件 HEVC 解碼器軟件解碼器可能需要解析每幀 NALU
注釋hvc1 適合片頭集中存儲參數集hev1 更接近原始編碼流(流媒體或片段式播放)

注意

  • 對于 hvc1,播放器在播放前可直接讀取 init segment 中的 VPS/SPS/PPS 初始化解碼器。
  • 對于 hev1,播放器可能需要在每個片段或每幀中解析 NALU 以獲取參數集,適合動態流式場景。

示例

C++ + FFmpeg 將 H.264/H.265 裸流封裝成 FMP4(Fragmented MP4),并設置 FourCC 為 avc1hvc1

#include <iostream>
#include <fstream>
#include <vector>
#include <cstdint>
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil/mem.h>
#include <libavutil/error.h>
}// 從裸流中提取 SPS/PPS/VPS 并生成 extradata
std::vector<uint8_t> extract_extradata(const std::vector<uint8_t>& stream_data, bool is_h265) {std::vector<uint8_t> extradata;size_t pos = 0;while (pos + 4 < stream_data.size()) {// 查找起始碼 0x00000001 或 0x000001size_t start = stream_data.find("\x00\x00\x00\x01", pos);if (start == std::string::npos) start = stream_data.find("\x00\x00\x01", pos);if (start == std::string::npos) break;size_t next_start = stream_data.find("\x00\x00\x00\x01", start + 4);if (next_start == std::string::npos) next_start = stream_data.size();uint8_t nal_unit_type = stream_data[start + 4] & 0x1F; // H.264if (is_h265) {nal_unit_type = (stream_data[start + 4] >> 1) & 0x3F; // H.265}// H.264: SPS=7, PPS=8; H.265: VPS=32, SPS=33, PPS=34if ((!is_h265 && (nal_unit_type == 7 || nal_unit_type == 8)) ||(is_h265 && (nal_unit_type == 32 || nal_unit_type == 33 || nal_unit_type == 34))) {extradata.insert(extradata.end(), stream_data.begin() + start, stream_data.begin() + next_start);}pos = next_start;}return extradata;
}// 將裸流封裝成 fMP4
int mux_fmp4(const char* input_file, const char* output_file, bool is_h265) {av_register_all();avformat_network_init();int ret;AVFormatContext* ofmt_ctx = nullptr;AVStream* out_stream = nullptr;// 創建輸出上下文ret = avformat_alloc_output_context2(&ofmt_ctx, nullptr, "mp4", output_file);if (ret < 0 || !ofmt_ctx) {std::cerr << "Could not create output context\n";return -1;}// 打開輸入裸流文件std::ifstream infile(input_file, std::ios::binary | std::ios::ate);if (!infile.is_open()) {std::cerr << "Failed to open input file\n";return -1;}auto file_size = infile.tellg();infile.seekg(0);std::vector<uint8_t> stream_data(file_size);infile.read(reinterpret_cast<char*>(stream_data.data()), file_size);// 提取 extradatastd::vector<uint8_t> extradata = extract_extradata(stream_data, is_h265);// 創建視頻流out_stream = avformat_new_stream(ofmt_ctx, nullptr);if (!out_stream) {std::cerr << "Failed to create stream\n";return -1;}AVCodecParameters* codecpar = out_stream->codecpar;codecpar->codec_type = AVMEDIA_TYPE_VIDEO;codecpar->codec_id = is_h265 ? AV_CODEC_ID_HEVC : AV_CODEC_ID_H264;codecpar->width = 1920;codecpar->height = 1080;out_stream->time_base = AVRational{1, 25};if (!extradata.empty()) {codecpar->extradata_size = extradata.size();codecpar->extradata = (uint8_t*)av_malloc(extradata.size() + AV_INPUT_BUFFER_PADDING_SIZE);memcpy(codecpar->extradata, extradata.data(), extradata.size());}// 設置 FMP4 flagsav_opt_set(ofmt_ctx->priv_data, "movflags", "frag_keyframe+empty_moov+default_base_moof", 0);// 打開輸出文件if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {ret = avio_open(&ofmt_ctx->pb, output_file, AVIO_FLAG_WRITE);if (ret < 0) {std::cerr << "Could not open output file\n";return -1;}}// 寫文件頭ret = avformat_write_header(ofmt_ctx, nullptr);if (ret < 0) {std::cerr << "Error writing header\n";return -1;}// 分幀寫入size_t pos = 0;int64_t pts = 0;while (pos + 4 < stream_data.size()) {// 查找起始碼size_t start = stream_data.find("\x00\x00\x00\x01", pos);if (start == std::string::npos) start = stream_data.find("\x00\x00\x01", pos);if (start == std::string::npos) break;size_t next_start = stream_data.find("\x00\x00\x00\x01", start + 4);if (next_start == std::string::npos) next_start = stream_data.size();AVPacket pkt;av_init_packet(&pkt);pkt.data = stream_data.data() + start;pkt.size = next_start - start;pkt.stream_index = out_stream->index;pkt.pts = pts;pkt.dts = pts;pkt.duration = 1;pkt.flags |= AV_PKT_FLAG_KEY; // 假設每幀都是關鍵幀,可根據 nal_unit_type 判斷ret = av_interleaved_write_frame(ofmt_ctx, &pkt);if (ret < 0) {std::cerr << "Error muxing packet\n";break;}pos = next_start;pts++;}av_write_trailer(ofmt_ctx);if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {avio_close(ofmt_ctx->pb);}avformat_free_context(ofmt_ctx);std::cout << "FMP4 muxing done.\n";return 0;
}int main(int argc, char* argv[]) {if (argc < 4) {std::cerr << "Usage: " << argv[0] << " input.264|265 output.mp4 is_h265(0|1)\n";return -1;}const char* input = argv[1];const char* output = argv[2];bool is_h265 = std::atoi(argv[3]) != 0;mux_fmp4(input, output, is_h265);return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/922431.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/922431.shtml
英文地址,請注明出處:http://en.pswp.cn/news/922431.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【WIT】編程百問一

01 什么時postman&#xff1f; Postman 是一款專門用于幫助開發人員處理 API 的工具&#xff0c;它的作用主要有以下幾個方面&#xff1a; 方便調試 API&#xff1a;就像你打電話給別人要先撥對號碼一樣&#xff0c;開發人員要讓不同的軟件系統之間通過 API 進行通信&#xff…

RAG 從入門到放棄?丐版 demo 實戰筆記(go+python)

背景 我當前有一個業務系統&#xff0c;希望能添加一個機器人助手。直接使用大模型&#xff0c;由于缺少相關的業務數據&#xff0c;效果并不理想&#xff0c;了解一下 RAG。 什么是 RAG RAG(Retrieval Augmented Generation)&#xff0c;搜索引擎 大模型。 簡單來說就是從…

《IDEA 突然“三無”?三秒找回消失的綠色啟動鍵、主菜單和項目樹!》

目錄 一、左上角綠色啟動鍵憑空消失 1.1 解決辦法 二、頂部 File / Edit / View... 整條主菜單欄 罷工 2.1 解決辦法 三、左側 Project 工具窗口 集體失聯&#xff0c;只剩 External Libraries 孤零零 3.1 解決辦法 昨天下午擼代碼&#xff0c;不知道按到了哪兒&#xff…

軟件工程實踐二:Spring Boot 知識回顧

文章目錄一、創建項目&#xff08;Spring Boot 向導&#xff09;二、項目最小代碼示例三、運行與驗證四、標準目錄結構與說明五、Maven 依賴最小示例&#xff08;僅供參考&#xff09;六、常用配置&#xff08;application.yml 示例&#xff09;七、返回 JSON 與統一異常八、Va…

【系列文章】Linux中的并發與競爭[04]-信號量

【系列文章】Linux中的并發與競爭[04]-信號量 該文章為系列文章&#xff1a;Linux中的并發與競爭中的第4篇 該系列的導航頁連接&#xff1a; 【系列文章】Linux中的并發與競爭-導航頁 文章目錄【系列文章】Linux中的并發與競爭[04]-信號量一、信號量二、實驗程序的編寫2.1驅動…

Elasticsearch啟動失敗?5步修復權限問題

文章目錄&#x1f6a8; 為什么會出現這個問題&#xff1f;? 解決方案&#xff1a;修復數據目錄權限并確保配置生效步驟 1&#xff1a;確認數據目錄存在且權限正確步驟 2&#xff1a;確認 elasticsearch.yml 中的配置步驟 3&#xff1a;**刪除或清空 /usr/share/elasticsearch/…

Docker push 命令:鏡像發布與管理的藝術

Docker push 命令&#xff1a;鏡像發布與管理的藝術1. 命令概述2. 命令語法3. 核心參數解析4. 推送架構圖解5. 完整工作流程6. 實戰場景示例6.1 基礎推送操作6.2 企業級推送流程6.3 多架構鏡像推送7. 鏡像命名規范詳解8. 安全最佳實踐8.1 內容信任機制8.2 最小權限原則9. 性能優…

智能合約測試框架全解析

概述 智能合約測試庫是區塊鏈開發中至關重要的工具&#xff0c;用于確保智能合約的安全性、正確性和可靠性。以下是主流的智能合約測試庫及其詳細解析。 一、主流測試框架對比 測試框架開發語言主要特點適用場景Hardhat WaffleJavaScript/TypeScript強大的調試功能&#xf…

【大模型算法工程師面試題】大模型領域新興的主流庫有哪些?

文章目錄 大模型領域新興主流庫全解析:國產化適配+優劣對比+選型指南(附推薦指數) 引言 一、總覽:大模型工具鏈選型框架(含推薦指數) 二、分模塊詳解:優劣對比+推薦指數+選型建議 2.1:訓練框架(解決“千億模型怎么訓”) 2.2:推理優化(解決“模型跑起來慢”) 2.3:…

端口打開與服務可用

端口打開與服務可用“端口已打開但服務不可用” 并非矛盾&#xff0c;而是網絡訪問中常見的分層問題。要理解這一點&#xff0c;需要先明確 “端口打開” 和 “服務可用” 的本質區別&#xff1a;1. 什么是 “端口打開”&#xff1f;“端口打開” 通常指 操作系統的網絡層監聽該…

ByteDance_FrontEnd

約面了&#xff0c;放輕松&#xff0c;好好面 盲點 基礎知識 Function 和 Object 都是函數&#xff0c;而函數也是對象。 Object.prototype 是幾乎所有對象的原型鏈終點&#xff08;其 proto 是 null&#xff09;。 Function.prototype 是所有函數的原型&#xff08;包括 Obje…

go語言,彩色驗證碼生成,加減法驗證,

代碼結構相關代碼 captcha/internal/captcha/generator.go package captchaimport (_ "embed" // &#x1f448; 啟用 embed"image""image/color""image/draw""image/png""io""math/rand""golang.…

PuTTY軟件訪問ZYNQ板卡的Linux系統

PuTTY 是一款非常經典、輕量級、免費的 SSH、Telnet 和串行端口連接客戶端&#xff0c;主要運行于 Windows 平臺。它是在開源許可下開發的&#xff0c;因其小巧、簡單、可靠而成為系統管理員、網絡工程師和開發人員的必備工具。網上有非常多的下載資源。 我們使用PuTTY軟件對ZY…

做一個RBAC權限

在分布式應用場景下&#xff0c;我們可以利用網關對請求進行集中處理&#xff0c;實現了低耦合&#xff0c;高內聚的特性。 登陸權限驗證和鑒權的功能都可以在網關層面進行處理&#xff1a; 用戶登錄后簽署的jwt保存在header中&#xff0c;用戶信息則保存在redis中網關應該對不…

【算法】day1 雙指針

1、移動零&#xff08;同向分3區域&#xff09; 283. 移動零 - 力扣&#xff08;LeetCode&#xff09; 題目&#xff1a; 思路&#xff1a;注意原地操作。快排也是這個方法&#xff1a;左邊小于等于 tmp&#xff0c;右邊大于 tmp&#xff0c;最后 tmp 放到 dest。 代碼&#…

Linux 日志分析:用 ELK 搭建個人運維監控平臺

Linux 日志分析&#xff1a;用 ELK 搭建個人運維監控平臺 &#x1f31f; Hello&#xff0c;我是摘星&#xff01; &#x1f308; 在彩虹般絢爛的技術棧中&#xff0c;我是那個永不停歇的色彩收集者。 &#x1f98b; 每一個優化都是我培育的花朵&#xff0c;每一個特性都是我放飛…

Linux網絡:socket編程UDP

文章目錄前言一&#xff0c;socket二&#xff0c;服務端socket3-1 創建socket3-2 綁定地址和端口3-3 接收數據3-4 回復數據3-5關閉socket3-6 完整代碼三&#xff0c;客戶端socket3-1 為什么客戶端通常不需要手動定義 IP 和端口前言 學習 socket 編程的意義在于&#xff1a;它讓…

【從零到公網】本地電腦部署服務并實現公網訪問(IPv4/IPv6/DDNS 全攻略)

從零到公網&#xff1a;本地電腦部署服務并實現公網訪問&#xff08;IPv4/IPv6/DDNS 全攻略&#xff09; 適用場景&#xff1a;本地 API 服務、大模型推理服務、NAS、遠程桌面等需要公網訪問的場景 關鍵詞&#xff1a;公網 IP、端口映射、內網穿透、IPv6、Cloudflare DDNS 一、…

模塊二 落地微服務

11 | 服務發布和引用的實踐 服務發布和引用常見的三種方式&#xff1a;Restful API、XML配置以及IDL文件。今天我將以XML配置方式為例&#xff0c;給你講解服務發布和引用的具體實踐以及可能會遇到的問題。 XML配置方式的服務發布和引用流程 1. 服務提供者定義接口 服務提供者發…

C++程序員速通C#:從Hello World到數據類型

C程序員光速入門C#&#xff08;一&#xff09;&#xff1a;總覽、數據類型、運算符 一.Hello world&#xff01; 隨著.NET的深入人心,作為一個程序員&#xff0c;當然不能在新技術面前停而止步&#xff0c;面對著c在.net中的失敗,雖然有一絲遺憾&#xff0c;但是我們應該認識到…