Android 表面结构&x27;s onFrameAvailable()方法调用总是太迟

Android 表面结构&x27;s onFrameAvailable()方法调用总是太迟,android,opengl-es,android-mediacodec,mediaextractor,Android,Opengl Es,Android Mediacodec,Mediaextractor,我正在尝试让以下MediaExtractor示例正常工作: 我遇到的问题是outputSurface.awaitNewImage();似乎总是抛出RuntimeException(“帧等待超时”),每当调用超时时就会抛出该异常。无论我将TIMEOUT\u MS设置为什么,onFrameAvailable()总是在超时发生后立即被调用。我试了50毫秒和30000毫秒,结果都一样 线程繁忙时,似乎无法执行onFrameAvailable()调用,一旦超时结束线程代码执行,它就可以解析onFrame

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

我遇到的问题是outputSurface.awaitNewImage();似乎总是抛出RuntimeException(“帧等待超时”),每当调用超时时就会抛出该异常。无论我将
TIMEOUT\u MS
设置为什么,
onFrameAvailable()
总是在超时发生后立即被调用。我试了50毫秒和30000毫秒,结果都一样

线程繁忙时,似乎无法执行
onFrameAvailable()
调用,一旦超时结束线程代码执行,它就可以解析
onFrameAvailable()
调用

是否有人成功地使此示例生效,或者知道MediaExtractor应该如何处理GL纹理

编辑:在使用API 4.4和4.1.1的设备上尝试了此操作,并且在这两种设备上都进行了相同的操作

编辑2:

感谢法登,它在4.4版上运行。问题是
ExtractMpegFramesWrapper.runTest()
方法调用了
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(Samsung 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
所以没有图像被保存。然而,相同的代码和视频在4.3上工作。我使用的视频是一个.mp4文件,带有“H264-MPEG-4avc(avc1)”视频编解码器和“MPEG-AAAC音频(mp4a)”音频编解码器

我还尝试了其他视频格式,但它们在4.1.x上似乎消失得更快,而在4.3上两者都可以工作

编辑3:

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

关于KEY_MAX_INPUT_SIZE,我尝试不设置,或者将其设置为0、20、200。。。200000000,所有信息的结果相同。大小=0

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

mSurfaceTexture = new SurfaceTexture(mTextureRender.getTextureId());
其中,
surfaceTexture
是在我的xml布局中定义的surfaceTexture:

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

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

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

假设帧可用回调“在任意线程上调用”。
SurfaceTexture
类有一些代码,在初始化()时执行以下操作:

帧可用事件通过通常的
活套
/
处理程序
机制传递到应用程序。该机制只是一个消息队列,这意味着线程需要坐在
循环器中等待它们到达。问题是,如果您在
waitingnewimage()
中睡觉,您就没有看到
Looper
队列。因此,事件发生了,但没有人看到它。最后,
waitingnewimage()
超时,线程返回到事件队列,在那里它会立即发现挂起的“新帧”消息

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

更新(对于“编辑3”):我有点难过,忽略“大小”字段有帮助,但在4.3之前,很难预测设备将如何运行

如果只想显示帧,请将从
SurfaceView
TextureView
获取的
Surface
传递到
MediaCodec
解码器
configure()
调用中。这样,您就根本不必处理
SurfaceTexture
——帧将在解码时显示。有关示例,请参见中的两个“播放视频”活动


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

感谢您的回复。我设法解决了线程问题,现在它可以在4.4上运行,但不能在4.1.1或4.1.2上运行。我已经用调试数据更新了帖子。为什么所有的输出缓冲区都有info.size=0?这很奇怪。如果忽略大小,只渲染未标记为流结束的帧,会发生什么情况?(这不是正确的方法,但我正在试图弄清楚它是否发送数据并误报大小,或者只是不发送任何数据。)您是否尝试过将
KEY\u MAX\u INPUT\u size
设置为一个大值而不是零?(我想我从来没有设置过——不知道你为什么会得到断言失败。)再次感谢你的帮助,我得到它是为了保存帧。我已经用细节更新了我的帖子。我现在无法显示它。我的纹理附件做得不对吗?更新的答案。我突然想到,你从来没有真正说出你的想法
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
}