SurfaceTexture的onFrameAvailable()方法总是调用得太迟了

我正在尝试使用以下MediaExtractor示例:

http://bigflake.com/mediacodec/ – ExtractMpegFramesTest.java(需要4.1,API 16)

我遇到的问题是outputSurface.awaitNewImage(); 似乎总是抛出RuntimeException(“帧等待超时”),每当mFrameSyncObject.wait(TIMEOUT_MS)调用超时时抛出它。 无论我将TIMEOUT_MS设置为什么, onFrameAvailable()总是在超时发生后立即被调用。 我尝试了50毫秒和30000毫秒,它是相同的。

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

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

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

编辑2:

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

在调用codec.configure() ,4.1.2上还有一个小问题,它给出了错误:

 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) 

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

 format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 0); 

然而,我现在在4.1.1(Galaxy S2 GT-I9100)和4.1.2(三星Galaxy Tab GT-P3110)上的问题是它们都始终将info.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 

所以没有图像得到保存。 但是相同的代码和video适用于4.3。 我正在使用的video是带有“H264-MPEG-4 AVC(avc1)”video编解码器和“MPEG AAAC Audio(mp4a)”音频编解码器的.mp4文件。

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

编辑3:

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

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

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

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

有了这个,其中surfaceTexture是我的xml-layout中定义的SurfaceTexture:

 mSurfaceTexture = textureView.getSurfaceTexture(); mSurfaceTexture.attachToGLContext(mTextureRender.getTextureId()); 

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

Solutions Collecting From Web of "SurfaceTexture的onFrameAvailable()方法总是调用得太迟了"

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

文档说,框架可用的回调“在任意线程上调用”。 SurfaceTexture类有一些代码在初始化时执行以下操作( 第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()超时,并且线程返回到观察事件队列,在那里它立即发现待处理的“新帧”消息。

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

更新(对于“编辑3”) :我有点难过忽略“大小”字段有帮助,但4.3之前很难预测设备的行为方式。

如果您只想显示框架,请将SurfaceViewTextureView获得的Surface传递给MediaCodec解码器configure()调用。 然后你根本不必弄乱SurfaceTexture – 在解码时会显示帧。 有关示例,请参阅Grafika中的两个“播放video”活动。

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