VideoCapturerAndroid: Move to android folder and split out camera enumeration into separate file

Pure code move of:
talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java
into:
talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java
talk/app/webrtc/java/android/org/webrtc/CameraEnumerationAndroid.java

NOPRESUBMIT=true

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

Cr-Commit-Position: refs/heads/master@{#9809}
This commit is contained in:
magjed
2015-08-28 05:22:19 -07:00
committed by Commit bot
parent 9e69abf85e
commit 6813ec84fb
5 changed files with 320 additions and 275 deletions

View File

@ -30,7 +30,7 @@ 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 org.webrtc.VideoCapturerAndroid.CaptureFormat; import org.webrtc.CameraEnumerationAndroid.CaptureFormat;
import org.webrtc.VideoRenderer.I420Frame; import org.webrtc.VideoRenderer.I420Frame;
import java.util.ArrayList; import java.util.ArrayList;
@ -173,7 +173,7 @@ public class VideoCapturerAndroidTest extends ActivityTestCase {
// to a Java video renderer using the front facing video capturer. // to a Java video renderer using the front facing video capturer.
// It tests both the Java and the C++ layer. // It tests both the Java and the C++ layer.
public void testStartFrontFacingVideoCapturer() throws Exception { public void testStartFrontFacingVideoCapturer() throws Exception {
starCapturerAndRender(VideoCapturerAndroid.getNameOfFrontFacingDevice()); starCapturerAndRender(CameraEnumerationAndroid.getNameOfFrontFacingDevice());
} }
@SmallTest @SmallTest
@ -184,7 +184,7 @@ public class VideoCapturerAndroidTest extends ActivityTestCase {
if (!HaveTwoCameras()) { if (!HaveTwoCameras()) {
return; return;
} }
starCapturerAndRender(VideoCapturerAndroid.getNameOfBackFacingDevice()); starCapturerAndRender(CameraEnumerationAndroid.getNameOfBackFacingDevice());
} }
@SmallTest @SmallTest
@ -246,14 +246,14 @@ public class VideoCapturerAndroidTest extends ActivityTestCase {
public void testStartStopWithDifferentResolutions() throws Exception { public void testStartStopWithDifferentResolutions() throws Exception {
FakeCapturerObserver observer = new FakeCapturerObserver(); FakeCapturerObserver observer = new FakeCapturerObserver();
String deviceName = VideoCapturerAndroid.getDeviceName(0); String deviceName = CameraEnumerationAndroid.getDeviceName(0);
ArrayList<CaptureFormat> formats = ArrayList<CaptureFormat> formats =
VideoCapturerAndroid.getSupportedFormats(0); CameraEnumerationAndroid.getSupportedFormats(0);
VideoCapturerAndroid capturer = VideoCapturerAndroid capturer =
VideoCapturerAndroid.create(deviceName, null); VideoCapturerAndroid.create(deviceName, null);
for(int i = 0; i < 3 ; ++i) { for(int i = 0; i < 3 ; ++i) {
VideoCapturerAndroid.CaptureFormat format = formats.get(i); CameraEnumerationAndroid.CaptureFormat format = formats.get(i);
capturer.startCapture(format.width, format.height, format.maxFramerate, capturer.startCapture(format.width, format.height, format.maxFramerate,
getInstrumentation().getContext(), observer); getInstrumentation().getContext(), observer);
assertTrue(observer.WaitForCapturerToStart()); assertTrue(observer.WaitForCapturerToStart());
@ -271,13 +271,13 @@ public class VideoCapturerAndroidTest extends ActivityTestCase {
public void testReturnBufferLate() throws Exception { public void testReturnBufferLate() throws Exception {
FakeCapturerObserver observer = new FakeCapturerObserver(); FakeCapturerObserver observer = new FakeCapturerObserver();
String deviceName = VideoCapturerAndroid.getDeviceName(0); String deviceName = CameraEnumerationAndroid.getDeviceName(0);
ArrayList<CaptureFormat> formats = ArrayList<CaptureFormat> formats =
VideoCapturerAndroid.getSupportedFormats(0); CameraEnumerationAndroid.getSupportedFormats(0);
VideoCapturerAndroid capturer = VideoCapturerAndroid capturer =
VideoCapturerAndroid.create(deviceName, null); VideoCapturerAndroid.create(deviceName, null);
VideoCapturerAndroid.CaptureFormat format = formats.get(0); CameraEnumerationAndroid.CaptureFormat format = formats.get(0);
capturer.startCapture(format.width, format.height, format.maxFramerate, capturer.startCapture(format.width, format.height, format.maxFramerate,
getInstrumentation().getContext(), observer); getInstrumentation().getContext(), observer);
assertTrue(observer.WaitForCapturerToStart()); assertTrue(observer.WaitForCapturerToStart());

View File

@ -0,0 +1,293 @@
/*
* 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 static java.lang.Math.abs;
import static java.lang.Math.ceil;
import android.hardware.Camera;
import android.util.Log;
import android.graphics.ImageFormat;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
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<List<CaptureFormat>> supportedFormats;
public static class CaptureFormat {
public final int width;
public final int height;
public final int maxFramerate;
public final int minFramerate;
// TODO(hbos): If VideoCapturerAndroid.startCapture is updated to support
// other image formats then this needs to be updated and
// VideoCapturerAndroid.getSupportedFormats need to return CaptureFormats of
// all imageFormats.
public final int imageFormat = ImageFormat.YV12;
public CaptureFormat(int width, int height, int minFramerate,
int maxFramerate) {
this.width = width;
this.height = height;
this.minFramerate = minFramerate;
this.maxFramerate = maxFramerate;
}
// Calculates the frame size of this capture format.
public int frameSize() {
return frameSize(width, height, imageFormat);
}
// Calculates the frame size of the specified image format. Currently only
// supporting ImageFormat.YV12. The YV12's stride is the closest rounded up
// multiple of 16 of the width and width and height are always even.
// Android guarantees this:
// http://developer.android.com/reference/android/hardware/Camera.Parameters.html#setPreviewFormat%28int%29
public static int frameSize(int width, int height, int imageFormat) {
if (imageFormat != ImageFormat.YV12) {
throw new UnsupportedOperationException("Don't know how to calculate "
+ "the frame size of non-YV12 image formats.");
}
int yStride = roundUp(width, 16);
int uvStride = roundUp(yStride / 2, 16);
int ySize = yStride * height;
int uvSize = uvStride * height / 2;
return ySize + uvSize * 2;
}
// Rounds up |x| to the closest value that is a multiple of |alignment|.
private static int roundUp(int x, int alignment) {
return (int)ceil(x / (double)alignment) * alignment;
}
@Override
public String toString() {
return width + "x" + height + "@[" + minFramerate + ":" + maxFramerate + "]";
}
@Override
public boolean equals(Object that) {
if (!(that instanceof CaptureFormat)) {
return false;
}
final CaptureFormat c = (CaptureFormat) that;
return width == c.width && height == c.height && maxFramerate == c.maxFramerate
&& minFramerate == c.minFramerate;
}
}
// Returns device names that can be used to create a new VideoCapturerAndroid.
public static String[] getDeviceNames() {
String[] names = new String[Camera.getNumberOfCameras()];
for (int i = 0; i < Camera.getNumberOfCameras(); ++i) {
names[i] = getDeviceName(i);
}
return names;
}
// Returns number of cameras on device.
public static int getDeviceCount() {
return Camera.getNumberOfCameras();
}
// Returns the name of the camera with camera index. Returns null if the
// camera can not be used.
public static String getDeviceName(int index) {
Camera.CameraInfo info = new Camera.CameraInfo();
try {
Camera.getCameraInfo(index, info);
} catch (Exception e) {
Log.e(TAG, "getCameraInfo failed on index " + index,e);
return null;
}
String facing =
(info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) ? "front" : "back";
return "Camera " + index + ", Facing " + facing
+ ", Orientation " + info.orientation;
}
// Returns the name of the front facing camera. Returns null if the
// camera can not be used or does not exist.
public static String getNameOfFrontFacingDevice() {
for (int i = 0; i < Camera.getNumberOfCameras(); ++i) {
Camera.CameraInfo info = new Camera.CameraInfo();
try {
Camera.getCameraInfo(i, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
return getDeviceName(i);
} catch (Exception e) {
Log.e(TAG, "getCameraInfo failed on index " + i, e);
}
}
return null;
}
// Returns the name of the back facing camera. Returns null if the
// camera can not be used or does not exist.
public static String getNameOfBackFacingDevice() {
for (int i = 0; i < Camera.getNumberOfCameras(); ++i) {
Camera.CameraInfo info = new Camera.CameraInfo();
try {
Camera.getCameraInfo(i, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK)
return getDeviceName(i);
} catch (Exception e) {
Log.e(TAG, "getCameraInfo failed on index " + i, e);
}
}
return null;
}
public static boolean initStatics() {
if (supportedFormats != null)
return true;
try {
Log.d(TAG, "Get supported formats.");
supportedFormats =
new ArrayList<List<CaptureFormat>>(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<CaptureFormat> 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<CaptureFormat> formats = supportedFormats.get(id);
JSONArray json_formats = new JSONArray();
for (CaptureFormat format : formats) {
JSONObject json_format = new JSONObject();
json_format.put("width", format.width);
json_format.put("height", format.height);
json_format.put("framerate", (format.maxFramerate + 999) / 1000);
json_formats.put(json_format);
}
Log.d(TAG, "Supported formats for camera " + id + ": "
+ json_formats.toString(2));
return json_formats.toString();
}
// Returns a list of CaptureFormat for the camera with index id.
public static ArrayList<CaptureFormat> getSupportedFormats(int id) {
ArrayList<CaptureFormat> formatList = new ArrayList<CaptureFormat>();
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<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange();
int[] range = {0, 0};
if (listFpsRange != null)
range = listFpsRange.get(listFpsRange.size() -1);
List<Camera.Size> 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<T> implements Comparator<T> {
// Difference between supported and requested parameter.
abstract int diff(T supportedParameter);
@Override
public int compare(T t1, T t2) {
return diff(t1) - diff(t2);
}
}
public static int[] getFramerateRange(Camera.Parameters parameters, final int framerate) {
List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange();
if (listFpsRange.isEmpty()) {
Log.w(TAG, "No supported preview fps range");
return new int[]{0, 0};
}
return Collections.min(listFpsRange,
new ClosestComparator<int[]>() {
@Override int diff(int[] range) {
return abs(framerate - range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX])
+ abs(framerate - range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
}
});
}
public static Camera.Size getClosestSupportedSize(
List<Camera.Size> supportedSizes, final int requestedWidth, final int requestedHeight) {
return Collections.min(supportedSizes,
new ClosestComparator<Camera.Size>() {
@Override int diff(Camera.Size size) {
return abs(requestedWidth - size.width) + abs(requestedHeight - size.height);
}
});
}
}

View File

@ -27,11 +27,7 @@
package org.webrtc; package org.webrtc;
import static java.lang.Math.abs;
import static java.lang.Math.ceil;
import android.content.Context; import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
import android.hardware.Camera; import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback; import android.hardware.Camera.PreviewCallback;
@ -44,15 +40,13 @@ import android.util.Log;
import android.view.Surface; import android.view.Surface;
import android.view.WindowManager; import android.view.WindowManager;
import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.CameraEnumerationAndroid.CaptureFormat;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.List; import java.util.List;
@ -100,9 +94,6 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
private volatile boolean pendingCameraSwitch; private volatile boolean pendingCameraSwitch;
private CapturerObserver frameObserver = null; private CapturerObserver frameObserver = null;
private CameraErrorHandler errorHandler = null; private CameraErrorHandler errorHandler = null;
// List of formats supported by all cameras. This list is filled once in order
// to be able to switch cameras.
private static List<List<CaptureFormat>> supportedFormats;
// Camera error callback. // Camera error callback.
private final Camera.ErrorCallback cameraErrorCallback = private final Camera.ErrorCallback cameraErrorCallback =
@ -158,69 +149,6 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
public void onCameraError(String errorDescription); public void onCameraError(String errorDescription);
} }
// Returns device names that can be used to create a new VideoCapturerAndroid.
public static String[] getDeviceNames() {
String[] names = new String[Camera.getNumberOfCameras()];
for (int i = 0; i < Camera.getNumberOfCameras(); ++i) {
names[i] = getDeviceName(i);
}
return names;
}
// Returns number of cameras on device.
public static int getDeviceCount() {
return Camera.getNumberOfCameras();
}
// Returns the name of the camera with camera index. Returns null if the
// camera can not be used.
public static String getDeviceName(int index) {
Camera.CameraInfo info = new Camera.CameraInfo();
try {
Camera.getCameraInfo(index, info);
} catch (Exception e) {
Log.e(TAG, "getCameraInfo failed on index " + index,e);
return null;
}
String facing =
(info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) ? "front" : "back";
return "Camera " + index + ", Facing " + facing
+ ", Orientation " + info.orientation;
}
// Returns the name of the front facing camera. Returns null if the
// camera can not be used or does not exist.
public static String getNameOfFrontFacingDevice() {
for (int i = 0; i < Camera.getNumberOfCameras(); ++i) {
Camera.CameraInfo info = new Camera.CameraInfo();
try {
Camera.getCameraInfo(i, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
return getDeviceName(i);
} catch (Exception e) {
Log.e(TAG, "getCameraInfo failed on index " + i, e);
}
}
return null;
}
// Returns the name of the back facing camera. Returns null if the
// camera can not be used or does not exist.
public static String getNameOfBackFacingDevice() {
for (int i = 0; i < Camera.getNumberOfCameras(); ++i) {
Camera.CameraInfo info = new Camera.CameraInfo();
try {
Camera.getCameraInfo(i, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK)
return getDeviceName(i);
} catch (Exception e) {
Log.e(TAG, "getCameraInfo failed on index " + i, e);
}
}
return null;
}
public static VideoCapturerAndroid create(String name, public static VideoCapturerAndroid create(String name,
CameraErrorHandler errorHandler) { CameraErrorHandler errorHandler) {
VideoCapturer capturer = VideoCapturer.create(name); VideoCapturer capturer = VideoCapturer.create(name);
@ -292,7 +220,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
} }
public synchronized List<CaptureFormat> getSupportedFormats() { public synchronized List<CaptureFormat> getSupportedFormats() {
return supportedFormats.get(id); return CameraEnumerationAndroid.supportedFormats.get(id);
} }
private VideoCapturerAndroid() { private VideoCapturerAndroid() {
@ -307,7 +235,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
// compatible with the generic VideoCapturer class. // compatible with the generic VideoCapturer class.
synchronized boolean init(String deviceName) { synchronized boolean init(String deviceName) {
Log.d(TAG, "init: " + deviceName); Log.d(TAG, "init: " + deviceName);
if (deviceName == null || !initStatics()) if (deviceName == null || !CameraEnumerationAndroid.initStatics())
return false; return false;
boolean foundDevice = false; boolean foundDevice = false;
@ -316,7 +244,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
foundDevice = true; foundDevice = true;
} else { } else {
for (int i = 0; i < Camera.getNumberOfCameras(); ++i) { for (int i = 0; i < Camera.getNumberOfCameras(); ++i) {
String existing_device = getDeviceName(i); String existing_device = CameraEnumerationAndroid.getDeviceName(i);
if (existing_device != null && deviceName.equals(existing_device)) { if (existing_device != null && deviceName.equals(existing_device)) {
this.id = i; this.id = i;
foundDevice = true; foundDevice = true;
@ -326,150 +254,8 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
return foundDevice; return foundDevice;
} }
private static boolean initStatics() {
if (supportedFormats != null)
return true;
try {
Log.d(TAG, "Get supported formats.");
supportedFormats =
new ArrayList<List<CaptureFormat>>(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<CaptureFormat> 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;
}
String getSupportedFormatsAsJson() throws JSONException { String getSupportedFormatsAsJson() throws JSONException {
return getSupportedFormatsAsJson(id); return CameraEnumerationAndroid.getSupportedFormatsAsJson(id);
}
public static class CaptureFormat {
public final int width;
public final int height;
public final int maxFramerate;
public final int minFramerate;
// TODO(hbos): If VideoCapturerAndroid.startCapture is updated to support
// other image formats then this needs to be updated and
// VideoCapturerAndroid.getSupportedFormats need to return CaptureFormats of
// all imageFormats.
public final int imageFormat = ImageFormat.YV12;
public CaptureFormat(int width, int height, int minFramerate,
int maxFramerate) {
this.width = width;
this.height = height;
this.minFramerate = minFramerate;
this.maxFramerate = maxFramerate;
}
// Calculates the frame size of this capture format.
public int frameSize() {
return frameSize(width, height, imageFormat);
}
// Calculates the frame size of the specified image format. Currently only
// supporting ImageFormat.YV12. The YV12's stride is the closest rounded up
// multiple of 16 of the width and width and height are always even.
// Android guarantees this:
// http://developer.android.com/reference/android/hardware/Camera.Parameters.html#setPreviewFormat%28int%29
public static int frameSize(int width, int height, int imageFormat) {
if (imageFormat != ImageFormat.YV12) {
throw new UnsupportedOperationException("Don't know how to calculate "
+ "the frame size of non-YV12 image formats.");
}
int yStride = roundUp(width, 16);
int uvStride = roundUp(yStride / 2, 16);
int ySize = yStride * height;
int uvSize = uvStride * height / 2;
return ySize + uvSize * 2;
}
// Rounds up |x| to the closest value that is a multiple of |alignment|.
private static int roundUp(int x, int alignment) {
return (int)ceil(x / (double)alignment) * alignment;
}
@Override
public String toString() {
return width + "x" + height + "@[" + minFramerate + ":" + maxFramerate + "]";
}
@Override
public boolean equals(Object that) {
if (!(that instanceof CaptureFormat)) {
return false;
}
final CaptureFormat c = (CaptureFormat) that;
return width == c.width && height == c.height && maxFramerate == c.maxFramerate
&& minFramerate == c.minFramerate;
}
}
private static String getSupportedFormatsAsJson(int id) throws JSONException {
List<CaptureFormat> formats = supportedFormats.get(id);
JSONArray json_formats = new JSONArray();
for (CaptureFormat format : formats) {
JSONObject json_format = new JSONObject();
json_format.put("width", format.width);
json_format.put("height", format.height);
json_format.put("framerate", (format.maxFramerate + 999) / 1000);
json_formats.put(json_format);
}
Log.d(TAG, "Supported formats for camera " + id + ": "
+ json_formats.toString(2));
return json_formats.toString();
}
// Returns a list of CaptureFormat for the camera with index id.
static ArrayList<CaptureFormat> getSupportedFormats(int id) {
ArrayList<CaptureFormat> formatList = new ArrayList<CaptureFormat>();
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<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange();
int[] range = {0, 0};
if (listFpsRange != null)
range = listFpsRange.get(listFpsRange.size() -1);
List<Camera.Size> 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;
} }
private class CameraThread extends Thread { private class CameraThread extends Thread {
@ -597,9 +383,9 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
// Find closest supported format for |width| x |height| @ |framerate|. // Find closest supported format for |width| x |height| @ |framerate|.
final Camera.Parameters parameters = camera.getParameters(); final Camera.Parameters parameters = camera.getParameters();
final int[] range = getFramerateRange(parameters, framerate * 1000); final int[] range = CameraEnumerationAndroid.getFramerateRange(parameters, framerate * 1000);
final Camera.Size previewSize = final Camera.Size previewSize = CameraEnumerationAndroid.getClosestSupportedSize(
getClosestSupportedSize(parameters.getSupportedPreviewSizes(), width, height); parameters.getSupportedPreviewSizes(), width, height);
final CaptureFormat captureFormat = new CaptureFormat( final CaptureFormat captureFormat = new CaptureFormat(
previewSize.width, previewSize.height, previewSize.width, previewSize.height,
range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX], range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
@ -625,8 +411,8 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
parameters.setPreviewFormat(captureFormat.imageFormat); parameters.setPreviewFormat(captureFormat.imageFormat);
// Picture size is for taking pictures and not for preview/video, but we need to set it anyway // 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. // as a workaround for an aspect ratio problem on Nexus 7.
final Camera.Size pictureSize = final Camera.Size pictureSize = CameraEnumerationAndroid.getClosestSupportedSize(
getClosestSupportedSize(parameters.getSupportedPictureSizes(), width, height); parameters.getSupportedPictureSizes(), width, height);
parameters.setPictureSize(pictureSize.width, pictureSize.height); parameters.setPictureSize(pictureSize.width, pictureSize.height);
// Temporarily stop preview if it's already running. // Temporarily stop preview if it's already running.
@ -747,42 +533,6 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
return orientation; return orientation;
} }
// Helper class for finding the closest supported format for the two functions below.
private static abstract class ClosestComparator<T> implements Comparator<T> {
// Difference between supported and requested parameter.
abstract int diff(T supportedParameter);
@Override
public int compare(T t1, T t2) {
return diff(t1) - diff(t2);
}
}
private static int[] getFramerateRange(Camera.Parameters parameters, final int framerate) {
List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange();
if (listFpsRange.isEmpty()) {
Log.w(TAG, "No supported preview fps range");
return new int[]{0, 0};
}
return Collections.min(listFpsRange,
new ClosestComparator<int[]>() {
@Override int diff(int[] range) {
return abs(framerate - range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX])
+ abs(framerate - range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
}
});
}
private static Camera.Size getClosestSupportedSize(
List<Camera.Size> supportedSizes, final int requestedWidth, final int requestedHeight) {
return Collections.min(supportedSizes,
new ClosestComparator<Camera.Size>() {
@Override int diff(Camera.Size size) {
return abs(requestedWidth - size.width) + abs(requestedHeight - size.height);
}
});
}
// Called on cameraThread so must not "synchronized". // Called on cameraThread so must not "synchronized".
@Override @Override
public void onPreviewFrame(byte[] data, Camera callbackCamera) { public void onPreviewFrame(byte[] data, Camera callbackCamera) {

View File

@ -140,15 +140,16 @@
# 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/CameraEnumerationAndroid.java',
'app/webrtc/java/android/org/webrtc/EglBase.java', 'app/webrtc/java/android/org/webrtc/EglBase.java',
'app/webrtc/java/android/org/webrtc/GlRectDrawer.java', 'app/webrtc/java/android/org/webrtc/GlRectDrawer.java',
'app/webrtc/java/android/org/webrtc/GlShader.java', 'app/webrtc/java/android/org/webrtc/GlShader.java',
'app/webrtc/java/android/org/webrtc/GlUtil.java', 'app/webrtc/java/android/org/webrtc/GlUtil.java',
'app/webrtc/java/android/org/webrtc/RendererCommon.java', 'app/webrtc/java/android/org/webrtc/RendererCommon.java',
'app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java',
'app/webrtc/java/android/org/webrtc/VideoRendererGui.java', 'app/webrtc/java/android/org/webrtc/VideoRendererGui.java',
'app/webrtc/java/src/org/webrtc/MediaCodecVideoEncoder.java',
'app/webrtc/java/src/org/webrtc/MediaCodecVideoDecoder.java', 'app/webrtc/java/src/org/webrtc/MediaCodecVideoDecoder.java',
'app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java', 'app/webrtc/java/src/org/webrtc/MediaCodecVideoEncoder.java',
'<(webrtc_modules_dir)/video_render/android/java/src/org/webrtc/videoengine/ViEAndroidGLES20.java', '<(webrtc_modules_dir)/video_render/android/java/src/org/webrtc/videoengine/ViEAndroidGLES20.java',
'<(webrtc_modules_dir)/video_render/android/java/src/org/webrtc/videoengine/ViERenderer.java', '<(webrtc_modules_dir)/video_render/android/java/src/org/webrtc/videoengine/ViERenderer.java',
'<(webrtc_modules_dir)/video_render/android/java/src/org/webrtc/videoengine/ViESurfaceRenderer.java', '<(webrtc_modules_dir)/video_render/android/java/src/org/webrtc/videoengine/ViESurfaceRenderer.java',

View File

@ -16,6 +16,7 @@ import android.util.Log;
import org.appspot.apprtc.AppRTCClient.SignalingParameters; import org.appspot.apprtc.AppRTCClient.SignalingParameters;
import org.appspot.apprtc.util.LooperExecutor; import org.appspot.apprtc.util.LooperExecutor;
import org.webrtc.CameraEnumerationAndroid;
import org.webrtc.DataChannel; import org.webrtc.DataChannel;
import org.webrtc.IceCandidate; import org.webrtc.IceCandidate;
import org.webrtc.Logging; import org.webrtc.Logging;
@ -329,7 +330,7 @@ public class PeerConnectionClient {
} }
// Check if there is a camera on device and disable video call if not. // Check if there is a camera on device and disable video call if not.
numberOfCameras = VideoCapturerAndroid.getDeviceCount(); numberOfCameras = CameraEnumerationAndroid.getDeviceCount();
if (numberOfCameras == 0) { if (numberOfCameras == 0) {
Log.w(TAG, "No camera on device. Switch to audio only call."); Log.w(TAG, "No camera on device. Switch to audio only call.");
videoCallEnabled = false; videoCallEnabled = false;
@ -434,9 +435,9 @@ public class PeerConnectionClient {
mediaStream = factory.createLocalMediaStream("ARDAMS"); mediaStream = factory.createLocalMediaStream("ARDAMS");
if (videoCallEnabled) { if (videoCallEnabled) {
String cameraDeviceName = VideoCapturerAndroid.getDeviceName(0); String cameraDeviceName = CameraEnumerationAndroid.getDeviceName(0);
String frontCameraDeviceName = String frontCameraDeviceName =
VideoCapturerAndroid.getNameOfFrontFacingDevice(); CameraEnumerationAndroid.getNameOfFrontFacingDevice();
if (numberOfCameras > 1 && frontCameraDeviceName != null) { if (numberOfCameras > 1 && frontCameraDeviceName != null) {
cameraDeviceName = frontCameraDeviceName; cameraDeviceName = frontCameraDeviceName;
} }