优质做网站费用,有没有做生物科技相关的网站,医药电商网站建设,磁县信息港视频编码的码流结构是指视频经过编码之后得到的二进制数据是怎么组织的#xff0c;或者说#xff0c;就是编码后的码流我们怎么将一帧帧编码后的图像数据分离出来#xff0c;以及在二进制码流数据中#xff0c;哪一块数据是一帧图像#xff0c;哪一块数据是另外一帧图像。…视频编码的码流结构是指视频经过编码之后得到的二进制数据是怎么组织的或者说就是编码后的码流我们怎么将一帧帧编码后的图像数据分离出来以及在二进制码流数据中哪一块数据是一帧图像哪一块数据是另外一帧图像。 而我们在工程开发中需要对编码后的数据进行一些解析以便用于之后的打包。同时我们在打包时也需要判断当前一帧图像数据它的开头和结尾在哪。
相关概念
在讲之前有一些必须的前置知识和概念需要先了解一下。首先清楚帧类型是图像的基础其次GOP 是以其中的 IDR 帧作为分隔点的最后的 Slice 是我们深入帧内部以后的一个重要概念。
帧类型
如果接触过视频编码你可能听说过I帧P帧B帧PPS帧SPS帧这些概念。 上一篇讲过为了减少空间冗余和时间冗余视频编码使用了帧内预测和帧间预测技术这些都涉及到帧。所以了解帧的类型是很有必要的。 我们知道帧内预测不需要参考已编码帧对已编码帧是没有依赖的并可以自行完成编码和解码。而帧间预测是需要参考已编码帧的并对已编码帧具有依赖性。帧间预测需要参考已经编码好了的帧内编码帧或者帧间编码帧。并且帧间编码帧又可以分为只参考前面帧的前向编码帧和既可以参考前面帧又可以参考后面帧的双向编码帧。为了做区分在 H264 中我们就将图像分为以下不同类型的帧。
帧类型预测方式参考帧优缺点I帧帧内编码帧只能进行帧内预测无自身能独立完成编码解码。压缩率低P帧前向编码帧可以进行帧间预测和帧内预测参考前面已编码的I帧或P帧压缩率比I帧高。必须要参考帧才能正确编码B帧双向编码帧可以进行帧间预测和帧内预测参考前面和后面已编码的I帧或P帧压缩率最高高需要缓存帧延时高实时场景不适合
P帧序列示意图 B帧序列示意图 由于 P 帧和 B 帧需要参考其它帧。如果编码或者解码的过程中有一个参考帧出现错误的话那依赖它的 P 帧和 B 帧肯定也会出现错误而这些有问题的 P 帧B 帧虽然也可以用来作为参考帧但是一般用的比较少所以这里不讨论又会继续作为之后 P 帧或 B 帧的参考帧。因此错误会不断的传递。为了避免错误的不断传递就有了一种特殊的 I 帧叫 IDR 帧也叫立即刷新帧。
H264 编码标准中规定IDR 帧之后的帧不能再参考 IDR 帧之前的帧。这样如果某一帧编码错误之后的帧参考了这个错误帧则也会出错。此时编码一个 IDR 帧由于它不参考其它帧所以只要它自己编码是正确的就不会有问题。之前有错误的帧也不会再被用作参考帧这样就截断了编码错误的传递且之后的帧就可以正常编 / 解码了。当然有 IDR 这种特殊的 I 帧也就有普通的 I 帧。普通的 I 帧就是指当前帧只使用帧内预测编码但是后面的 P 帧和 B 帧还是可以参考普通 I 帧之前的帧。但是这里我要说明一下一般来说我们不太会使用这种普通 I 帧大多数情况下还是直接使用 IDR 帧尤其是在流媒体场景比如 RTC 场景。只是说如果你非要用这种普通 I 帧标准也是支持的。
GOP
在 H264 中还有一个 GOP 的概念也经常会遇到它是什么意思呢从一个 IDR 帧开始到下一个 IDR 帧的前一帧为止这里面包含的 IDR 帧、普通 I 帧、P 帧和 B 帧我们称为一个 GOP图像组这是 closed GOP还有一种 opened GOP比较少见这里不讨论。 我们可以看到 GOP 的大小是由 IDR 帧之间的间隔来确定的而这个间隔我们有一个重要的概念来表示叫做关键帧间隔。关键帧间隔越大两个 IDR 相隔就会越远GOP 也就越大关键帧间隔越小IDR 相隔也就越近GOP 就越小。 GOP 越大编码的 I 帧就会越少。相比而言P 帧、B 帧的压缩率更高因此整个视频的编码效率就会越高。但是 GOP 太大也会导致 IDR 帧距离太大点播场景时进行视频的 seek 操作就会不方便。并且在 RTC 和直播场景中可能会因为网络原因导致丢包而引起接收端的丢帧大的 GOP 最终可能导致参考帧丢失而出现解码错误从而引起长时间花屏和卡顿。这一块我们会在之后用单独的一节课来详细讲述。总之GOP 不是越大越好也不是越小越好需要根据实际的场景来选择。前面我们讲的是视频图像序列的层次结构那图像内的层次结构是怎样的呢
Slice
这就不得不提到另一个概念了Slice也叫做“片”。Slice 其实是为了并行编码设计的。什么意思呢就是说我们可以将一帧图像划分成几个 Slice并且 Slice 之间相互独立、互不依赖、独立编码。那么在机器性能比较高的情况下我们就可以多线程并行对多个 Slice 进行编码从而提升速度。但也因为一帧内的几个 Slice 是相互独立的所以如果帧内预测的话就不能跨 Slice 进行因此编码性能会差一些。而在 H264 中编码的基本单元是宏块所以一个 Slice 又包含整数个宏块。我们在前一节课中也讲了宏块 MB 大小是 16 x 16。 在做帧内和帧间预测的时候我们又可以将宏块继续划分成不同大小的子块用来给复杂区域做精细化编码。总结来说图像内的层次结构就是一帧图像可以划分成一个或多个 Slice而一个 Slice 包含多个宏块且一个宏块又可以划分成多个不同尺寸的子块。如下图所示
H264 的码流结构
码流格式
H264 码流有两种格式一种是 Annexb 格式一种是 MP4 格式。两种格式的区别是 Annexb 格式 Annexb 格式使用起始码来表示一个编码数据的开始。起始码本身不是图像编码的内容只是用来分隔用的。起始码有两种一种是 4 字节的“00 00 00 01”一种是 3 字节的“00 00 01”。 这里需要注意一下由于图像编码出来的数据中也有可能出现“00 00 00 01”和“00 00 01”的数据。那这种情况怎么办呢为了防止出现这种情况H264 会将图像编码数据中的下面的几种字节串做如下处理 1“00 00 00”修改为“00 00 03 00” 2“00 00 01”修改为“00 00 03 01” 3“00 00 02”修改为“00 00 03 02” 4“00 00 03”修改为“00 00 03 03”。 同样地在解码端我们在去掉起始码之后也需要将对应的字节串转换回来。 MP4 格式 MP4 格式没有起始码而是在图像编码数据的开始使用了 4 个字节作为长度标识用来表示编码数据的长度这样我们每次读取 4 个字节计算出编码数据长度然后取出编码数据再继续读取 4 个字节得到长度一直继续下去就可以取出所有的编码数据了。 这两种格式差别不大接下来我们主要使用 Annexb 格式来讲解 H264 码流中的 NALU。
NALU
前面我们讲了图像分成 I 帧、P 帧和 B 帧这三种类型的帧。其实除了图像数据视频编码的时候还有一些编码参数数据为了能够将一些通用的编码参数提取出来不在图像编码数据中重复H264 设计了两个重要的参数集一个是 SPS序列参数集一个是 PPS图像参数集。其中SPS 主要包含的是图像的宽、高、YUV 格式和位深等基本信息PPS 则主要包含熵编码类型、基础 QP 和最大参考帧数量等基本编码信息。如果没有 SPS、PPS 里面的基础信息之后的 I 帧、P 帧、B 帧就都没办法进行解码。因此 SPS 和 PPS 是至关重要的。 结合前面我们讲的内容我们现在可以知道H264 码流主要包含了 SPS、PPS、I 帧、P 帧和 B 帧。由于帧又可以划分成一个或多个 Slice。因此帧在码流中实际上是以 Slice 的形式呈现的。所以H264 的码流主要是由 SPS、PPS、I Slice、P Slice和B Slice 组成的。如下图所示 我们知道了 H264 码流主要由 SPS、PPS 和三种 Slice 组成那我们如何在码流中区分这几种数据呢为了解决这个问题H264 设计了 NALU网络抽象层单元。SPS 是一个 NALU、PPS 是一个 NALU、每一个 Slice 也是一个 NALU。每一个 NALU 又都是由一个 1 字节的 NALU Header 和若干字节的 NALU Data 组成的。而对于每一个 Slice NALU其 NALU Data 又是由 Slice Header 和 Slice Data 组成并且 Slice Data 又是由一个个 MB Data 组成。其结构如下 NALU Header具体解析见nalu 这里不进行展开了。
小结
这篇文章主要讨论了 H264 的编码层次结构和码流结构。在一个视频图像序列中我们将其划分成一个个 GOP。GOP 包含一个 IDR 帧到下一个 IDR 帧的前一帧中的所有帧。GOP 的大小选择需要根据实际应用场景来选择一般 RTC 和直播场景可以稍微大一些而点播场景一般小一些。 在 H264 中每一帧图像又可以分为 I 帧、P 帧和 B 帧而 I 帧又包含了普通 I 帧和 IDR 帧。帧可以划分为一个或者多个 Slice并且最后帧都是以 Slice 的方式在码流中呈现。同时 H264 码流中除了 Slice 数据之外还有 SPS 和 PPS 两个参数集分别用来存放基础图像信息和基础编码参数。SPS 和 PPS 非常重要如果丢失了将无法进行解码。每一个 Slice 和 SPS、PPS 都是通过 NALU 来封装的且 NALU 含有一个 1 字节的 NALU Header。我们可以通过 NALU Header 中的 NALU Type 来判断 NALU 的类型。同时每一个 NALU 的分隔有两种方式一种是 Annexb 格式通过使用起始码分隔一种是 MP4 格式通过一个 4 字节的长度来表示 NALU 的大小从而起到分隔的作用。