ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

MP4 容器到底在“管”什么?——FFmpeg 解码 / 编码时的 MP4 实战避雷手册

2026/8/11 1:14:00 拓冰建站 浏览量
MP4 容器到底在“管”什么?——FFmpeg 解码 / 编码时的 MP4 实战避雷手册 目录一、MP4 在 FFmpeg 眼里长什么样二、MP4 最反直觉的 3 个事实1️. MP4 有「头在尾巴」问题faststart2️. MP4 的 timebase 不统一音视频各玩各的3️. B 帧 ctts → dts / pts 分离三、用 FFmpeg 解码 MP490% 的坑都在这里标准解码链路坑 1start_time 不处理前 1 秒画面“没声音”坑 2avformat_seek_file 后画面绿 / 花坑 3AAC 在 MP4 里是 ADTS是 ASC ❗四、用 FFmpeg 写 MP4编码 / 录像的 6 条军规军规 1video timebase 写死 90k军规 2pts / dts 必须递增muxer 会 reject军规 3flags 一定要设军规 4先 write header再 write frame最后 trailer军规 5想网络可播 → faststart关键军规 6MP4 Opus / FLAC 要新 FFmpeg五、MP4 常见 movflags六、Qt / C 项目里 MP4 的“最小正确配置”七、MP4 FFmpeg 调试 checklist八、一句话总结觉得有用就请您帮忙点赞转发收藏吧您的鼓励是我创作的动力多谢看官。由于能力水平有限文中的错误或不严谨的地方在所难免还请批评指正。FFmpeg 操作 MP4 核心在于‌严格匹配编码兼容性H.264/AAC、确保 moov 原子位置正确、区分流复制与重编码场景‌避免生成无法播放或损坏的文件 。‌‌一句话定性​MP4 不是编码格式是“带目录的快递箱”。FFmpeg 里真正干活的是 H.264 / AACMP4 只负责怎么摆、怎么找、什么时候播、音画怎么对齐。这篇文章不堆 ISO/IEC 14496-12 术语只讲一件事用 FFmpeg 读 / 写 MP4 时哪些地方会让你莫名其妙没声音、卡顿、seek 飞走。一、MP4 在 FFmpeg 眼里长什么样你打开一个test.mp4FFmpeg 看到的是moov (全局目录) ├─ mvhd时长、timebase ├─ trak (video) │ ├─ tkhd │ ├─ mdia → h264 / hevc │ └─ stts / stsc / stco / ctts时间戳索引 └─ trak (audio) └─ mdia → aac / opusAVFormatContext 就是 moov 的解析结果MP4 概念FFmpeg 对应trakAVStreamsampleAVPacketchunkFFmpeg 内部 IOmdhd timebaseAVStream::time_baseedit liststart_time/seek坑点二、MP4 最反直觉的 3 个事实1️. MP4 有「头在尾巴」问题faststart很多 MP4 结构是ftyp mdat ← 音视频数据巨大 moov ← 索引最后才写结果ffplay test.mp4 # 能播 curl http | ffplay - # 卡死FFmpeg 解码前必须本地文件自动avformat_find_stream_info()读尾部网络流没 moov 就播不了结论MP4 不适合裸 RTSP / HTTP 直推除非你 faststart2️. MP4 的 timebase 不统一音视频各玩各的video_stream-time_base {1, 90000}; // 固定 90kHz audio_stream-time_base {1, 44100}; // 或 48000 / 1000所以音视频 pts 不能直接比同步必须av_rescale_q()到同一 clockvid_ts_ms av_rescale_q(vpkt-pts, vst-time_base, {1,1000}); aud_ts_ms av_rescale_q(apkt-pts, ast-time_base, {1,1000});MP4 里 90k timebase 是历史遗产MPEG-2 来的3️. B 帧 ctts → dts / pts 分离MP4 视频常见dts: 0 1 2 3 pts: 2 0 1 3FFmpeg 帮你算好了但APIpts 还是 dtsav_read_frame()pts 可能有偏移​AVPacket::dts解码顺序AVFrame::pts显示顺序解码send_packet(dts 无所谓)​同步 / seek永远用 frame-pts三、用 FFmpeg 解码 MP490% 的坑都在这里标准解码链路avformat_open_input (mp4) └─ avformat_find_stream_info ← 读 moov / sample table └─ av_read_frame └─ avcodec_send_packet └─ avcodec_receive_frame坑 1start_time 不处理前 1 秒画面“没声音”MP4 经常有edit liststream-start_time ! 0结果音频从 0 开始视频从 600 个 timebase 开始正确做法Qt / 播放器double base stream-start_time * av_q2d(stream-time_base);或者干脆忽略但seek 必须算进去坑 2avformat_seek_file 后画面绿 / 花原因seek 到I 帧附近但 packet 是 B 帧 trak 依赖正确 flagavformat_seek_file(fmt, vid_idx, INT64_MIN, seek_ts, seek_ts, AVSEEK_FLAG_BACKWARD); avformat_flush_packets(fmt);然后解码器avcodec_flush_buffers()继续 read 直到keyframe1坑 3AAC 在 MP4 里是 ADTS是 ASC ❗容器AAC 形态TS / rawADTSMP4AudioSpecificConfigextradata​FFmpeg 自动处理但你要注意codecpar-extradata_size 2 // MP4 AAC 必须有自己写裸 AAC 推流时MP4 → 抽 extradata → 拼 ADTS header四、用 FFmpeg 写 MP4编码 / 录像的 6 条军规军规 1video timebase 写死 90kst-time_base av_make_q(1, 90000);MP4 muxer 会谢你。军规 2pts / dts 必须递增muxer 会 rejectpkt-pts next_pts; pkt-dts pkt-pts; // 无 B 帧 pkt-duration av_rescale_q(1, {1,fps}, st-time_base);军规 3flags 一定要设pkt-flags | AV_PKT_FLAG_KEY; // I 帧MP4 用这个建 sync sample table。军规 4先 write header再 write frame最后 traileravformat_write_header(fmt_ctx, nullptr); while (encode) av_write_frame(fmt_ctx, pkt); av_write_trailer(fmt_ctx);忘了av_write_trailer()→ moov 没写 → 文件打不开军规 5想网络可播 → faststart关键FFmpeg CLIffmpeg -i in.mp4 -c copy -movflags faststart out.mp4C API 等价av_opt_set(fmt_ctx-priv_data, movflags, faststart, 0);原理moov 挪到 ftyp 后面军规 6MP4 Opus / FLAC 要新 FFmpegcodec要求H.264 / AAC随便HEVCmovflags brandOpusFFmpeg ≥ 4FLACFFmpeg ≥ 4.3五、MP4 常见 movflagsmovflags faststart // 网页播放 empty_moov // 碎片化 / 流 frag_keyframe // dash-likeQt 桌面录制推荐faststartfrag_keyframeempty_moov六、Qt / C 项目里 MP4 的“最小正确配置”AVDictionary *opt nullptr; av_dict_set(opt, movflags, faststart, 0); avformat_alloc_output_context2(oc, nullptr, nullptr, out.mp4); avformat_write_header(oc, opt);七、MP4 FFmpeg 调试 checklistav_dump_format()看什么trak 数time_baseextradata sizestart_timeffprobe 一条命令救命ffprobe -v error -show_streams -show_format test.mp4八、一句话总结MP4 是“索引比数据重要”的容器。​FFmpeg 解码 MP4 时不关心 codec 以外的 MP4 会出时间轴 bug不处理 moov / edit list 会 seek 飞不 faststart 会网络卡写 MP4 是“给索引写日记”写错一行播放器全乱。