ff_filter_graph_run_once() 是 FFmpeg 滤镜系统内部用于驱动 单次滤镜链路调度 的核心函数。它负责查找当前最需要执行的滤镜,并激活它处理数据。
下面的表格整理了它的核心功能与工作原理和关键作用:
| 特性维度 | 功能描述 | 在项目中的关键意义 |
|---|---|---|
| 主要功能 | 驱动单步执行:每调用一次,就推动整个滤镜链路向前执行"一步"。 | 在"等待并排空 "场景中,必须反复循环调用此函数 ,才能将队列中的帧逐一处理并最终输出到编码器。 |
| 工作原理 | 1. 查找就绪滤镜 :遍历图中所有滤镜 (AVFilterContext),基于其ready字段等状态,找出当前优先级最高且准备就绪 的滤镜。 2. 激活处理 :调用 ff_filter_activate() 激活该滤镜。这会触发滤镜从输入链路的 FIFO 队列中取出帧进行处理,或通过 request_frame 向上游请求新帧。 |
保证了所有滤镜(包括异步滤镜 )都能按正确的调度顺序被触发,从而让已入队的帧能流转起来。 |
| 调度机制 | 基于优先级调度 。当一个滤镜通过 ff_filter_frame() 向下游发送帧时,会同时设置下游滤镜的 ready 优先级。run_once 就依据此优先级决定激活顺序。 |
理解此机制,能帮助分析在等待异步帧时,为何需要循环调用它来"驱动"整个链路,而不是仅仅等待。 |
| 返回值 | - 0 :成功执行了一个滤镜。 - AVERROR(EAGAIN) :本次调用没有找到可以立即执行的滤镜(例如所有输入队列为空或滤镜在等待)。这是一个关键信号。 |
当返回 EAGAIN 时,意味着当前队列中所有能立即处理的帧都已处理完 。需要结合async_frame_count判断:如果还有异步帧未完成,就应稍作等待。 |
| 与异步的关系 | 它是同步、主动的驱动者 。它本身不管理异步任务,但负责执行那些已由异步回调将帧放入队列后的滤镜。 | 需要在异步回调函数中,在调用 ff_filter_frame() 将处理完的帧送回滤镜链路后,主动调用 (或安排其他线程调用)run_once 或其外层驱动函数(如 push_frame),才能让帧继续流动。 |
思路:如何与异步插件结合
分辨率变化的时候,所有滤镜会直接uninit,此时异步处理帧会丢失,如何解决该问题,核心思路如下:
-
异步处理完成时 :在filter异步处理回调
async_process_complete_callback中,在调用ff_filter_frame()之后,需要设法触发一次ff_filter_graph_run_once()。如果整个滤镜图运行在独立线程或push_frame这样的循环中,这可能意味着要设置一个状态标志或发送信号来通知主驱动循环。 -
在
wait_and_drain_graph函数中 :这里需要循环调用ff_filter_graph_run_once(),直到它返回AVERROR(EAGAIN)并且async_frame_count降为 0。while (1) { ret = ff_filter_graph_run_once(graph); if (ret == AVERROR(EAGAIN)) { if (atomic_load(&graph->async_frame_count) == 0) { break; // 所有帧都处理完毕 } // 还有异步帧在途中,等待一小段时间或等待条件变量 av_usleep(1000); continue; } if (ret < 0) { // 处理错误 break; } // 成功处理了一帧,继续循环 }
总结来说,ff_filter_graph_run_once() 是确保滤镜链路动起来的"发动机"。在异步插件场景中,必须确保在所有合适的时机(异步回调后、等待排空时)主动"踩油门"(调用它),才能让数据走完整个流程。