我正在尝试使用GPU将效果应用于视频帧,然后将这些帧重新编码为新的结果视频。
为了表现,我实施了以下流程:
有3个不同的线程,每个线程都有自己的OpenGL上下文。这些上下文的设置方式使它们在它们之间共享纹理。
线程1 从视频中提取帧并将其作为纹理保存在GPU内存中,类似于 this 示例。
线程2 使用修改后的GPUImage版本处理纹理,该版本也会在GPU内存中输出纹理。
最后,主题3 将从线程2获得的纹理写入一个新的视频文件,类似于描述的方法 here
使用线程1和2以及线程2和3之间的队列维护帧顺序。纹理在>>用于处理/写入之后手动从内存中删除。
这个流程的重点是将每个进程分开,希望最终的性能是3个线程中最慢的。
问题:
最终视频是90%的黑色帧,其中只有一些是正确的。
我已经检查了提取和处理的各个结果,并且它们按预期工作。另请注意,3个线程中描述的3个组件在单个线程中可以很好地协同工作。
我试图同步线程1和线程3,并且在为线程1添加额外的100ms休眠时间后,视频结果很好,可能有1或2个黑帧。在我看来,解码器和编码器的两个实例无法同时工作。
我将使用任何额外请求的详细信息编辑此帖子。
答案 0 :(得分:3)
在OpenGL ES上下文之间共享纹理需要一些小心。它在Grafika's“show + capture camera”中实现的方式被破坏了;有关详细信息,请参阅this issue。基本问题是你在更新纹理时基本上需要发出内存障碍;实际上,这意味着在生产者方面发布glFinish()
,并在消费者方面重新绑定纹理,并在synchronized
块中执行所有这些操作。
如果您可以在单个线程上完成所有GLES工作,那么您的生活将变得更简单(也更高效)。根据我的经验,一次有多个GLES上下文是不明智的,你可以通过寻找替代方案来节省一些痛苦。
你可能想要更像这样的东西:
在所有情况下,线程之间(以及引擎盖下的进程)之间的唯一通信是通过Surface完成的。 SurfaceTexture和MediaCodec实例是从单个线程创建和使用的;只传递生产者端点(Surface)。
一个潜在的问题点在于流量控制 - 如果你过快地喂它们,SurfaceTextures会丢帧。根据具体情况,组合线程#1和#2可能有意义。