鴥彼晚风
发布于 2026-07-01 / 2 阅读
0
0

PyAV 视频异常处理 (Errors) 速查手册

1. 核心设计理念

PyAV 采用双重继承策略:FFmpeg 错误既继承自 av.FFmpegError,也映射到 Python 内置异常(如 ValueError, OSError)。这意味着你可以用任一方式捕获。

# ✅ 两种等价捕获方式
try:
    container.decode(video=0)
except av.InvalidDataError:      # PyAV 专用异常
    handle_corrupt_frame()

try:
    container.decode(video=0)
except ValueError as e:          # Python 内置异常(同样可捕获)
    if e.errno == av.error.INVALIDDATA:
        handle_corrupt_frame()

💡 推荐风格:优先使用 av.*Error 专用异常类,语义更清晰;仅在需要统一处理多种底层 OS 错误时才回退到 OSError/ValueError


2. FFmpegError 基类属性

所有 PyAV FFmpeg 异常均继承自 av.FFmpegError,提供以下诊断信息:

属性

类型

说明

errno

int

FFmpeg 整数错误码

strerror

str

FFmpeg 原始错误消息

filename

str | None

操作的文件路径(如有)

type

ErrorType

错误类型枚举对象

log

tuple | None

最近一条 FFmpeg 日志(来自 av.logging.get_last_log()

诊断最佳实践

except av.FFmpegError as e:
    print(f"[{e.type.name}] {e.strerror}")
    print(f"File: {e.filename}")
    if e.log:
        print(f"FFmpeg Log: {e.log}")

3. 视频高频异常速查表

🔴 编解码相关

异常类

错误码

触发场景

处理建议

DecoderNotFoundError

DECODER_NOT_FOUND

系统缺少对应视频解码器

检查 FFmpeg 编译选项;安装完整 ffmpeg

EncoderNotFoundError

ENCODER_NOT_FOUND

编码器名称拼写错误或未编译

Codec(name, 'w') 预检查;查 supported_codecs

BSFNotFoundError

BSF_NOT_FOUND

比特流过滤器不可用

确认过滤器名称;检查 FFmpeg 版本

InvalidDataError

INVALIDDATA

视频数据损坏/格式非法

跳过当前帧/Packet;记录损坏位置;启用 OUTPUT_CORRUPT 容错

ExperimentalError

EXPERIMENTAL

使用了实验性编解码器

设置 `ctx.flags2

BugError

BUG

FFmpeg 内部 bug

升级 FFmpeg/PyAV;提交 issue 并附 e.log

🟡 容器与 I/O 相关

异常类

错误码

触发场景

处理建议

DemuxerNotFoundError

DEMUXER_NOT_FOUND

无法识别输入格式

检查文件完整性;手动指定 format

MuxerNotFoundError

MUXER_NOT_FOUND

输出格式不支持

ContainerFormat(name, 'w') 确认

EOFError

EOF

读到文件末尾

正常结束信号,不要当错误处理

BufferTooSmallError

BUFFER_TOO_SMALL

内部缓冲区不足

增大 buffer 或减小输入块大小

ProtocolNotFoundError

PROTOCOL_NOT_FOUND

URL scheme 不支持(如 srt://)

检查 FFmpeg 协议支持列表

InputChangedError

INPUT_CHANGED

流参数中途变化

重新初始化解码器或丢弃后续帧

OutputChangedError

OUTPUT_CHANGED

输出参数不兼容

检查编码器参数是否与容器匹配

🌐 网络流相关

异常类

HTTP 状态

处理建议

HTTPBadRequestError

400

检查 URL/请求头格式

HTTPUnauthorizedError

401

补充认证信息

HTTPForbiddenError

403

检查 IP 白名单/Token 权限

HTTPNotFoundError

404

验证资源路径

HTTPOtherClientError

4xx

通用客户端错误,记录状态码

HTTPServerError

5xx

服务端问题,重试+退避

⚙️ 其他

异常类

说明

FilterNotFoundError

FFmpeg filter 不可用

OptionNotFoundError

设置了无效的 codec/container option

PatchWelcomeError

功能未实现

ExternalError

外部库(x264/libvpx等)报错

UnknownError

未分类错误,必查 e.log

ExitError

FFmpeg 请求立即退出


4. ErrorType 枚举用法

当需要精确匹配错误码而非异常类型时(如在通用 OSError 处理器中区分 FFmpeg 错误):

import av.error

try:
    do_video_processing()
except OSError as e:
    # 精确匹配 FFmpeg 错误码
    if e.errno == av.error.BSF_NOT_FOUND:
        fallback_without_bsf()
    elif e.errno == av.error.INVALIDDATA:
        skip_and_continue()
    else:
        raise  # 非目标错误,重新抛出

⚠️ 注意e.errno 是 FFmpeg 内部错误码,不等于 POSIX errno。不要与 errno.ENOENT 等混用。


5. 视频处理异常处理模式

模式一:容错解码循环

for packet in container.demux(video=0):
    try:
        frames = packet.decode()
        for frame in frames:
            process(frame)
    except av.InvalidDataError:
        logger.warning(f"Corrupt packet at PTS={packet.pts}, skipping")
        continue
    except av.EOFError:
        break

模式二:编码器安全初始化

try:
    ctx = Codec('libx265', 'w').create()
    ctx.open()
except av.EncoderNotFoundError:
    logger.warning("libx265 unavailable, falling back to libx264")
    ctx = Codec('libx264', 'w').create()
    ctx.open()
except av.ExperimentalError:
    ctx.flags2 |= 'FAST'
    ctx.open()

模式三:网络流重试

from time import sleep

for attempt in range(3):
    try:
        container = av.open(stream_url, timeout=5)
        break
    except (av.HTTPServerError, av.HTTPForbiddenError) as e:
        logger.warning(f"Attempt {attempt+1} failed: {e.strerror}")
        sleep(2 ** attempt)
else:
    raise RuntimeError("Stream unavailable after retries")

6. ⚠️ 常见陷阱

陷阱

说明

正确做法

把 EOF 当错误

EOFError 是正常的流结束信号

单独捕获并优雅退出循环

忽略 e.log

很多错误的 strerror 过于笼统

始终在调试/日志中打印 e.log

except Exception 兜底

会吞掉键盘中断和系统退出

至少用 except av.FFmpegError

混淆 POSIX 和 FFmpeg errno

e.errno == errno.ENOENT 永远为 False

FFmpeg 错误用 av.error.* 常量比较

未处理 InputChangedError

直播流分辨率切换导致崩溃

捕获后重建解码器上下文

忘记异常也有 filename

批量处理时不知道哪个文件出错

日志中包含 e.filename

💡 核心原则:视频处理是"脏数据"密集型任务,永远假设输入可能损坏。对 InvalidDataError 做降级而非崩溃;对网络错误做重试而非放弃;对所有 FFmpeg 错误保留 log 用于事后诊断。优先使用 av.*Error 专用异常类保持代码可读性。


评论