Move Camera1 specific methods to Camera1Enumerator and create CameraEnumerator interface.

The plan is to use CameraEnumerator as a "factory" for camera objects in
the future. This CL prepares for that by moving Camera1 specific stuff
away from CameraEnumerationAndroid to Camera1Enumerator. Because
CameraEnumerationAndroid methods were part of public API there are
deprecated mocks for now.

When making these changes, I noticed that code duplication in
CameraVideoCapturer tests implementing TestObjectFactory could be
decreased by making TestObjectFactory an abstract class that uses
CameraEnumerator.

BUG=webrtc:5519

Review-Url: https://codereview.webrtc.org/2071803002
Cr-Commit-Position: refs/heads/master@{#13185}
This commit is contained in:
sakal 2016-06-17 03:45:45 -07:00 committed by Commit bot
parent 72e735d386
commit 62379c89d0
8 changed files with 258 additions and 191 deletions

View File

@ -22,7 +22,7 @@ public class Camera1CapturerUsingByteBufferTest extends InstrumentationTestCase
static final String TAG = "Camera1CapturerUsingByteBufferTest";
private class TestObjectFactory
implements CameraVideoCapturerTestFixtures.TestObjectFactory {
extends CameraVideoCapturerTestFixtures.TestObjectFactory {
@Override
public CameraVideoCapturer createCapturer(
String name,
@ -30,28 +30,16 @@ public class Camera1CapturerUsingByteBufferTest extends InstrumentationTestCase
return new VideoCapturerAndroid(name, eventsHandler, isCapturingToTexture());
}
@Override
public String getNameOfFrontFacingDevice() {
return CameraEnumerationAndroid.getNameOfFrontFacingDevice();
}
@Override
public String getNameOfBackFacingDevice() {
return CameraEnumerationAndroid.getNameOfBackFacingDevice();
}
// Return true if the device under test have at least two cameras.
@SuppressWarnings("deprecation")
@Override
public boolean haveTwoCameras() {
return (android.hardware.Camera.getNumberOfCameras() >= 2);
}
@Override
public boolean isCapturingToTexture() {
return false;
}
@Override
public CameraEnumerator getCameraEnumerator() {
return new Camera1Enumerator();
}
@Override
public Context getAppContext() {
return getInstrumentation().getTargetContext();
@ -60,7 +48,7 @@ public class Camera1CapturerUsingByteBufferTest extends InstrumentationTestCase
@SuppressWarnings("deprecation")
@Override
public Object rawOpenCamera(String cameraName) {
return android.hardware.Camera.open(CameraEnumerationAndroid.getCameraIndex(cameraName));
return android.hardware.Camera.open(Camera1Enumerator.getCameraIndex(cameraName));
}
@SuppressWarnings("deprecation")

View File

@ -22,34 +22,10 @@ public class Camera1CapturerUsingTextureTest extends InstrumentationTestCase {
static final String TAG = "Camera1CapturerUsingTextureTest";
private class TestObjectFactory
implements CameraVideoCapturerTestFixtures.TestObjectFactory {
extends CameraVideoCapturerTestFixtures.TestObjectFactory {
@Override
public CameraVideoCapturer createCapturer(
String name,
CameraVideoCapturer.CameraEventsHandler eventsHandler) {
return new VideoCapturerAndroid(name, eventsHandler, isCapturingToTexture());
}
@Override
public String getNameOfFrontFacingDevice() {
return CameraEnumerationAndroid.getNameOfFrontFacingDevice();
}
@Override
public String getNameOfBackFacingDevice() {
return CameraEnumerationAndroid.getNameOfBackFacingDevice();
}
// Return true if the device under test have at least two cameras.
@SuppressWarnings("deprecation")
@Override
public boolean haveTwoCameras() {
return (android.hardware.Camera.getNumberOfCameras() >= 2);
}
@Override
public boolean isCapturingToTexture() {
return true;
public CameraEnumerator getCameraEnumerator() {
return new Camera1Enumerator();
}
@Override
@ -60,7 +36,7 @@ public class Camera1CapturerUsingTextureTest extends InstrumentationTestCase {
@SuppressWarnings("deprecation")
@Override
public Object rawOpenCamera(String cameraName) {
return android.hardware.Camera.open(CameraEnumerationAndroid.getCameraIndex(cameraName));
return android.hardware.Camera.open(Camera1Enumerator.getCameraIndex(cameraName));
}
@SuppressWarnings("deprecation")

View File

@ -240,19 +240,55 @@ class CameraVideoCapturerTestFixtures {
public FakeAsyncRenderer fakeAsyncRenderer;
}
public interface TestObjectFactory {
CameraVideoCapturer createCapturer(
String name, CameraVideoCapturer.CameraEventsHandler eventsHandler);
String getNameOfFrontFacingDevice();
String getNameOfBackFacingDevice();
boolean haveTwoCameras();
boolean isCapturingToTexture();
Context getAppContext();
public abstract static class TestObjectFactory {
final CameraEnumerator cameraEnumerator;
TestObjectFactory() {
cameraEnumerator = getCameraEnumerator();
}
public CameraVideoCapturer createCapturer(
String name,
CameraVideoCapturer.CameraEventsHandler eventsHandler) {
return cameraEnumerator.createCapturer(name, eventsHandler);
}
public String getNameOfFrontFacingDevice() {
for (String deviceName : cameraEnumerator.getDeviceNames()) {
if (cameraEnumerator.isFrontFacing(deviceName)) {
return deviceName;
}
}
return null;
}
public String getNameOfBackFacingDevice() {
for (String deviceName : cameraEnumerator.getDeviceNames()) {
if (cameraEnumerator.isBackFacing(deviceName)) {
return deviceName;
}
}
return null;
}
public boolean haveTwoCameras() {
return cameraEnumerator.getDeviceNames().length >= 2;
}
public boolean isCapturingToTexture() {
// In the future, we plan to only support capturing to texture, so default to true
return true;
}
abstract public CameraEnumerator getCameraEnumerator();
abstract public Context getAppContext();
// CameraVideoCapturer API is too slow for some of our tests where we need to open a competing
// camera. These methods are used instead.
Object rawOpenCamera(String cameraName);
void rawCloseCamera(Object camera);
abstract public Object rawOpenCamera(String cameraName);
abstract public void rawCloseCamera(Object camera);
}
private PeerConnectionFactory peerConnectionFactory;

View File

@ -0,0 +1,159 @@
/*
* Copyright 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
package org.webrtc;
import org.webrtc.CameraEnumerationAndroid.CaptureFormat;
import android.os.SystemClock;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("deprecation")
public class Camera1Enumerator implements CameraEnumerator {
private final static String TAG = "Camera1Enumerator";
// 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 static List<List<CaptureFormat>> cachedSupportedFormats;
public boolean isFrontFacing(String deviceName) {
android.hardware.Camera.CameraInfo info = getCameraInfo(getCameraIndex(deviceName));
return info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT;
}
public boolean isBackFacing(String deviceName) {
android.hardware.Camera.CameraInfo info = getCameraInfo(getCameraIndex(deviceName));
return info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK;
}
public CameraVideoCapturer createCapturer(String deviceName,
CameraVideoCapturer.CameraEventsHandler eventsHandler) {
return new VideoCapturerAndroid(deviceName, eventsHandler, true);
}
private static android.hardware.Camera.CameraInfo getCameraInfo(int index) {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
try {
android.hardware.Camera.getCameraInfo(index, info);
} catch (Exception e) {
Logging.e(TAG, "getCameraInfo failed on index " + index,e);
return null;
}
return info;
}
static synchronized List<CaptureFormat> getSupportedFormats(int cameraId) {
if (cachedSupportedFormats == null) {
cachedSupportedFormats = new ArrayList<List<CaptureFormat>>();
for (int i = 0; i < CameraEnumerationAndroid.getDeviceCount(); ++i) {
cachedSupportedFormats.add(enumerateFormats(i));
}
}
return cachedSupportedFormats.get(cameraId);
}
private static List<CaptureFormat> enumerateFormats(int cameraId) {
Logging.d(TAG, "Get supported formats for camera index " + cameraId + ".");
final long startTimeMs = SystemClock.elapsedRealtime();
final android.hardware.Camera.Parameters parameters;
android.hardware.Camera camera = null;
try {
Logging.d(TAG, "Opening camera with index " + cameraId);
camera = android.hardware.Camera.open(cameraId);
parameters = camera.getParameters();
} catch (RuntimeException e) {
Logging.e(TAG, "Open camera failed on camera index " + cameraId, e);
return new ArrayList<CaptureFormat>();
} finally {
if (camera != null) {
camera.release();
}
}
final List<CaptureFormat> formatList = new ArrayList<CaptureFormat>();
try {
int minFps = 0;
int maxFps = 0;
final List<int[]> 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[android.hardware.Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
maxFps = range[android.hardware.Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
}
for (android.hardware.Camera.Size size : parameters.getSupportedPreviewSizes()) {
formatList.add(new CaptureFormat(size.width, size.height, minFps, maxFps));
}
} catch (Exception e) {
Logging.e(TAG, "getSupportedFormats() failed on camera index " + cameraId, e);
}
final long endTimeMs = SystemClock.elapsedRealtime();
Logging.d(TAG, "Get supported formats for camera index " + cameraId + " done."
+ " Time spent: " + (endTimeMs - startTimeMs) + " ms.");
return formatList;
}
// Convert from android.hardware.Camera.Size to Size.
public static List<Size> convertSizes(List<android.hardware.Camera.Size> cameraSizes) {
final List<Size> sizes = new ArrayList<Size>();
for (android.hardware.Camera.Size size : cameraSizes) {
sizes.add(new Size(size.width, size.height));
}
return sizes;
}
// Convert from int[2] to CaptureFormat.FramerateRange.
public static List<CaptureFormat.FramerateRange> convertFramerates(List<int[]> arrayRanges) {
final List<CaptureFormat.FramerateRange> ranges = new ArrayList<CaptureFormat.FramerateRange>();
for (int[] range : arrayRanges) {
ranges.add(new CaptureFormat.FramerateRange(
range[android.hardware.Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
range[android.hardware.Camera.Parameters.PREVIEW_FPS_MAX_INDEX]));
}
return ranges;
}
// Returns device names that can be used to create a new VideoCapturerAndroid.
public String[] getDeviceNames() {
String[] names = new String[android.hardware.Camera.getNumberOfCameras()];
for (int i = 0; i < android.hardware.Camera.getNumberOfCameras(); ++i) {
names[i] = getDeviceName(i);
}
return names;
}
// Returns the camera index for camera with name |deviceName|, or throws IllegalArgumentException
// if no such camera can be found.
static int getCameraIndex(String deviceName) {
Logging.d(TAG, "getCameraIndex: " + deviceName);
for (int i = 0; i < android.hardware.Camera.getNumberOfCameras(); ++i) {
if (deviceName.equals(CameraEnumerationAndroid.getDeviceName(i))) {
return i;
}
}
throw new IllegalArgumentException("No such camera: " + deviceName);
}
// Returns the name of the camera with camera index. Returns null if the
// camera can not be used.
static String getDeviceName(int index) {
android.hardware.Camera.CameraInfo info = getCameraInfo(index);
String facing =
(info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT) ? "front" : "back";
return "Camera " + index + ", Facing " + facing
+ ", Orientation " + info.orientation;
}
}

View File

@ -114,57 +114,48 @@ public class CameraEnumerationAndroid {
}
}
// Returns device names that can be used to create a new VideoCapturerAndroid.
/**
* @deprecated
* Please use Camera1Enumerator.getDeviceNames() instead.
*/
@Deprecated
public static String[] getDeviceNames() {
String[] names = new String[android.hardware.Camera.getNumberOfCameras()];
for (int i = 0; i < android.hardware.Camera.getNumberOfCameras(); ++i) {
names[i] = getDeviceName(i);
}
return names;
return new Camera1Enumerator().getDeviceNames();
}
// Returns number of cameras on device.
/**
* @deprecated
* Please use Camera1Enumerator.getDeviceNames().length instead.
*/
@Deprecated
public static int getDeviceCount() {
return android.hardware.Camera.getNumberOfCameras();
return new Camera1Enumerator().getDeviceNames().length;
}
// Returns the name of the camera with camera index. Returns null if the
// camera can not be used.
/**
* @deprecated
* Please use Camera1Enumerator.getDeviceNames().get(index) instead.
*/
@Deprecated
public static String getDeviceName(int index) {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
try {
android.hardware.Camera.getCameraInfo(index, info);
} catch (Exception e) {
Logging.e(TAG, "getCameraInfo failed on index " + index,e);
return null;
}
String facing =
(info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT) ? "front" : "back";
return "Camera " + index + ", Facing " + facing
+ ", Orientation " + info.orientation;
return new Camera1Enumerator().getDeviceName(index);
}
// Returns the camera index for camera with name |deviceName|, or throws IllegalArgumentException
// if no such camera can be found.
public static int getCameraIndex(String deviceName) {
Logging.d(TAG, "getCameraIndex: " + deviceName);
for (int i = 0; i < android.hardware.Camera.getNumberOfCameras(); ++i) {
if (deviceName.equals(CameraEnumerationAndroid.getDeviceName(i))) {
return i;
}
}
throw new IllegalArgumentException("No such camera: " + deviceName);
}
// Returns the name of the front facing camera. Returns null if the
// camera can not be used or does not exist.
/**
* @deprecated
* Please use Camera1Enumerator.isFrontFacing(String deviceName) instead.
*/
@Deprecated
public static String getNameOfFrontFacingDevice() {
return getNameOfDevice(android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT);
}
// Returns the name of the back facing camera. Returns null if the
// camera can not be used or does not exist.
/**
* @deprecated
* Please use Camera1Enumerator.isBackFacing(String deviceName) instead.
*/
@Deprecated
public static String getNameOfBackFacingDevice() {
return getNameOfDevice(android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015 The WebRTC project authors. All Rights Reserved.
* Copyright 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
@ -10,94 +10,11 @@
package org.webrtc;
import android.os.SystemClock;
public interface CameraEnumerator {
public String[] getDeviceNames();
public boolean isFrontFacing(String deviceName);
public boolean isBackFacing(String deviceName);
import org.webrtc.CameraEnumerationAndroid.CaptureFormat;
import org.webrtc.Logging;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("deprecation")
public class CameraEnumerator {
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 static List<List<CaptureFormat>> cachedSupportedFormats;
public static synchronized List<CaptureFormat> getSupportedFormats(int cameraId) {
if (cachedSupportedFormats == null) {
cachedSupportedFormats = new ArrayList<List<CaptureFormat>>();
for (int i = 0; i < CameraEnumerationAndroid.getDeviceCount(); ++i) {
cachedSupportedFormats.add(enumerateFormats(i));
}
}
return cachedSupportedFormats.get(cameraId);
}
private static List<CaptureFormat> enumerateFormats(int cameraId) {
Logging.d(TAG, "Get supported formats for camera index " + cameraId + ".");
final long startTimeMs = SystemClock.elapsedRealtime();
final android.hardware.Camera.Parameters parameters;
android.hardware.Camera camera = null;
try {
Logging.d(TAG, "Opening camera with index " + cameraId);
camera = android.hardware.Camera.open(cameraId);
parameters = camera.getParameters();
} catch (RuntimeException e) {
Logging.e(TAG, "Open camera failed on camera index " + cameraId, e);
return new ArrayList<CaptureFormat>();
} finally {
if (camera != null) {
camera.release();
}
}
final List<CaptureFormat> formatList = new ArrayList<CaptureFormat>();
try {
int minFps = 0;
int maxFps = 0;
final List<int[]> 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[android.hardware.Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
maxFps = range[android.hardware.Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
}
for (android.hardware.Camera.Size size : parameters.getSupportedPreviewSizes()) {
formatList.add(new CaptureFormat(size.width, size.height, minFps, maxFps));
}
} catch (Exception e) {
Logging.e(TAG, "getSupportedFormats() failed on camera index " + cameraId, e);
}
final long endTimeMs = SystemClock.elapsedRealtime();
Logging.d(TAG, "Get supported formats for camera index " + cameraId + " done."
+ " Time spent: " + (endTimeMs - startTimeMs) + " ms.");
return formatList;
}
// Convert from android.hardware.Camera.Size to Size.
public static List<Size> convertSizes(
List<android.hardware.Camera.Size> cameraSizes) {
final List<Size> sizes = new ArrayList<Size>();
for (android.hardware.Camera.Size size : cameraSizes) {
sizes.add(new Size(size.width, size.height));
}
return sizes;
}
// Convert from int[2] to CaptureFormat.FramerateRange.
public static List<CaptureFormat.FramerateRange> convertFramerates(
List<int[]> arrayRanges) {
final List<CaptureFormat.FramerateRange> ranges = new ArrayList<CaptureFormat.FramerateRange>();
for (int[] range : arrayRanges) {
ranges.add(new CaptureFormat.FramerateRange(
range[android.hardware.Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
range[android.hardware.Camera.Parameters.PREVIEW_FPS_MAX_INDEX]));
}
return ranges;
}
public CameraVideoCapturer createCapturer(String deviceName,
CameraVideoCapturer.CameraEventsHandler eventsHandler);
}

View File

@ -208,7 +208,7 @@ public class VideoCapturerAndroid implements
@Override
public List<CaptureFormat> getSupportedFormats() {
return CameraEnumerator.getSupportedFormats(getCurrentCameraId());
return Camera1Enumerator.getSupportedFormats(getCurrentCameraId());
}
// Returns true if this VideoCapturer is setup to capture video frames to a SurfaceTexture.
@ -224,7 +224,7 @@ public class VideoCapturerAndroid implements
if (cameraName == null || cameraName.equals("")) {
this.id = 0;
} else {
this.id = CameraEnumerationAndroid.getCameraIndex(cameraName);
this.id = Camera1Enumerator.getCameraIndex(cameraName);
}
this.eventsHandler = eventsHandler;
isCapturingToTexture = captureToTexture;
@ -391,14 +391,14 @@ public class VideoCapturerAndroid implements
// Find closest supported format for |width| x |height| @ |framerate|.
final android.hardware.Camera.Parameters parameters = camera.getParameters();
final List<CaptureFormat.FramerateRange> supportedFramerates =
CameraEnumerator.convertFramerates(parameters.getSupportedPreviewFpsRange());
Camera1Enumerator.convertFramerates(parameters.getSupportedPreviewFpsRange());
Logging.d(TAG, "Available fps ranges: " + supportedFramerates);
final CaptureFormat.FramerateRange fpsRange =
CameraEnumerationAndroid.getClosestSupportedFramerateRange(supportedFramerates, framerate);
final Size previewSize = CameraEnumerationAndroid.getClosestSupportedSize(
CameraEnumerator.convertSizes(parameters.getSupportedPreviewSizes()), width, height);
Camera1Enumerator.convertSizes(parameters.getSupportedPreviewSizes()), width, height);
final CaptureFormat captureFormat =
new CaptureFormat(previewSize.width, previewSize.height, fpsRange);
@ -427,7 +427,7 @@ public class VideoCapturerAndroid implements
// Picture size is for taking pictures and not for preview/video, but we need to set it anyway
// as a workaround for an aspect ratio problem on Nexus 7.
final Size pictureSize = CameraEnumerationAndroid.getClosestSupportedSize(
CameraEnumerator.convertSizes(parameters.getSupportedPictureSizes()), width, height);
Camera1Enumerator.convertSizes(parameters.getSupportedPictureSizes()), width, height);
parameters.setPictureSize(pictureSize.width, pictureSize.height);
// Temporarily stop preview if it's already running.

View File

@ -48,7 +48,7 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) {
LoadClass(jni, "java/nio/ByteBuffer");
LoadClass(jni, "java/util/ArrayList");
LoadClass(jni, "org/webrtc/AudioTrack");
LoadClass(jni, "org/webrtc/CameraEnumerator");
LoadClass(jni, "org/webrtc/Camera1Enumerator");
LoadClass(jni, "org/webrtc/Camera2Enumerator");
LoadClass(jni, "org/webrtc/CameraEnumerationAndroid");
LoadClass(jni, "org/webrtc/DataChannel");