eglSwapBuffers завершается ошибкой с EGL_BAD_SURFACE при использовании Surface из MediaCodec - PullRequest
0 голосов
/ 26 марта 2019

Я пытаюсь закодировать фильм с помощью MediaCodec и Surfaces (режим пиксельного буфера работает, но производительность не достаточно хорошая).Однако каждый раз, когда я пытаюсь вызвать eglSwapBuffers(), происходит сбой с EGL_BAD_SURFACE, и поэтому dequeueOutputBuffer() всегда возвращает -1 (INFO_TRY_AGAIN_LATER)

. Я видел примеры на Bigflake и Grafika иУ меня есть другой рабочий проект, где все в порядке, но мне нужно, чтобы это работало в другой настройке, которая немного отличается.

В настоящее время у меня есть GLSurfaceView, который выполняет визуализацию экрана и поставляется с пользовательским EGLContextFactory / EGLConfigChooser.Это позволяет мне создавать общие контексты, которые будут использоваться для отдельной визуализации OpenGL в собственной библиотеке.Они создаются с использованием EGL10, но это не должно вызывать проблем, поскольку базовые контексты заботятся только о версии клиента, насколько я знаю.

Я убедился, что контекст доступен для записи, используя следующую конфигурацию:

private android.opengl.EGLConfig chooseConfig14(android.opengl.EGLDisplay display) {
        // Configure EGL for recording and OpenGL ES 3.x
        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, EGLExt.EGL_OPENGL_ES3_BIT_KHR,
                EGLExt.EGL_RECORDABLE_ANDROID, 1,
                EGL14.EGL_NONE
        };

        android.opengl.EGLConfig[] configs = new android.opengl.EGLConfig[1];
        int[] numConfigs = new int[1];
        if (!EGL14.eglChooseConfig(display, attribList, 0, configs, 0,
                configs.length, numConfigs, 0)) {
            return null;
        }

        return configs[0];
    }

Теперь я попытался упростить сценарий, поэтому при запуске записи я инициализирую экземпляр MediaCodec в качестве кодировщика и вызываю createInputSurface() в потоке GLSurfaceView.Получив поверхность, я превращаю ее в EGLSurface (EGL14) следующим образом:

EGLSurface createEGLSurface(Surface surface) {
        if (surface == null) return null;

        int[] surfaceAttribs = { EGL14.EGL_NONE };

        android.opengl.EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);

        android.opengl.EGLConfig config = chooseConfig14(display);

        EGLSurface eglSurface = EGL14.eglCreateWindowSurface(display, config, surface, surfaceAttribs, 0);

        return eglSurface;
    }

Когда с камеры приходит новый кадр, я отправляю его на экран и в другой класс, который выполняет запись,Этот класс просто отображает его на EGLSurface, построенном из входной поверхности MediaCodec, следующим образом:

public void drawToSurface(EGLSurface targetSurface, int width, int height, long timestampNano, boolean ignoreOrientation) {
        if (mTextures[0] == null) {
            Log.w(TAG, "Attempting to draw without a source texture");
            return;
        }

        EGLContext currentContext = EGL14.eglGetCurrentContext();
        EGLDisplay currentDisplay = EGL14.eglGetCurrentDisplay();

        EGL14.eglMakeCurrent(currentDisplay, targetSurface, targetSurface, currentContext);
        int error = EGL14.eglGetError();

        ShaderProgram program = getProgramForTextureType(mTextures[0].getTextureType());

        program.draw(width, height, TextureRendererView.LayoutType.LINEAR_HORIZONTAL, 0, 1, mTextures[0]);
        error = EGL14.eglGetError();

        EGLExt.eglPresentationTimeANDROID(currentDisplay, targetSurface, timestampNano);
        error = EGL14.eglGetError();

        EGL14.eglSwapBuffers(currentDisplay, targetSurface);
        error = EGL14.eglGetError();

        Log.d(TAG, "drawToSurface");
    }

По какой-то причине eglSwapBuffers() завершается неудачно и сообщает EGL_BAD_SURFACE, и я не нашел способадалее отладка.

Обновление Я пытался запросить текущую поверхность после вызова, который делает ее текущей, и она всегда возвращает поврежденную поверхность (заглянув внутрь, я вижу, что ручка 0 и он всегда терпит неудачу при запросе).Похоже, что eglMakeCurrent() молча не удается установить привязку поверхности к контексту.

Более того, я определил, что эта проблема появляется на чипах Qualcomm (Adreno), а не на Kirin, поэтому он определенно связан с нативнымРеализация OpenGL (это как-то забавно, потому что я всегда замечал, что Adreno более терпим, когда дело доходит до «плохих» конфигураций OpenGL)

1 Ответ

0 голосов
/ 28 марта 2019

Исправлена! Оказывается, EGL10 и EGL14, кажется, хорошо играют вместе, но в некоторых случаях дают сбой очень тонкими способами, такими как тот, с которым я столкнулся.

В моем случае EGLContextFactory, который я написал, создавал базовый контекст OpenGL ES с использованием EGL10, а затем создавал дополнительные общие контексты по требованию, снова используя EGL10. Хотя я мог получить их, используя EGL14 (либо в Java, либо в C), и дескрипторы контекста всегда были правильными (совместное использование текстур между контекстами работало как чудо), он загадочным образом не удался при попытке использовать EGLSurface, созданный из контекста или источника EGL10 ... на чипах Adreno.

Решением было переключить EGLContextFactory для запуска из контекста, созданного с помощью EGL14, и продолжить создавать общие контексты с использованием EGL14. Для GLSurfaceView, который все еще требовал EGL10, мне пришлось использовать хак

    @Override
    public javax.microedition.khronos.egl.EGLContext createContext(EGL10 egl10, javax.microedition.khronos.egl.EGLDisplay eglDisplay, javax.microedition.khronos.egl.EGLConfig eglConfig) {
        EGLContext context = createContext();
        boolean success = EGL14.eglMakeCurrent(mBaseEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, context);

        if (!success) {
            int error = EGL14.eglGetError();
            Log.w(TAG, "Failed to create a context. Error: " + error);
        }

        javax.microedition.khronos.egl.EGLContext egl14Context = egl10.eglGetCurrentContext(); //get an EGL10 context representation of our EGL14 context
        javax.microedition.khronos.egl.EGLContext trueEGL10Context = egl10.eglCreateContext(eglDisplay, eglConfig, egl14Context, glAttributeList);

        destroyContext(context);
        return trueEGL10Context;
    }

Для этого нужно создать новый общий контекст с EGL14, сделать его текущим и затем получить его версию EGL10. Эта версия не может использоваться непосредственно (по причине, которую я не могу точно понять), но другой общий контекст из этого работает хорошо. Начальный контекст EGL14 затем может быть уничтожен (в моем случае он помещается в стек для повторного использования позже).

Мне бы очень хотелось понять, зачем нужен этот взлом, но я рад, что у меня все еще есть рабочее решение.

...