我正在制作一个视频流应用程序,它可以根据可用的上行链路带宽调整视频比特率,我希望它能够动态更改视频分辨率,以便在较低的比特率下不会出现太多的压缩伪影。虽然我通过释放 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/