视频编码流水线——线程设计

Video encoding pipeline - threads design

我在一个系统上工作,该系统对多个通道进行视频捕获和编码。每个阶段都需要时间。 capture/encoding 是在硬件中完成的,但仍然需要一些时间才能完成。

capture frames->encode->file-save (or stream to network)

我有两难选择什么更好approach/design:

  1. 每个通道一个线程,依次调用管道阻塞API,例如:

    while(1)
    {
     frame = get_next_capture_frame(); //no blocking api - every 1/60 sec
     prev_bitstream =  send_to_encode_and_get_any_already_encoded_frame(frame); //no blocking api
     send_to_save_bitstream(prev_bitstream); //no blocking api
     delay(1/60); //wait 1/60
    }
    

还是使用多个线程各自完成其工作更好: 一个线程用于捕获,另一个用于编码,另一个用于文件管理。 这个问题变得更加复杂,因为涉及多个通道(大约 6 个通道 - 这可能导致第一种方法中有 6 个线程,第二种方法中有 18 个线程)

  1. 这个问题领域的另一个难题:线程应该定期唤醒并在队列中等待作业(比如每 60fps 唤醒一次),还是应该根据新事件(用于捕获的新缓冲区、用于编码器的新缓冲区)唤醒线程等)

这在某种程度上取决于要求。如果您知道您将始终以 60 FPS 的速度拥有 6 个通道,并且 Capture/Encode/Save 过程将花费不到 1/60 秒,那么每个通道一个线程是最容易编码的。但请注意,如果有时编码或保存时间过长,您将无法如期获得下一帧。

您可以使用流水线方法(类似于您的第二个选项),但不能基于每个线程。也就是说,如果您可以拥有一个线程,它除了每秒从每个通道读取和存储帧 60 次之外什么都不做。这些帧进入捕获队列。您有一个单独的线程(或者可能是多个线程)从 Captured 队列读取数据,对数据进行编码,并将结果保存到 Output 队列。最后,一个或多个输出线程读取输出队列并保存编码数据。

队列是共享的,因此所有的编码线程,例如,从同一个捕获队列读取并写入同一个输出队列。现在大多数编程语言都有高效的线程安全队列。许多具有不需要忙等待的结构。也就是说,编码线程可以在捕获队列为空时进行非忙碌等待。当队列中有内容时,线程将收到通知。例如,参见 .NET 的 BlockingCollection 或 Java 的 ConcurrentLinkedQueue

该模型扩展性很好。例如,如果您需要更多编码线程来跟上吞吐量,您可以只增加这些线程。例如,您最终可能会得到两个捕获线程、8 个编码器和一个输出线程。你可以根据你的工作量来平衡它。

至于调度,我怀疑您希望您的捕获线程定期运行(即每 1/60 秒一次,或者无论您的帧速率是多少)。编码和输出线程应配置为在各自的队列中等待。例如,输出线程没有理由持续轮询输出队列以获取数据。相反,它可以空闲(等待)并在数据包放入输出队列时收到通知。

有关如何为视频编码器执行此操作的详细信息可能会使该方法不必要地混乱。我真的不知道。如果编码器需要通道特定的状态信息,就会变得更加困难。当您考虑到该模型将允许两个编码器处理来自同一通道的帧时,这尤其困难。如果发生这种情况,那么您需要某种方法来对输出进行排序。

你的第一种方法是最简单的,这也是我在第一次剪辑程序时所做的。如果这不能维持您需要的吞吐量,那么我会考虑更复杂的方法。