Отображение потока кадров байтового массива в текстуру

Я пытаюсь отобразить поток кадров, полученных по сети, и отобразить их в TextureView. Мой конвейер выглядит следующим образом:

  1. Получение видео с помощью GStreamer. Я использую НДК. Код Gstreamer находится на C. Я использую обратный вызов JNI для отправки отдельных кадров, полученных в appsink, с C на Java. Я не хочу использовать ANativeWindow из NDK для отображения на поверхности, как это делается в примере приложения GStreamer Tutorial-3.
  2. В Java эти кадры добавляются в очередь ArrayBlockingQueue. Из этой очереди вытягивается отдельный поток.

Ниже приведен обратный вызов из потока pullFromQueue, который остается активным, пока работает приложение. Кадр byte[] представляет собой кадр формата NV21 известной ширины и высоты.

@DebugLog
private void pullFromQueueMethod() {
    try {
        long start = System.currentTimeMillis();
        byte frame[] = framesQueue.take();
    }

Отсюда я хотел бы использовать OpenGL для изменения яркости, контрастности и применения шейдеров к отдельным кадрам. Меня очень беспокоит производительность, поэтому я не могу преобразовать byte[] в Bitmap, а затем отобразить в SurfaceView. Я пробовал это, и для кадра 768x576 на Nexus 5 требуется почти 50 мс.

Удивительно, но я нигде не могу найти пример, чтобы сделать то же самое. Во всех примерах используются встроенные функции Camera или MediaPlayer для направления предварительного просмотра на поверхность/текстуру. Например: camera.setPreviewTexture(surfaceTexture);. Это связывает вывод с SurfaceTexture и, следовательно, вам никогда не приходится обрабатывать отображение отдельных кадров (никогда не приходится иметь дело с массивами байтов).

Что я пытался до сих пор:

Видел этот ответ на StackOverflow. Он предлагает использовать Grafika's createImageTexture(). Как только я получу дескриптор текстуры, как мне передать его в SurfaceTexture и постоянно обновлять? Вот частичный код того, что я реализовал до сих пор:

public class CameraActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener {

int textureId = -1;
SurfaceTexture surfaceTexture;
TextureView textureView;

...

protected void onCreate(Bundle savedInstanceState) {
    textureView = new TextureView(this);
    textureView.setSurfaceTextureListener(this);
}

private void pullFromQueueMethod() {
try {
    long start = System.currentTimeMillis();
    byte frame[] = framesQueue.take();

    if (textureId == -1){
        textureId = GlUtil.createImageTexture(frame);
        surfaceTexture = new SurfaceTexture(textureId);
        textureView.setSurfaceTexture(surfaceTexture);
    } else {
        GlUtil.updateImageTexture(textureId); // self defined method that doesn't create a new texture, but calls GLES20.glTexImage2D() to update that texture
    }

    surfaceTexture.updateTexImage();

    /* What do I do from here? Is this the right approach? */

}
}

Подводить итоги. Все, что мне действительно нужно, — это эффективный способ отображения потока кадров (массивов байтов). Как мне этого добиться?


person Crearo Rotar    schedule 13.01.2017    source источник
comment
это снова я, вы можете опубликовать свой ответ? Я буду голосовать за это!   -  person Amos    schedule 31.10.2018
comment
Я разместил решение здесь   -  person Crearo Rotar    schedule 07.11.2018