CameraEnumerationAndroid: Add getSupportedFormats() implementation using android.hardware.camera2

Enumerating using android.hardware.camera2 is 10x faster than enumerating using android.hardware.camera, but they don't list exactly the same formats. android.hardware.camera2 support higher resolutions for some cameras, and also different framerates.

R=tommi@webrtc.org

Review URL: https://codereview.webrtc.org/1321893003 .

Cr-Commit-Position: refs/heads/master@{#9861}
This commit is contained in:
Magnus Jedvert
2015-09-04 15:13:24 +02:00
parent 242d6384c4
commit e7a0de773a
4 changed files with 155 additions and 0 deletions

View File

@ -29,12 +29,15 @@ package org.webrtc;
import android.hardware.Camera; import android.hardware.Camera;
import android.test.ActivityTestCase; import android.test.ActivityTestCase;
import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.SmallTest;
import android.util.Size;
import org.webrtc.CameraEnumerationAndroid.CaptureFormat; import org.webrtc.CameraEnumerationAndroid.CaptureFormat;
import org.webrtc.VideoRenderer.I420Frame; import org.webrtc.VideoRenderer.I420Frame;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public class VideoCapturerAndroidTest extends ActivityTestCase { public class VideoCapturerAndroidTest extends ActivityTestCase {
@ -174,6 +177,37 @@ public class VideoCapturerAndroidTest extends ActivityTestCase {
getInstrumentation().getContext(), true, true, true)); getInstrumentation().getContext(), true, true, true));
} }
@SmallTest
// Test that enumerating formats using android.hardware.camera2 will give the same formats as
// android.hardware.camera in the range 320x240 to 1280x720. Often the camera2 API may contain
// some high resolutions that are not supported in camera1, but it may also be the other way
// around in some cases. Supported framerates may also differ, so don't compare those.
public void testCamera2Enumerator() {
if (!Camera2Enumerator.isSupported()) {
return;
}
final CameraEnumerationAndroid.Enumerator camera1Enumerator = new CameraEnumerator();
final CameraEnumerationAndroid.Enumerator camera2Enumerator =
new Camera2Enumerator(getInstrumentation().getContext());
for (int i = 0; i < CameraEnumerationAndroid.getDeviceCount(); ++i) {
final Set<Size> resolutions1 = new HashSet<Size>();
for (CaptureFormat format : camera1Enumerator.getSupportedFormats(i)) {
resolutions1.add(new Size(format.width, format.height));
}
final Set<Size> resolutions2 = new HashSet<Size>();
for (CaptureFormat format : camera2Enumerator.getSupportedFormats(i)) {
resolutions2.add(new Size(format.width, format.height));
}
for (Size size : resolutions1) {
if (size.getWidth() >= 320 && size.getHeight() >= 240
&& size.getWidth() <= 1280 && size.getHeight() <= 720) {
assertTrue(resolutions2.contains(size));
}
}
}
}
@SmallTest @SmallTest
public void testCreateAndRelease() throws Exception { public void testCreateAndRelease() throws Exception {
VideoCapturerAndroid capturer = VideoCapturerAndroid.create("", null); VideoCapturerAndroid capturer = VideoCapturerAndroid.create("", null);

View File

@ -0,0 +1,119 @@
/*
* 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.content.Context;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.os.Build;
import android.os.SystemClock;
import android.util.Log;
import android.util.Range;
import android.util.Size;
import org.webrtc.CameraEnumerationAndroid.CaptureFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Camera2Enumerator implements CameraEnumerationAndroid.Enumerator {
private final static String TAG = "Camera2Enumerator";
private final static double NANO_SECONDS_PER_SECOND = 1.0e9;
private final CameraManager cameraManager;
// Each entry contains the supported formats for a given camera index. The formats are enumerated
// lazily in getSupportedFormats(), and cached for future reference.
private final Map<Integer, List<CaptureFormat>> cachedSupportedFormats =
new HashMap<Integer, List<CaptureFormat>>();
public static boolean isSupported() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
}
public Camera2Enumerator(Context context) {
cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
}
@Override
public List<CaptureFormat> getSupportedFormats(int cameraId) {
synchronized (cachedSupportedFormats) {
if (cachedSupportedFormats.containsKey(cameraId)) {
return cachedSupportedFormats.get(cameraId);
}
Log.d(TAG, "Get supported formats for camera index " + cameraId + ".");
final long startTimeMs = SystemClock.elapsedRealtime();
final CameraCharacteristics cameraCharacteristics;
try {
cameraCharacteristics = cameraManager.getCameraCharacteristics(Integer.toString(cameraId));
} catch (Exception ex) {
Log.e(TAG, "getCameraCharacteristics(): " + ex);
return new ArrayList<CaptureFormat>();
}
// Calculate default max fps from auto-exposure ranges in case getOutputMinFrameDuration() is
// not supported.
final Range<Integer>[] fpsRanges =
cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
int defaultMaxFps = 0;
for (Range<Integer> fpsRange : fpsRanges) {
defaultMaxFps = Math.max(defaultMaxFps, fpsRange.getUpper());
}
final StreamConfigurationMap streamMap =
cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
final Size[] sizes = streamMap.getOutputSizes(ImageFormat.YUV_420_888);
if (sizes == null) {
throw new RuntimeException("ImageFormat.YUV_420_888 not supported.");
}
final List<CaptureFormat> formatList = new ArrayList<CaptureFormat>();
for (Size size : sizes) {
long minFrameDurationNs = 0;
try {
minFrameDurationNs = streamMap.getOutputMinFrameDuration(ImageFormat.YUV_420_888, size);
} catch (Exception e) {
// getOutputMinFrameDuration() is not supported on all devices. Ignore silently.
}
final int maxFps = (minFrameDurationNs == 0)
? defaultMaxFps
: (int) Math.round(NANO_SECONDS_PER_SECOND / minFrameDurationNs);
formatList.add(new CaptureFormat(size.getWidth(), size.getHeight(), 0, maxFps * 1000));
}
cachedSupportedFormats.put(cameraId, formatList);
final long endTimeMs = SystemClock.elapsedRealtime();
Log.d(TAG, "Get supported formats for camera index " + cameraId + " done."
+ " Time spent: " + (endTimeMs - startTimeMs) + " ms.");
return formatList;
}
}
}

View File

@ -72,6 +72,7 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) {
#if defined(ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) #if defined(ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD)
LoadClass(jni, "android/graphics/SurfaceTexture"); LoadClass(jni, "android/graphics/SurfaceTexture");
LoadClass(jni, "org/webrtc/CameraEnumerator"); LoadClass(jni, "org/webrtc/CameraEnumerator");
LoadClass(jni, "org/webrtc/Camera2Enumerator");
LoadClass(jni, "org/webrtc/CameraEnumerationAndroid"); LoadClass(jni, "org/webrtc/CameraEnumerationAndroid");
LoadClass(jni, "org/webrtc/VideoCapturerAndroid"); LoadClass(jni, "org/webrtc/VideoCapturerAndroid");
LoadClass(jni, "org/webrtc/VideoCapturerAndroid$NativeObserver"); LoadClass(jni, "org/webrtc/VideoCapturerAndroid$NativeObserver");

View File

@ -141,6 +141,7 @@
# included here, or better yet, build a proper .jar in webrtc # included here, or better yet, build a proper .jar in webrtc
# and include it here. # and include it here.
'android_java_files': [ 'android_java_files': [
'app/webrtc/java/android/org/webrtc/Camera2Enumerator.java',
'app/webrtc/java/android/org/webrtc/CameraEnumerationAndroid.java', 'app/webrtc/java/android/org/webrtc/CameraEnumerationAndroid.java',
'app/webrtc/java/android/org/webrtc/CameraEnumerator.java', 'app/webrtc/java/android/org/webrtc/CameraEnumerator.java',
'app/webrtc/java/android/org/webrtc/EglBase.java', 'app/webrtc/java/android/org/webrtc/EglBase.java',