AVI、MP4 和“原始”h264 流中的 h264。 NAL 单元格式不同(或 ffmpeg bug)

2023-11-24

TL;DR:我想从 AVI/MP4 文件读取原始 h264 流,甚至是损坏/不完整的。

几乎所有有关 h264 的文档都告诉我它由 NAL 数据包组成。好的。几乎所有地方都告诉我,数据包应该以这样的签名开头00 00 01 or 00 00 00 01。例如,https://stackoverflow.com/a/18638298/8167678, https://stackoverflow.com/a/17625537/8167678

H.264的格式是由NAL单元组成,每个单元开始 具有三个字节的起始前缀,值为 0x00、0x00、0x01 每个单位都有不同的类型,具体取决于第四个的值 这 3 个起始字节之后的字节。一个 NAL 单元不是一帧 视频中,每一帧由多个NAL单元组成。

Okay.

我下载了 random_youtube_video.mp4 并从中删除一帧:

ffmpeg -ss 10 -i random_youtube_video.mp4 -frames 1 -c copy pic.avi

And got: hexdump of AVI Red part - this is part of AVI container, other - actual data. As you can see, here I have 00 00 24 A9 instead of 00 00 00 01

这个AVI文件可以完美播放

I do same for mp4 container: hexdump of mp4

正如您所看到的,这里的字节完全相同。这个MP4文件可以完美播放

I try to strip out raw data: ffmpeg -i pic.avi -c copy pic.h264 Raw data

This file can't play in VLC or even ffmpeg, which produced this file, can't parse it: ffmpeg error

I downloaded mp4 stream analyzer and got: Analysis

MP4Box告诉我:

 Cannot find H264 start code
 Error importing pic.h264: BitStream Not Compliant

当什么都不起作用时,学习 h264 的内部结构是非常困难的。

所以,我有疑问:

  1. mp4里面的实际数据是什么?
  2. 我必须阅读什么来解码该数据(我的意思是不同的附件)
  3. 如何从这个“损坏的”原始流中读取流并获取解码图像(即使使用 ffmpeg)?

UPDATE:

ffmpeg 中似乎有错误:

当我进行双重转换时:

         ffmpeg -ss 10 -i random_youtube_video.mp4 -frames 1 -c copy pic.mp4
         ffmpeg pic.mp4 -c copy pic.h264

enter image description here

但是当我直接转换文件时:

ffmpeg -ss 10 -i random_youtube_video.mp4 -frames 1 -c copy pic.h264 with NALs

我有 NAL 签名和一个额外的 NAL 单元。其他字节相同(选定)。

这是错误?

UPDATE

不,这不是错误,您必须使用选项 -bsf h264_mp4toannexb 将流保存为“Annex B”格式(带前缀)


“我想从 AVI 文件中读取原始 h264 流,甚至是损坏/不完整的文件。”

“几乎所有地方都告诉我,数据包应该以如下签名开头:
00 00 01 or 00 00 00 01"

“......如你所见,我在这里00 00 24 A9代替00 00 00 01"

您的 H264 是 AVCC 格式,这意味着它使用数据sizes(而不是数据起始码)。只有附件 B 才会有您提到的签名作为起始代码。

您寻找帧,不是通过寻找起始代码,而是只是跳过帧大小以达到(请求的)帧的最终正确偏移量......

AVI 处理:

  • 读取大小(四)字节(32 位整数,小尾数法).

  • 提取接下来的字节,直至达到 size amount。

  • 这是您的 H.264 帧(AVCC 格式),解码字节以查看图像。

  • 要转换为附件 B,请尝试替换 H.264 的前 4 个字节帧字节 with 00 00 00 01.

考虑您显示的 AVI 字节(请参阅first图片) :

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00     ................
00 00 00 00 4C 49 53 54 BA 24 00 00 6D 6F 76 69     ....LISTº$..movi
30 30 64 63 AD 24 00 00 00 00 24 A9 65 88 84 27     00dc.$....$©eˆ„'
C7 11 FE B3 C7 83 08 00 08 2A 7B 6E 59 B5 71 E1     Ç.þ³Çƒ...*{nYµqá
E3 9C 0E 73 E7 10 50 00 18 E9 25 F7 AA 7D 9C 30     ãœ.sç.P..é%÷ª}œ0
E6 2F 0F 20 00 3A 64 AA CA 5E 4F CA FF AE 20 04     æ/. .:dªÊ^OÊÿ® .
07 81 40 00 48 00 0A 28 71 21 84 48 06 18 90 0C     [email protected]..(q!„H....
31 14 57 9E 7A CD 63 A0 E0 9B 96 69 C5 18 AE F2     1.WžzÍc à›–iÅ.®ò
E6 07 02 29 01 20 10 70 A1 0F 8C BC 73 F0 78 FA     æ..). .p¡.Œ¼sðxú
9E 1D E1 C2 BF 8C 62 CE CE AC 14 5A A4 E1 45 44     ž.á¿ŒbÎά.Z¤áED
38 38 85 DB 12 57 3E F6 E0 FB AE 03 04 21 62 8D     88…Û.W>öàû®..!b.
F6 F1 1E 37 1C A2 FF 75 1C F1 02 66 0C 92 07 06     öñ.7.¢ÿu.ñ.f.’..
15 7C 90 15 6F 7D FC BD 13 1E 2B 0C 14 3C 0C 00     .|..o}ü½..+..<..
B0 EA 6F 53 B4 98 D7 80 7A 68 3E 34 69 20 D2 FA     °êoS´˜×€zh>4i Òú
F0 91 FC 75 C6 00 01 18 C0 00 3B 9A C5 E2 7D BF     ð‘üuÆ...À.;šÅâ}¿

一些解释:

  • 忽略前导多个00 bytes.

  • 4C 49 53 54 D6 3C 00 00 6D 6F 76 69包括30 30 64 63= AVI“列表”标题。

  • AD 24 00 00== 小数9389是AVI自己的H264项目大小(必须读入小尾数法).

请注意,AVI 字节包括...
-的一个注释item总计size (AD 24 00 00...或反转为 Little Endian :00 00 24 AD)
-其次是item data (00 00 24 A9 65 88 84 27 ... etc ... C5 E2 7D BF).

This size包括 AVI 的 4 个字节”size" 条目 + 预期字节长度item自己的字节。可以简单地写为:

AVI_Item_Size = ( 4 + item_H264_Frame.length );

AVI 中的 H.264 视频帧字节 :

接下来是item数据,即 H.264视频帧。由于格式/字节布局的纯粹巧合,它也包含一个 4 字节条目data's size(由于您的 H264 是 AVCC 格式,如果它是附件 B,那么您将在此处看到起始代码字节而不是大小字节)。

与 AVI 字节不同,这些 H264size字节被写入大尾数法 format.

  • 00 00 24 A9= 该视频帧的字节大小(而不是起始代码:00 00 00 01).

  • 65 88 84 27 C7 11 FE B3 C7= H.264keyframe(始终以 X5 开头,其中X值基于其他设置)。

  • 请记住,在四个大小字节(甚至起始代码)之后,如果后面跟着...

    • byte X5= 关键帧 (IDR),示例字节65.
    • byte X1= P 或 B 帧,示例字节41.
    • byte X6= SEI(补充增强信息)。
    • byte X7= SPS(序列参数集)。
    • byte X8= PPS(图片参数集)。
    • bytes 00 00 00 X9= 访问单元分隔符。

如果您在 AVI 文件中搜索完全相同的字节,则可以找到 H.264。看third图片,这些是您的 H.264 字节(它们被剪切并粘贴到 AVI 容器中)。

有时,一个帧会被分割成不同的 NAL 单元。因此,如果您提取关键帧并且它仅显示 1/2 或 1/3 而不是完整图像,只需抓取下一个或两个 NAL 并重新尝试解码。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

AVI、MP4 和“原始”h264 流中的 h264。 NAL 单元格式不同(或 ffmpeg bug) 的相关文章

  • 如何拦截 .Net 中第三方库对非虚拟方法的调用?

    我认为我需要的是 net 人们称之为 透明动态代理 的东西 但到目前为止我所看到的所有实现 Castle DynamicProxy Spring NET AOP 等 都要求我至少执行以下操作之一 将拦截的方法声明为虚拟方法 包装类并创建包装
  • 将数据集导出到 EXCEL

    我使用以下代码将数据库表中的字段导出到 Excel 中 我想要做的是能够编写一条 SQL 语句从多个表中检索字段并将其导出到 Excel 中 这段代码只允许我导出一张表 另外 如何显示保存提示对话框 示例代码将不胜感激 非常感谢 prote
  • 如何查找boost运行时版本

    我正在编写一个使用 boost 的 C 库 在这个库中 我想包含有关用于编译我的库的二进制版本的 boost 版本的信息 我可以使用宏BOOST VERSION这很好 我还想确定哪个是 boost 的运行时版本 以便我可以与用于编译我的库的
  • 无法使用 Unity 函数在 Visual Studio Code 中获得完整的 Intellisense

    好吧 我知道这个问题已经被问过并回答过很多次了 但我花了大约 3 天的时间试图解决这个问题 但到目前为止我所做的一切都没有奏效 我基本上在 Visual Studio Code 中有部分智能感知 也就是说 它似乎只识别 Unity 类和变量
  • Reflection.Emit 中的短格式操作码错误

    我正在制作一种与以下非常相似的小语言hlsl但仅支持像素着色器 该语言使用reflection emit构建实现相同功能的 NET 程序集 我目前正在测试分支指令的实现if在我的一个单元测试中 一个大的if与内if elses 失败并显示以
  • 将 try_emplace 与 shared_ptr 一起使用

    所以我有一个std unordered map
  • 用 C++ 解密文件,该文件使用 openssl -aes-128-cbc 加密

    我正在尝试用 C 解密文件 该文件使用以下命令加密 openssl enc nosalt aes 128 cbc pass pass test in test txt out test enc txt p 控制台显示key 098F6BCD
  • 无法更新 .mdf 数据库,因为该数据库是只读的(Windows 应用程序)

    我使用 C 创建了一个数据库 Windows 应用程序 我的应用程序在 Windows XP 上成功运行 但在 Vista 或 Windows 7 系统上无法正确执行 我的应用程序显示类似以下内容的消息 无法更新 mdf 数据库 因为该数据
  • Code First - 实体框架 - 如何公开外键

    我有以下数据对象 public class Customer System Data Entity ModelConfiguration EntityTypeConfiguration
  • 使用 C 创建立体声正弦波

    我正在尝试用 C 创建立体声正弦 WAV 并且可能有不同的 可能是空白的 左声道和右声道 使用此函数为每个通道生成一个音调 int16 t create tone float frequency float amplitude float
  • 标准头文件中的 C 编译器错误 - 未定义的 C++ 定义

    我正在尝试编译 C 程序 但收到许多错误 这些错误是在标准 C 头文件 inttypes h stdio h stat h 等 中遇到的 错误的来源是以下未定义的常量 BEGIN DECLS END DECLS BEGIN NAMESPAC
  • 如何使用包含的转换的排名来比较两个标准转换序列

    include
  • gcc 中的“假设”子句

    gcc 最新版本 4 8 4 9 是否有类似于以下的 假设 子句 assume 内置icc支持吗 例如 assume n 8 0 从 gcc 4 8 2 开始 gcc 中没有 assume 的等效项 我不知道为什么 这会非常有用 马夫索建议
  • 在同一条线上铸造两次

    我在项目中看到了这段代码 b的类型是void void b int a int unsigned long b 这条线毫无意义吗 我的意思是 这与a int b在所有情况下 这可能会避免 64 位 Unix 系统上的编译器警告unsigne
  • #define, #ifdef #undef #endif

    我有以下代码 define PROC ADD void main void while 1 ifdef PROC ADD Do this code here then undefined it to run the code in the
  • 如何从标准输入读取一行,阻塞直到找到换行符?

    我试图从命令行的标准输入一次读取任意长度的一行 我不确定是否能够包含 GNU readline 并且更喜欢使用库函数 我读过的文档表明getline应该可以工作 但在我的实验中它不会阻塞 我的示例程序 include
  • 在 C# 中将 ulong 映射到 long ?

    我正在尝试将 ulong 映射到 long 反之亦然 将 uint 映射到 int 反之亦然 如下所示 为了将值保存在具有签名类型的 MS SQL 数据库中仅限整数和大整数 我这样做是因为我必须检查 在数据库中 一个数字 uint ulon
  • lambda 表达式是多线程的吗?

    lambda 表达式是多线程的吗 假设当你将数学公式编写为 lambda 方法时 当你将其传递给另一个方法时 它会是多线程的吗 不是100 清楚你问的是什么 您是否想问 lambda 是否自然地在不同的线程上运行 如果是这样 则它们只是 S
  • 组合框由于某种原因被链接

    我有以下代码来填充 3 个组合框 private void PopulateDDLs SqlConnection connection SqlCommand command SqlDataReader reader DataTable dt
  • C++20 范围太多 |运营商?

    我在这段代码中使用 g 10 2 有谁知道为什么我最后收到编译器错误std views reverse on results3 include

随机推荐