Воспроизведение видео с OpenGL и MediaCodec

Я пытаюсь воспроизвести одно и то же видео одновременно в двух разных текстурах. Я использовал код из grafika (MoviePlayer и ContinuousCaptureActivity), чтобы попытаться заставить его работать (спасибо, fadden). Чтобы упростить задачу, я сначала пытаюсь сделать это только с одним TextureView.

На данный момент я создал TextureView, и как только он получит SurfaceTexture, я создаю WindowSurface и делаю его текущим. Затем я генерирую TextureID, сгенерированный с использованием объекта FullFrameRect.

@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int
        width, int height) {
    mSurfaceTexture = surface;
    mEGLCore = new EglCore(null, EglCore.FLAG_TRY_GLES3);
    Log.d("EglCore", "EGL core made");
    mDisplaySurface = new WindowSurface(mEGLCore, mSurfaceTexture);
    mDisplaySurface.makeCurrent();
    Log.d("DisplaySurface", "mDisplaySurface made");
    mFullFrameBlit = new FullFrameRect(new Texture2dProgram(Texture2dProgram.ProgramType.TEXTURE_EXT));
    mTextureID = mFullFrameBlit.createTextureObject();

    //mSurfaceTexture.attachToGLContext(mTextureID);
    clickPlayStop(null);
}

Затем я получаю SurfaceTexture за пределами экрана, связываю его с TextureID, который я получил выше, и создаю поверхность для передачи в MoviePlayer следующим образом:

public void clickPlayStop(@SuppressWarnings("unused") View unused) {
    if (mShowStopLabel) {
        Log.d(TAG, "stopping movie");
        stopPlayback();
        // Don't update the controls here -- let the task thread do it after the movie has
        // actually stopped.
        //mShowStopLabel = false;
        //updateControls();
    } else {
        if (mPlayTask != null) {
            Log.w(TAG, "movie already playing");
            return;
        }
        Log.d(TAG, "starting movie");
        SpeedControlCallback callback = new SpeedControlCallback();
        callback.setFixedPlaybackRate(24);

        MoviePlayer player = null;
        MovieTexture = new SurfaceTexture(mTextureID);
        MovieTexture.setOnFrameAvailableListener(this);
        Surface surface = new Surface(MovieTexture);
        try {
            player = new MoviePlayer(surface, callback, this);//TODO
        } catch (IOException ioe) {
            Log.e(TAG, "Unable to play movie", ioe);

            return;
        }

        adjustAspectRatio(player.getVideoWidth(), player.getVideoHeight());

        mPlayTask = new MoviePlayer.PlayTask(player, this);
        mPlayTask.setLoopMode(true);


        mShowStopLabel = true;
        mPlayTask.execute();
    }
}

Идея состоит в том, что SurfaceTexture получает необработанный кадр, который я могу использовать в качестве текстуры OES_external для выборки с помощью OpenGL. Затем я могу вызвать DrawFrame() из моего EGLContext после установки моего WindowSurface в качестве текущего.

private void drawFrame() {
    Log.d(TAG, "drawFrame");
    if (mEGLCore == null) {
        Log.d(TAG, "Skipping drawFrame after shutdown");
        return;
    }

    // Latch the next frame from the camera.
    mDisplaySurface.makeCurrent();
    MovieTexture.updateTexImage();
    MovieTexture.getTransformMatrix(mTransformMatrix);

    // Fill the WindowSurface with it.
    int viewWidth = mTextureView.getWidth();
    int viewHeight = mTextureView.getHeight();
    GLES20.glViewport(0, 0, viewWidth, viewHeight);
    mFullFrameBlit.drawFrame(mTextureID, mTransformMatrix);
    mDisplaySurface.swapBuffers();
}

Если бы я хотел сделать это с двумя объектами TextureView, идея заключалась бы в вызове makeCurrent() и рисовании в каждом буфере для каждого вида, а затем вызове swapBuffers() после завершения рисования.

Это то, что я хочу сделать, но я уверен, что это не то, что на самом деле делает мой код. Может ли кто-нибудь помочь мне понять, что мне нужно изменить, чтобы заставить его работать?

@Фадден

Обновление: это интересно. Я изменил код в onSurfaceTextureAvailable на это:

@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int
        width, int height) {
    mSurfaceTexture = surface;
    TextureHeight = height;
    TextureWidth = width;
    //mEGLCore = new EglCore(null, EglCore.FLAG_TRY_GLES3);
    Log.d("EglCore", "EGL core made");
    //mDisplaySurface = new WindowSurface(mEGLCore, mSurfaceTexture);
    //mDisplaySurface.makeCurrent();
    Log.d("DisplaySurface", "mDisplaySurface made");
    //mFullFrameBlit = new FullFrameRect(new Texture2dProgram(Texture2dProgram.ProgramType.OPENGL_TEST));
    //mTextureID = mFullFrameBlit.createTextureObject();
    //clickPlayStop(null);


    // Fill the SurfaceView with it.
    //int viewWidth = width;
    //int viewHeight = height;
    //GLES20.glViewport(0, 0, viewWidth, viewHeight);
    //mFullFrameBlit.drawFrame(mTextureID, mTransformMatrix);
    //mFullFrameBlit.openGLTest();
    //mFullFrameBlit.testDraw(mDisplaySurface.getHeight(),mDisplaySurface.getWidth());
    //mDisplaySurface.swapBuffers();
}

Итак, он не должен вызывать ничего другого, просто показать пустой TextureView - и это то, что я вижу...

Пустой вид текстуры


person Kongo    schedule 15.07.2015    source источник
comment
Ваш план звучит прекрасно. Используйте один контекст EGL для обеих поверхностей, затем makeCurrent() + draw + swapBuffers() в каждом TextureView. В зависимости от вашего макета вы также можете использовать один TextureView и изменить рендеринг GLES, чтобы каждый из них отрисовывался на половине представления.   -  person fadden    schedule 15.07.2015
comment
Прохладно. Это хорошие новости. Для SurfaceTexture, принадлежащего TextureView, я добавил прослушиватель onFrameAvailable. Однако я продолжаю получать исключение IllegalStateException: невозможно обновить содержимое текстуры при вызове surfaceTexture.updateTexImage(); Есть ли какие-то настройки, которые мне нужно сделать?   -  person Kongo    schedule 16.07.2015
comment
Из документации SurfaceTexture: объекты SurfaceTexture могут быть созданы в любом потоке. updateTexImage() может вызываться только в потоке с контекстом OpenGL ES, который содержит объект текстуры. Обратный вызов, доступный для кадра, вызывается в произвольном потоке, поэтому, если не соблюдать особую осторожность, updateTexImage() не следует вызывать непосредственно из обратного вызова.   -  person fadden    schedule 16.07.2015
comment
О, так мне нужен обработчик для вызова обновления в том же потоке?... О, понятно... вот почему у вас есть все эти обратные вызовы, обработчики и прочее. Спасибо   -  person Kongo    schedule 17.07.2015
comment
API OpenGL ES опирается на локальное хранилище потока, поэтому вам нужно хорошо знать, что происходит в каком потоке. Это немного добавляет к кривой обучения при работе с этим материалом.   -  person fadden    schedule 17.07.2015
comment
Происходит какое-то странное поведение. Я могу сказать, что фильм воспроизводится, потому что цвета на экране меняются, но экран однороден по цвету. Посмотреть изменить   -  person Kongo    schedule 08.08.2015
comment
В коде нет ничего явно неправильного. Обычно ошибки YUV имеют тенденцию выглядеть более зелеными (YUV 0,0,0 — средне-зеленый), что делает сплошной синий цвет немного странным. Я предполагаю, что где-то есть путаница в текстурах, и либо это рендеринг с неправильным идентификатором, либо использование правильного идентификатора, но из неправильного контекста. Все ли происходит в потоке пользовательского интерфейса? Можете ли вы выполнить рендеринг в TextureView с помощью простых вызовов GL (например, нарисовать прямоугольник с помощью glScissor/glClear, как это делает doAnimation() в TextureViewGLActivity.java) из drawFrame()?   -  person fadden    schedule 08.08.2015
comment
Собственно, начало видео - синий экран. Я думаю точно такой же синий. Первоначально я думал, что это могут быть неправильные векторы, потому что цвет меняется во время воспроизведения видео. Я считаю, что все происходит в потоке пользовательского интерфейса. Окончательная текстура поверхности, я просто позволил сделать свою работу. Я попробую некоторые базовые вещи opengl и отпишусь   -  person Kongo    schedule 09.08.2015
comment
Итак... проверьте это. Понятия не имею, что это.... не так ли?   -  person Kongo    schedule 10.08.2015
comment
Я также пытался нарисовать простой треугольник, и происходит что-то очень странное. Цвета кажутся хорошими, я могу изменить их без проблем. Я думаю, проблема в вершинном шейдере, но я не понимаю, что именно может быть не так.   -  person Kongo    schedule 10.08.2015
comment
Я также использовал «показать границы макета» на скриншоте. Файл макета, который я использую, имеет только вид текстуры внутри макета кадра. Но, кажется, есть лишние вещи, и я не знаю, почему название активности отображается так много раз...   -  person Kongo    schedule 10.08.2015
comment
Хм. Хорошая новость заключается в том, что вы можете перестать беспокоиться о том, как все видеоматериалы сочетаются друг с другом, и просто сосредоточиться на GLES. Это на устройстве или эмуляторе? Какое устройство/версия Android? У вас есть другие устройства, которые вы можете протестировать, чтобы сравнить? Повторяющееся имя действия вызывает подозрение — если вы внимательно присмотритесь, окажется, что повторяющиеся значки дроидов совпадают с красными полосами на правом краю, что заставляет меня думать, что это вызвано одной и той же проблемой.   -  person fadden    schedule 10.08.2015
comment
Ха-ха. Ну вот хорошо. Я начал чувствовать себя уверенным, что понял, как все это сочетается друг с другом, а потом БАМ. Уверенность ушла. Это на Nexus 4, последняя версия Android (5.1.1). Других устройств у меня нет, но могу попробовать на эмуляторе. Что может вызвать дополнительные биты в макете, если все, что у меня должно быть, это просмотр текстуры на экране?   -  person Kongo    schedule 11.08.2015
comment
Я не совсем уверен, что вызывает это. Я рекомендую начинать с чего-то простого, что работает правильно, и постепенно добавлять кусочки и кусочки, пока не рухнет. (Или начните со сломанного кода и удаляйте вещи, пока они не перестанут вести себя странно.)   -  person fadden    schedule 11.08.2015
comment
Я удалил почти все, кроме TextureView. Это то, что он показывает .... это нормально?   -  person Kongo    schedule 14.08.2015
comment
Похоже, что это изображение имеет дублированную панель навигации, как будто оно использует неправильную текстуру. Вы видите странное поведение в демо Grafika (например, простой GL в TextureView)? Он делает почти то же самое, что и вы, хотя использует отдельный поток для рендеринга. Хм. Вы выполняете рендеринг на нем из потока пользовательского интерфейса? Если да, пытались ли вы сохранить и восстановить предыдущий контекст EGL после завершения рендеринга? (Интересно, пытается ли View UI отображать из неправильного контекста EGL.)   -  person fadden    schedule 14.08.2015
comment
Из этого изображения я даже не устанавливаю контекст EGL, это просто TextureView сам по себе. Я ничего не делаю на нем. Когда я пытаюсь декодировать видео непосредственно на SurfaceTexture, видео смещается вниз, и я получаю забавную видеостатическую вещь сверху, где она, кажется, глючит. Раньше такого никогда не было... только с этой активностью, другие демо работают нормально   -  person Kongo    schedule 15.08.2015
comment
попробую рендерить из другой темы   -  person Kongo    schedule 15.08.2015


Ответы (1)


Спасибо Фадену за помощь.

Таким образом, возникла некоторая неизвестная проблема, которая была решена, когда я использовал новый поток для декодирования и создания кадров. Я не узнал, что вызвало первоначальную проблему, но я нашел способ ее обойти.

person Kongo    schedule 17.08.2015