VideoCapturerAndroid allocates direct buffers so that the frame buffers can be used in C++ without a copy. However byte[] array = ByteBuffer.array() seems to point to the beginning of the underlaying buffer and that is what the camera fills. But it turns out that ByteBuffer.arrayOffset() returns an offset and it seems like the pointer returned by jni->GetDirectBufferAddress(j_frame). This cl reverts back to pass the byte[] to c++ and use  jni->GetByteArrayElements to get the address of the buffer.

R=glaznev@webrtc.org, magjed@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/35349004

Cr-Commit-Position: refs/heads/master@{#8535}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8535 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java b/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java
index 23cc214..1b0983a 100644
--- a/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java
+++ b/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java
@@ -33,7 +33,6 @@
 import org.webrtc.VideoCapturerAndroid.CaptureFormat;
 import org.webrtc.VideoRenderer.I420Frame;
 
-import java.nio.ByteBuffer;
 import java.util.ArrayList;
 
 @SuppressWarnings("deprecation")
@@ -79,12 +78,11 @@
     }
 
     @Override
-    public void OnFrameCaptured(ByteBuffer frame, int rotation,
+    public void OnFrameCaptured(byte[] frame, int length, int rotation,
         long timeStamp) {
-      assertTrue(frame.isDirect());
       synchronized (frameLock) {
         ++framesCaptured;
-        frameSize = frame.capacity();
+        frameSize = length;
         frameLock.notify();
       }
     }
diff --git a/talk/app/webrtc/java/jni/androidvideocapturer_jni.cc b/talk/app/webrtc/java/jni/androidvideocapturer_jni.cc
index b445be5..dfe1b1b 100644
--- a/talk/app/webrtc/java/jni/androidvideocapturer_jni.cc
+++ b/talk/app/webrtc/java/jni/androidvideocapturer_jni.cc
@@ -178,13 +178,20 @@
 JNIEnv* AndroidVideoCapturerJni::jni() { return AttachCurrentThreadIfNeeded(); }
 
 JOW(void, VideoCapturerAndroid_00024NativeObserver_nativeOnFrameCaptured)
-    (JNIEnv* jni, jclass, jlong j_capturer, jobject j_frame,
+    (JNIEnv* jni, jclass, jlong j_capturer, jbyteArray j_frame, jint length,
         jint rotation, jlong ts) {
-  void* bytes = jni->GetDirectBufferAddress(j_frame);
-  DCHECK(bytes != NULL);
-  jlong length = jni->GetDirectBufferCapacity(j_frame);
-  reinterpret_cast<AndroidVideoCapturerJni*>(
-      j_capturer)->OnIncomingFrame(bytes, length, rotation, ts);
+  jboolean is_copy = true;
+  jbyte* bytes = jni->GetByteArrayElements(j_frame, &is_copy);
+  if (!is_copy) {
+    reinterpret_cast<AndroidVideoCapturerJni*>(
+        j_capturer)->OnIncomingFrame(bytes, length, rotation, ts);
+  }  else {
+    // If this is a copy of the original frame, it means that the memory
+    // is not direct memory and thus VideoCapturerAndroid does not guarantee
+    // that the memory is valid when we have released |j_frame|.
+    DCHECK(false) << "j_frame is a copy.";
+  }
+  jni->ReleaseByteArrayElements(j_frame, bytes, JNI_ABORT);
 }
 
 JOW(void, VideoCapturerAndroid_00024NativeObserver_nativeCapturerStarted)
diff --git a/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java b/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java
index 8b4771d..bb0b338 100644
--- a/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java
+++ b/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java
@@ -536,10 +536,11 @@
       rotation = 360 - rotation;
     }
     rotation = (info.orientation + rotation) % 360;
-
-    frameObserver.OnFrameCaptured(
-        videoBuffers.reserveByteBuffer(data, captureTimeMs),
-        rotation,
+    // Mark the frame owning |data| as used.
+    // Note that since data is directBuffer,
+    // data.length >= videoBuffers.frameSize.
+    videoBuffers.reserveByteBuffer(data, captureTimeMs);
+    frameObserver.OnFrameCaptured(data, videoBuffers.frameSize, rotation,
         captureTimeMs);
   }
 
@@ -578,14 +579,14 @@
     // potentially stalling the capturer if it runs out of buffers to write to).
     private static int numCaptureBuffers = 3;
     private final Frame cameraFrames[];
+    public final int frameSize;
 
     private static class Frame {
-      public final ByteBuffer buffer;
+      private final ByteBuffer buffer;
       public long timeStamp = -1;
 
-      Frame(int width, int height, int format) {
-        int bufSize = width * height * ImageFormat.getBitsPerPixel(format) / 8;
-        buffer = ByteBuffer.allocateDirect(bufSize);
+      Frame(int frameSize) {
+        buffer = ByteBuffer.allocateDirect(frameSize);
       }
 
       byte[] data() {
@@ -595,8 +596,9 @@
 
     FramePool(int width, int height, int format) {
       cameraFrames = new Frame[numCaptureBuffers];
+      frameSize = width * height * ImageFormat.getBitsPerPixel(format) / 8;
       for (int i = 0; i < numCaptureBuffers; i++) {
-        cameraFrames[i] = new Frame(width, height, format);
+        cameraFrames[i] = new Frame(frameSize);
       }
     }
 
@@ -608,11 +610,11 @@
       }
     }
 
-    ByteBuffer reserveByteBuffer(byte[] data, long timeStamp) {
+    void reserveByteBuffer(byte[] data, long timeStamp) {
       for (Frame frame : cameraFrames) {
         if (data == frame.data()) {
           frame.timeStamp = timeStamp;
-          return frame.buffer;
+          return;
         }
       }
       throw new RuntimeException("unknown data buffer?!?");
@@ -639,7 +641,8 @@
 
     // Delivers a captured frame. Called on a Java thread owned by
     // VideoCapturerAndroid.
-    abstract void OnFrameCaptured(ByteBuffer buffer, int rotation, long timeStamp);
+    abstract void OnFrameCaptured(byte[] data, int length, int rotation,
+        long timeStamp);
   }
 
   // An implementation of CapturerObserver that forwards all calls from
@@ -657,14 +660,14 @@
     }
 
     @Override
-    public void OnFrameCaptured(ByteBuffer byteBuffer, int rotation,
+    public void OnFrameCaptured(byte[] data, int length, int rotation,
         long timeStamp) {
-      nativeOnFrameCaptured(nativeCapturer, byteBuffer, rotation, timeStamp);
+      nativeOnFrameCaptured(nativeCapturer, data, length, rotation, timeStamp);
     }
 
     private native void nativeCapturerStarted(long nativeCapturer,
         boolean success);
     private native void nativeOnFrameCaptured(long nativeCapturer,
-        ByteBuffer byteBuffer, int rotation, long timeStamp);
+        byte[] data, int length, int rotation, long timeStamp);
   }
 }