修改ExtractMpegFramesTest示例以在屏幕上呈现解码输出

时间:2018-04-22 14:49:00

标签: android mediacodec grafika

我尝试修改ExtractMpegFramesTest以在屏幕上进行渲染,并仍然使用glReadPixels来提取帧。

我复制了相关代码,用于从ExtractMpegFramesTest(CodecOutputSurface和STextureRender类)中提取帧,并且在屏幕外渲染时帧提取按预期工作。

我有TextureView SurfaceTextureListener,当我收到onSurfaceTextureAvailable时,我收到SurfaceTexture并开始解码过程。 我将此SurfaceTexture传递给CodecOutputSurface,但它无效。

我不确定这是否相关,但主线程上收到onSurfaceTextureAvailableSurfaceTexture,并且所有解码(包括CodecOutputSurface构造函数调用)都在不同的线程上完成。

我尝试使用来自herehere的建议,但我无法让它发挥作用。

我在日志中看到了这一点:

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());
}

由于

1 个答案:

答案 0 :(得分:1)

如果我正确理解您要尝试做的事情,那么您需要将每个帧解码为SurfaceTexture,这样可以为您提供GLES"外部"纹理与其中的数据。然后,您可以将其呈现给TextureView,在glReadPixels()之前调用eglSwapBuffers()

一旦将数据发送到屏幕Surface,您就无法读取数据,因为数据的使用者生活在不同的进程中。高效的视频路径只是通过"外部"纹理到Surface,但这不会在这里工作。理想情况下,您将克隆外部纹理参考,将一个副本转发到显示Surface,并使用另一个副本渲染到可以从中提取像素的屏幕外缓冲区。 (Camera2 API可以做这样的多输出技巧,但我不知道它是否暴露在MediaCodec中。虽然我还没看好。)