音视频解封装流程深度解析

在这里插入图片描述

😎 作者介绍:欢迎来到我的主页👈,我是程序员行者孙,一个热爱分享技术的制能工人计算机本硕,人工制能研究生。公众号:AI Sun(领取大厂面经等资料),欢迎加我的微信交流:sssun902
🎈 本文专栏:本文收录于《音视频》系列专栏,相信一份耕耘一份收获,我会分享音视频相关学习内容,不说废话,祝大家都offer拿到手软
🤓 欢迎大家关注其他专栏,我将分享Web前后端开发、人工智能、机器学习、深度学习从0到1系列文章。
🖥随时欢迎您跟我沟通,一起交流,一起成长、进步!

在这里插入图片描述

1. 解封装概述

解封装是音视频处理中的一个关键步骤,它涉及到将封装好的音视频文件按照特定的规则拆分成音频流和视频流,以便于进一步的处理和播放。

1.1 解封装定义

解封装,又称为“Demuxing”,是指将一个封装好的音视频文件(如MP4、FLV等)中的音频和视频数据流分离出来的过程。这个过程是封装的逆操作,它允许我们访问和操作原始的音视频数据,而不受到封装格式的限制。

1.2 音视频封装格式简介

音视频封装格式是指将音频和视频数据按照一定的规则组织存储到文件中的格式。以下是一些常见的音视频封装格式:

  • MP4 (MPEG-4 Part 14): 一种广泛使用的封装格式,支持H.264和AAC编解码器,常用于互联网视频流和移动设备。
  • FLV (Flash Video): 由Adobe Systems开发,常用于在线视频平台,支持H.264视频和AAC音频。
  • AVI (Audio Video Interleave): 微软开发的早期视频格式,支持多种编解码器,但文件较大,不支持现代特性如字幕。
  • MKV (Matroska Video): 开源的自由容器格式,支持多种编解码器和元数据,适合高清视频存储。

每种封装格式都有其特定的结构和规则,解封装过程需要根据这些规则来正确地分离音视频流。

2. 解封装技术流程

在这里插入图片描述

2.1 打开媒体文件

解封装的第一步是打开媒体文件,这通常涉及到使用特定的库或工具来读取文件头信息。例如,使用FFmpeg库中的avformat_open_input函数,可以打开一个媒体文件并获取其封装格式的上下文。这个过程是至关重要的,因为它为后续的读取和解析设置了基础。

AVFormatContext* format_context = NULL;
int ret = avformat_open_input(&format_context, file_path, NULL, NULL);
if (ret < 0) {
    // 错误处理:无法打开文件
}

2.2 读取媒体信息

一旦媒体文件被成功打开,下一步是读取媒体信息。这包括了解媒体文件中的流数量、每个流的类型(视频、音频或字幕)以及它们的编码格式。FFmpeg库提供了avformat_find_stream_info函数来探测媒体文件中的流信息。

ret = avformat_find_stream_info(format_context, NULL);
if (ret < 0) {
    // 错误处理:无法找到流信息
}

此外,可以使用av_dump_format函数来打印媒体文件的详细信息,这对于调试和分析媒体文件结构非常有用。

2.3 解析媒体流

解析媒体流是解封装过程中的核心步骤,它涉及到从封装格式中提取原始的音视频数据。这通常意味着需要对每个流进行解码,将压缩的数据转换为可播放的格式。

对于视频流,可以使用av_read_frame函数来读取压缩的视频帧,然后使用相应的解码器(如avcodec_decode_video2)进行解码。对于音频流,过程类似,但使用不同的解码函数(如avcodec_decode_audio4)。

AVPacket packet;
AVFrame *frame = av_frame_alloc();
while (av_read_frame(format_context, &packet) >= 0) {
    // 根据packet的stream_index确定是视频还是音频流
    if (packet.stream_index == video_stream_idx) {
        // 解码视频帧
        ret = avcodec_decode_video2(video_codec_context, frame, &got_picture, &packet);
        if (ret < 0) {
            // 错误处理:解码失败
        }
        if (got_picture) {
            // 处理解码后的视频帧
        }
    } else if (packet.stream_index == audio_stream_idx) {
        // 解码音频帧
        ret = avcodec_decode_audio4(audio_codec_context, frame, &got_frame, &packet);
        if (ret < 0) {
            // 错误处理:解码失败
        }
        if (got_frame) {
            // 处理解码后的音频帧
        }
    }
    av_packet_unref(&packet);
}

av_frame_free(&frame);

在解析媒体流的过程中,重要的是要注意音视频同步问题,确保解码后的数据能够按照正确的时间顺序进行播放。这通常涉及到使用时间戳(PTS)和解码时间戳(DTS)来同步解码后的数据流。

3. 核心组件与数据结构

3.1 AVFormatContext

AVFormatContext 是 FFmpeg 中用于存储媒体文件格式信息的核心结构体。它包含了媒体文件的封装格式、流信息、时长、码率等关键信息。

  • 封装格式信息iformat 指向 AVInputFormat 结构体,表示输入文件的封装格式。
  • 媒体流数量nb_streams 表示媒体文件中流的数量。
  • 媒体流数组streams 是一个指向 AVStream 结构体的数组,每个元素代表一个媒体流。
  • 文件时长与码率durationbit_rate 分别表示媒体文件的时长和总码率。
  • 关键函数avformat_open_input() 用于打开媒体文件并初始化 AVFormatContextavformat_close_input() 用于释放 AVFormatContext 及相关资源。

3.2 AVStream

AVStream 结构体用于表示单个媒体流(如视频流或音频流)的信息。

  • 流索引index 唯一标识一个流。
  • 编解码器上下文codecpar 指向 AVCodecParameters 结构体,包含编码器的参数。
  • 时间基准time_base 定义了时间戳的基准,用于将时间戳转换为实际时间。
  • 流时长duration 表示该流的时长。
  • 帧率avg_frame_rate 表示视频流的平均帧率。

3.3 AVPacket 和 AVFrame

AVPacketAVFrame 是 FFmpeg 中处理音视频数据的两个关键结构体。
在这里插入图片描述

  • AVPacket:表示压缩后的音视频数据包。

    • 数据指针data 指向压缩数据。
    • 数据大小size 表示 data 的大小。
    • 显示时间戳pts 表示数据包的显示时间戳。
    • 解码时间戳dts 表示数据包的解码时间戳。
  • AVFrame:表示解码后的原始音视频数据帧。

    • 数据指针数组data 数组指向解码后的视频或音频数据。
    • 行大小数组linesize 数组表示每行数据的大小。
    • 帧宽度和高度widthheight 表示视频帧的尺寸。
    • 帧类型pict_type 表示帧的类型(如 I 帧、P 帧等)。
    • 关键帧标识key_frame 表示是否为关键帧。
    • 时间戳pts 表示帧的显示时间戳。

这些结构体和组件共同协作,完成音视频数据的解封装、解码、处理和播放等流程。通过 API 如 av_read_frame() 读取 AVPacket,然后使用相应的解码器填充 AVFrame 数据,最终实现音视频的流畅播放。

4. 解封装关键操作

4.1 读取帧数据

解封装过程中,读取帧数据是核心步骤之一。通过av_read_frame函数,我们可以从封装格式的文件中提取出音频或视频的压缩数据包(Packet)。此函数返回一个AVPacket结构体,它包含了帧的编码数据和相关的元信息,如PTS(Presentation Time Stamp)、DTS(Decoding Time Stamp)等。

  • 帧数据读取:av_read_frame函数从解封装上下文AVFormatContext中读取一个数据包。
  • 数据包结构:每个AVPacket包含了stream_index,用于标识该包属于哪个音视频流。

示例代码:

AVFormatContext *fmt_ctx;
AVPacket packet;
avformat_open_input(&fmt_ctx, file_path, NULL, NULL);
avformat_find_stream_info(fmt_ctx, NULL);
while (av_read_frame(fmt_ctx, &packet) >= 0) {
    // 处理packet
    av_packet_unref(&packet);
}
avformat_close_input(&fmt_ctx);

4.2 音视频同步

音视频同步是确保在播放过程中音频和视频能够协调一致地展现给用户。在解封装阶段,我们通过分析PTS和DTS来实现音视频同步。

  • PTS(Presentation Time Stamp):表示帧应该被显示的时间。
  • DTS(Decoding Time Stamp):表示帧应该被解码的时间。

在有B帧的视频流中,由于B帧可能依赖于未来的帧,因此解码顺序和显示顺序可能不同。这时,我们需要根据PTS来安排帧的显示顺序。

示例分析:

假设我们有一系列帧的PTS和DTS,我们需要按照PTS的顺序来显示帧,即使某些帧的解码(DTS)是在其显示时间之前完成的。

4.3 处理 PTS 和 DTS

在解封装过程中,正确处理PTS和DTS对于实现音视频同步至关重要。我们需要将PTS和DTS从流中的时间基转换为解码器或播放器可以识别的时间基。

  • 时间基转换:使用av_packet_rescale_ts函数,我们可以将PTS和DTS从其原始时间基转换为另一个时间基。
  • 应用场景:在转封装或转码过程中,源和目标容器可能有不同的时间基,这时就需要转换PTS和DTS以保证时间的一致性。

示例代码:

AVPacket packet;
av_read_frame(fmt_ctx, &packet);
av_packet_rescale_ts(&packet, fmt_ctx->streams[packet.stream_index]->time_base,
                      decoder_ctx->time_base);

在上述代码中,我们读取了一个数据包,并将其PTS和DTS从文件的时间基转换为解码器的时间基,以便进行后续的解码操作。正确处理PTS和DTS对于确保解码后的视频帧能够以正确的顺序和时间显示至关重要。

5. 应用实例分析

5.1 MP4 文件解封装

MP4文件作为广泛使用的视频容器格式,其解封装流程对于视频处理至关重要。MP4文件通常由以下几个关键部分组成:

  • Ftyp Box:文件类型信息,标识文件及其兼容的编解码格式。
  • Moov Box:包含媒体文件的元数据,如时间戳、音视频参数等。
  • Mdat Box:存储实际的音视频数据。

解封装MP4文件的步骤通常包括:

  1. 使用工具或库(如FFmpeg)打开MP4文件,并读取文件头,获取Ftyp信息。
  2. 解析Moov Box,提取关键的元数据信息,包括但不限于视频的宽度、高度、帧率,音频的采样率、声道数等。
  3. 访问Mdat Box,读取音视频数据。这些数据通常以帧为单位,每帧数据前可能包含时间戳信息。

例如,使用FFmpeg进行MP4文件解封装的命令可能如下:

ffmpeg -i input.mp4 -vn -acodec copy audio.aac

此命令提取MP4文件中的音频流并保存为AAC格式。

5.2 TS 流解封装

TS(Transport Stream)流是一种常见的广播和流媒体格式,广泛应用于数字电视和网络流。TS流由固定长度的包组成,每个包包含音视频数据或其他流信息。

TS流的解封装流程涉及以下关键步骤:

  1. 同步字节识别:每个TS包以0x47作为同步字节开始。
  2. 解析包头:获取PID、payload_unit_start_indicator等关键信息。
  3. 提取PES包:根据payload_unit_start_indicator识别PES包的开始,并根据PES包头信息提取ES流数据。
  4. 解码ES流:将提取的ES流数据解码为原始的音视频数据。

在实际应用中,TS流的解封装可以通过软件如Elecard或使用开源库(如FFmpeg)来实现。例如,使用FFmpeg解封装TS流的命令如下:

ffmpeg -i input.ts -c copy -map 0:v -map 0:a output.mkv

此命令将TS流中的音视频流复制到MKV容器中,不进行重新编码。

祝大家学习顺利~
如有任何错误,恳请批评指正~~
以上是我通过各种方式得出的经验和方法,欢迎大家评论区留言讨论呀,如果文章对你们产生了帮助,也欢迎点赞收藏,我会继续努力分享更多干货~


🎈关注我的公众号AI Sun可以获取Chatgpt最新发展报告以及腾讯字节等众多大厂面经
😎也欢迎大家和我交流,相互学习,提升技术,风里雨里,我在等你~


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员行者孙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值