Bug 1296744 - Make cameraCallbackBridge use new native JNI bindings; r=snorp
authorJim Chen <nchen@mozilla.com>
Fri, 26 Aug 2016 12:26:46 -0400
changeset 311460 da87045db58056e2c495ec4df8da74fc1093640d
parent 311459 db1826bc2da5ebb8984eacb7a622b376a8b1598f
child 311461 f2786b15fa37b4678e59b9d71b8539bfaa43e124
push id20400
push userkwierso@gmail.com
push dateFri, 26 Aug 2016 23:33:52 +0000
treeherderfx-team@3c4c4accb139 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1296744
milestone51.0a1
Bug 1296744 - Make cameraCallbackBridge use new native JNI bindings; r=snorp Make the GeckoAppShell.cameraCallbackBridge callback use the new native JNI bindings. Also refactor the relevant code in GeckoAppShell and CameraStreamImpl.
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
netwerk/protocol/device/CameraStreamImpl.cpp
netwerk/protocol/device/CameraStreamImpl.h
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
@@ -65,16 +65,17 @@ import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.ImageFormat;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.SurfaceTexture;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.hardware.Camera;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.location.Criteria;
 import android.location.Location;
 import android.location.LocationListener;
 import android.location.LocationManager;
@@ -1821,23 +1822,40 @@ public class GeckoAppShell
     public static GeckoInterface getGeckoInterface() {
         return sGeckoInterface;
     }
 
     public static void setGeckoInterface(GeckoInterface aGeckoInterface) {
         sGeckoInterface = aGeckoInterface;
     }
 
-    public static android.hardware.Camera sCamera;
+    /* package */ static Camera sCamera;
+
+    private static final int kPreferredFPS = 25;
+    private static byte[] sCameraBuffer;
 
-    static native void cameraCallbackBridge(byte[] data);
+    private static class CameraCallback implements Camera.PreviewCallback {
+        @WrapForJNI(calledFrom = "gecko")
+        private static native void onFrameData(int camera, byte[] data);
+
+        private final int mCamera;
 
-    static final int kPreferredFPS = 25;
-    static byte[] sCameraBuffer;
+        public CameraCallback(int camera) {
+            mCamera = camera;
+        }
 
+        @Override
+        public void onPreviewFrame(byte[] data, Camera camera) {
+            onFrameData(mCamera, data);
+
+            if (sCamera != null) {
+                sCamera.addCallbackBuffer(sCameraBuffer);
+            }
+        }
+    }
 
     @WrapForJNI(calledFrom = "gecko")
     static int[] initCamera(String aContentType, int aCamera, int aWidth, int aHeight) {
         ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
                     try {
                         if (getGeckoInterface() != null)
@@ -1848,24 +1866,24 @@ public class GeckoAppShell
 
         // [0] = 0|1 (failure/success)
         // [1] = width
         // [2] = height
         // [3] = fps
         int[] result = new int[4];
         result[0] = 0;
 
-        if (android.hardware.Camera.getNumberOfCameras() == 0) {
+        if (Camera.getNumberOfCameras() == 0) {
             return result;
         }
 
         try {
-            sCamera = android.hardware.Camera.open(aCamera);
+            sCamera = Camera.open(aCamera);
 
-            android.hardware.Camera.Parameters params = sCamera.getParameters();
+            Camera.Parameters params = sCamera.getParameters();
             params.setPreviewFormat(ImageFormat.NV21);
 
             // use the preview fps closest to 25 fps.
             int fpsDelta = 1000;
             try {
                 Iterator<Integer> it = params.getSupportedPreviewFrameRates().iterator();
                 while (it.hasNext()) {
                     int nFps = it.next();
@@ -1874,21 +1892,21 @@ public class GeckoAppShell
                         params.setPreviewFrameRate(nFps);
                     }
                 }
             } catch (Exception e) {
                 params.setPreviewFrameRate(kPreferredFPS);
             }
 
             // set up the closest preview size available
-            Iterator<android.hardware.Camera.Size> sit = params.getSupportedPreviewSizes().iterator();
+            Iterator<Camera.Size> sit = params.getSupportedPreviewSizes().iterator();
             int sizeDelta = 10000000;
             int bufferSize = 0;
             while (sit.hasNext()) {
-                android.hardware.Camera.Size size = sit.next();
+                Camera.Size size = sit.next();
                 if (Math.abs(size.width * size.height - aWidth * aHeight) < sizeDelta) {
                     sizeDelta = Math.abs(size.width * size.height - aWidth * aHeight);
                     params.setPreviewSize(size.width, size.height);
                     bufferSize = size.width * size.height;
                 }
             }
 
             try {
@@ -1902,24 +1920,17 @@ public class GeckoAppShell
                 }
             } catch (IOException | RuntimeException e) {
                 Log.w(LOGTAG, "Error setPreviewXXX:", e);
             }
 
             sCamera.setParameters(params);
             sCameraBuffer = new byte[(bufferSize * 12) / 8];
             sCamera.addCallbackBuffer(sCameraBuffer);
-            sCamera.setPreviewCallbackWithBuffer(new android.hardware.Camera.PreviewCallback() {
-                @Override
-                public void onPreviewFrame(byte[] data, android.hardware.Camera camera) {
-                    cameraCallbackBridge(data);
-                    if (sCamera != null)
-                        sCamera.addCallbackBuffer(sCameraBuffer);
-                }
-            });
+            sCamera.setPreviewCallbackWithBuffer(new CameraCallback(aCamera));
             sCamera.startPreview();
             params = sCamera.getParameters();
             result[0] = 1;
             result[1] = params.getPreviewSize().width;
             result[2] = params.getPreviewSize().height;
             result[3] = params.getPreviewFrameRate();
         } catch (RuntimeException e) {
             Log.w(LOGTAG, "initCamera RuntimeException.", e);
--- a/netwerk/protocol/device/CameraStreamImpl.cpp
+++ b/netwerk/protocol/device/CameraStreamImpl.cpp
@@ -1,55 +1,61 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CameraStreamImpl.h"
+#include "GeneratedJNINatives.h"
 #include "nsCRTGlue.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "mozilla/Monitor.h"
 
-/**
- * JNI part & helper runnable
- */
-
-extern "C" {
-    NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_cameraCallbackBridge(JNIEnv *, jclass, jbyteArray data);
-}
-
-NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoAppShell_cameraCallbackBridge(JNIEnv *env, jclass, jbyteArray data) {
-    mozilla::net::CameraStreamImpl* impl = mozilla::net::CameraStreamImpl::GetInstance(0);
-    
-    impl->transmitFrame(env, &data);
-}
-
 using namespace mozilla;
 
 namespace mozilla {
 namespace net {
 
 static CameraStreamImpl* mCamera0 = nullptr;
 static CameraStreamImpl* mCamera1 = nullptr;
 
+class CameraStreamImpl::Callback
+    : public java::GeckoAppShell::CameraCallback::Natives<Callback>
+{
+public:
+    static void OnFrameData(int32_t aCamera, jni::ByteArray::Param aData)
+    {
+        MOZ_ASSERT(NS_IsMainThread());
+
+        CameraStreamImpl* impl = GetInstance(uint32_t(aCamera));
+        if (impl) {
+            impl->TransmitFrame(aData);
+        }
+    }
+};
+
 /**
  * CameraStreamImpl
  */
 
-void CameraStreamImpl::transmitFrame(JNIEnv *env, jbyteArray *data) {
-    if (!mCallback)
-      return;
-    jboolean isCopy;
-    jbyte* jFrame = env->GetByteArrayElements(*data, &isCopy);
-    uint32_t length = env->GetArrayLength(*data);
-    if (length > 0) {
-        mCallback->ReceiveFrame((char*)jFrame, length);
+void CameraStreamImpl::TransmitFrame(jni::ByteArray::Param aData) {
+    if (!mCallback) {
+        return;
     }
-    env->ReleaseByteArrayElements(*data, jFrame, 0);
+
+    JNIEnv* const env = jni::GetGeckoThreadEnv();
+    const size_t length = size_t(env->GetArrayLength(aData.Get()));
+
+    if (!length) {
+        return;
+    }
+
+    jbyte* const data = env->GetByteArrayElements(aData.Get(), nullptr);
+    mCallback->ReceiveFrame(reinterpret_cast<char*>(data), length);
+    env->ReleaseByteArrayElements(aData.Get(), data, JNI_ABORT);
 }
 
 CameraStreamImpl* CameraStreamImpl::GetInstance(uint32_t aCamera) {
     CameraStreamImpl* res = nullptr;
     switch(aCamera) {
         case 0:
             if (mCamera0)
                 res = mCamera0;
@@ -81,17 +87,27 @@ CameraStreamImpl::~CameraStreamImpl()
     NS_WARNING("CameraStreamImpl::~CameraStreamImpl()");
 }
 
 bool CameraStreamImpl::Init(const nsCString& contentType, const uint32_t& camera, const uint32_t& width, const uint32_t& height, FrameCallback* aCallback)
 {
     mCallback = aCallback;
     mWidth = width;
     mHeight = height;
-    return AndroidBridge::Bridge()->InitCamera(contentType, camera, &mWidth, &mHeight, &mFps);
+
+    Callback::Init();
+    jni::IntArray::LocalRef retArray = java::GeckoAppShell::InitCamera(
+            contentType, int32_t(camera), int32_t(width), int32_t(height));
+    nsTArray<int32_t> ret = retArray->GetElements();
+
+    mWidth = uint32_t(ret[1]);
+    mHeight = uint32_t(ret[2]);
+    mFps = uint32_t(ret[3]);
+
+    return !!ret[0];
 }
 
 void CameraStreamImpl::Close() {
     java::GeckoAppShell::CloseCamera();
     mCallback = nullptr;
 }
 
 } // namespace net
--- a/netwerk/protocol/device/CameraStreamImpl.h
+++ b/netwerk/protocol/device/CameraStreamImpl.h
@@ -1,64 +1,67 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __CAMERASTREAMIMPL_H__
 #define __CAMERASTREAMIMPL_H__
 
+#include "mozilla/jni/Refs.h"
+
 #include "nsString.h"
-#include "AndroidBridge.h"
 
 /**
  * This singleton class handles communication with the Android camera
  * through JNI. It is used by the IPDL parent or directly from the chrome process
  */
 
 namespace mozilla {
 namespace net {
-    
+
 class CameraStreamImpl {
 public:
     class FrameCallback {
     public:
         virtual void ReceiveFrame(char* frame, uint32_t length) = 0;
     };
-    
+
     /**
      * instance bound to a given camera
      */
     static CameraStreamImpl* GetInstance(uint32_t aCamera);
-    
+
     bool initNeeded() {
         return !mInit;
     }
-    
+
     FrameCallback* GetFrameCallback() {
         return mCallback;
     }
-    
+
     bool Init(const nsCString& contentType, const uint32_t& camera, const uint32_t& width, const uint32_t& height, FrameCallback* callback);
     void Close();
-    
+
     uint32_t GetWidth() { return mWidth; }
     uint32_t GetHeight() { return mHeight; }
     uint32_t GetFps() { return mFps; }
-    
+
     void takePicture(const nsAString& aFileName);
-    
-    void transmitFrame(JNIEnv *env, jbyteArray *data);
-    
+
 private:
+    class Callback;
+
     CameraStreamImpl(uint32_t aCamera);
     CameraStreamImpl(const CameraStreamImpl&);
     CameraStreamImpl& operator=(const CameraStreamImpl&);
 
     ~CameraStreamImpl();
 
+    void TransmitFrame(jni::ByteArray::Param aData);
+
     bool mInit;
     uint32_t mCamera;
     uint32_t mWidth;
     uint32_t mHeight;
     uint32_t mFps;
     FrameCallback* mCallback;
 };
 
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -677,39 +677,16 @@ namespace mozilla {
     {
         if (sTracerRunnable)
             return sTracerRunnable->Signal();
     }
 
 }
 
 
-bool
-AndroidBridge::InitCamera(const nsCString& contentType, uint32_t camera, uint32_t *width, uint32_t *height, uint32_t *fps)
-{
-    auto arr = GeckoAppShell::InitCamera
-      (NS_ConvertUTF8toUTF16(contentType), (int32_t) camera, (int32_t) *width, (int32_t) *height);
-
-    if (!arr)
-        return false;
-
-    JNIEnv* const env = arr.Env();
-    jint *elements = env->GetIntArrayElements(arr.Get(), 0);
-
-    *width = elements[1];
-    *height = elements[2];
-    *fps = elements[3];
-
-    bool res = elements[0] == 1;
-
-    env->ReleaseIntArrayElements(arr.Get(), elements, 0);
-
-    return res;
-}
-
 void
 AndroidBridge::GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo)
 {
     ALOG_BRIDGE("AndroidBridge::GetCurrentBatteryInformation");
 
     // To prevent calling too many methods through JNI, the Java method returns
     // an array of double even if we actually want a double and a boolean.
     auto arr = GeckoAppShell::GetCurrentBatteryInformation();
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -187,18 +187,16 @@ public:
 
     // Returns a global reference to the Context for Fennec's Activity. The
     // caller is responsible for ensuring this doesn't leak by calling
     // DeleteGlobalRef() when the context is no longer needed.
     jobject GetGlobalContextRef(void);
 
     void HandleGeckoMessage(JSContext* cx, JS::HandleObject message);
 
-    bool InitCamera(const nsCString& contentType, uint32_t camera, uint32_t *width, uint32_t *height, uint32_t *fps);
-
     void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo);
 
     nsresult GetSegmentInfoForText(const nsAString& aText,
                                    nsIMobileMessageCallback* aRequest);
     void SendMessage(const nsAString& aNumber, const nsAString& aText,
                      nsIMobileMessageCallback* aRequest);
     void GetMessage(int32_t aMessageId, nsIMobileMessageCallback* aRequest);
     void DeleteMessage(int32_t aMessageId, nsIMobileMessageCallback* aRequest);