Chromium Code Reviews| Index: webrtc/api/java/android/org/webrtc/Camera2Enumerator.java | 
| diff --git a/webrtc/api/java/android/org/webrtc/Camera2Enumerator.java b/webrtc/api/java/android/org/webrtc/Camera2Enumerator.java | 
| index 8c342c5c25b51716dd6f1149945330bb22535d9e..1342538ba6c91397b2b4829b2a2454ececbea4d3 100644 | 
| --- a/webrtc/api/java/android/org/webrtc/Camera2Enumerator.java | 
| +++ b/webrtc/api/java/android/org/webrtc/Camera2Enumerator.java | 
| @@ -14,13 +14,15 @@ import android.annotation.TargetApi; | 
| import android.content.Context; | 
| import android.graphics.ImageFormat; | 
| +import android.graphics.SurfaceTexture; | 
| +import android.hardware.camera2.CameraAccessException; | 
| import android.hardware.camera2.CameraCharacteristics; | 
| import android.hardware.camera2.CameraManager; | 
| +import android.hardware.camera2.CameraMetadata; | 
| import android.hardware.camera2.params.StreamConfigurationMap; | 
| import android.os.Build; | 
| import android.os.SystemClock; | 
| import android.util.Range; | 
| -import android.util.Size; | 
| import org.webrtc.CameraEnumerationAndroid.CaptureFormat; | 
| import org.webrtc.Logging; | 
| @@ -31,7 +33,7 @@ import java.util.List; | 
| import java.util.Map; | 
| @TargetApi(21) | 
| -public class Camera2Enumerator { | 
| +public class Camera2Enumerator implements CameraEnumerator { | 
| private final static String TAG = "Camera2Enumerator"; | 
| private final static double NANO_SECONDS_PER_SECOND = 1.0e9; | 
| @@ -40,21 +42,100 @@ public class Camera2Enumerator { | 
| private static final Map<String, List<CaptureFormat>> cachedSupportedFormats = | 
| new HashMap<String, List<CaptureFormat>>(); | 
| + final Context context; | 
| + final CameraManager cameraManager; | 
| + | 
| + public Camera2Enumerator(Context context) { | 
| + this.context = context; | 
| + this.cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); | 
| + } | 
| + | 
| + public String[] getDeviceNames() { | 
| + try { | 
| + return cameraManager.getCameraIdList(); | 
| + } catch (CameraAccessException e) { | 
| + Logging.e(TAG, "Camera access exception: " + e); | 
| + return new String[] {}; | 
| + } | 
| + } | 
| + | 
| + public boolean isFrontFacing(String deviceName) { | 
| + CameraCharacteristics characteristics | 
| + = getCameraCharacteristics(deviceName); | 
| + | 
| + return characteristics != null | 
| + && characteristics.get(CameraCharacteristics.LENS_FACING) | 
| + == CameraMetadata.LENS_FACING_FRONT; | 
| + } | 
| + | 
| + public boolean isBackFacing(String deviceName) { | 
| + CameraCharacteristics characteristics | 
| + = getCameraCharacteristics(deviceName); | 
| + | 
| + return characteristics != null | 
| + && characteristics.get(CameraCharacteristics.LENS_FACING) | 
| + == CameraMetadata.LENS_FACING_BACK; | 
| + } | 
| + | 
| + public CameraVideoCapturer createCapturer(String deviceName, | 
| + CameraVideoCapturer.CameraEventsHandler eventsHandler) { | 
| + return new Camera2Capturer(context, deviceName, eventsHandler); | 
| + } | 
| + | 
| + private CameraCharacteristics getCameraCharacteristics(String deviceName) { | 
| + try { | 
| + return cameraManager.getCameraCharacteristics(deviceName); | 
| + } catch (CameraAccessException e) { | 
| + Logging.e(TAG, "Camera access exception: " + e); | 
| + return null; | 
| + } | 
| + } | 
| + | 
| public static boolean isSupported() { | 
| return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; | 
| } | 
| - public static List<CaptureFormat> getSupportedFormats(Context context, String cameraId) { | 
| + static List<CaptureFormat.FramerateRange> getSupportedFramerateRanges( | 
| + CameraCharacteristics cameraCharacteristics) { | 
| + final Range<Integer>[] fpsRanges = | 
| + cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES); | 
| + | 
| + if (fpsRanges == null) { | 
| + return new ArrayList<CaptureFormat.FramerateRange>(); | 
| + } | 
| + | 
| + int maxFps = 0; | 
| + for (Range<Integer> fpsRange : fpsRanges) { | 
| + maxFps = Math.max(maxFps, fpsRange.getUpper()); | 
| + } | 
| + int unitFactor = maxFps < 1000 ? 1000 : 1; | 
| + return convertFramerates(fpsRanges, unitFactor); | 
| + } | 
| + | 
| + static List<Size> getSupportedSizes( | 
| + CameraCharacteristics cameraCharacteristics) { | 
| + final StreamConfigurationMap streamMap = | 
| + cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); | 
| + final android.util.Size[] sizes = streamMap.getOutputSizes(SurfaceTexture.class); | 
| + if (sizes == null) { | 
| + Logging.e(TAG, "No supported camera output sizes."); | 
| + return new ArrayList<Size>(); | 
| + } | 
| + return convertSizes(sizes); | 
| + } | 
| + | 
| + static List<CaptureFormat> getSupportedFormats(Context context, String cameraId) { | 
| return getSupportedFormats( | 
| (CameraManager) context.getSystemService(Context.CAMERA_SERVICE), cameraId); | 
| } | 
| - public static List<CaptureFormat> getSupportedFormats( | 
| + static List<CaptureFormat> getSupportedFormats( | 
| CameraManager cameraManager, String cameraId) { | 
| synchronized (cachedSupportedFormats) { | 
| if (cachedSupportedFormats.containsKey(cameraId)) { | 
| return cachedSupportedFormats.get(cameraId); | 
| } | 
| + | 
| Logging.d(TAG, "Get supported formats for camera index " + cameraId + "."); | 
| final long startTimeMs = SystemClock.elapsedRealtime(); | 
| @@ -66,35 +147,37 @@ public class Camera2Enumerator { | 
| 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."); | 
| + | 
| + List<CaptureFormat.FramerateRange> framerateRanges = getSupportedFramerateRanges( | 
| + cameraCharacteristics); | 
| + List<Size> sizes = getSupportedSizes(cameraCharacteristics); | 
| + | 
| + int defaultMaxFps = 0; | 
| + for (CaptureFormat.FramerateRange framerateRange : framerateRanges) { | 
| + defaultMaxFps = Math.max(defaultMaxFps, framerateRange.max); | 
| + } | 
| + if (defaultMaxFps < 1000) { | 
| 
 
magjed_webrtc
2016/06/27 10:33:24
This check should not be needed for CaptureFormat.
 
sakal
2016/06/27 12:08:28
You're right.
 
 | 
| + defaultMaxFps *= 1000; | 
| } | 
| final List<CaptureFormat> formatList = new ArrayList<CaptureFormat>(); | 
| for (Size size : sizes) { | 
| long minFrameDurationNs = 0; | 
| try { | 
| - minFrameDurationNs = streamMap.getOutputMinFrameDuration(ImageFormat.YUV_420_888, size); | 
| + minFrameDurationNs = streamMap.getOutputMinFrameDuration(SurfaceTexture.class, | 
| + new android.util.Size(size.width, size.height)); | 
| } 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)); | 
| + ? defaultMaxFps | 
| + : (int) Math.round(NANO_SECONDS_PER_SECOND / minFrameDurationNs) * 1000; | 
| + formatList.add(new CaptureFormat(size.width, size.height, 0, maxFps)); | 
| + Logging.d(TAG, "Format: " + size.width + "x" + size.height + "@" + maxFps); | 
| } | 
| + | 
| cachedSupportedFormats.put(cameraId, formatList); | 
| final long endTimeMs = SystemClock.elapsedRealtime(); | 
| Logging.d(TAG, "Get supported formats for camera index " + cameraId + " done." | 
| @@ -102,4 +185,25 @@ public class Camera2Enumerator { | 
| return formatList; | 
| } | 
| } | 
| + | 
| + // Convert from android.util.Size to Size. | 
| + public static List<Size> convertSizes(android.util.Size[] cameraSizes) { | 
| 
 
magjed_webrtc
2016/06/27 10:33:24
Can this be a private method instead?
 
sakal
2016/06/27 12:08:28
I changed this. I reduced visibility in Camera1Enu
 
 | 
| + final List<Size> sizes = new ArrayList<Size>(); | 
| + for (android.util.Size size : cameraSizes) { | 
| + sizes.add(new Size(size.getWidth(), size.getHeight())); | 
| + } | 
| + return sizes; | 
| + } | 
| + | 
| + // Convert from android.util.Range<Integer> to CaptureFormat.FramerateRange. | 
| + public static List<CaptureFormat.FramerateRange> convertFramerates( | 
| 
 
magjed_webrtc
2016/06/27 10:33:24
ditto, private?
 
 | 
| + Range<Integer>[] arrayRanges, int unitFactor) { | 
| + final List<CaptureFormat.FramerateRange> ranges = new ArrayList<CaptureFormat.FramerateRange>(); | 
| + for (Range<Integer> range : arrayRanges) { | 
| + ranges.add(new CaptureFormat.FramerateRange( | 
| + range.getLower() * unitFactor, | 
| + range.getUpper() * unitFactor)); | 
| + } | 
| + return ranges; | 
| + } | 
| } |