MP4分离前须确认真实音视频流:用ffprobe或遍历streams检查codec_type,避免误判伪MP4单流、封面图干扰;分离时按packet.stream_index路由,正确复制extradata、设置codec_tag及handler_name,处理时间基与DTS/PTS对齐。

分离前先确认MP4里到底有没有独立音视频流
很多用户直接用 avformat_open_input 打开 MP4 后就急着找流,结果发现 format_ctx->nb_streams 是 2,但调 avcodec_parameters_copy 失败或解码崩溃——根本原因是 MP4 里可能用了 mp4a 或 avc1 这类封装格式,参数存在 AVStream->codecpar,但没触发完整解码器初始化;更隐蔽的是有些 MP4 把音频 mux 进了 video stream(比如某些 QuickTime 导出的“伪 MP4”),实际只有一条 stream。
实操建议:
- 用
ffprobe -v quiet -show_entries stream=codec_type,index -of csv your.mp4先看真实流类型和索引 - 在代码里遍历
format_ctx->streams[i],检查stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO或AVMEDIA_TYPE_AUDIO,别只靠索引顺序假设第0条是视频 - 注意
AVStream->disposition & AV_DISPOSITION_ATTACHED_PIC可能干扰判断,这种是封面图,不算有效视频流
用 av_read_frame + 流索引过滤是最稳的分离方式
别用“把整个 AVFormatContext 拆成两个新上下文”这种重封装思路——MP4 的 moov box 和碎片化mdat 决定了它不适合运行时切分。正确做法是单次打开,逐包读取,按 packet.stream_index 路由到对应输出文件。
关键点:
立即学习“C++免费学习笔记(深入)”;
- 输出文件必须用
avformat_write_header初始化,且写入的AVStream必须从输入流codecpar复制参数,不能直接用avcodec_parameters_from_context(后者需要已打开的解码器) - 音频 packet 时间基要转换:
av_packet_rescale_ts(&pkt, in_stream->time_base, out_stream->time_base),否则 AAC 写出来会快进或卡顿 - MP4 要求 moov 在文件开头,所以必须设
AVFormatContext->oformat->flags & AVFMT_TS_NEGATIVE并在av_write_header前调avformat_new_stream预占位,否则生成的文件无法播放
写视频流时记得处理 B-frame 和 DTS/PTS 对齐
MP4 中 H.264 视频常含 B-frame,原始 packet 的 pts 和 dts 可能乱序(如 pts=3, dts=1),而 av_interleaved_write_frame 要求严格按 dts 递增。直接转发会导致 muxer 报错 Non-monotonous DTS 或输出花屏。
解决方法:
- 对视频 stream 开启
AVFMT_TS_NONSTRICT标志(仅限输出格式上下文) - 更可靠的是用
av_packet_rescale_ts后,手动调av_compare_ts(pkt.pts, out_stream->time_base, pkt.dts, out_stream->time_base) 判断是否需调整,必要时用 <code>av_packet_unref+av_packet_ref重建 packet - 绕过麻烦?加个
AVOutputFormat->flags |= AVFMT_ALLOW_FLUSH,让 muxer 自动缓冲重排(FFmpeg 4.4+ 支持)
分离后文件无法播放?重点查 extradata 和 codec_tag
常见现象:视频文件能打开但黑屏,音频能播但有爆音。大概率是 codecpar->extradata 没正确复制,或 codecpar->codec_tag 错误(比如本该是 AV_RL32("avc1") 却成了 0)。
必须检查:
- 复制
codecpar后,立刻验证out_stream->codecpar->extradata不为 NULL,长度 > 0(H.264 至少 7 字节 SPS) - 手动设置
out_stream->codecpar->codec_tag = av_codec_get_tag(out_format->codec_tag, codecpar->codec_id),尤其对 AAC,不设 tag 会导致 iOS 播放器拒绝解码 - MP4 的 audio stream 必须带
AVStream->metadata中的"handler_name",否则部分播放器静音;可简单补一句av_dict_set(&out_stream->metadata, "handler_name", "SoundHandler", 0)
extradata 错误比解码失败更难调试——它不会 crash,只会让播放器默默跳过整条流。


















