SurfaceTexture的onFrameAvailable()方法总是被调用太晚了

时间:2014-03-17 14:54:57

标签: android opengl-es mediacodec mediaextractor

我试图让以下MediaExtractor示例正常工作:

http://bigflake.com/mediacodec/ - ExtractMpegFramesTest.java (requires 4.1, API 16)

我遇到的问题是outputSurface.awaitNewImage();似乎总是抛出RuntimeException(&#34;帧等待超时&#34;),这会在mFrameSyncObject.wait(TIMEOUT_MS)调用超时时抛出。无论我将TIMEOUT_MS设置为什么,{<1}}总是在超时发生后立即调用。我尝试了50毫秒和30000毫秒,它是一样的。

似乎在线程繁忙时无法进行onFrameAvailable()调用,一旦超时发生并结束线程代码执行,它就可以解析onFrameAvailable()调用。 / p>

有没有人设法让这个例子起作用,或者知道MediaExtractor应该如何使用GL纹理?

编辑在使用API​​ 4.4和4.1.1的设备上尝试了这一点,两者都发生了同样的情况。

编辑2:

得益于fadden 4.4。问题是名为onFrameAvailable()的{​​{1}}方法阻止了主线程并阻止了ExtractMpegFramesWrapper.runTest()调用的处理。一旦我发表评论th.join();,它就适用于4.4。我想也许onFrameAvailable()本身应该在另一个线程上运行,所以主线程没有被阻止。

在调用th.join();时4.1.2上也有一个小问题,它给出了错误:

ExtractMpegFramesWrapper.runTest()

我通过在通话前添加以下内容解决了这个问题:

codec.configure()

然而,我现在在4.1.1(Galaxy S2 GT-I9100)和4.1.2(三星Galaxy Tab GT-P3110)上遇到的问题是它们都始终将info.size设置为0以用于所有帧。这是日志输出:

A/ACodec(2566): frameworks/av/media/libstagefright/ACodec.cpp:1041 CHECK(def.nBufferSize >= size) failed.
A/libc(2566): Fatal signal 11 (SIGSEGV) at 0xdeadbaad (code=1), thread 2625 (CodecLooper)

所以没有图像被保存。但是相同的代码和视频适用于4.3。我正在使用的视频是带有&#34; H264的.mp4文件 - MPEG-4 AVC(avc1)&#34;视频编解码器和&#34; MPEG AAAC音频(mp4a)&#34;音频编解码器。

我也尝试过其他视频格式,但它们似乎在4.1.x时更早死亡,而两者都在4.3上工作。

编辑3:

我按照你的建议做了,似乎正确地保存了帧图像。谢谢。

关于KEY_MAX_INPUT_SIZE,我尝试不设置,或将其设置为0,20,200,... 200000000,所有结果与info.size = 0相同。

我现在无法在我的布局上将渲染设置为SurfaceView或TextureView。我尝试更换这一行:

format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 0);

有了这个,其中loop input buffer not available no output from decoder available loop input buffer not available no output from decoder available loop input buffer not available no output from decoder available loop input buffer not available no output from decoder available loop submitted frame 0 to dec, size=20562 no output from decoder available loop submitted frame 1 to dec, size=7193 no output from decoder available loop [... skipped 18 lines ...] submitted frame 8 to dec, size=6531 no output from decoder available loop submitted frame 9 to dec, size=5639 decoder output format changed: {height=240, what=1869968451, color-format=19, slice-height=240, crop-left=0, width=320, crop-bottom=239, crop-top=0, mime=video/raw, stride=320, crop-right=319} loop submitted frame 10 to dec, size=6272 surface decoder given buffer 0 (size=0) loop [... skipped 1211 lines ...] submitted frame 409 to dec, size=456 surface decoder given buffer 1 (size=0) loop sent input EOS surface decoder given buffer 0 (size=0) loop surface decoder given buffer 1 (size=0) loop surface decoder given buffer 0 (size=0) loop surface decoder given buffer 1 (size=0) loop [... skipped 27 lines all with size=0 ...] surface decoder given buffer 1 (size=0) loop surface decoder given buffer 0 (size=0) output EOS Saving 0 frames took ? us per frame // edited to avoid division-by-zero error 是我的xml-layout中定义的SurfaceTexture:

mSurfaceTexture = new SurfaceTexture(mTextureRender.getTextureId());

但它在第二行引发了surfaceTexture的奇怪错误。我无法找到任何其他方法让它在某种视图上绘制。如何更改解码器以在Surface / SurfaceView / TextureView上显示帧而不是保存它们?

1 个答案:

答案 0 :(得分:14)

SurfaceTexture的工作方式使得这一点变得有点棘手。

docs表示在任意线程上调用可用帧回调&#34;&#34;。 SurfaceTexture类有一些代码在初始化(line 318)时执行以下操作:

if (this thread has a looper) {
    handle events on this thread
} else if (there's a "main" looper) {
    handle events on the main UI thread
} else {
    no events for you
}

框架可用的事件通过通常的Looper / Handler机制传递到您的应用。该机制只是一个消息队列,这意味着线程需要位于Looper事件循环中,等待它们到达。问题是,如果你在awaitNewImage()中睡觉,你就不会看Looper队列了。事件到来了,但没有人看到它。最终awaitNewImage()超时,并且线程返回观察事件队列,在那里它立即发现待处理的&#34;新帧&#34;消息。

所以诀窍是确保框架可用事件到达与awaitNewImage()中的线程不同的线程。在ExtractMpegFramesTest示例中,这是通过在新创建的线程中运行测试来完成的(请参阅ExtractMpegFramesWrapper类),该线程没有Looper。 (由于某种原因,执行CTS测试的线程有一个looper。)可用帧的事件到达主UI线程。

更新(针对&#34;编辑3&#34;):我有点难过忽略&#34;尺寸&#34;领域有所帮助,但在4.3之前很难预测设备的运行方式。

如果只是想要显示相框,请将SurfaceSurfaceView中的TextureView传递到MediaCodec解码器{ {1}}致电。然后你根本不必弄乱configure() - 在解码它们时会显示帧。请参阅两个&#34;播放视频&#34;例如Grafika中的活动。

如果您真的想要浏览SurfaceTexture,则需要更改CodecOutputSurface以渲染到窗口表面而不是pbuffer。 (屏幕外渲染已完成,因此我们可以在无头测试中使用SurfaceTexture。)