【问题标题】:MediaCodec with Surface Input: Recording in background带有 Surface 输入的 MediaCodec:在后台录制
【发布时间】:2013-11-26 18:16:35
【问题描述】:

我正在开发一个视频编码应用程序,我想防止它在托管 Activity 进入后台或屏幕循环关闭/打开时停止。

我的编码器架构源自出色的CameraToMpegTest 示例,并在 GLSurfaceView 中添加了显示相机帧(请参阅下面的 Github 链接)。我目前正在使用两种状态的解决方案进行后台录制:

  • 当托管 Activity 在前台时,每次调用 GLSurfaceView.RendereronDrawFrame 时编码一个视频帧。这使我可以突发​​访问 GLSurfaceView 的 EGL 状态,以免阻塞排队到渲染器线程的其他事件。

  • 当宿主 Activity 进入后台时,停止 onDrawFrame 编码并在循环内的另一个后台线程上编码帧。此模式与 CameraToMpegTest 示例相同。

然而如果屏幕关闭,GLSurfaceView 的 EGLContext 会丢失,并且会重新调用 onSurfaceCreated。在这种情况下,我们必须重新创建连接到 MediaCodec 的输入 Surface 的 EGL 窗口表面。不幸的是,第二次调用eglCreateWindowSurface 产生:

E/libEGL(18839): EGLNativeWindowType 0x7a931098 already connected to another API

打电话之前,我release all EGL resources connected to the Android Surface

有没有办法交换连接到 MediaCodec 的输入 Surface 的 EGLSurface?

我的测试应用程序的完整来源在GithubMain Activity.

更新我将在这里学到的经验应用到基于 MediaCodec 和 MediaMuxer 类的video sdk for Android 中。希望对您有所帮助!

【问题讨论】:

  • MediaCodec 不应受到(或什至意识到)在后台的影响。参见例如在 Android 4.4 中添加的 screenrecord 命令,它在幕后愉快地运行。它正在编码 anything 的事实意味着它仍在接收输入数据,有人猜测是某些东西正在影响Camera。我不明白为什么它会导致预览Surface 为空白,而预览byte[] 有真实数据。
  • 更新了我的问题。我现在能够过渡到后台录制(无 GLSurfaceView 显示),然后是前台录制(GLSurfaceView 显示)除了,在此期间发生屏幕关闭/开启事件...
  • 您在主要活动 onPause() 中缺少glSurfaceView.onPause()。不确定这是否重要。我明天也许可以玩一下,看看我是否可以复制这种行为。
  • Activity 的onPause() 调用一个方法,该方法使用glSurfaceView.queueEvent() 对必须在渲染线程上发生的操作进行排队,然后glSurfaceView.onPause() 暂停它。 See here
  • 屏幕关闭/打开会导致 GLSurfaceView 的 EGLContext 丢失,因此我们必须创建一个新的 CodecInputSurface.mEGLSurface 来共享新的 EGLContext。但是在断开 MediaCodec 输入 Surface 时遇到问题,以避免在 eglCreateWindowSurface 上出现 already connected to another API 错误。这应该可能吗?

标签: android android-mediacodec


【解决方案1】:

背景优先...

当您调用eglCreateWindowSurface() 时,Android EGL wrapper 会在您传入的Surface 上调用native_window_api_connect()。这最终会变成BufferQueue 生产者连接调用,这意味着这个EGL 表面现在是唯一的Surface 的图形缓冲区来源。

EGL 表面保持与Surface 的连接,直到 EGL 表面被破坏。如果是,surface destructor 调用native_window_api_disconnect() 以断开 EGL 表面与BufferQueue 的连接。 EGL 表面是引用计数的,当表面被传递给eglMakeCurrent() 时,引用计数会增加,因此必须发生两件事才能被销毁:

  1. 必须调用eglDestroySurface()
  2. EGL 表面不得在任何线程中为“当前”

第二个项目需要使用另一个 EGL 表面(或 EGL_NO_SURFACE)调用 eglMakeCurrent(),或在之前使用该表面的任何线程上调用 eglReleaseThread()。确认完成的一种快速方法是在调用 eglMakeCurrent() 之前添加日志记录,当表面变为当前和非当前时,并通过查看 logcat 输出与 adb logcat -v threadtime 比较线程 ID。使用 eglGetCurrentSurface(EGL_DRAW) 之类的 EGL 查询也可能很有用,以确认您正在使表面成为电流的线程中执行非电流。

如果 EGL 表面没有被破坏,它不会与 Surface 断开连接,并且尝试连接新的生产者(通过调用 eglCreateWindowSurface 与新的 EGL 表面)将被拒绝,并显示“已经连接"消息。

更新:我的实现现在在Grafika test project 中可用。如果你安装这个,选择“显示+捕获相机”,开始录制,切换电源,然后停止录制,你应该有一个完整的电影,中间有很长的停顿。您可以退出,选择“播放视频”,然后选择“camera-test.mp4”进行查看。

【讨论】:

  • 尤里卡!涂鸦很漂亮。看到所有移动部件都正确模块化,让我的工艺闻起来像蜡笔和胶水。
  • @fadden 在 ContinuousCaptureActivity.java(github.com/google/grafika/blob/master/src/com/android/grafika/…) 中,onPause 时先释放 mDisplaySurface,再释放 mEglCore。 mDisplaySurface.release() 最终将调用 EGL14.eglDestroySurface,但此时您没有确认“当前”问题。 eglDestroySurface 会成功吗?
  • 我不确定您所说的“没有确认‘当前’问题”是什么意思。如答案中所述,资源是参考计数的;电流增加了一个额外的参考。因此,当 eglDestroySurface() 被调用 并且 它不再是当前的时,该表面将被丢弃。 一个与此代码相关的错误 -- github.com/google/grafika/issues/24 -- 但更多的是关于我在 onResume() 中未能做的事情。
猜你喜欢
  • 2013-10-06
  • 2013-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-29
  • 2020-10-11
  • 2015-12-03
相关资源
最近更新 更多