ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

FFmpeg 调试全指南:从「黑盒卡死」到「精准定位」的实战手册

FFmpeg 调试全指南:从「黑盒卡死」到「精准定位」的实战手册 目录一、先建立心智模型FFmpeg 为什么难调试二、第一层命令行级调试80% 问题止步于此1️. 把日志等级拉满必会2️. 用 ffprobe先排除「输入源问题」高频坑3️. 拆解 filtergraphfilter 调试核心技巧三、第二层API 级调试libav* 项目必看1️. 接管 FFmpeg 日志别用 printf2️. 打印时间戳90% 音画不同步的根因3️. AVERROR一定要转成可读字符串四、第三层GDB / 崩溃级调试真·硬核1️. 编译 FFmpeg必须带符号2️. GDB 启动 断点套路3️. SIGSEGV / Assertion failed 标准流程五、第四层性能调试卡顿 / CPU 100%1️. 先确认瓶颈在哪一层2️. 性能三件套看 filter 成本Linux 神器 perf硬件加速快速验证六、GUI / Overlay / Qt 场景的特殊坑你这个场景重点线程模型一定要对overlay filter 调试参数七、FFmpeg 调试 Checklist觉得有用就请您帮忙点赞转发收藏吧您的鼓励是我创作的动力多谢看官。由于能力水平有限文中的错误或不严谨的地方在所难免还请批评指正。本文面向中高级开发者系统梳理 FFmpeg 调试的 4 个层级命令行级 → 日志级 → 源码/GDB 级 → 性能 崩溃级。你将掌握为什么 FFmpeg 报错信息总是「语焉不详」以及如何让它开口说话-v、-report、AV_LOG_DEBUG的正确打开方式用 GDB core dump 定位SEGV/Assertion failed的完整流程filtergraph尤其是 overlay / scale调试的专用技巧性能问题卡顿、掉帧、CPU 飙高如何用ffprobe perf VTune拆解一套可直接复用的「FFmpeg 调试 Checklist」一、先建立心智模型FFmpeg 为什么难调试FFmpeg 调试难本质不是工具问题而是架构问题CLI (ffmpeg.c) └── libavformat (demux / mux) └── libavcodec (decode / encode) └── libavfilter (filtergraph) └── 硬件加速 / 线程池 / 内部 buffer 池典型痛点现象真实原因只说Error while filteringfiltergraph 某节点 init 失败但错误信息被吞卡住不动线程池死锁 / muxer 等 B-frame 缓存花屏时间基不一致 / width/height 对齐问题偶发崩多线程 use-after-free结论不要只盯着最后一行 Error要顺着「模块边界」查。二、第一层命令行级调试80% 问题止步于此1️. 把日志等级拉满必会ffmpeg -v debug -i in.mp4 -vf scaleiw/2:ih/2 out.mp4日志等级优先级由低到高quiet panic fatal error warning info verbose debug trace实战建议场景推荐看 filter 初始化-v verbose看时间戳 / PTS-v debug看内存分配/refcount-v trace非常吵黄金组合ffmpeg -v verbose -report -i in.mp4 ...-report会自动生成ffmpeg-YYYYMMDD-hhmmss.log包含完整命令行 环境变量 所有日志线上问题必开。2️. 用ffprobe先排除「输入源问题」高频坑原则ffmpeg 崩 ≠ ffmpeg 的锅先证伪输入文件ffprobe -v debug -show_streams -show_frames -select_streams v in.mp4重点看codec_typevideo width1920 height1080 pix_fmtyuv420p r_frame_rate30/1 start_time1.4 ← 很多花屏/卡顿的根因 time_base1/90000经典信号ffprobe 输出含义start_time 0 concatPTS 漂移pix_fmtnv12但 filter 只认 yuv420pswscale 自动插入隐式慢nb_framesunknownVBR 缺索引3️. 拆解 filtergraphfilter 调试核心技巧错误姿势-vf scale1080:-2,overlay10:10,drawtexttexttest,formatyuv420p正确姿势二分法拆 graph# Step1 只跑 scale -vf scale1080:-2,formatyuv420p # Step2 加 overlay -vf scale1080:-2,formatyuv420p,overlay10:10 # Step3 加 drawtext高级技巧dump filter 中间结果ffmpeg -i in.mp4 -vf scaleiw/2:ih/2,split2[a][b];[a]null[aout] \ -map [aout] step1.yuv或直接输出 rawvideoffmpeg -i in.mp4 -vf scale -f rawvideo -pix_fmt yuv420p out.yuv用ffplay看中间帧ffplay -f rawvideo -pix_fmt yuv420p -s 960x540 out.yuv三、第二层API 级调试libav* 项目必看1️. 接管 FFmpeg 日志别用 printfav_log_set_level(AV_LOG_DEBUG); av_log_set_callback(my_log_cb); void my_log_cb(void *ptr, int level, const char *fmt, va_list vl) { if (level av_log_get_level()) return; fprintf(stderr, [FFmpeg][%s] , av_default_item_name(ptr)); vfprintf(stderr, fmt, vl); }小技巧av_default_item_name(ptr)会打出模块名[h264 0x55...] [Parsed_overlay_0]一眼知道是哪个模块在说话。2️. 打印时间戳90% 音画不同步的根因av_log(NULL, AV_LOG_INFO, pts%s dts%s tb%d/%d\n, av_ts2str(pkt-pts), av_ts2str(pkt-dts), st-time_base.num, st-time_base.den);推荐宏#define PTS_LOG(pkt, st) \ av_log(NULL, AV_LOG_DEBUG, %s pts:%s dts:%s\n, \ st-codecpar-codec_type AVMEDIA_TYPE_VIDEO ? V : A, \ av_ts2timestr(pkt-pts, st-time_base), \ av_ts2timestr(pkt-dts, st-time_base))3️.AVERROR一定要转成可读字符串新手写法if (ret 0) printf(error %d\n, ret);正确写法char errbuf[AV_ERROR_MAX_STRING_SIZE]; av_strerror(ret, errbuf, sizeof(errbuf)); fprintf(stderr, avcodec_send_packet failed: %s\n, errbuf);四、第三层GDB / 崩溃级调试真·硬核1️. 编译 FFmpeg必须带符号./configure \ --enable-debug3 \ --disable-stripping \ --disable-optimizations \ --extra-cflags-O0 -fno-omit-frame-pointer make -jRelease 优化下 GDB backtrace 基本不可读调试时必须关 O22️. GDB 启动 断点套路gdb --args ./ffmpeg -i in.mp4 out.mp4常用断点break avcodec_send_packet break av_buffersink_get_frame break avfilter_graph_config break report_and_exit看 filter 初始化失败b avfilter_graph_config run bt3️. SIGSEGV / Assertion failed 标准流程ulimit -c unlimited ./ffmpeg ... gdb ./ffmpeg coreGDB 三板斧bt full # 看完整调用栈 frame N # 切到 ffmpeg / 你的 frame p *ctx p *frame p frame-buf[0] # 看 buffer ref 是否悬空FFmpeg 常见崩溃模式速查backtrace 特征结论libavfilter/avfilter.c:144filter 没 unref frameff_thread_decode_frame线程 unsafe 的 codecav_malloc - abortOOM / 内存池被吃光五、第四层性能调试卡顿 / CPU 100%1️. 先确认瓶颈在哪一层ffmpeg -v verbose -benchmark -i in.mp4 out.mp4看结尾输出bench: utime12.34s stime1.2s rtime5.6s现象瓶颈rtime ≈ utime单线程 CPUutime 很低但 rtime 很长IO / mux 阻塞filtergraph 吃满scale / overlay 太重2️. 性能三件套看 filter 成本-vf scale1920:1080,setptsPTS,null加/减 filter 对比时间一测一个准。Linux 神器 perfperf top -p $(pidof ffmpeg)看到swscale/hscale_fast→ 换zscale或 hwaccel硬件加速快速验证ffmpeg -hwaccel cuda -i in.mp4 -vf hwupload,scale_nppw1280:h720 out.mp4六、GUI / Overlay / Qt 场景的特殊坑你这个场景重点你在 Qt 里画 OverlayWidget FFmpeg典型问题线程模型一定要对解码线程 → AVFrame (YUV) → 拷贝 → UI 线程 → QImage/QPainter常见错在avcodec_receive_frame里直接QImage::loadFromDataAVFrame被 filter 自动 unref 后 UI 还在画安全模式av_frame_ref(ui_frame, frame); // 或 copy 到 cv::Mat / QImage buffer emit frameReady();overlay filter 调试参数overlayxW-w-10:yH-h-10:formatyuv420p:eof_actionpass调试时加-vf formatyuv420p,overlay...,formatyuv420p避免swscale 在 graph 里偷偷插 3 次七、FFmpeg 调试 Checklist是否先用 ffprobe 确认输入文件 是否开了 -v verbose -report filtergraph 是否二分法拆解过 AVERROR 是否 av_strerror 打印 崩溃是否用带符号版本 gdb bt full 性能问题是否确认 scale/filter 成本 多线程是否保证 AVFrame 生命周期
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进