diff --git a/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java b/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java index 162fad1c3b..33b8c95986 100644 --- a/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java +++ b/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java @@ -276,8 +276,7 @@ public class VideoCapturerAndroidTest extends ActivityTestCase { FakeCapturerObserver observer = new FakeCapturerObserver(); String deviceName = CameraEnumerationAndroid.getDeviceName(0); - ArrayList formats = - CameraEnumerationAndroid.getSupportedFormats(0); + List formats = CameraEnumerationAndroid.getSupportedFormats(0); VideoCapturerAndroid capturer = VideoCapturerAndroid.create(deviceName, null); @@ -301,8 +300,7 @@ public class VideoCapturerAndroidTest extends ActivityTestCase { FakeCapturerObserver observer = new FakeCapturerObserver(); String deviceName = CameraEnumerationAndroid.getDeviceName(0); - ArrayList formats = - CameraEnumerationAndroid.getSupportedFormats(0); + List formats = CameraEnumerationAndroid.getSupportedFormats(0); VideoCapturerAndroid capturer = VideoCapturerAndroid.create(deviceName, null); diff --git a/talk/app/webrtc/java/android/org/webrtc/CameraEnumerationAndroid.java b/talk/app/webrtc/java/android/org/webrtc/CameraEnumerationAndroid.java index d6fa1274e4..136c232b7f 100644 --- a/talk/app/webrtc/java/android/org/webrtc/CameraEnumerationAndroid.java +++ b/talk/app/webrtc/java/android/org/webrtc/CameraEnumerationAndroid.java @@ -46,9 +46,23 @@ import java.util.List; @SuppressWarnings("deprecation") public class CameraEnumerationAndroid { private final static String TAG = "CameraEnumerationAndroid"; - // List of formats supported by all cameras. This list is filled once in order - // to be able to switch cameras. - public static List> supportedFormats; + // Synchronized on |CameraEnumerationAndroid.this|. + private static Enumerator enumerator = new CameraEnumerator(); + + public interface Enumerator { + /** + * Returns a list of supported CaptureFormats for the camera with index |cameraId|. + */ + List getSupportedFormats(int cameraId); + } + + public static synchronized void setEnumerator(Enumerator enumerator) { + CameraEnumerationAndroid.enumerator = enumerator; + } + + public static synchronized List getSupportedFormats(int cameraId) { + return enumerator.getSupportedFormats(cameraId); + } public static class CaptureFormat { public final int width; @@ -175,37 +189,8 @@ public class CameraEnumerationAndroid { return null; } - public static boolean initStatics() { - if (supportedFormats != null) - return true; - try { - Log.d(TAG, "Get supported formats."); - supportedFormats = - new ArrayList>(Camera.getNumberOfCameras()); - // Start requesting supported formats from camera with the highest index - // (back camera) first. If it fails then likely camera is in bad state. - for (int i = Camera.getNumberOfCameras() - 1; i >= 0; i--) { - ArrayList supportedFormat = getSupportedFormats(i); - if (supportedFormat.size() == 0) { - Log.e(TAG, "Fail to get supported formats for camera " + i); - supportedFormats = null; - return false; - } - supportedFormats.add(supportedFormat); - } - // Reverse the list since it is filled in reverse order. - Collections.reverse(supportedFormats); - Log.d(TAG, "Get supported formats done."); - return true; - } catch (Exception e) { - supportedFormats = null; - Log.e(TAG, "InitStatics failed",e); - } - return false; - } - public static String getSupportedFormatsAsJson(int id) throws JSONException { - List formats = supportedFormats.get(id); + List formats = getSupportedFormats(id); JSONArray json_formats = new JSONArray(); for (CaptureFormat format : formats) { JSONObject json_format = new JSONObject(); @@ -219,42 +204,6 @@ public class CameraEnumerationAndroid { return json_formats.toString(); } - // Returns a list of CaptureFormat for the camera with index id. - public static ArrayList getSupportedFormats(int id) { - ArrayList formatList = new ArrayList(); - - Camera camera; - try { - Log.d(TAG, "Opening camera " + id); - camera = Camera.open(id); - } catch (Exception e) { - Log.e(TAG, "Open camera failed on id " + id, e); - return formatList; - } - - try { - Camera.Parameters parameters; - parameters = camera.getParameters(); - // getSupportedPreviewFpsRange returns a sorted list. - List listFpsRange = parameters.getSupportedPreviewFpsRange(); - int[] range = {0, 0}; - if (listFpsRange != null) - range = listFpsRange.get(listFpsRange.size() -1); - - List supportedSizes = parameters.getSupportedPreviewSizes(); - for (Camera.Size size : supportedSizes) { - formatList.add(new CaptureFormat(size.width, size.height, - range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX], - range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX])); - } - } catch (Exception e) { - Log.e(TAG, "getSupportedFormats failed on id " + id, e); - } - camera.release(); - camera = null; - return formatList; - } - // Helper class for finding the closest supported format for the two functions below. private static abstract class ClosestComparator implements Comparator { // Difference between supported and requested parameter. diff --git a/talk/app/webrtc/java/android/org/webrtc/CameraEnumerator.java b/talk/app/webrtc/java/android/org/webrtc/CameraEnumerator.java new file mode 100644 index 0000000000..0e6b978c9d --- /dev/null +++ b/talk/app/webrtc/java/android/org/webrtc/CameraEnumerator.java @@ -0,0 +1,102 @@ +/* + * libjingle + * Copyright 2015 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.webrtc; + +import android.hardware.Camera; +import android.os.SystemClock; +import android.util.Log; + +import org.webrtc.CameraEnumerationAndroid.CaptureFormat; + +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("deprecation") +public class CameraEnumerator implements CameraEnumerationAndroid.Enumerator { + private final static String TAG = "CameraEnumerator"; + // Each entry contains the supported formats for corresponding camera index. The formats for all + // cameras are enumerated on the first call to getSupportedFormats(), and cached for future + // reference. + private List> cachedSupportedFormats; + + @Override + public List getSupportedFormats(int cameraId) { + synchronized (this) { + if (cachedSupportedFormats == null) { + cachedSupportedFormats = new ArrayList>(); + for (int i = 0; i < CameraEnumerationAndroid.getDeviceCount(); ++i) { + cachedSupportedFormats.add(enumerateFormats(i)); + } + } + } + return cachedSupportedFormats.get(cameraId); + } + + private List enumerateFormats(int cameraId) { + Log.d(TAG, "Get supported formats for camera index " + cameraId + "."); + final long startTimeMs = SystemClock.elapsedRealtime(); + final Camera.Parameters parameters; + Camera camera = null; + try { + Log.d(TAG, "Opening camera with index " + cameraId); + camera = Camera.open(cameraId); + parameters = camera.getParameters(); + } catch (RuntimeException e) { + Log.e(TAG, "Open camera failed on camera index " + cameraId, e); + return new ArrayList(); + } finally { + if (camera != null) { + camera.release(); + } + } + + final List formatList = new ArrayList(); + try { + int minFps = 0; + int maxFps = 0; + final List listFpsRange = parameters.getSupportedPreviewFpsRange(); + if (listFpsRange != null) { + // getSupportedPreviewFpsRange() returns a sorted list. Take the fps range + // corresponding to the highest fps. + final int[] range = listFpsRange.get(listFpsRange.size() - 1); + minFps = range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX]; + maxFps = range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]; + } + for (Camera.Size size : parameters.getSupportedPreviewSizes()) { + formatList.add(new CaptureFormat(size.width, size.height, minFps, maxFps)); + } + } catch (Exception e) { + Log.e(TAG, "getSupportedFormats() failed on camera index " + cameraId, e); + } + + final long endTimeMs = SystemClock.elapsedRealtime(); + Log.d(TAG, "Get supported formats for camera index " + cameraId + " done." + + " Time spent: " + (endTimeMs - startTimeMs) + " ms."); + return formatList; + } +} diff --git a/talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java b/talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java index d4251f6021..ffda916cbd 100644 --- a/talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java +++ b/talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java @@ -220,7 +220,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba } public synchronized List getSupportedFormats() { - return CameraEnumerationAndroid.supportedFormats.get(id); + return CameraEnumerationAndroid.getSupportedFormats(id); } // Return a list of timestamps for the frames that have been sent out, but not returned yet. @@ -234,14 +234,11 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba } // Called by native code. - // Enumerates resolution and frame rates for all cameras to be able to switch - // cameras. Initializes local variables for the camera named |deviceName| and - // starts a thread to be used for capturing. - // If deviceName is empty, the first available device is used in order to be - // compatible with the generic VideoCapturer class. + // Initializes local variables for the camera named |deviceName|. If |deviceName| is empty, the + // first available device is used in order to be compatible with the generic VideoCapturer class. synchronized boolean init(String deviceName) { Log.d(TAG, "init: " + deviceName); - if (deviceName == null || !CameraEnumerationAndroid.initStatics()) + if (deviceName == null) return false; boolean foundDevice = false; diff --git a/talk/app/webrtc/java/jni/classreferenceholder.cc b/talk/app/webrtc/java/jni/classreferenceholder.cc index 2c4f1e6358..520b5a7a19 100644 --- a/talk/app/webrtc/java/jni/classreferenceholder.cc +++ b/talk/app/webrtc/java/jni/classreferenceholder.cc @@ -71,6 +71,8 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { LoadClass(jni, "org/webrtc/IceCandidate"); #if defined(ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) LoadClass(jni, "android/graphics/SurfaceTexture"); + LoadClass(jni, "org/webrtc/CameraEnumerator"); + LoadClass(jni, "org/webrtc/CameraEnumerationAndroid"); LoadClass(jni, "org/webrtc/VideoCapturerAndroid"); LoadClass(jni, "org/webrtc/VideoCapturerAndroid$NativeObserver"); LoadClass(jni, "org/webrtc/EglBase"); diff --git a/talk/libjingle.gyp b/talk/libjingle.gyp index 5a61ca4ee8..339fa54695 100755 --- a/talk/libjingle.gyp +++ b/talk/libjingle.gyp @@ -141,6 +141,7 @@ # and include it here. 'android_java_files': [ 'app/webrtc/java/android/org/webrtc/CameraEnumerationAndroid.java', + 'app/webrtc/java/android/org/webrtc/CameraEnumerator.java', 'app/webrtc/java/android/org/webrtc/EglBase.java', 'app/webrtc/java/android/org/webrtc/GlRectDrawer.java', 'app/webrtc/java/android/org/webrtc/GlShader.java',