H.264分帧-1.码流结构学习

引言

 H.264的分帧在Mpeg2 ts or ps内是有规范定义的,也就是每一帧的开头都要有AUD单元,一个AUD单元包含一个H.264 access unit。因此除非按照H.264分帧策略去分帧,否则没有办法区分帧,因为NAL层最多只能将slice区分,而不能找到frame。

概述

 很多情况下,提供不了与H.264流媒体文件对应的帧长度文件,就无法解析,按帧发送流媒体文件,故展开学习,结合H.264标准通过对H.264码流分析,识别分帧并解析帧长度,以达到不需要帧长度文件即可发送视频文件的目的。

相关知识

 根据摘要介绍,我们可以知道,要进行帧边界的识别,除非在H.264视频文件中有AUD单元,否则是无法只通过NAL层对其分帧的。故需要深入到slice层,通过对slice层中的元素进行解析对比,获得分帧策略。因此,我们自上而下的对H.264码流结构进行学习介绍,直到收集完与分帧策略相关的元素,最后对问题进行解决。

1. H.264原始码流

 H.264原始码流(又称为裸流),是有一个接一个的 NALU组成的,而它的功能分为两层:视频编码层(VCL, Video Coding Layer)和网络提取层(NAL, Network Abstraction Layer)。
 VCL数据即编码处理的输出,它表示被压缩编码后的视频数据序列。在 VCL 数据传输或存储之前,这些编码的 VCL 数据,先被映射或封装进 NAL 单元(以下简称 NALU,Nal Unit) 中。每个 NALU 包括一个原始字节序列负荷(RBSP, Raw Byte Sequence Payload)、一组 对应于视频编码的 NALU 头部信息。RBSP 的基本结构是:在原始编码数据的后面填加了结尾比特。一个 bit“1”若干比特“0”,以便字节对齐。该结构见下图。
H.264原始码流

1.1 NALU

 NALU是由RBSP和一个NALU头部信息组成。上图的NAL头加上一个RBSP就组成了NALU。NALU是H.264中的一个重要角色,H.264中的结构都是以NALU为主的。那么NALU究竟与一帧图像有什么关联。
图像分片
 该图中可以看出一帧图像被分割成许多个slice数据(实际上至少是1个,也可以是多个),分割的过程实际上就是视频编码处理的过程。而在视频文件中,作为这些slice的载体,就是NALU了。当然,NALU内装载的并不一定是slice,这是充分不必要条件。NALU中还可能有sei(补充增强信息), sps(序列参数集), pps(图像参数集)等内容,这些其他内容的作用是规范序列化描述视频信息。
 一个原始的H.264 NALU 单元常由 [StartCode] [NALU Header] [NALU Payload] 三部分组成,其中 start_code_prefix用于标示这是一个NALU 单元的开始,必须是"0x 00 00 00 01" 或"0x 00 00 01"。
 access unit 访问单元:一组NAL单元,通常包含一幅基本编码图像,即通常所说的一帧。其在码流(字节流格式)中的表现如下图:
NALU在码流中的表现
 H.264码流就是由这样的一个个搭载着不同信息NALU组成。而一帧图像由一个或多个NALU组成。并且NALU在字节流中是通过"0x 00 00 00 01" 或"0x 00 00 01"分隔开来。
note:
 一个NALU包中的数据并不包含它的大小(长度)信息,因此不能简单的连接NALU包来建立一个流,因为你不知道一个包从哪里结束,另一个包从哪里开始。(PS:包传输时,一包对应一个NALU,不需要考虑分界问题,流传输则需要)
 Annex B格式(字节流格式)用开始码来解决这个问题,即给每个NALU加上前缀码:2个或者3个0x00,后面再加一个0x01, 如:0x000001或者0x00000001。
 4字节类型的开始码在在连续的数据传输中非常有用,因为用字节来对齐、分割流数据,比如:用连续的31个bit0后接一个bit1来分割流数据,是很容易的。因此码流中通常是以4字节形式存在。
 NAL规定一种通用的格式,既适合面向包传输,也适合流传送。实际上,包传输和流传输的方式是相同的,不同之处是传输前面增加了一个起始码前缀。

1.2 slice

 上节中看到大致看到slice是作为图像分割后的数据存在的。本节对slice进行详细介绍。
 Slice,也称片,亦称条带(下文提到这三者,都是指slice),是 H.264 中提出的新概念,是通过编码图片后切分通过高效的方式整合出来的概念,一帧视频图像可编码成一个或更多个slice。
 slice的主要作用是用作宏块(Macroblock)的载体。slice之所以被创造出来,主要目的是为限制误码的扩散和传输。 每个slice都应该是互相独立被传输的(存放在NALU中),某slice的预测(片(slice)内预测和片(slice)间预测)不能以其它slice中的宏块(Macroblock)为参考图像。
 Slice的具体结构,
slice结构
 上图结构中不难看出,slice是由一个slice头和一个slice数据组成的,而slice数据中又包含了若干宏块(一个或多个)。

1.3 宏块

 宏块是视频信息的主要承载者,因为它包含着每一个像素的亮度和色度信息。视频解码最主要的工作则是提供高效的方式从码流中获得宏块中的像素阵列。
 组成部分:一个宏块由一个16×16亮度像素和附加的一个8×8 Cb和一个 8×8 Cr 彩色像素块组成。每个图象中,若干宏块被排列成片的形式。宏块结构如下图:
宏块结构
 由于进行分帧所需要用到知识与宏块层及下层关联不大,故宏块层及宏块层以下暂不做深入学习。

H.264学习小结

 通过自上而下,从整个码流的组成到细小零件的学习。整个码流分层结构的表示可以用H.264文档中的一幅图表示。
码流分层结构图
 经过以上的概念学习,可以看到在H.264结构中,数据的存储都是由一个数据头和一个数据体组成的。头中包含了对与之对应的数据体的详细描述。
 回到最初,要解决的分帧和获取帧长度的的问题,发现对于一个NALU中只包含一个slice且该slice直接表示一帧图像的情况,可以直接通过判断NALU的分隔符起始码0x 00 00 00 01来分帧并根据字节流中的位置差来获得帧长度。而查阅相关资料并结合上述知识发现,实际的码流还存在着一帧图像由多个slice组成。因此只通过找出NALU的边界,是无法对该情况进行解析的。所以我们需要对NALU中的数据进行更为详细的解析。

发布了60 篇原创文章 · 获赞 18 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/BadAyase/article/details/103487096