FFmpeg解封装与解码

656 阅读11分钟

1. 音视频解封装流程分析

音视频解封装是指将音频流和视频流从多媒体文件(如MP4、MKV、FLV等)中提取出来的过程。在FFmpeg中,我们通过以下步骤来完成解封装:

  1. 初始化:首先我们需要使用avformat_open_input()函数打开媒体文件。这个函数会根据输入的文件名和文件格式,打开相应的文件并为其分配一个AVFormatContext结构。
  2. 获取流信息:使用avformat_find_stream_info()函数获取媒体文件中的流信息。这个函数会填充AVFormatContext结构中的各个字段,如流的数量、每个流的类型(音频流、视频流或字幕流)等。
  3. 查找解码器:每个流都与一个解码器相关联,我们需要找到并打开这个解码器。我们可以通过AVCodecParameters结构中的codec_id字段来获取解码器的ID,然后使用avcodec_find_decoder()函数查找解码器。找到解码器后,我们需要使用avcodec_open2()函数打开解码器。
  4. 读取和解码数据包:使用av_read_frame()函数从AVFormatContext中读取数据包。这个函数会返回一个AVPacket结构,该结构包含了数据包的所有信息,如数据包所在的流、数据包的大小、数据包的时间戳等。然后我们需要使用avcodec_send_packet()avcodec_receive_frame()函数将数据包发送到解码器并获取解码后的帧。
  5. 关闭和清理:解封装和解码完成后,我们需要关闭解码器和输入文件,并释放所有分配的内存。我们可以使用avcodec_close()函数关闭解码器,使用avformat_close_input()函数关闭输入文件。

2. ACC ADTS分析

AAC ADTS(Audio Data Transport Stream)是一种用于在无损或有损压缩音频数据中,传输 AAC(Advanced Audio Coding)编码音频数据的格式。下面是一个 ADTS 数据帧的头格式:

ADTS Fixed header (28 bits):
  - Syncword (12 bits): always 0xFFF
  - MPEG Version (1 bit): 0 for MPEG-4, 1 for MPEG-2
  - Layer (2 bits): always 0
  - Protection absent (1 bit)
  - Profile (2 bits)
  - Sampling frequency index (4 bits)
  - Private bit (1 bit)
  - Channel configuration (3 bits)
  - Originality (1 bit)
  - Home (1 bit)
  - Copyright identifier bit (1 bit)
  - Copyright identifier start (1 bit)
  
ADTS Variable header (28 bits):
  - Frame length (13 bits)
  - Buffer fullness (11 bits)
  - Number of AAC frames (RDBs) in ADTS frame (2 bits)

ADTS Error check (16 bits, optional): CRC

以下是各个字段的详细解释:

  • Syncword: 0xFFF,固定的同步字,标志着 ADTS 帧的开始。
  • MPEG Version: 用于标识 AAC 的版本,0 代表 MPEG-4,1 代表 MPEG-2。
  • Layer: 在 ADTS 中,这个字段总是为 0。
  • Protection absent: 如果设置为 1,则表示没有 CRC 校验,否则有 CRC 校验。
  • Profile: 标志着 AAC 的编码级别。
  • Sampling frequency index: 对应到采样率,例如 44.1 KHz、48 KHz 等。
  • Private bit: 私有位,一般总是为 0。
  • Channel configuration: 声道配置,例如立体声、5.1 声道等。
  • Originality, Home, Copyright identifier bit, Copyright identifier start: 这些字段在大多数情况下都被设置为 0。
  • Frame length: 该字段包含整个 ADTS 帧的长度,包括头部和数据。
  • Buffer fullness: 编码器的缓冲区充满程度。
  • Number of AAC frames (RDBs) in ADTS frame: 表示每个 ADTS 帧中包含的 AAC 帧数量。

下面是一个例子:为一个 AAC 数据帧创建 ADTS 头部。这个例子使用 MPEG-4、采样率为 44.1 KHz、2 声道的配置,展示如何在 C 语言中生成一个 ADTS 头部:

#include <stdio.h>
#include <stdint.h>

// 创建 ADTS 头
void createAdtsHeader(uint8_t* header, int dataLength) {
    // 变量定义
    int sampling_frequency_index = 4;  // 44.1KHz
    int channel_config = 2;  // 声道

    // ADTS 固定头部
    header[0] = 0xFF;  // 同步字: 0xFFF, 所有位都必须是 1
    header[1] = 0xF1;  // MPEG-4, 层: 0, 无 CRC
    header[2] = ((1<<6) + (sampling_frequency_index<<2) + (channel_config>>2));
    header[3] = ((channel_config&3)<<6) + ((dataLength+7)>>11);
    header[4] = ((dataLength+7) & 0x7FF) >> 3;
    header[5] = (((dataLength+7) & 7) << 5) + 0x1F;
    header[6] = 0xFC;
}

// 主函数
int main() {
    uint8_t adts_header[7];
    int dataLength = 1024;  // 假设一个 AAC 帧的长度是 1024 字节
    createAdtsHeader(adts_header, dataLength);
    for (int i = 0; i < 7; i++) {
        printf("%02x ", adts_header[i]);
    }
    printf("\n");
    return 0;
}

在这段代码中,我们首先定义了我们要使用的音频配置:MPEG-4、44.1 KHz 的采样率和立体声声道。然后我们定义了一个函数 createAdtsHeader,它接受一个字节数组和数据长度作为输入,并填充字节数组以创建 ADTS 头部。ADTS 头部总是 7 个字节。

在主函数中,我们创建一个字节数组,指定一个假设的 AAC 帧长度(在实际应用中,这应该是封装的 AAC 数据帧的真实长度),然后调用 createAdtsHeader 函数。最后打印出 ADTS 头部的内容,每个字节以十六进制格式显示。

运行这段代码会得到一个 7 字节的 ADTS 头部,它可以直接前置到 AAC 数据帧前,形成一个完整的 ADTS 帧。

3. H264 NALU分析

H.264,也被称为AVC(Advanced Video Coding,高级视频编码),是一种视频压缩标准,其目标是提供良好的视频质量,同时降低数据率(即文件大小)。 H.264编码原理:

  1. 预测编码:H.264使用两种预测技术,即空间预测和时间预测。空间预测用于I帧(内部帧),而时间预测用于P帧(预测帧)和B帧(双向预测帧)。
  2. 变换和量化:预测误差通过离散余弦变换(DCT)变换为频域,然后通过量化步骤简化。
  3. 熵编码:H.264编码器使用CAVLC(上下文自适应变长编码)或CABAC(上下文自适应二进制算术编码)进行熵编码。这两种方法都能进一步压缩数据,不过CABAC更复杂,但能提供更好的压缩性能。

ipb帧.PNG

H.264编码结构解析:

  1. NAL单元:H.264的编码结构被称为网络抽象层(Network Abstraction Layer,简称NAL)。NAL单元(NALU)是H.264编码的最小数据单元。每个NALU都有一个头部和有效载荷。头部包含NALU的类型,有效载荷包含编码的视频数据。

  2. 编码类型:H.264编码中有多种不同类型的帧,包括I帧、P帧和B帧。I帧是完全自我编码的帧。P帧是基于之前的I帧或P帧预测的帧。B帧则是基于之前和之后的帧进行预测的帧。

  3. SPS和PPS:序列参数集(Sequence Parameter Sets,简称SPS)和图像参数集(Picture Parameter Sets,简称PPS)是H.264视频的重要组成部分。SPS包含视频序列的参数,而PPS包含特定图像的参数。视频解码器需要这两种参数集才能正确解码视频。

H.264 的数据编码结构被称为网络抽象层(Network Abstraction Layer,简称 NAL)。NAL 单元(NALU)是 H.264 编码的最小数据单元。NALU 主要由两部分组成:NALU 头部和载荷(Payload)。

  1. NALU 头部:头部包含一个字节,主要包括两个字段:forbidden_zero_bit、NAL reference idc和 NAL unit type。

    • forbidden_zero_bit:始终为 0。如果为 1,表示此 NALU 存在错误,需要立即丢弃。
    • NAL reference idc:表示该 NALU 的重要性,例如是否关键帧等信息。
    • NAL unit type:NALU 类型,表示 NALU 是序列参数集、图像参数集、切片等类型。
  2. 载荷(Payload) :Payload 通常包含原始的 H.264 视频数据。

H.264 视频流就是由一个个 NALU 串接起来的。在 H.264 的实际应用中,为了能够区分两个 NALU 之间的边界,会在每个 NALU 前面加上一个开始码(Start Code),它通常是 0x000001 或者 0x00000001。

对于 NAL unit type,常见的类型包括:

  • 类型为 5:IDR(即时刷新)图像的 NAL 单元。在这个 NALU 之后,所有图像都独立于以前的图像。通常情况下,编码器会周期性地插入 IDR 图像。IDR 图像之间的图像序列称为一个“GOP”。
  • 类型为 7:序列参数集(SPS)的 NAL 单元。它包含关于视频序列的一些参数。
  • 类型为 8:图像参数集(PPS)的 NAL 单元。它包含关于图像的一些参数。

4. FLV解封装格式

FLV(Flash Video)是Adobe开发的一种流媒体格式,主要用于网络视频播放。FLV文件主要包括头部信息(Header)、FLV标签(FLV Tags,包括音频、视频以及脚本标签)以及其他数据。

1. FLV头部信息(Header)

FLV文件的头部包括了9个或更多字节,分为3个部分:SignatureVersionFlags,以及Header Size

  • Signature:占3个字节,一般为"FLV",表示文件类型。
  • Version:占1个字节,表示FLV的版本,目前为1。
  • Flags:占1个字节,表示文件体中存在的类型,例如音频或视频。
  • Header Size:占4个字节,表示头部的长度,一般为9。

2. FLV标签(FLV Tags)

每个FLV标签都对应一块数据(音频、视频或者脚本)。标签的开始总是由Previous Tag Size字段标记的。

  • Previous Tag Size:占4个字节,表示前一个Tag的长度。
  • Tag Type:占1个字节,表示Tag的类型,音频(8)、视频(9)或脚本(18)。
  • Data Size:占3个字节,表示数据区的大小。
  • Timestamp:占3个字节,表示该Tag在流中的时间基准,单位是毫秒。
  • Timestamp Extended:占1个字节,扩展时间戳,当24位时间戳不够用时使用。
  • StreamID:占3个字节,总是0。

之后就是Tag Data,长度由Data Size字段给出,存储具体的音频数据、视频数据或者元数据。

3. 音频数据和视频数据

音频和视频数据位于FLV标签中,音频或视频数据的格式取决于其编码方式。例如,音频数据可以被编码为AAC,MP3等,视频数据可以被编码为H.264,VP6等。

4. 元数据(Script Data)

元数据主要包含了与整个FLV文件有关的信息,例如duration(时长)、width、height(视频的宽和高)、videodatarate(视频数据率)等。

元数据是以脚本标签的形式存在,可以在文件的任何位置,但通常在文件开始部分,方便快速获取视频的基本信息。

具体的FLV标签数据的解析,需要根据FLV规范,和具体的音视频编码格式去解析。

5. MP4封装格式剖析

MP4(MPEG-4 Part 14)是一种常见的多媒体容器格式,用于封装音频、视频、字幕和元数据等多种媒体数据。下面是对MP4封装格式的详细剖析,包括其各个组成部分和它们之间的关系:

  1. 头部(Header)

    • MP4文件以一个头部开始,包含了一些关键信息,如文件类型和版本信息。
    • 头部通常有12个字节,包括一个固定的8字节签名("ftyp")和一个4字节的版本信息。
  2. 元数据(Metadata)

    • MP4格式支持多种元数据格式,如ID3标签、iTunes元数据、Exif数据等。
    • 这些元数据可以包含有关媒体文件的信息,如标题、艺术家、发行日期等。
  3. 音频轨道(Audio Track)

    • MP4可以容纳一个或多个音频轨道,每个轨道都包括音频编解码器类型、采样率、通道数和音频数据。
    • 音频数据通常以AAC(Advanced Audio Coding)或MP3等格式进行编码。
  4. 视频轨道(Video Track)

    • MP4可以容纳一个或多个视频轨道,每个轨道都包括视频编解码器类型、帧率、分辨率和视频数据。
    • 视频数据通常以H.264、H.265(HEVC)或VP9等编码格式进行编码。
  5. 时间轴(Timecode)

    • MP4文件包括一个时间轴,用于记录每个媒体样本(音频或视频帧)的时间戳信息,以确保正确的播放顺序和同步。
  6. 同步轨道(Synchronization Track)

    • 同步轨道用于指示媒体文件中音频和视频轨道之间的同步关系,以确保它们在播放时能够正确地配合。
  7. 字幕和章节轨道(Subtitles and Chapters Track)

    • MP4还支持包含字幕和章节信息的轨道,这些信息可以用于添加字幕、章节导航和其他交互式功能。
  8. 样本表(Sample Table)

    • 样本表是MP4文件中最重要的部分之一,它记录了媒体文件中每个样本的位置、大小和时间戳信息。
    • 样本表包括时间戳表(Time-to-Sample)、同步采样表(Sync Sample)、块表(Chunk Offset)、样本描述表(Sample Description)等。
  9. 媒体数据(Media Data)

    • 媒体数据部分包含了实际的音频和视频样本数据,这些数据通常是压缩过的。
    • 样本表中的信息帮助解析器定位和解码媒体数据。
  10. 索引表(Index Table)

    • 索引表用于加速访问媒体数据,它包含了样本的索引信息,以便快速跳转到特定的媒体位置。

MP4文件的结构允许将多种类型的媒体数据有序封装在一起,以便于播放和传输。不同类型的轨道和元数据一起构成了完整的媒体体验。解码器和播放器使用MP4文件中的头部信息、样本表和索引表来解析和播放媒体文件,确保音频和视频的同步和正确顺序。这种封装格式的灵活性和通用性使得MP4成为流行的多媒体容器格式之一。