使用Surface作为输入的MediaCodec:生成分块输出

7
我正在尝试通过MediaCodec.createInputSurface()从CameraPreview数据生成短的连续mp4文件。然而,重新创建MediaCodec及其相关表面需要停止相机以允许另一个调用mCamera.setPreviewTexture(...)。这种延迟会导致无法接受的丢帧量。
因此,我需要定期生成CODEC_CONFIGEND_OF_STREAM数据,而无需重新创建输入表面,从而不必调用mCamera.setPreviewTexture(...)。假设MediaFormat未更改,是否可能实现此目标?
(我正在适应fadden的CameraToMpegTest示例。我的完整代码在here未成功尝试: 调用MediaCodec.signalEndOfInputStream(),在分块之间排空MediaCodec,然后调用MediaCodec.flush()将会在第二次调用MediaCodec.signalEndOfInputStream()时产生一个IllegalStateException异常。
在分块之间调用MediaCodec.signalEndOfInputStream(),排空MediaCodec,然后调用MediaCodec.stop(); MediaCodec.configure(...), MediaCodec.start()而不再调用MediaCodec.createInputSurface()会产生以下错误:
    09-30 13:12:49.889  17638-17719/x.xx.xxxx E/Surface﹕ queueBuffer: error queuing buffer to SurfaceTexture, -19
09-30 13:12:49.889  17638-17719/x.xx.xxxx E/IMGSRV﹕ :0: UnlockPostBuffer: Failed to queue buffer 0x592e1e70
09-30 13:12:49.889  17638-17719/x.xx.xxxx E/CameraToMpegTest﹕ Encoding loop exception!
09-30 13:12:49.889  17638-17719/x.xx.xxxx W/System.err﹕ java.lang.RuntimeException: eglSwapBuffers: EGL error: 0x300b
09-30 13:12:49.896  17638-17719/x.xx.xxxx W/System.err﹕ at x.xx.xxxx.ChunkedHWRecorder$CodecInputSurface.checkEglError(ChunkedHWRecorder.java:731)
09-30 13:12:49.896  17638-17719/x.xx.xxxx W/System.err﹕ at x.xx.xxxx.ChunkedHWRecorder$CodecInputSurface.swapBuffers(ChunkedHWRecorder.java:713)
09-30 13:12:49.896  17638-17719/x.xx.xxxx W/System.err﹕ at x.xx.xxxx.ChunkedHWRecorder.startRecording(ChunkedHWRecorder.java:164)
09-30 13:12:49.896  17638-17719/x.xx.xxxx W/System.err﹕ at x.xx.xxxx.HWRecorderActivity$CameraToMpegWrapper.run(HWRecorderActivity.java:76)
09-30 13:12:49.896  17638-17719/x.xx.xxxx W/System.err﹕ at java.lang.Thread.run(Thread.java:841)

已解决 感谢fadden。完整的解决方案源代码在这里

1个回答

10
signalEndOfInputStream()调用会更新媒体编解码器栈中的各层状态。你可以从MediaCodecTest测试代码的注释中了解到一些有效操作,但总的来说,对于“非常规”用法,MediaCodec的行为并没有明确定义。
因此,您需要查看代码。输入表面的生命周期与OMXNodeInstance的生命周期绑定;这由GraphicBufferSource表示。一旦您发出EOS信号,GraphicBufferSource将忽略附加帧(请参见第426行)。没有方法可以重置EOS标志而不拆除GraphicBufferSource,但是当您这样做时,它会断开Surface下面的缓冲区队列。
因此,我认为您将无法停止/重新启动MediaCodec并继续使用Surface。

然而...你不应该需要这样做。 CameraToMpegTest将相机预览路由到SurfaceTexture,然后使用GLES将纹理渲染到编码器的输入表面上。SurfaceTexture与编码器解耦,不需要更改。我认为需要更改的是CodecInputSurface,它使用来自MediaCodec的Surface调用eglCreateWindowSurface(),告诉GLES在哪里绘制。如果在那里添加一个新的“更新Surface”API(销毁旧的EGLSurface,创建新的EGLSurface,eglMakeCurrent),并在启动新的MediaCodec时调用它,我认为一切都会正常工作。

更新以回应评论

重要的是只更改EGLSurfaceGLConsumer.cpp中的checkAndUpdateEglStateLocked()函数检查确保EGLDisplayEGLContext一旦设置就不会更改。因为它会更改EGLContext,所以你不能在CodecInputSurface中调用release()/eglSetup()。你只想销毁并重新创建EGLSurface


非常感谢!CodecInputSurface.updateSurface(newSurface) 执行 release(); mSurface = newSurface; eglSetup();。然后我调用 mMediaCodec.start(); mInputSurface.makeCurrent(); STextureRender.surfaceCreated();。在下一次调用 SurfaceTexture.updateTexImage() 时,我得到了 E/GLConsumer﹕ checkAndUpdateEglState: invalid current EGLContext。有什么想法吗?简述完整代码。我会继续努力的。 - dbro
1
我需要请你喝一杯啤酒。在每次调用CodecInputSurface.updateSurface(newSurface)后,我还必须删除对STextureRender.surfaceCreated()的调用。 - dbro
1
哦,没错,那会在 SurfaceTexture 下更改纹理 ID。 - fadden

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接