Я пытаюсь отобразить поток кадров, полученных по сети, и отобразить их в TextureView. Мой конвейер выглядит следующим образом:
- Получение видео с помощью GStreamer. Я использую НДК. Код Gstreamer находится на C. Я использую обратный вызов JNI для отправки отдельных кадров, полученных в appsink, с C на Java. Я не хочу использовать ANativeWindow из NDK для отображения на поверхности, как это делается в примере приложения GStreamer Tutorial-3.
- В 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? */
}
}
Подводить итоги. Все, что мне действительно нужно, — это эффективный способ отображения потока кадров (массивов байтов). Как мне этого добиться?