基础概念

缩写编码标准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;但是我们应该认识到…