修改 ExtractMpegFramesTest 示例以在屏幕上呈现解码输出
Modify ExtractMpegFramesTest example to render decoded output on screen
我正在尝试修改 ExtractMpegFramesTest 以在屏幕上进行渲染,并且仍然使用 glReadPixels
来提取帧。
我复制了从 ExtractMpegFramesTest(CodecOutputSurface 和 STextureRender 类)中提取帧的相关代码,并且在屏幕外渲染时帧提取按预期工作。
我有一个 TextureView
和一个 SurfaceTextureListener
,当我收到 onSurfaceTextureAvailable
时,我得到 SurfaceTexture
并开始解码过程。
我将此 SurfaceTexture
传递给 CodecOutputSurface
但它不起作用。
我不确定这是否相关,但是 onSurfaceTextureAvailable
和 SurfaceTexture
是在主线程上接收的,所有解码(包括 CodecOutputSurface 构造函数调用)都是在不同的线程上完成的。
我尝试使用 here and here 的建议,但我无法让它工作。
我在日志中看到了这个:
E/BufferQueueProducer: [SurfaceTexture-0-11068-20] connect(P): already connected (cur=1 req=3)
I/MediaCodec: native window already connected. Assuming no change of surface
E/MediaCodec: configure failed with err 0xffffffea, resetting...
我对 ExtractMpegFramesTest eglSetup 方法进行了以下修改:
private void eglSetup() {
mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
throw new RuntimeException("unable to get EGL14 display");
}
int[] version = new int[2];
if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
mEGLDisplay = null;
throw new RuntimeException("unable to initialize EGL14");
}
int[] attribList = {
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_WINDOW_BIT, // tell it to use a window
EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfigs = new int[1];
if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,
numConfigs, 0)) {
throw new RuntimeException("unable to find RGB888+recordable ES2 EGL config");
}
// Configure context for OpenGL ES 2.0.
int[] attrib_list = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
EGL14.EGL_NONE
};
mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT,
attrib_list, 0);
checkEglError("eglCreateContext");
if (mEGLContext == null) {
throw new RuntimeException("null context");
}
int[] surfaceAttribs = {
EGL14.EGL_RENDER_BUFFER, EGL14.EGL_SINGLE_BUFFER,
EGL14.EGL_NONE
};
mSurfaceTexture.setOnFrameAvailableListener(this);
mSurface = new Surface(mSurfaceTexture);
mPixelBuf = ByteBuffer.allocateDirect(mWidth * mHeight * 4);
mPixelBuf.order(ByteOrder.LITTLE_ENDIAN);
mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0], mSurface,
surfaceAttribs, 0); // create window surface instead of eglCreatePbufferSurface
checkEglError("eglCreateWindowSurface");
if (mEGLSurface == null) {
throw new RuntimeException("surface was null");
}
}
以及ExtractMpegFramesTest设置方法:
private void setup() {
mTextureRender = new STextureRender();
mTextureRender.surfaceCreated();
if (VERBOSE) Log.d(TAG, "textureID=" + mTextureRender.getTextureId());
}
谢谢
如果我正确理解了您要执行的操作,您可能希望将每个帧解码为 SurfaceTexture,这会为您提供包含数据的 GLES "external" 纹理。然后,您可以将其渲染到 TextureView,在 eglSwapBuffers()
.
之前调用 glReadPixels()
一旦数据发送到屏幕 Surface,您就无法读回数据,因为数据的使用者处于不同的进程中。高效的视频路径只是将 "external" 纹理传递给 Surface,但这在这里不起作用。理想情况下,您将克隆外部纹理参考,将一个副本转发到显示表面,并使用另一个副本渲染到您可以从中提取像素的屏幕外缓冲区。 (Camera2 API 可以做这样的多输出技巧,但我不知道它是否暴露在 MediaCodec 中。虽然我有一段时间没看。)
我正在尝试修改 ExtractMpegFramesTest 以在屏幕上进行渲染,并且仍然使用 glReadPixels
来提取帧。
我复制了从 ExtractMpegFramesTest(CodecOutputSurface 和 STextureRender 类)中提取帧的相关代码,并且在屏幕外渲染时帧提取按预期工作。
我有一个 TextureView
和一个 SurfaceTextureListener
,当我收到 onSurfaceTextureAvailable
时,我得到 SurfaceTexture
并开始解码过程。
我将此 SurfaceTexture
传递给 CodecOutputSurface
但它不起作用。
我不确定这是否相关,但是 onSurfaceTextureAvailable
和 SurfaceTexture
是在主线程上接收的,所有解码(包括 CodecOutputSurface 构造函数调用)都是在不同的线程上完成的。
我尝试使用 here and here 的建议,但我无法让它工作。
我在日志中看到了这个:
E/BufferQueueProducer: [SurfaceTexture-0-11068-20] connect(P): already connected (cur=1 req=3)
I/MediaCodec: native window already connected. Assuming no change of surface
E/MediaCodec: configure failed with err 0xffffffea, resetting...
我对 ExtractMpegFramesTest eglSetup 方法进行了以下修改:
private void eglSetup() {
mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
throw new RuntimeException("unable to get EGL14 display");
}
int[] version = new int[2];
if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
mEGLDisplay = null;
throw new RuntimeException("unable to initialize EGL14");
}
int[] attribList = {
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_WINDOW_BIT, // tell it to use a window
EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfigs = new int[1];
if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,
numConfigs, 0)) {
throw new RuntimeException("unable to find RGB888+recordable ES2 EGL config");
}
// Configure context for OpenGL ES 2.0.
int[] attrib_list = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
EGL14.EGL_NONE
};
mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT,
attrib_list, 0);
checkEglError("eglCreateContext");
if (mEGLContext == null) {
throw new RuntimeException("null context");
}
int[] surfaceAttribs = {
EGL14.EGL_RENDER_BUFFER, EGL14.EGL_SINGLE_BUFFER,
EGL14.EGL_NONE
};
mSurfaceTexture.setOnFrameAvailableListener(this);
mSurface = new Surface(mSurfaceTexture);
mPixelBuf = ByteBuffer.allocateDirect(mWidth * mHeight * 4);
mPixelBuf.order(ByteOrder.LITTLE_ENDIAN);
mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0], mSurface,
surfaceAttribs, 0); // create window surface instead of eglCreatePbufferSurface
checkEglError("eglCreateWindowSurface");
if (mEGLSurface == null) {
throw new RuntimeException("surface was null");
}
}
以及ExtractMpegFramesTest设置方法:
private void setup() {
mTextureRender = new STextureRender();
mTextureRender.surfaceCreated();
if (VERBOSE) Log.d(TAG, "textureID=" + mTextureRender.getTextureId());
}
谢谢
如果我正确理解了您要执行的操作,您可能希望将每个帧解码为 SurfaceTexture,这会为您提供包含数据的 GLES "external" 纹理。然后,您可以将其渲染到 TextureView,在 eglSwapBuffers()
.
glReadPixels()
一旦数据发送到屏幕 Surface,您就无法读回数据,因为数据的使用者处于不同的进程中。高效的视频路径只是将 "external" 纹理传递给 Surface,但这在这里不起作用。理想情况下,您将克隆外部纹理参考,将一个副本转发到显示表面,并使用另一个副本渲染到您可以从中提取像素的屏幕外缓冲区。 (Camera2 API 可以做这样的多输出技巧,但我不知道它是否暴露在 MediaCodec 中。虽然我有一段时间没看。)