Android: Update MediaCodecVideoDecoder to output VideoFrames

Bug: webrtc:9181
Change-Id: I7eba15167536e453956c511a056143b039f52b92
Reviewed-on: https://webrtc-review.googlesource.com/71664
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22988}
diff --git a/sdk/android/api/org/webrtc/MediaCodecVideoDecoder.java b/sdk/android/api/org/webrtc/MediaCodecVideoDecoder.java
index 8ab033d..a9368db 100644
--- a/sdk/android/api/org/webrtc/MediaCodecVideoDecoder.java
+++ b/sdk/android/api/org/webrtc/MediaCodecVideoDecoder.java
@@ -30,6 +30,8 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import javax.annotation.Nullable;
+import org.webrtc.EglBase;
+import org.webrtc.VideoFrame;
 
 // Java-side of peerconnection.cc:MediaCodecVideoDecoder.
 // This class is an implementation detail of the Java PeerConnection API.
@@ -320,16 +322,16 @@
     }
   }
 
-  // Pass null in |surfaceTextureHelper| to configure the codec for ByteBuffer output.
+  // Pass null in |eglContext| to configure the codec for ByteBuffer output.
   @CalledByNativeUnchecked
-  private boolean initDecode(VideoCodecType type, int width, int height,
-      @Nullable SurfaceTextureHelper surfaceTextureHelper) {
+  private boolean initDecode(
+      VideoCodecType type, int width, int height, @Nullable EglBase.Context eglContext) {
     if (mediaCodecThread != null) {
       throw new RuntimeException("initDecode: Forgot to release()?");
     }
 
     String mime = null;
-    useSurface = (surfaceTextureHelper != null);
+    useSurface = (eglContext != null);
     String[] supportedCodecPrefixes = null;
     if (type == VideoCodecType.VIDEO_CODEC_VP8) {
       mime = VP8_MIME_TYPE;
@@ -359,9 +361,14 @@
       stride = width;
       sliceHeight = height;
 
-      if (useSurface && surfaceTextureHelper != null) {
-        textureListener = new TextureListener(surfaceTextureHelper);
-        surface = new Surface(surfaceTextureHelper.getSurfaceTexture());
+      if (useSurface) {
+        @Nullable
+        final SurfaceTextureHelper surfaceTextureHelper =
+            SurfaceTextureHelper.create("Decoder SurfaceTextureHelper", eglContext);
+        if (surfaceTextureHelper != null) {
+          textureListener = new TextureListener(surfaceTextureHelper);
+          surface = new Surface(surfaceTextureHelper.getSurfaceTexture());
+        }
       }
 
       MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
@@ -567,8 +574,7 @@
 
   // Helper struct for dequeueTextureBuffer() below.
   private static class DecodedTextureBuffer {
-    private final int textureID;
-    private final float[] transformMatrix;
+    private final VideoFrame.Buffer videoFrameBuffer;
     // Presentation timestamp returned in dequeueOutputBuffer call.
     private final long presentationTimeStampMs;
     // C++ inputImage._timeStamp value for output frame.
@@ -585,11 +591,9 @@
 
     // A DecodedTextureBuffer with zero |textureID| has special meaning and represents a frame
     // that was dropped.
-    public DecodedTextureBuffer(int textureID, float[] transformMatrix,
-        long presentationTimeStampMs, long timeStampMs, long ntpTimeStampMs, long decodeTimeMs,
-        long frameDelay) {
-      this.textureID = textureID;
-      this.transformMatrix = transformMatrix;
+    public DecodedTextureBuffer(VideoFrame.Buffer videoFrameBuffer, long presentationTimeStampMs,
+        long timeStampMs, long ntpTimeStampMs, long decodeTimeMs, long frameDelay) {
+      this.videoFrameBuffer = videoFrameBuffer;
       this.presentationTimeStampMs = presentationTimeStampMs;
       this.timeStampMs = timeStampMs;
       this.ntpTimeStampMs = ntpTimeStampMs;
@@ -598,13 +602,8 @@
     }
 
     @CalledByNative("DecodedTextureBuffer")
-    int getTextureId() {
-      return textureID;
-    }
-
-    @CalledByNative("DecodedTextureBuffer")
-    float[] getTransformMatrix() {
-      return transformMatrix;
+    VideoFrame.Buffer getVideoFrameBuffer() {
+      return videoFrameBuffer;
     }
 
     @CalledByNative("DecodedTextureBuffer")
@@ -634,8 +633,7 @@
   }
 
   // Poll based texture listener.
-  private static class TextureListener
-      implements SurfaceTextureHelper.OnTextureFrameAvailableListener {
+  private class TextureListener implements SurfaceTextureHelper.OnTextureFrameAvailableListener {
     private final SurfaceTextureHelper surfaceTextureHelper;
     // |newFrameLock| is used to synchronize arrival of new frames with wait()/notifyAll().
     private final Object newFrameLock = new Object();
@@ -674,9 +672,10 @@
           throw new IllegalStateException("Already holding a texture.");
         }
         // |timestampNs| is always zero on some Android versions.
-        renderedBuffer = new DecodedTextureBuffer(oesTextureId, transformMatrix,
-            bufferToRender.presentationTimeStampMs, bufferToRender.timeStampMs,
-            bufferToRender.ntpTimeStampMs, bufferToRender.decodeTimeMs,
+        final VideoFrame.Buffer buffer = surfaceTextureHelper.createTextureBuffer(
+            width, height, RendererCommon.convertMatrixToAndroidGraphicsMatrix(transformMatrix));
+        renderedBuffer = new DecodedTextureBuffer(buffer, bufferToRender.presentationTimeStampMs,
+            bufferToRender.timeStampMs, bufferToRender.ntpTimeStampMs, bufferToRender.decodeTimeMs,
             SystemClock.elapsedRealtime() - bufferToRender.endDecodeTimeMs);
         bufferToRender = null;
         newFrameLock.notifyAll();
@@ -709,10 +708,11 @@
       surfaceTextureHelper.stopListening();
       synchronized (newFrameLock) {
         if (renderedBuffer != null) {
-          surfaceTextureHelper.returnTextureFrame();
+          renderedBuffer.getVideoFrameBuffer().release();
           renderedBuffer = null;
         }
       }
+      surfaceTextureHelper.dispose();
     }
   }
 
@@ -844,8 +844,9 @@
       }
 
       mediaCodec.releaseOutputBuffer(droppedFrame.index, false /* render */);
-      return new DecodedTextureBuffer(0, null, droppedFrame.presentationTimeStampMs,
-          droppedFrame.timeStampMs, droppedFrame.ntpTimeStampMs, droppedFrame.decodeTimeMs,
+      return new DecodedTextureBuffer(null /* videoFrameBuffer */,
+          droppedFrame.presentationTimeStampMs, droppedFrame.timeStampMs,
+          droppedFrame.ntpTimeStampMs, droppedFrame.decodeTimeMs,
           SystemClock.elapsedRealtime() - droppedFrame.endDecodeTimeMs);
     }
     return null;
diff --git a/sdk/android/src/jni/androidmediadecoder.cc b/sdk/android/src/jni/androidmediadecoder.cc
index 0a3d81a7..88cfb96 100644
--- a/sdk/android/src/jni/androidmediadecoder.cc
+++ b/sdk/android/src/jni/androidmediadecoder.cc
@@ -112,7 +112,6 @@
   bool use_surface_;
   VideoCodec codec_;
   I420BufferPool decoded_frame_pool_;
-  rtc::scoped_refptr<SurfaceTextureHelper> surface_texture_helper_;
   DecodedImageCallback* callback_;
   int frames_received_;  // Number of frames received by decoder.
   int frames_decoded_;  // Number of frames decoded by decoder.
@@ -224,26 +223,12 @@
 
   ResetVariables();
 
-  if (use_surface_) {
-    surface_texture_helper_ = SurfaceTextureHelper::create(
-        jni, "Decoder SurfaceTextureHelper",
-        JavaParamRef<jobject>(render_egl_context_));
-    if (!surface_texture_helper_) {
-      ALOGE << "Couldn't create SurfaceTextureHelper - fallback to SW codec";
-      sw_fallback_required_ = true;
-      return WEBRTC_VIDEO_CODEC_ERROR;
-    }
-  }
-
   ScopedJavaLocalRef<jobject> j_video_codec_enum =
       Java_VideoCodecType_fromNativeIndex(jni, codecType_);
-  jobject j_surface_texture_helper =
-      use_surface_
-          ? surface_texture_helper_->GetJavaSurfaceTextureHelper().obj()
-          : nullptr;
+  jobject j_egl_context = use_surface_ ? render_egl_context_ : nullptr;
   bool success = Java_MediaCodecVideoDecoder_initDecode(
       jni, j_media_codec_video_decoder_, j_video_codec_enum, codec_.width,
-      codec_.height, JavaParamRef<jobject>(j_surface_texture_helper));
+      codec_.height, JavaParamRef<jobject>(j_egl_context));
 
   if (CheckException(jni) || !success) {
     ALOGE << "Codec initialization error - fallback to SW codec.";
@@ -325,7 +310,6 @@
   ScopedLocalRefFrame local_ref_frame(jni);
   input_buffers_.clear();
   Java_MediaCodecVideoDecoder_release(jni, j_media_codec_video_decoder_);
-  surface_texture_helper_ = nullptr;
   inited_ = false;
   rtc::MessageQueueManager::Clear(this);
   if (CheckException(jni)) {
@@ -604,18 +588,14 @@
     decode_time_ms =
         Java_DecodedTextureBuffer_getDecodeTimeMs(jni, j_decoder_output_buffer);
 
-    const int texture_id =
-        Java_DecodedTextureBuffer_getTextureId(jni, j_decoder_output_buffer);
-    if (texture_id != 0) {  // |texture_id| == 0 represents a dropped frame.
-      ScopedJavaLocalRef<jfloatArray> j_transform_matrix =
-          Java_DecodedTextureBuffer_getTransformMatrix(jni,
-                                                       j_decoder_output_buffer);
+    ScopedJavaLocalRef<jobject> j_video_frame_buffer =
+        Java_DecodedTextureBuffer_getVideoFrameBuffer(jni,
+                                                      j_decoder_output_buffer);
+    // |video_frame_buffer| == null represents a dropped frame.
+    if (!j_video_frame_buffer.is_null()) {
       frame_delayed_ms = Java_DecodedTextureBuffer_getFrameDelayMs(
           jni, j_decoder_output_buffer);
-
-      // Create VideoFrameBuffer with native texture handle.
-      frame_buffer = surface_texture_helper_->CreateTextureFrame(
-          width, height, NativeHandleImpl(jni, texture_id, j_transform_matrix));
+      frame_buffer = AndroidVideoBuffer::Adopt(jni, j_video_frame_buffer);
     } else {
       EnableFrameLogOnWarning();
     }