Android: Generate JNI code for MediaCodecVideoEncoder/Decoder

Bug: webrtc:8278
Change-Id: I19cff18b5d110720ea50d16254ddc4377adc3dbe
Reviewed-on: https://webrtc-review.googlesource.com/31261
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21204}
diff --git a/sdk/android/api/org/webrtc/MediaCodecVideoDecoder.java b/sdk/android/api/org/webrtc/MediaCodecVideoDecoder.java
index 82957ed..5330871 100644
--- a/sdk/android/api/org/webrtc/MediaCodecVideoDecoder.java
+++ b/sdk/android/api/org/webrtc/MediaCodecVideoDecoder.java
@@ -49,7 +49,16 @@
   private static final String FORMAT_KEY_CROP_BOTTOM = "crop-bottom";
 
   // Tracks webrtc::VideoCodecType.
-  public enum VideoCodecType { VIDEO_CODEC_VP8, VIDEO_CODEC_VP9, VIDEO_CODEC_H264 }
+  public enum VideoCodecType {
+    VIDEO_CODEC_VP8,
+    VIDEO_CODEC_VP9,
+    VIDEO_CODEC_H264;
+
+    @CalledByNative("VideoCodecType")
+    static VideoCodecType fromNativeIndex(int nativeIndex) {
+      return values()[nativeIndex];
+    }
+  }
 
   // Timeout for input buffer dequeue.
   private static final int DEQUEUE_INPUT_TIMEOUT = 500000;
@@ -144,21 +153,25 @@
   }
 
   // Functions to query if HW decoding is supported.
+  @CalledByNativeUnchecked
   public static boolean isVp8HwSupported() {
     return !hwDecoderDisabledTypes.contains(VP8_MIME_TYPE)
         && (findDecoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes) != null);
   }
 
+  @CalledByNativeUnchecked
   public static boolean isVp9HwSupported() {
     return !hwDecoderDisabledTypes.contains(VP9_MIME_TYPE)
         && (findDecoder(VP9_MIME_TYPE, supportedVp9HwCodecPrefixes) != null);
   }
 
+  @CalledByNativeUnchecked
   public static boolean isH264HwSupported() {
     return !hwDecoderDisabledTypes.contains(H264_MIME_TYPE)
         && (findDecoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes) != null);
   }
 
+  @CalledByNative
   public static boolean isH264HighProfileHwSupported() {
     if (hwDecoderDisabledTypes.contains(H264_MIME_TYPE)) {
       return false;
@@ -265,6 +278,9 @@
     return null; // No HW decoder.
   }
 
+  @CalledByNative
+  MediaCodecVideoDecoder() {}
+
   private void checkOnMediaCodecThread() throws IllegalStateException {
     if (mediaCodecThread.getId() != Thread.currentThread().getId()) {
       throw new IllegalStateException("MediaCodecVideoDecoder previously operated on "
@@ -273,6 +289,7 @@
   }
 
   // Pass null in |surfaceTextureHelper| to configure the codec for ByteBuffer output.
+  @CalledByNativeUnchecked
   private boolean initDecode(
       VideoCodecType type, int width, int height, SurfaceTextureHelper surfaceTextureHelper) {
     if (mediaCodecThread != null) {
@@ -346,6 +363,7 @@
 
   // Resets the decoder so it can start decoding frames with new resolution.
   // Flushes MediaCodec and clears decoder output buffers.
+  @CalledByNativeUnchecked
   private void reset(int width, int height) {
     if (mediaCodecThread == null || mediaCodec == null) {
       throw new RuntimeException("Incorrect reset call for non-initialized decoder.");
@@ -362,6 +380,7 @@
     droppedFrames = 0;
   }
 
+  @CalledByNativeUnchecked
   private void release() {
     Logging.d(TAG, "Java releaseDecoder. Total number of dropped frames: " + droppedFrames);
     checkOnMediaCodecThread();
@@ -408,6 +427,7 @@
 
   // Dequeue an input buffer and return its index, -1 if no input buffer is
   // available, or -2 if the codec is no longer operative.
+  @CalledByNativeUnchecked
   private int dequeueInputBuffer() {
     checkOnMediaCodecThread();
     try {
@@ -418,6 +438,7 @@
     }
   }
 
+  @CalledByNativeUnchecked
   private boolean queueInputBuffer(int inputBufferIndex, int size, long presentationTimeStamUs,
       long timeStampMs, long ntpTimeStamp) {
     checkOnMediaCodecThread();
@@ -475,6 +496,41 @@
     private final long decodeTimeMs;
     // System time when this frame decoding finished.
     private final long endDecodeTimeMs;
+
+    @CalledByNative("DecodedOutputBuffer")
+    int getIndex() {
+      return index;
+    }
+
+    @CalledByNative("DecodedOutputBuffer")
+    int getOffset() {
+      return offset;
+    }
+
+    @CalledByNative("DecodedOutputBuffer")
+    int getSize() {
+      return size;
+    }
+
+    @CalledByNative("DecodedOutputBuffer")
+    long getPresentationTimestampMs() {
+      return presentationTimeStampMs;
+    }
+
+    @CalledByNative("DecodedOutputBuffer")
+    long getTimestampMs() {
+      return timeStampMs;
+    }
+
+    @CalledByNative("DecodedOutputBuffer")
+    long getNtpTimestampMs() {
+      return ntpTimeStampMs;
+    }
+
+    @CalledByNative("DecodedOutputBuffer")
+    long getDecodeTimeMs() {
+      return decodeTimeMs;
+    }
   }
 
   // Helper struct for dequeueTextureBuffer() below.
@@ -508,6 +564,41 @@
       this.decodeTimeMs = decodeTimeMs;
       this.frameDelayMs = frameDelay;
     }
+
+    @CalledByNative("DecodedTextureBuffer")
+    int getTextureId() {
+      return textureID;
+    }
+
+    @CalledByNative("DecodedTextureBuffer")
+    float[] getTransformMatrix() {
+      return transformMatrix;
+    }
+
+    @CalledByNative("DecodedTextureBuffer")
+    long getPresentationTimestampMs() {
+      return presentationTimeStampMs;
+    }
+
+    @CalledByNative("DecodedTextureBuffer")
+    long getTimeStampMs() {
+      return timeStampMs;
+    }
+
+    @CalledByNative("DecodedTextureBuffer")
+    long getNtpTimestampMs() {
+      return ntpTimeStampMs;
+    }
+
+    @CalledByNative("DecodedTextureBuffer")
+    long getDecodeTimeMs() {
+      return decodeTimeMs;
+    }
+
+    @CalledByNative("DecodedTextureBuffer")
+    long getFrameDelayMs() {
+      return frameDelayMs;
+    }
   }
 
   // Poll based texture listener.
@@ -596,6 +687,7 @@
   // Throws IllegalStateException if call is made on the wrong thread, if color format changes to an
   // unsupported format, or if |mediaCodec| is not in the Executing state. Throws CodecException
   // upon codec error.
+  @CalledByNativeUnchecked
   private DecodedOutputBuffer dequeueOutputBuffer(int dequeueTimeoutMs) {
     checkOnMediaCodecThread();
     if (decodeStartTimeMs.isEmpty()) {
@@ -679,6 +771,7 @@
   // unsupported format, or if |mediaCodec| is not in the Executing state. Throws CodecException
   // upon codec error. If |dequeueTimeoutMs| > 0, the oldest decoded frame will be dropped if
   // a frame can't be returned.
+  @CalledByNativeUnchecked
   private DecodedTextureBuffer dequeueTextureBuffer(int dequeueTimeoutMs) {
     checkOnMediaCodecThread();
     if (!useSurface) {
@@ -740,6 +833,7 @@
   // Throws IllegalStateException if the call is made on the wrong thread, if codec is configured
   // for surface decoding, or if |mediaCodec| is not in the Executing state. Throws
   // MediaCodec.CodecException upon codec error.
+  @CalledByNativeUnchecked
   private void returnDecodedOutputBuffer(int index)
       throws IllegalStateException, MediaCodec.CodecException {
     checkOnMediaCodecThread();
@@ -748,4 +842,39 @@
     }
     mediaCodec.releaseOutputBuffer(index, false /* render */);
   }
+
+  @CalledByNative
+  ByteBuffer[] getInputBuffers() {
+    return inputBuffers;
+  }
+
+  @CalledByNative
+  ByteBuffer[] getOutputBuffers() {
+    return outputBuffers;
+  }
+
+  @CalledByNative
+  int getColorFormat() {
+    return colorFormat;
+  }
+
+  @CalledByNative
+  int getWidth() {
+    return width;
+  }
+
+  @CalledByNative
+  int getHeight() {
+    return height;
+  }
+
+  @CalledByNative
+  int getStride() {
+    return stride;
+  }
+
+  @CalledByNative
+  int getSliceHeight() {
+    return sliceHeight;
+  }
 }
diff --git a/sdk/android/api/org/webrtc/MediaCodecVideoEncoder.java b/sdk/android/api/org/webrtc/MediaCodecVideoEncoder.java
index b009893..054930c 100644
--- a/sdk/android/api/org/webrtc/MediaCodecVideoEncoder.java
+++ b/sdk/android/api/org/webrtc/MediaCodecVideoEncoder.java
@@ -29,6 +29,8 @@
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import org.webrtc.EglBase14;
+import org.webrtc.VideoFrame;
 
 // Java-side of peerconnection_jni.cc:MediaCodecVideoEncoder.
 // This class is an implementation detail of the Java PeerConnection API.
@@ -43,7 +45,16 @@
   private static final String TAG = "MediaCodecVideoEncoder";
 
   // Tracks webrtc::VideoCodecType.
-  public enum VideoCodecType { VIDEO_CODEC_VP8, VIDEO_CODEC_VP9, VIDEO_CODEC_H264 }
+  public enum VideoCodecType {
+    VIDEO_CODEC_VP8,
+    VIDEO_CODEC_VP9,
+    VIDEO_CODEC_H264;
+
+    @CalledByNative("VideoCodecType")
+    static VideoCodecType fromNativeIndex(int nativeIndex) {
+      return values()[nativeIndex];
+    }
+  }
 
   private static final int MEDIA_CODEC_RELEASE_TIMEOUT_MS = 5000; // Timeout for codec releasing.
   private static final int DEQUEUE_TIMEOUT = 0; // Non-blocking, no wait.
@@ -193,7 +204,7 @@
       COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m};
   private static final int[] supportedSurfaceColorList = {CodecCapabilities.COLOR_FormatSurface};
   private VideoCodecType type;
-  private int colorFormat; // Used by native code.
+  private int colorFormat;
 
   // Variables used for dynamic bitrate adjustment.
   private BitrateAdjustmentType bitrateAdjustmentType = BitrateAdjustmentType.NO_ADJUSTMENT;
@@ -242,6 +253,7 @@
   }
 
   // Functions to query if HW encoding is supported.
+  @CalledByNative
   public static boolean isVp8HwSupported() {
     return !hwEncoderDisabledTypes.contains(VP8_MIME_TYPE)
         && (findHwEncoder(VP8_MIME_TYPE, vp8HwList(), supportedColorList) != null);
@@ -255,11 +267,13 @@
     }
   }
 
+  @CalledByNative
   public static boolean isVp9HwSupported() {
     return !hwEncoderDisabledTypes.contains(VP9_MIME_TYPE)
         && (findHwEncoder(VP9_MIME_TYPE, vp9HwList, supportedColorList) != null);
   }
 
+  @CalledByNative
   public static boolean isH264HwSupported() {
     return !hwEncoderDisabledTypes.contains(H264_MIME_TYPE)
         && (findHwEncoder(H264_MIME_TYPE, h264HwList, supportedColorList) != null);
@@ -387,6 +401,9 @@
     return null; // No HW encoder.
   }
 
+  @CalledByNative
+  MediaCodecVideoEncoder() {}
+
   private void checkOnMediaCodecThread() {
     if (mediaCodecThread.getId() != Thread.currentThread().getId()) {
       throw new RuntimeException("MediaCodecVideoEncoder previously operated on " + mediaCodecThread
@@ -416,6 +433,7 @@
     }
   }
 
+  @CalledByNativeUnchecked
   boolean initEncode(VideoCodecType type, int profile, int width, int height, int kbps, int fps,
       EglBase14.Context sharedContext) {
     final boolean useSurface = sharedContext != null;
@@ -535,6 +553,7 @@
     return true;
   }
 
+  @CalledByNativeUnchecked
   ByteBuffer[] getInputBuffers() {
     ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
     Logging.d(TAG, "Input buffers: " + inputBuffers.length);
@@ -568,6 +587,7 @@
     }
   }
 
+  @CalledByNativeUnchecked
   boolean encodeBuffer(
       boolean isKeyframe, int inputBuffer, int size, long presentationTimestampUs) {
     checkOnMediaCodecThread();
@@ -581,6 +601,7 @@
     }
   }
 
+  @CalledByNativeUnchecked
   boolean encodeTexture(boolean isKeyframe, int oesTextureId, float[] transformationMatrix,
       long presentationTimestampUs) {
     checkOnMediaCodecThread();
@@ -600,9 +621,9 @@
   }
 
   /**
-   * Encodes a new style VideoFrame. Called by JNI. |bufferIndex| is -1 if we are not encoding in
-   * surface mode.
+   * Encodes a new style VideoFrame. |bufferIndex| is -1 if we are not encoding in surface mode.
    */
+  @CalledByNativeUnchecked
   boolean encodeFrame(long nativeEncoder, boolean isKeyframe, VideoFrame frame, int bufferIndex) {
     checkOnMediaCodecThread();
     try {
@@ -637,7 +658,7 @@
         if (dataV.capacity() < strideV * chromaHeight) {
           throw new RuntimeException("V-plane buffer size too small.");
         }
-        nativeFillBuffer(
+        fillNativeBuffer(
             nativeEncoder, bufferIndex, dataY, strideY, dataU, strideU, dataV, strideV);
         i420Buffer.release();
         // I420 consists of one full-resolution and two half-resolution planes.
@@ -652,6 +673,7 @@
     }
   }
 
+  @CalledByNativeUnchecked
   void release() {
     Logging.d(TAG, "Java releaseEncoder");
     checkOnMediaCodecThread();
@@ -733,6 +755,7 @@
     Logging.d(TAG, "Java releaseEncoder done");
   }
 
+  @CalledByNativeUnchecked
   private boolean setRates(int kbps, int frameRate) {
     checkOnMediaCodecThread();
 
@@ -775,6 +798,7 @@
 
   // Dequeue an input buffer and return its index, -1 if no input buffer is
   // available, or -2 if the codec is no longer operative.
+  @CalledByNativeUnchecked
   int dequeueInputBuffer() {
     checkOnMediaCodecThread();
     try {
@@ -799,10 +823,31 @@
     public final ByteBuffer buffer;
     public final boolean isKeyFrame;
     public final long presentationTimestampUs;
+
+    @CalledByNative("OutputBufferInfo")
+    int getIndex() {
+      return index;
+    }
+
+    @CalledByNative("OutputBufferInfo")
+    ByteBuffer getBuffer() {
+      return buffer;
+    }
+
+    @CalledByNative("OutputBufferInfo")
+    boolean isKeyFrame() {
+      return isKeyFrame;
+    }
+
+    @CalledByNative("OutputBufferInfo")
+    long getPresentationTimestampUs() {
+      return presentationTimestampUs;
+    }
   }
 
   // Dequeue and return an output buffer, or null if no output is ready.  Return
   // a fake OutputBufferInfo with index -1 if the codec is no longer operable.
+  @CalledByNativeUnchecked
   OutputBufferInfo dequeueOutputBuffer() {
     checkOnMediaCodecThread();
     try {
@@ -925,6 +970,7 @@
 
   // Release a dequeued output buffer back to the codec for re-use.  Return
   // false if the codec is no longer operable.
+  @CalledByNativeUnchecked
   boolean releaseOutputBuffer(int index) {
     checkOnMediaCodecThread();
     try {
@@ -936,7 +982,17 @@
     }
   }
 
+  @CalledByNative
+  int getColorFormat() {
+    return colorFormat;
+  }
+
+  @CalledByNative
+  static boolean isTextureBuffer(VideoFrame.Buffer buffer) {
+    return buffer instanceof VideoFrame.TextureBuffer;
+  }
+
   /** Fills an inputBuffer with the given index with data from the byte buffers. */
-  private static native void nativeFillBuffer(long nativeEncoder, int inputBuffer, ByteBuffer dataY,
+  private static native void fillNativeBuffer(long nativeEncoder, int inputBuffer, ByteBuffer dataY,
       int strideY, ByteBuffer dataU, int strideU, ByteBuffer dataV, int strideV);
 }