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

我试图让下面的MediaExtractor例子工作:

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

我有的问题是outputSurface.awaitNewImage(); 似乎总是抛出RuntimeException(“帧等待超时”),这是每当mFrameSyncObject.wait(TIMEOUT_MS)调用超时引发的。 无论我设置了TIMEOUT_MSonFrameAvailable()总是在超时发生后立即被调用。 我尝试了50ms和30000ms,这是一样的。

在线程忙的时候, onFrameAvailable()调用看起来不可能完成,一旦超时发生,线程代码执行结束,它可以parsingonFrameAvailable()调用。

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

编辑:在API 4.4和4.1.1的设备上尝试过,两者都发生同样的情况。

编辑2:

得到它的工作4.4感谢fadden。 问题是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) 

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

 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)”audio编解码器的.mp4文件。

我也尝试过其他的video格式,但是它们在4.1.x上似乎很快就会死掉,而它们都在4.3上工作。

编辑3:

我按照你的build议,并且似乎正确地保存帧图像。 谢谢。

关于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引发了一个奇怪的错误。 我无法find任何其他方式来绘制某种视图。 我怎样才能改变解码器显示在Surface / SurfaceView / TextureView而不是保存它们的帧?

SurfaceTexture工作方式使得这样做有点棘手。

文档说框架可用callback“在任意线程上调用”。 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()超时,线程返回到观看事件队列,立即发现挂起的“新帧”消息。

所以诀窍就是确保frame-available事件从awaitNewImage()的一个到另一个线程。 在ExtractMpegFramesTest示例中,这是通过在新创build的线程(参见ExtractMpegFramesWrapper类)中运行testing来完成的,该testing没有Looper 。 (出于某种原因,执行CTStesting的线程有一个活套。)框架可用事件到达主UI线程。

更新(“编辑3”) :忽略“尺寸”字段有点让人伤心,但4.3之前很难预测设备的行为。

如果您只想显示框架,请将从SurfaceViewTextureView获取的Surface传递到MediaCodec解码器的configure()调用中。 那么你根本就不必乱用SurfaceTexture – 这些帧会在你解码的时候显示出来。 以Grafika中的两个“播放video”活动为例。

如果你真的想要通过一个SurfaceTexture ,你需要改变CodecOutputSurface渲染到一个窗口表面,而不是一个pbuffer。 (离屏渲染完成后,我们可以在无头testing中使用glReadPixels() 。)