ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

FFmpeg 入门绕不开的两个词:封装是快递盒,解封装是拆快递

FFmpeg 入门绕不开的两个词:封装是快递盒,解封装是拆快递 目录一、先扔一句人话定义二、想象一个快递包裹经典比喻三、解封装Demux到底干了什么1️.读文件头认出“这是什么盒子”2️.把 Stream 列出来但还没解码3️.开始吐 Packetav_read_frame四、封装Mux是反过来的事流程五、一个最容易混淆的点MP4 里存的不是“视频”吗六、用一张 FFmpeg 调用链串起来七、为什么“解封装”会出问题实战经验1️.你以为读到的是一帧画面2️.时间戳是“容器给的”3️.裸流没有封装.h264 / .aac八、一句话记忆法九、给 Qt / C 程序员的映射表觉得有用就请您帮忙点赞转发收藏吧您的鼓励是我创作的动力多谢看官。由于能力水平有限文中的错误或不严谨的地方在所难免还请批评指正。FFmpeg 是一个非常强大的开源项目它包含了大量用于处理视频和音频的库和工具。其中封装Muxing和解封装Demuxing是处理多媒体文件时常见的操作。封装是将多个媒体流例如视频、音频和字幕流合并成一个单一的文件格式例如 MP4 或 MKV。解封装则是将封装好的文件拆分成原始的媒体流以便单独处理或编辑。很多人第一次看 FFmpeg 示例都会被一句话劝退“先解封装再解码”心里 OS封装解码不是一个意思吗今天把话讲清楚FFmpeg 里「封装 / 解封装」和「编码 / 解码」完全是两层世界的事。一、先扔一句人话定义名词FFmpeg 叫法人话封装Mux / Muxing​把音视频塞进一个文件解封装Demux / Demuxing​从一个文件里把音视频拆出来编码Encode原始数据 → 压缩数据解码Decode压缩数据 → 原始数据一句话封装 ≠ 压缩​MP4 / MKV / FLV 是“盒子”H264 / HEVC / AAC 是“盒子里的货”二、想象一个快递包裹经典比喻你下载的movie.mp4┌─────────────── movie.mp4 (Container) ───────────────┐ │ │ │ ┌─────────────── Video Stream ─────────────┐ │ │ │ H264 / HEVC bitstream (.h264 / .m4s) │ │ │ └──────────────────────────────────────────┘ │ │ │ │ ┌─────────────── Audio Stream ─────────────┐ │ │ │ AAC / Opus packets │ │ │ └──────────────────────────────────────────┘ │ │ │ │ Metadata / Subtitle / Chapter / Cover │ └─────────────────────────────────────────────────────┘FFmpeg 的职责是解封装Demux拆盒子 → 拿出 N 个“数据流”解码Decode把每个流里的压缩数据 → PCM / YUV三、解封装Demux到底干了什么对应 FFmpeg 代码世界AVFormatContext *fmt_ctx avformat_alloc_context(); avformat_open_input(fmt_ctx, test.mp4, NULL, NULL); avformat_find_stream_info(fmt_ctx, NULL);这一步只干三件事1️.读文件头认出“这是什么盒子”FFmpeg 会说“哦这是 MP4ISOBMFF有 2 个流”fmt_ctx-iformat-name mov,mp4,m4a,3gp,3g2,mj22️.把 Stream 列出来但还没解码fmt_ctx-nb_streams; // 通常是 2每个流AVStream *st fmt_ctx-streams[i]; st-codecpar-codec_type; // AVMEDIA_TYPE_VIDEO / AUDIO你得到的是Streamcodecpar 告诉你videoH264 / HEVC / VP9audioAAC / OPUSsubtitleass / srt / webvtt注意此时你连一帧画面都没解码3️.开始吐 Packetav_read_framewhile (av_read_frame(fmt_ctx, pkt) 0) { // pkt 属于某个 stream_index }AVPacket是什么一个“时间连续的压缩数据块”视频一个 / 多个 NALU不一定是完整帧音频若干采样点的 AAC ADTS frame重点av_read_frame()只拆快递不拆箱子里的东西四、封装Mux是反过来的事你做播放器时很少用你做转码 / 录屏 / 存文件​ 时天天用。流程YUV → encode → AVPacket (video) PCM → encode → AVPacket (audio) ↓ av_write_frame() ↓ mp4 / mkv / flv代码骨架avformat_alloc_output_context2(oc, NULL, NULL, outfile); avformat_new_stream(oc, codec_ctx); avformat_write_header(oc, NULL); while (has_pkt) { av_interleaved_write_frame(oc, pkt); } av_write_trailer(oc);FFmpeg 负责按时间戳交错写audio/video interleaving写 moov box写 index / duration / metadata你决定“怎么塞”FFmpeg 决定“盒子怎么封口”五、一个最容易混淆的点MP4 里存的不是“视频”吗严格说❌ MP4 ≠ 视频格式✅ MP4 容器格式✅ H264 编码格式所以这句话是对的同一个 MP4 里可以装 H264 / HEVC / AV1也可以装 AAC / FLAC / OpusFFmpeg 里模块管什么libavformat封装 / 解封装libavcodec编码 / 解码libswscaleYUV 换尺寸libswresamplePCM 换采样率六、用一张 FFmpeg 调用链串起来.mp4 ↓ avformat_open_input ← 解封装开始 ↓ av_read_frame ↓ AVPacket (compressed) ↓ avcodec_send_packet ← 解码开始 ↓ avcodec_receive_frame ↓ AVFrame (YUV420P / PCM)反过来AVFrame ↓ encode ↓ AVPacket ↓ av_write_frame ← 封装开始 ↓ .mkv / .mp4七、为什么“解封装”会出问题实战经验1️.你以为读到的是一帧画面av_read_frame()H264 Annex-B可能是 SPS/PPSB 帧后面才显示HEVCVPS/SPS/PPS slicepacket ≠ frame​一个 frame 可能拆成 N 个 packet2️.时间戳是“容器给的”pkt-pts/pkt-dts来自demuxdecode 后frame-pts是解码器重新算的所以音画不同步90% 在封装层时间基没处理好av_q2d(stream-time_base);3️.裸流没有封装.h264 / .aac你直接读.h264没 stream没 timebase没 duration没 audio/video 绑定关系FFmpeg 示例里要手写AVFormatContext新手直接劝退现场八、一句话记忆法封装 把多股绳子捆成一捆​解封装 把绳子一根一根解开​编码 把棉花压成砖​解码 把砖拆回棉花九、给 Qt / C 程序员的映射表FFmpegQt 类比AVFormatContextQIODevice 文件头解析器AVStreamQMediaPlayer::mediaStatus() 前的元数据AVPacketreadyRead() 吐的一块 bufferAVFrameQImage / QAudioBuffer
返回列表