FFmpeg日志系统基于递增数值级别(如AV_LOG_INFO为默认),按严重程度过滤输出。从致命错误(FATAL)到极细追踪(TRACE),级别越高越详尽。命令行通过
-loglevel控制,生产环境推荐使用ERROR或WARNING,调试时启用VERBOSE/DEBUG,避免日志洪泛。自定义回调可集成至统一日志系统,实现高效问题排查与工程化管理。
一、核心设计:数值化的日志门槛
FFmpeg 的日志系统定义在libavutil/log.h中,所有级别本质上是递增的整数常量。其过滤逻辑非常直白:设置一个阈值L,只有level <= L的日志才会被输出。数值越小,严重程度越高;数值越大,信息越详尽。
级别宏 | 数值 | 定位 |
|---|---|---|
| -8 | 完全静默 |
| 0 | 系统级崩溃,即将异常退出 |
| 8 | 致命错误,流程无法继续 |
| 16 | 可感知的错误,但后续流程可能继续 |
| 24 | 可疑但不致命的异常 |
| 32 | 默认级别,常规运行信息 |
| 40 | 详细信息,比 INFO 多一层 |
| 48 | 开发者级调试信息 |
| 56 | 极细粒度追踪,日志量巨大 |
设计规律:相邻级别间隔为 8,形成清晰的严重度梯度。FFmpeg 命令行工具的默认级别是
AV_LOG_INFO,这也是大多数用户日常看到的输出量。
二、关键级别逐一拆解
AV_LOG_FATAL — 流程终结者
AV_LOG_FATAL不是"有错误",而是"没法往下走了"。它意味着当前执行上下文已经处于不可恢复状态:
输出格式强依赖的头部信息缺失(如 FLV header 损坏)
参数组合在逻辑上完全非法(如不存在的像素格式 + 硬件编码器组合)
关键资源分配彻底失败(如连最基础的上下文都无法创建)
出现FATAL级日志后,程序通常会直接退出,不存在"降级继续"的可能。在自定义日志回调中,这个级别往往对应exit(1)或抛出异常。
AV_LOG_ERROR — 局部失败,全局尚可
与FATAL不同,ERROR表示某一步操作失败,但解码器/编码器/复用器整体还能继续运转:
单个数据包解封装失败,但后续包可能正常
某路流被丢弃,其余流仍可处理
单帧解码损坏,但参考帧链未断裂
生产环境中,至少应保留ERROR级别,它是发现数据异常的第一道防线。
AV_LOG_INFO — 默认的信息基线
这是 FFmpeg 命令行不指定-loglevel时的输出级别,面向的是"正常使用的人需要知道什么":
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'in.mp4': Duration: 00:02:30.45, start: 0.000000, bitrate: 3245 kb/s Stream #0:0(und): Video: h264, yuv420p, 1920x1080, 3000 kb/s Stream #0:1(und): Audio: aac, 48000 Hz, stereo, 128 kb/s流参数、编码映射、整体进度——这些信息足以确认"任务在正常推进",但又不会刷屏。
AV_LOG_VERBOSE — INFO 的加长版
VERBOSE在 INFO 的基础上增加了内部状态和参数细节,适合排查"为什么选了这个编码器""为什么探测到这些流""滤镜图是怎么构建的":
探测阶段的候选格式列表
编码器参数协商的完整过程
滤镜链中每个节点的输入输出格式
它比 DEBUG 克制,不会深入到帧/包级别的细节,但已经足够排查大部分配置类问题。
AV_LOG_DEBUG — 帧级与包级的内部视角
DEBUG面向的是"改 FFmpeg 源码、写封装层、调滤镜和编解码参数"的开发者:
每个数据包的大小、flags、pts/dts
选项是否真正生效(很多选项会被覆盖或忽略,DEBUG 日志会告诉你)
各模块内部的分支走向
日志量已经很大,生产环境不应常开,但在排查花屏、丢帧、时间戳异常时不可或缺。
AV_LOG_TRACE — 函数级的状态机追踪
TRACE是比DEBUG更极端的级别,会输出函数进出、状态机转换、底层循环级信息。基本只用于libavcodec/libavformat开发者定位极端边界 bug,日常开发中几乎不需要。
三、命令行控制
FFmpeg 提供了-loglevel(简写-v)来控制日志门槛:
ffmpeg -loglevel error -i in.mp4 out.mp4 # 只输出错误 ffmpeg -loglevel warning -i in.mp4 out.mp4 # 错误 + 警告 ffmpeg -loglevel info -i in.mp4 out.mp4 # 默认,常规信息 ffmpeg -loglevel verbose -i in.mp4 out.mp4 # 详细信息 ffmpeg -loglevel debug -i in.mp4 out.mp4 # 调试信息 ffmpeg -loglevel trace -i in.mp4 out.mp4 # 极详细追踪还可以叠加输出修饰符:
ffmpeg -loglevel repeat+level+verbose -i in.mp4 out.mp4repeat:重复日志只打印一次(去重)level:每行日志前显示级别前缀level+verbose:同时显示级别且使用 verbose 门槛
-report是一个特殊选项,会自动以DEBUG级别将完整日志写入当前目录下的ffmpeg-YYYYMMDD-HHMMSS.log文件,专门用于提交 bug report。
四、代码中使用
设置全局日志级别
#include <libavutil/log.h> av_log_set_level(AV_LOG_INFO); // 全局阈值输出日志
av_log(NULL, AV_LOG_ERROR, "decode failed at pts %" PRId64 "\n", pts); av_log(ctx, AV_LOG_DEBUG, "packet size=%d, flags=%d\n", pkt.size, pkt.flags);第一个参数是AVClass指针(用于标识日志来源),传NULL则使用全局上下文。
自定义日志回调
在库或服务中,通常需要将 FFmpeg 的日志接入自己的日志系统:
static void my_log_callback(void *avcl, int level, const char *fmt, va_list vl) { if (level > av_log_get_level()) return; char buf[1024]; vsnprintf(buf, sizeof(buf), fmt, vl); switch (level) { case AV_LOG_FATAL: syslog(LOG_CRIT, "[ffmpeg] %s", buf); break; case AV_LOG_ERROR: syslog(LOG_ERR, "[ffmpeg] %s", buf); break; case AV_LOG_WARNING: syslog(LOG_WARNING, "[ffmpeg] %s", buf); break; case AV_LOG_INFO: syslog(LOG_INFO, "[ffmpeg] %s", buf); break; default: syslog(LOG_DEBUG, "[ffmpeg] %s", buf); break; } } av_log_set_callback(my_log_callback);五、实战选型矩阵
场景 | 推荐级别 | 理由 |
|---|---|---|
线上转码服务 |
| 只关注真正影响输出的问题,避免日志 I/O 成为瓶颈 |
批量处理脚本 |
| 兼顾异常发现与日志量控制 |
普通用户转文件 | 默认 | 确认参数正确、进度可见 |
排查流被丢弃/编码器选错 |
| 看到参数协商和流映射全貌 |
花屏/丢帧/时间戳异常 |
| 帧级和包级细节是定位关键 |
libav 源码级 bug |
| 函数级追踪,仅限开发环境 |
自动化 CI/CD |
| 只关心退出码,减少噪音 |
典型排查命令:
# 只关心真出错,适合 cron / systemd 定时任务 ffmpeg -loglevel error -i in.mp4 -c copy out.mp4 # 排查为什么没有音轨 ffmpeg -loglevel verbose -i in.mp4 # 深度排查滤镜或解码问题,重定向到文件 ffmpeg -loglevel debug -i in.mp4 -vf scale=1280:720 out.mp4 2>debug.log六、总结
FFmpeg 的日志级别体系遵循一个简单的原则:数值越小越严重,数值越大越详尽。理解每个级别的定位,核心在于区分"流程是否还能继续":
FATAL和ERROR回答的是"出事了没有"INFO和VERBOSE回答的是"正在干什么"DEBUG和TRACE回答的是"内部到底发生了什么"
合理设置日志级别,既能保证问题可排查,又能避免日志洪泛拖垮系统。在封装 FFmpeg 的服务中,通过av_log_set_callback将日志接入统一系统,再配合级别路由,是工程化的最佳实践。