安卓相机2 : the most optimal and fast way to change the output surface set on-the-fly

标签 android android-camera android-mediacodec android-camera2

我正在制作一个视频流应用程序,它可以根据可用的上行链路带宽调整视频比特率,我希望它能够动态更改视频分辨率,以便在较低的比特率下不会出现太多的压缩伪影。虽然我通过释放 MediaCodec 并在 CameraCaptureSession 上调用 abortCaptures()stopRepeating() 来完成这项工作> 然后为新分辨率配置所有内容,这会导致流中出现非常明显的中断 - 在我的测试中至少有半秒。

当相机本身不支持所需的分辨率时,我使用 OpenGL 缩放图像,类似于 this .我使用两个表面初始化捕获 session - 一个用于向用户预览(使用 TextureView),另一个用于编码器,即 MediaCodec 的直接输入表面或我的 OpenGL 纹理表面。

这可以通过使用 MediaCodec.createPersistentInputSurface() 来解决,因为我将能够在分辨率更改时重用这个缩放器实例,而不必对捕获 session 做任何事情因为就相机而言,表面没有发生变化,但它仅在 API 23 之后可用,我也需要此实现来支持 API 21。

然后还有表面失效和重新创建的问题。例如,当用户按下后退按钮时, Activity 及其包含的 TextureView 将被销毁,从而使预览表面无效。然后,当用户再次导航到该 Activity 时,将创建一个新的 TextureView,我需要开始在其中显示预览,而不会给缩放器/编码器看到的流带来任何延迟。

所以,我的一般问题是:如何更改 CameraCaptureSession 中的输出表面集,或重新创建 CameraCaptureSession,同时尽可能少地延迟尽可能视频流?

最佳答案

事实证明,实际上保存纹理的 OpenGL 上下文(包括相机向其提供帧的纹理)与任何特定的输出目标无关。因此,我能够让我的视频缩放器在初始化后更改其输出表面,如下所示:

...
}else if(inputMessage.what==MSG_CHANGE_SURFACE){
    // Detach the current thread from the context, as a precaution
    EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
    checkEglError("eglMakeCurrent 1");

    // Destroy the old EGL surface and release its Android counterpart
    // the old surface belongs to the previous, now released, MediaCodec instance
    EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
    checkEglError("eglDestroySurface");
    surface.release(); // surface is a field that holds the current MediaCodec encoder input surface

    surface=(Surface) inputMessage.obj;
    dstW=inputMessage.arg1; // these are used in glViewport and the fragment shader
    dstH=inputMessage.arg2;

    // Create a new EGL surface for the new MediaCodec instance
    int[] surfaceAttribs={
        EGL14.EGL_NONE
    };
    mEGLSurface=EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0], surface, surfaceAttribs, 0);
    checkEglError("eglCreateWindowSurface");

    // Make it current for the current thread
    EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
    checkEglError("eglMakeCurrent 2");

    // That's it, any subsequent draw calls will render to the new surface
}

使用这种方法,不需要重新初始化 CameraCaptureSession,因为相机输出到的表面集没有变化。

关于安卓相机2 : the most optimal and fast way to change the output surface set on-the-fly,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55043378/

相关文章:

android - 在Android 4.2上调整Mediacodec解码器的缓冲区大小

android - 视频渲染损坏 MediaCodec H.264 流

java - Android MediaCodec 在异步模式下比在同步模式下慢?

android 时间戳解析出错(总是在 1970 年)

自定义搜索栏上的 Android 标签

java - 如何通过 OnItemClickListener 使 imageView 可点击?

android - 如何在 VR 模式下显示相机 View ?

android - 如何存储多个JSON数组的项目并在 ListView 中显示?

android - Android Camera 参数中, PictureSize() 、 PreviewSize 和显示尺寸之间的关系是什么?

javascript - 在 React Native 移动应用程序中检测相机的高度和宽度