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

PyAV 视频滤镜 (Filters) 核心速查手册

1. 核心组件关系

PyAV 的滤镜系统是对 FFmpeg libavfilter 的封装,采用图(Graph)模型

Graph (滤镜图容器)
 ├── FilterContext (滤镜实例节点)
 │    ├── FilterPad (输入/输出端口定义)
 │    └── FilterLink (节点间连接线)
 └── Buffer/BufferSink (数据入口/出口)

角色

视频开发关键职责

Filter

滤镜描述符

查询滤镜能力(线程、动态端口等),不直接处理数据

Graph

滤镜图容器

添加节点、配置图、推送/拉取视频帧

FilterContext

滤镜实例

初始化参数、建立节点间链接

FilterLink

连接对象

表示两个 Pad 之间的已建立连接

FilterPad

端口定义

声明端口的媒体类型(video/audio)、方向

FilterContextPad

实例端口

运行时端口,可查询是否已连接 (linked)

💡 核心原则:视频帧通过 vpush() 进入 Graph,经过一系列 FilterContext 处理后,通过 vpull() 取出。Graph 必须先 configure() 才能推拉数据。


2. Graph:视频滤镜图操作

构建与配置

graph = av.filter.Graph()

# 1. 添加视频缓冲入口(必须指定尺寸和格式)
buffer = graph.add_buffer(width=1920, height=1080, format='yuv420p', time_base=Fraction(1, 30))

# 2. 添加视频滤镜节点
scale = graph.add('scale', '640:360')
fps   = graph.add('fps', fps=25)

# 3. 添加视频缓冲出口
sink = graph.add('buffersink')

# 4. 连接节点(简洁链式写法)
graph.link_nodes(buffer, scale, fps, sink)

# 5. 配置图(验证连接、协商格式)
graph.configure()

核心方法速查

方法

说明

视频注意事项

add_buffer(template, width, height, format, time_base)

添加视频入口

必须提供宽高和像素格式;可用 template Frame 自动填充

add(filter, args, **kwargs)

添加任意滤镜节点

args 为位置参数字符串,kwargs 为命名参数

link_nodes(*nodes)

按顺序串联节点

仅适用于简单线性链;复杂拓扑需手动 link_to

configure(auto_buffer, force)

验证并激活图

auto_buffer=True 自动插入格式转换;force=True 强制重配

vpush(frame)

推入 VideoFrame

仅接受视频帧,类型安全

vpull()

拉出 VideoFrame

返回 VideoFrameNone(无输出时)

push(frame) / pull()

通用推拉

不检查类型,可能返回音频帧

⚠️ vpush/vpull 使用要点

  • vpull() 可能返回 None:某些滤镜(如 fps、overlay)需要累积多帧才输出一帧

  • vpush() 后应循环 vpull() 直到返回 None,避免帧积压

  • 图未 configure() 前调用 push/pull 会抛异常


3. FilterContext:滤镜节点管理

手动连接(复杂拓扑)

link_nodes 无法满足需求时(如多输入 overlay、分支图):

# 手动连接:src 的输出端口 → dst 的输入端口
src.link_to(dst, output_idx=0, input_idx=0)

# overlay 示例:base 连 pad 0,overlay 连 pad 1
base_ctx.link_to(overlay_ctx, output_idx=0, input_idx=0)
logo_ctx.link_to(overlay_ctx, output_idx=0, input_idx=1)

节点属性

属性

说明

filter

关联的 Filter 描述符

graph

所属 Graph

name

节点名称(可自定义,用于调试)

inputs / outputs

FilterContextPad 列表

init(args, **kwargs)

延迟初始化(通常在 add 时已完成)


4. Filter:滤镜描述符查询

在构建图之前查询滤镜能力,避免运行时错误:

属性

视频开发用途

name / description

确认滤镜标识和功能说明

inputs / outputs

查看端口数量、名称、媒体类型

dynamic_inputs / dynamic_outputs

是否支持动态端口数(如 stack、concat)

slice_threads

是否支持切片级多线程

timeline_support

是否支持 timeline 编辑(enable 表达式)

command_support

是否支持运行时命令(如动态调整参数)

flags

滤镜标志位集合

options

可配置选项列表

f = av.filter.Filter('overlay')
print(f.inputs)          # [FilterPad(name='main', type='video'), FilterPad(name='overlay', type='video')]
print(f.dynamic_inputs)  # False
print(f.timeline_support) # True → 可用 enable='between(t,1,5)'

FilterPad(端口定义)

属性

说明

type

媒体类型字符串:'video', 'audio', 'subtitle'

is_input / is_output

端口方向

name

端口名(如 'main', 'overlay', 'default'

index

端口索引

FilterContextPad(运行时端口)

属性

说明

linked

布尔值,是否已建立连接

link

关联的 FilterLink 对象(未连接时为 None)

FilterLink(连接)

属性

说明

input / output

连接的输入/输出 FilterContextPad

graph

所属 Graph

💡 调试技巧:遍历 context.inputs 检查 pad.linked 可快速定位未连接的断点。


6. 视频滤镜常见工作流

缩放 + 帧率转换

graph = av.filter.Graph()
buf  = graph.add_buffer(template=input_frame)
scale = graph.add('scale', '1280:720:flags=bilinear')
fps   = graph.add('fps', fps=30)
sink  = graph.add('buffersink')
graph.link_nodes(buf, scale, fps, sink)
graph.configure()

graph.vpush(input_frame)
while (out := graph.vpull()) is not None:
    process(out)

视频叠加(Overlay)

graph = av.filter.Graph()
base_buf  = graph.add_buffer(template=base_frame, name='base_in')
logo_buf  = graph.add_buffer(template=logo_frame, name='logo_in')
overlay   = graph.add('overlay', '10:10:enable=between(t,0,5)')
sink      = graph.add('buffersink')

base_buf.link_to(overlay, 0, 0)   # base → overlay.main
logo_buf.link_to(overlay, 0, 1)   # logo → overlay.overlay
overlay.link_to(sink, 0, 0)

graph.configure()

动态参数调整

# 仅当 filter.command_support == True 时可用
# 通过 FilterContext 发送运行时命令(具体API依FFmpeg版本而定)

7. ⚠️ 开发排查备忘

问题

原因

解决方案

configure() 报错

端口类型/格式不匹配或未连接

检查 pad.linked;启用 auto_buffer=True 自动插格式转换

vpull() 始终返回 None

滤镜需要更多输入帧

继续 vpush();检查 fps/decimate 等累积型滤镜

vpush() 类型错误

误用 push() 传入了非视频帧

始终使用 vpush()/vpull() 保证类型安全

输出画面全黑/花屏

像素格式协商失败

显式指定 buffer format;或在 scale 中指定输出格式

内存泄漏

拉出的帧未释放

vpull() 返回的帧用完及时 del 或用 with

复杂图连接错误

link_nodes 不支持多输入

改用 link_to(output_idx, input_idx) 手动连接

滤镜不存在

名称拼写错误或 FFmpeg 未编译

先用 Filter(name) 查询;检查 av.filter.Filter 列表

性能瓶颈

未利用多线程

检查 filter.slice_threads;确保 Graph 在线程安全环境使用

💡 最佳实践总结

  • 入口必设格式add_buffer 必须明确 width/height/format,否则协商可能失败

  • 类型安全推拉:视频处理始终用 vpush/vpull,避免意外混入音频帧

  • 先查后用:构建图前用 Filter 描述符确认端口数和类型

  • 循环拉取:每次 push 后循环 pull 至 None,防止内部缓冲溢出

  • auto_buffer:生产环境建议开启,让 FFmpeg 自动插入 format/scale 适配节点


评论