Android Camera2:即时更改输出表面设置的最佳和快速方式

问题描述 投票:3回答:1

我正在制作一个视频流应用程序,使视频比特率适应可用的上行链路带宽,我希望它能动态地改变视频分辨率,以便在较低的比特率上没有那么多的压缩伪像。虽然我通过发布MediaCodec并在abortCaptures()上调用stopRepeating()CameraCaptureSession然后为新分辨率配置所有内容来实现这一点,但这会导致流中断非常明显 - 在我的测试中至少半秒钟。

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

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

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

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

android android-camera mediacodec android-camera2
1个回答
4
投票

事实证明,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,因为相机输出的表面组没有变化。

© www.soinside.com 2019 - 2024. All rights reserved.