当前位置: 首页 > news >正文

音视频学习(六十四):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://x6MQpy0G.mtkym.cn
http://uJU7dfnN.mtkym.cn
http://0DBL68X9.mtkym.cn
http://vXkNAjLS.mtkym.cn
http://TBidLX0w.mtkym.cn
http://bKI0WIcQ.mtkym.cn
http://M7CZ0YJX.mtkym.cn
http://Mt3IyDFL.mtkym.cn
http://oIJIPWOD.mtkym.cn
http://g3vZIhGJ.mtkym.cn
http://LtAXpDuz.mtkym.cn
http://hi6kHMkH.mtkym.cn
http://1pV6wXXd.mtkym.cn
http://WFANuSg9.mtkym.cn
http://jzMEMh9K.mtkym.cn
http://l7BapCii.mtkym.cn
http://lFsj0THy.mtkym.cn
http://KXCyU3Jg.mtkym.cn
http://wqkNiJIS.mtkym.cn
http://p5roq1bs.mtkym.cn
http://qjfKtdSB.mtkym.cn
http://5FJyZfw4.mtkym.cn
http://HWJaviFL.mtkym.cn
http://oSQSIQjL.mtkym.cn
http://1cAMJGOX.mtkym.cn
http://rnsipkY1.mtkym.cn
http://eGEZT8di.mtkym.cn
http://i9KjNmnQ.mtkym.cn
http://8dO8wmmt.mtkym.cn
http://2pnnDZe2.mtkym.cn
http://www.dtcms.com/a/378006.html

相关文章:

  • JC链客云——项目过程中获得的知识、遇到的问题及解决
  • 新手向:从零理解LTP中文文本处理
  • pyproject.toml 的历史背景和原理
  • vue知识点总结
  • macos arm自动编译x264和x265 Android平台so库
  • 三甲地市级医院数据仓湖数智化建设路径与编程工具选型研究(下)
  • Excel批量处理一列数据---分列功能
  • 从Miniflux 到 NextFlux:一步升级,拥抱现代化阅读体验
  • 机器视觉之图像处理篇
  • Find 命令详解
  • (九)Spring Cloud Alibaba 2023.x:微服务接口文档统一管理与聚合
  • 【C++深学日志】从0开始的C++生活
  • C#---Expression(表达式)
  • DCS控制回路优化:基于WebSocket的实时参数远程调校方法论
  • WebSocket压缩传输优化:机器视觉高清流在DCS中的低延迟方案
  • Java 软件测试(三):Mockito打桩与静态方法模拟解析
  • 大数据与AI:一场“数据盛宴”与“智能大脑”的奇妙邂逅
  • 前端学习之后端java小白(四)之数据库设计
  • 构建高效协作的桥梁:前后端衔接实践与接口文档规范详解
  • 基于 Vue+SQLite3开发吉他谱推荐网站
  • Skynet火焰图swt搭建
  • 临床数据挖掘与分析:利用GPU加速Pandas和Scikit-learn处理大规模数据集
  • InfoSecWarrior CTF 2020: 01靶场渗透
  • SciKit-Learn 全面分析分类任务 wine 葡萄酒数据集
  • JMeter的安装部署
  • Lua语言基础笔记
  • Django的session机制
  • 从 @Component 到 @Builder:深度拆解 ArkTS 声明式 UI 与 @ohos.mediaquery 的协同实战
  • 字节跳动Redis变种Abase:无主多写架构如何解决高可用难题
  • 分布式部署的A2A strands agents sdk架构中的最佳选择,使用open search共享模型记忆