summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authormcasas@chromium.org <mcasas@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-01 08:48:00 +0000
committermcasas@chromium.org <mcasas@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-01 08:48:00 +0000
commitb1d26db2602f3b7ba8824995771da53e9af367f3 (patch)
tree89aec58efdfe019b9e8c2263178b522a58fce221 /media
parent5c963e67502659d49c28c81bbdd151ccbecce903 (diff)
downloadchromium_src-b1d26db2602f3b7ba8824995771da53e9af367f3.zip
chromium_src-b1d26db2602f3b7ba8824995771da53e9af367f3.tar.gz
chromium_src-b1d26db2602f3b7ba8824995771da53e9af367f3.tar.bz2
Add Project Tango video capture for Android
Project Tango is a special Android device [1]. It supports a number of special cameras, see bug. VideoCapture.java is divided into a base class and a VideoCaptureFactory. The base class VideoCapture.java abstracts most of the code commong to VideoCaptureAndroid and VideoCaptureTango, the two extensions. VideoCaptureFactory creates either a VideoCaptureAndroid (normal devices and Tango front facing camera), or a VideoCaptureTango if the device is identified as a Tango. This division clarifies the already large VideoCapture.java and is expected to be used further down the line in migrating VideoCaptureAndroid to using VideoFrames, hence reducing copies. The identification of the Tango comes from checking the android.os.Build.{MODEL,DEVICE} in the static class VideoCapture.ChromiumCameraInfo. The extra cameras are then added after the "normal" ones in the mId-indexed. list. Tested via [2], click on GetDevices, then open any of the listed cameras, "depth", "fisheye", "4MP". [1] http://www.google.com/atap/projecttango/ [1] https://src.chromium.org/svn/trunk/src/chrome/test/data/webrtc/manual/peerconnection.html BUG=352542 Review URL: https://codereview.chromium.org/151513006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@260798 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/base/android/java/src/org/chromium/media/VideoCapture.java382
-rw-r--r--media/base/android/java/src/org/chromium/media/VideoCaptureAndroid.java208
-rw-r--r--media/base/android/java/src/org/chromium/media/VideoCaptureFactory.java175
-rw-r--r--media/base/android/java/src/org/chromium/media/VideoCaptureTango.java193
-rw-r--r--media/base/android/media_jni_registrar.cc3
-rw-r--r--media/media.gyp3
-rw-r--r--media/video/capture/android/video_capture_device_android.cc108
-rw-r--r--media/video/capture/android/video_capture_device_android.h2
-rw-r--r--media/video/capture/android/video_capture_device_factory_android.cc111
-rw-r--r--media/video/capture/android/video_capture_device_factory_android.h40
10 files changed, 846 insertions, 379 deletions
diff --git a/media/base/android/java/src/org/chromium/media/VideoCapture.java b/media/base/android/java/src/org/chromium/media/VideoCapture.java
index 8182c30..1bb2de5 100644
--- a/media/base/android/java/src/org/chromium/media/VideoCapture.java
+++ b/media/base/android/java/src/org/chromium/media/VideoCapture.java
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -9,7 +9,6 @@ import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
-import android.hardware.Camera.Size;
import android.opengl.GLES20;
import android.util.Log;
import android.view.Surface;
@@ -19,19 +18,21 @@ import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
-/** This class implements the listener interface for receiving copies of preview
- * frames from the camera, plus a series of methods to manipulate camera and its
- * capture from the C++ side. Objects of this class are created via
- * createVideoCapture() and are explicitly owned by the creator. All methods
- * are invoked by this owner, including the callback OnPreviewFrame().
+/**
+ * Video Capture Device base class to interface to native Chromium.
**/
@JNINamespace("media")
-public class VideoCapture implements PreviewCallback {
- static class CaptureFormat {
+public abstract class VideoCapture implements PreviewCallback {
+
+ protected static class CaptureFormat {
+ int mWidth;
+ int mHeight;
+ final int mFramerate;
+ final int mPixelFormat;
+
public CaptureFormat(
int width, int height, int framerate, int pixelformat) {
mWidth = width;
@@ -39,185 +40,56 @@ public class VideoCapture implements PreviewCallback {
mFramerate = framerate;
mPixelFormat = pixelformat;
}
- public int mWidth;
- public int mHeight;
- public final int mFramerate;
- public final int mPixelFormat;
- @CalledByNative("CaptureFormat")
+
public int getWidth() {
return mWidth;
}
- @CalledByNative("CaptureFormat")
+
public int getHeight() {
return mHeight;
}
- @CalledByNative("CaptureFormat")
+
public int getFramerate() {
return mFramerate;
}
- @CalledByNative("CaptureFormat")
+
public int getPixelFormat() {
return mPixelFormat;
}
}
- // Some devices don't support YV12 format correctly, even with JELLY_BEAN or
- // newer OS. To work around the issues on those devices, we have to request
- // NV21. Some other devices have troubles with certain capture resolutions
- // under a given one: for those, the resolution is swapped with a known
- // good. Both are supposed to be temporary hacks.
- private static class BuggyDeviceHack {
- private static class IdAndSizes {
- IdAndSizes(String model, String device, int minWidth, int minHeight) {
- mModel = model;
- mDevice = device;
- mMinWidth = minWidth;
- mMinHeight = minHeight;
- }
- public final String mModel;
- public final String mDevice;
- public final int mMinWidth;
- public final int mMinHeight;
- }
- private static final IdAndSizes s_CAPTURESIZE_BUGGY_DEVICE_LIST[] = {
- new IdAndSizes("Nexus 7", "flo", 640, 480)
- };
-
- private static final String[] s_COLORSPACE_BUGGY_DEVICE_LIST = {
- "SAMSUNG-SGH-I747",
- "ODROID-U2",
- };
-
- static void applyMinDimensions(CaptureFormat format) {
- // NOTE: this can discard requested aspect ratio considerations.
- for (IdAndSizes buggyDevice : s_CAPTURESIZE_BUGGY_DEVICE_LIST) {
- if (buggyDevice.mModel.contentEquals(android.os.Build.MODEL) &&
- buggyDevice.mDevice.contentEquals(android.os.Build.DEVICE)) {
- format.mWidth = (buggyDevice.mMinWidth > format.mWidth)
- ? buggyDevice.mMinWidth
- : format.mWidth;
- format.mHeight = (buggyDevice.mMinHeight > format.mHeight)
- ? buggyDevice.mMinHeight
- : format.mHeight;
- }
- }
- }
-
- static int getImageFormat() {
- if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {
- return ImageFormat.NV21;
- }
-
- for (String buggyDevice : s_COLORSPACE_BUGGY_DEVICE_LIST) {
- if (buggyDevice.contentEquals(android.os.Build.MODEL)) {
- return ImageFormat.NV21;
- }
- }
- return ImageFormat.YV12;
- }
- }
-
- private Camera mCamera;
- public ReentrantLock mPreviewBufferLock = new ReentrantLock();
- private Context mContext = null;
+ protected Camera mCamera;
+ protected CaptureFormat mCaptureFormat = null;
+ // Lock to mutually exclude execution of OnPreviewFrame {start/stop}Capture.
+ protected ReentrantLock mPreviewBufferLock = new ReentrantLock();
+ protected Context mContext = null;
// True when native code has started capture.
- private boolean mIsRunning = false;
+ protected boolean mIsRunning = false;
- private static final int NUM_CAPTURE_BUFFERS = 3;
- private int mExpectedFrameSize = 0;
- private int mId = 0;
+ protected int mId;
// Native callback context variable.
- private long mNativeVideoCaptureDeviceAndroid = 0;
- private int[] mGlTextures = null;
- private SurfaceTexture mSurfaceTexture = null;
- private static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65;
-
- private int mCameraOrientation = 0;
- private int mCameraFacing = 0;
- private int mDeviceOrientation = 0;
-
- CaptureFormat mCaptureFormat = null;
+ protected long mNativeVideoCaptureDeviceAndroid;
+ protected int[] mGlTextures = null;
+ protected SurfaceTexture mSurfaceTexture = null;
+ protected static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65;
+
+ protected int mCameraOrientation;
+ protected int mCameraFacing;
+ protected int mDeviceOrientation;
private static final String TAG = "VideoCapture";
- @CalledByNative
- public static VideoCapture createVideoCapture(
- Context context, int id, long nativeVideoCaptureDeviceAndroid) {
- return new VideoCapture(context, id, nativeVideoCaptureDeviceAndroid);
- }
-
- @CalledByNative
- public static CaptureFormat[] getDeviceSupportedFormats(int id) {
- Camera camera;
- try {
- camera = Camera.open(id);
- } catch (RuntimeException ex) {
- Log.e(TAG, "Camera.open: " + ex);
- return null;
- }
- Camera.Parameters parameters = getCameraParameters(camera);
- if (parameters == null) {
- return null;
- }
-
- ArrayList<CaptureFormat> formatList = new ArrayList<CaptureFormat>();
- // getSupportedPreview{Formats,FpsRange,PreviewSizes}() returns Lists
- // with at least one element, but when the camera is in bad state, they
- // can return null pointers; in that case we use a 0 entry, so we can
- // retrieve as much information as possible.
- List<Integer> pixelFormats = parameters.getSupportedPreviewFormats();
- if (pixelFormats == null) {
- pixelFormats = new ArrayList<Integer>();
- }
- if (pixelFormats.size() == 0) {
- pixelFormats.add(ImageFormat.UNKNOWN);
- }
- for (Integer previewFormat : pixelFormats) {
- int pixelFormat =
- AndroidImageFormatList.ANDROID_IMAGEFORMAT_UNKNOWN;
- if (previewFormat == ImageFormat.YV12) {
- pixelFormat = AndroidImageFormatList.ANDROID_IMAGEFORMAT_YV12;
- } else if (previewFormat == ImageFormat.NV21) {
- continue;
- }
-
- List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange();
- if (listFpsRange == null) {
- listFpsRange = new ArrayList<int[]>();
- }
- if (listFpsRange.size() == 0) {
- listFpsRange.add(new int[] {0, 0});
- }
- for (int[] fpsRange : listFpsRange) {
- List<Camera.Size> supportedSizes =
- parameters.getSupportedPreviewSizes();
- if (supportedSizes == null) {
- supportedSizes = new ArrayList<Camera.Size>();
- }
- if (supportedSizes.size() == 0) {
- supportedSizes.add(camera.new Size(0, 0));
- }
- for (Camera.Size size : supportedSizes) {
- formatList.add(new CaptureFormat(size.width, size.height,
- (fpsRange[0] + 999 ) / 1000, pixelFormat));
- }
- }
- }
- camera.release();
- return formatList.toArray(new CaptureFormat[formatList.size()]);
- }
-
- public VideoCapture(
- Context context, int id, long nativeVideoCaptureDeviceAndroid) {
+ VideoCapture(Context context,
+ int id,
+ long nativeVideoCaptureDeviceAndroid) {
mContext = context;
mId = id;
mNativeVideoCaptureDeviceAndroid = nativeVideoCaptureDeviceAndroid;
}
- // Returns true on success, false otherwise.
@CalledByNative
- public boolean allocate(int width, int height, int frameRate) {
+ boolean allocate(int width, int height, int frameRate) {
Log.d(TAG, "allocate: requested (" + width + "x" + height + ")@" +
- frameRate + "fps");
+ frameRate + "fps");
try {
mCamera = Camera.open(mId);
} catch (RuntimeException ex) {
@@ -290,27 +162,21 @@ public class VideoCapture implements PreviewCallback {
Log.e(TAG, "allocate: can not find a multiple-of-32 resolution");
return false;
}
-
- mCaptureFormat = new CaptureFormat(
- matchedWidth, matchedHeight, frameRate,
- BuggyDeviceHack.getImageFormat());
- // Hack to avoid certain capture resolutions under a minimum one,
- // see http://crbug.com/305294
- BuggyDeviceHack.applyMinDimensions(mCaptureFormat);
- Log.d(TAG, "allocate: matched (" + mCaptureFormat.mWidth + "x" +
- mCaptureFormat.mHeight + ")");
+ Log.d(TAG, "allocate: matched (" + matchedWidth + "x" + matchedHeight + ")");
if (parameters.isVideoStabilizationSupported()) {
- Log.d(TAG, "Image stabilization supported, currently: "
- + parameters.getVideoStabilization() + ", setting it.");
+ Log.d(TAG, "Image stabilization supported, currently: " +
+ parameters.getVideoStabilization() + ", setting it.");
parameters.setVideoStabilization(true);
} else {
Log.d(TAG, "Image stabilization not supported.");
}
+
+ setCaptureParameters(matchedWidth, matchedHeight, frameRate, parameters);
parameters.setPreviewSize(mCaptureFormat.mWidth,
mCaptureFormat.mHeight);
- parameters.setPreviewFormat(mCaptureFormat.mPixelFormat);
parameters.setPreviewFpsRange(fpsMinMax[0], fpsMinMax[1]);
+ parameters.setPreviewFormat(mCaptureFormat.mPixelFormat);
mCamera.setParameters(parameters);
// Set SurfaceTexture. Android Capture needs a SurfaceTexture even if
@@ -332,7 +198,6 @@ public class VideoCapture implements PreviewCallback {
mSurfaceTexture = new SurfaceTexture(mGlTextures[0]);
mSurfaceTexture.setOnFrameAvailableListener(null);
-
try {
mCamera.setPreviewTexture(mSurfaceTexture);
} catch (IOException ex) {
@@ -340,48 +205,11 @@ public class VideoCapture implements PreviewCallback {
return false;
}
- int bufSize = mCaptureFormat.mWidth *
- mCaptureFormat.mHeight *
- ImageFormat.getBitsPerPixel(
- mCaptureFormat.mPixelFormat) / 8;
- for (int i = 0; i < NUM_CAPTURE_BUFFERS; i++) {
- byte[] buffer = new byte[bufSize];
- mCamera.addCallbackBuffer(buffer);
- }
- mExpectedFrameSize = bufSize;
-
+ allocateBuffers();
return true;
}
@CalledByNative
- public int queryWidth() {
- return mCaptureFormat.mWidth;
- }
-
- @CalledByNative
- public int queryHeight() {
- return mCaptureFormat.mHeight;
- }
-
- @CalledByNative
- public int queryFrameRate() {
- return mCaptureFormat.mFramerate;
- }
-
- @CalledByNative
- public int getColorspace() {
- switch (mCaptureFormat.mPixelFormat) {
- case ImageFormat.YV12:
- return AndroidImageFormatList.ANDROID_IMAGEFORMAT_YV12;
- case ImageFormat.NV21:
- return AndroidImageFormatList.ANDROID_IMAGEFORMAT_NV21;
- case ImageFormat.UNKNOWN:
- default:
- return AndroidImageFormatList.ANDROID_IMAGEFORMAT_UNKNOWN;
- }
- }
-
- @CalledByNative
public int startCapture() {
if (mCamera == null) {
Log.e(TAG, "startCapture: camera is null");
@@ -397,14 +225,13 @@ public class VideoCapture implements PreviewCallback {
} finally {
mPreviewBufferLock.unlock();
}
- mCamera.setPreviewCallbackWithBuffer(this);
+ setPreviewCallback(this);
try {
mCamera.startPreview();
} catch (RuntimeException ex) {
Log.e(TAG, "startCapture: Camera.startPreview: " + ex);
return -1;
}
-
return 0;
}
@@ -426,7 +253,7 @@ public class VideoCapture implements PreviewCallback {
}
mCamera.stopPreview();
- mCamera.setPreviewCallbackWithBuffer(null);
+ setPreviewCallback(null);
return 0;
}
@@ -449,87 +276,51 @@ public class VideoCapture implements PreviewCallback {
}
}
- @Override
- public void onPreviewFrame(byte[] data, Camera camera) {
- mPreviewBufferLock.lock();
- try {
- if (!mIsRunning) {
- return;
- }
- if (data.length == mExpectedFrameSize) {
- int rotation = getDeviceOrientation();
- if (rotation != mDeviceOrientation) {
- mDeviceOrientation = rotation;
- Log.d(TAG,
- "onPreviewFrame: device orientation=" +
- mDeviceOrientation + ", camera orientation=" +
- mCameraOrientation);
- }
- if (mCameraFacing == Camera.CameraInfo.CAMERA_FACING_BACK) {
- rotation = 360 - rotation;
- }
- rotation = (mCameraOrientation + rotation) % 360;
- nativeOnFrameAvailable(mNativeVideoCaptureDeviceAndroid,
- data, mExpectedFrameSize, rotation);
- }
- } finally {
- mPreviewBufferLock.unlock();
- if (camera != null) {
- camera.addCallbackBuffer(data);
- }
- }
- }
-
- // TODO(wjia): investigate whether reading from texture could give better
- // performance and frame rate, using onFrameAvailable().
+ // Local hook to allow derived classes to fill capture format and modify
+ // camera parameters as they see fit.
+ abstract void setCaptureParameters(
+ int width,
+ int height,
+ int frameRate,
+ Camera.Parameters cameraParameters);
- private static class ChromiumCameraInfo {
- private final int mId;
- private final Camera.CameraInfo mCameraInfo;
+ // Local hook to allow derived classes to configure and plug capture
+ // buffers if needed.
+ abstract void allocateBuffers();
- private ChromiumCameraInfo(int index) {
- mId = index;
- mCameraInfo = getCameraInfo(mId);
- }
-
- @CalledByNative("ChromiumCameraInfo")
- private static int getNumberOfCameras() {
- return Camera.getNumberOfCameras();
- }
+ // Local method to be overriden with the particular setPreviewCallback to be
+ // used in the implementations.
+ abstract void setPreviewCallback(Camera.PreviewCallback cb);
- @CalledByNative("ChromiumCameraInfo")
- private static ChromiumCameraInfo getAt(int index) {
- return new ChromiumCameraInfo(index);
- }
+ @CalledByNative
+ public int queryWidth() {
+ return mCaptureFormat.mWidth;
+ }
- @CalledByNative("ChromiumCameraInfo")
- private int getId() {
- return mId;
- }
+ @CalledByNative
+ public int queryHeight() {
+ return mCaptureFormat.mHeight;
+ }
- @CalledByNative("ChromiumCameraInfo")
- private String getDeviceName() {
- if (mCameraInfo == null) {
- return "";
- }
- return "camera " + mId + ", facing " +
- (mCameraInfo.facing ==
- Camera.CameraInfo.CAMERA_FACING_FRONT ? "front" : "back");
- }
+ @CalledByNative
+ public int queryFrameRate() {
+ return mCaptureFormat.mFramerate;
+ }
- @CalledByNative("ChromiumCameraInfo")
- private int getOrientation() {
- return (mCameraInfo == null ? 0 : mCameraInfo.orientation);
+ @CalledByNative
+ public int getColorspace() {
+ switch (mCaptureFormat.mPixelFormat) {
+ case ImageFormat.YV12:
+ return AndroidImageFormatList.ANDROID_IMAGEFORMAT_YV12;
+ case ImageFormat.NV21:
+ return AndroidImageFormatList.ANDROID_IMAGEFORMAT_NV21;
+ case ImageFormat.UNKNOWN:
+ default:
+ return AndroidImageFormatList.ANDROID_IMAGEFORMAT_UNKNOWN;
}
}
- private native void nativeOnFrameAvailable(
- long nativeVideoCaptureDeviceAndroid,
- byte[] data,
- int length,
- int rotation);
-
- private int getDeviceOrientation() {
+ protected int getDeviceOrientation() {
int orientation = 0;
if (mContext != null) {
WindowManager wm = (WindowManager) mContext.getSystemService(
@@ -553,7 +344,14 @@ public class VideoCapture implements PreviewCallback {
return orientation;
}
- private static Camera.Parameters getCameraParameters(Camera camera) {
+ // Method for VideoCapture implementations to call back native code.
+ public native void nativeOnFrameAvailable(
+ long nativeVideoCaptureDeviceAndroid,
+ byte[] data,
+ int length,
+ int rotation);
+
+ protected static Camera.Parameters getCameraParameters(Camera camera) {
Camera.Parameters parameters;
try {
parameters = camera.getParameters();
@@ -565,7 +363,7 @@ public class VideoCapture implements PreviewCallback {
return parameters;
}
- private static Camera.CameraInfo getCameraInfo(int id) {
+ private Camera.CameraInfo getCameraInfo(int id) {
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
try {
Camera.getCameraInfo(id, cameraInfo);
@@ -575,4 +373,4 @@ public class VideoCapture implements PreviewCallback {
}
return cameraInfo;
}
-}
+} \ No newline at end of file
diff --git a/media/base/android/java/src/org/chromium/media/VideoCaptureAndroid.java b/media/base/android/java/src/org/chromium/media/VideoCaptureAndroid.java
new file mode 100644
index 0000000..1fb2f76
--- /dev/null
+++ b/media/base/android/java/src/org/chromium/media/VideoCaptureAndroid.java
@@ -0,0 +1,208 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.media;
+
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.hardware.Camera;
+import android.hardware.Camera.Size;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class extends the VideoCapture base class for manipulating normal video
+ * capture devices in Android, including receiving copies of preview frames via
+ * Java-allocated buffers. It also includes class BuggyDeviceHack to deal with
+ * troublesome devices.
+ **/
+public class VideoCaptureAndroid extends VideoCapture {
+
+ // Some devices don't support YV12 format correctly, even with JELLY_BEAN or
+ // newer OS. To work around the issues on those devices, we have to request
+ // NV21. Some other devices have troubles with certain capture resolutions
+ // under a given one: for those, the resolution is swapped with a known
+ // good. Both are supposed to be temporary hacks.
+ private static class BuggyDeviceHack {
+ private static class IdAndSizes {
+ IdAndSizes(String model, String device, int minWidth, int minHeight) {
+ mModel = model;
+ mDevice = device;
+ mMinWidth = minWidth;
+ mMinHeight = minHeight;
+ }
+ public final String mModel;
+ public final String mDevice;
+ public final int mMinWidth;
+ public final int mMinHeight;
+ }
+
+ private static final IdAndSizes s_CAPTURESIZE_BUGGY_DEVICE_LIST[] = {
+ new IdAndSizes("Nexus 7", "flo", 640, 480)
+ };
+
+ private static final String[] s_COLORSPACE_BUGGY_DEVICE_LIST = {
+ "SAMSUNG-SGH-I747",
+ "ODROID-U2",
+ };
+
+ static void applyMinDimensions(CaptureFormat format) {
+ // NOTE: this can discard requested aspect ratio considerations.
+ for (IdAndSizes buggyDevice : s_CAPTURESIZE_BUGGY_DEVICE_LIST) {
+ if (buggyDevice.mModel.contentEquals(android.os.Build.MODEL) &&
+ buggyDevice.mDevice.contentEquals(android.os.Build.DEVICE)) {
+ format.mWidth = (buggyDevice.mMinWidth > format.mWidth)
+ ? buggyDevice.mMinWidth : format.mWidth;
+ format.mHeight = (buggyDevice.mMinHeight > format.mHeight)
+ ? buggyDevice.mMinHeight : format.mHeight;
+ }
+ }
+ }
+
+ static int getImageFormat() {
+ if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+ return ImageFormat.NV21;
+ }
+
+ for (String buggyDevice : s_COLORSPACE_BUGGY_DEVICE_LIST) {
+ if (buggyDevice.contentEquals(android.os.Build.MODEL)) {
+ return ImageFormat.NV21;
+ }
+ }
+ return ImageFormat.YV12;
+ }
+ }
+
+ private int mExpectedFrameSize;
+ private static final int NUM_CAPTURE_BUFFERS = 3;
+ private static final String TAG = "VideoCaptureAndroid";
+
+ static CaptureFormat[] getDeviceSupportedFormats(int id) {
+ Camera camera;
+ try {
+ camera = Camera.open(id);
+ } catch (RuntimeException ex) {
+ Log.e(TAG, "Camera.open: " + ex);
+ return null;
+ }
+ Camera.Parameters parameters = getCameraParameters(camera);
+ if (parameters == null) {
+ return null;
+ }
+
+ ArrayList<CaptureFormat> formatList = new ArrayList<CaptureFormat>();
+ // getSupportedPreview{Formats,FpsRange,PreviewSizes}() returns Lists
+ // with at least one element, but when the camera is in bad state, they
+ // can return null pointers; in that case we use a 0 entry, so we can
+ // retrieve as much information as possible.
+ List<Integer> pixelFormats = parameters.getSupportedPreviewFormats();
+ if (pixelFormats == null) {
+ pixelFormats = new ArrayList<Integer>();
+ }
+ if (pixelFormats.size() == 0) {
+ pixelFormats.add(ImageFormat.UNKNOWN);
+ }
+ for (Integer previewFormat : pixelFormats) {
+ int pixelFormat =
+ AndroidImageFormatList.ANDROID_IMAGEFORMAT_UNKNOWN;
+ if (previewFormat == ImageFormat.YV12) {
+ pixelFormat = AndroidImageFormatList.ANDROID_IMAGEFORMAT_YV12;
+ } else if (previewFormat == ImageFormat.NV21) {
+ continue;
+ }
+
+ List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange();
+ if (listFpsRange == null) {
+ listFpsRange = new ArrayList<int[]>();
+ }
+ if (listFpsRange.size() == 0) {
+ listFpsRange.add(new int[] {0, 0});
+ }
+ for (int[] fpsRange : listFpsRange) {
+ List<Camera.Size> supportedSizes =
+ parameters.getSupportedPreviewSizes();
+ if (supportedSizes == null) {
+ supportedSizes = new ArrayList<Camera.Size>();
+ }
+ if (supportedSizes.size() == 0) {
+ supportedSizes.add(camera.new Size(0, 0));
+ }
+ for (Camera.Size size : supportedSizes) {
+ formatList.add(new CaptureFormat(size.width,
+ size.height,
+ (fpsRange[0] + 999) / 1000,
+ pixelFormat));
+ }
+ }
+ }
+ camera.release();
+ return formatList.toArray(new CaptureFormat[formatList.size()]);
+ }
+
+ VideoCaptureAndroid(Context context,
+ int id,
+ long nativeVideoCaptureDeviceAndroid) {
+ super(context, id, nativeVideoCaptureDeviceAndroid);
+ }
+
+ @Override
+ protected void setCaptureParameters(
+ int width,
+ int height,
+ int frameRate,
+ Camera.Parameters cameraParameters) {
+ mCaptureFormat = new CaptureFormat(
+ width, height, frameRate, BuggyDeviceHack.getImageFormat());
+ // Hack to avoid certain capture resolutions under a minimum one,
+ // see http://crbug.com/305294.
+ BuggyDeviceHack.applyMinDimensions(mCaptureFormat);
+ }
+
+ @Override
+ protected void allocateBuffers() {
+ mExpectedFrameSize = mCaptureFormat.mWidth * mCaptureFormat.mHeight *
+ ImageFormat.getBitsPerPixel(mCaptureFormat.mPixelFormat) / 8;
+ for (int i = 0; i < NUM_CAPTURE_BUFFERS; i++) {
+ byte[] buffer = new byte[mExpectedFrameSize];
+ mCamera.addCallbackBuffer(buffer);
+ }
+ }
+
+ @Override
+ protected void setPreviewCallback(Camera.PreviewCallback cb) {
+ mCamera.setPreviewCallbackWithBuffer(cb);
+ }
+
+ @Override
+ public void onPreviewFrame(byte[] data, Camera camera) {
+ mPreviewBufferLock.lock();
+ try {
+ if (!mIsRunning) {
+ return;
+ }
+ if (data.length == mExpectedFrameSize) {
+ int rotation = getDeviceOrientation();
+ if (rotation != mDeviceOrientation) {
+ mDeviceOrientation = rotation;
+ }
+ if (mCameraFacing == Camera.CameraInfo.CAMERA_FACING_BACK) {
+ rotation = 360 - rotation;
+ }
+ rotation = (mCameraOrientation + rotation) % 360;
+ nativeOnFrameAvailable(mNativeVideoCaptureDeviceAndroid,
+ data, mExpectedFrameSize, rotation);
+ }
+ } finally {
+ mPreviewBufferLock.unlock();
+ if (camera != null) {
+ camera.addCallbackBuffer(data);
+ }
+ }
+ }
+
+ // TODO(wjia): investigate whether reading from texture could give better
+ // performance and frame rate, using onFrameAvailable().
+}
diff --git a/media/base/android/java/src/org/chromium/media/VideoCaptureFactory.java b/media/base/android/java/src/org/chromium/media/VideoCaptureFactory.java
new file mode 100644
index 0000000..6083861
--- /dev/null
+++ b/media/base/android/java/src/org/chromium/media/VideoCaptureFactory.java
@@ -0,0 +1,175 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.media;
+
+import android.content.Context;
+import android.hardware.Camera;
+import android.util.Log;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+// Needed for jni_generator.py to guess correctly the origin of
+// VideoCapture.CaptureFormat.
+import org.chromium.media.VideoCapture;
+
+/**
+ * This class implements a factory of Android Video Capture objects for Chrome.
+ * The static createVideoCapture() returns either a "normal" VideoCaptureAndroid
+ * or a "special" VideoCaptureTango. Cameras are identified by |id|, where Tango
+ * cameras have |id| above the standard ones. Video Capture objects allocated
+ * via createVideoCapture() are explicitly owned by the caller.
+ * ChromiumCameraInfo is an internal class with some static methods needed from
+ * the native side to enumerate devices and collect their names and info. It
+ * takes into account the mentioned special devices.
+ **/
+@JNINamespace("media")
+class VideoCaptureFactory {
+
+ protected static class CamParams {
+ final int mId;
+ final String mName;
+ final int mWidth;
+ final int mHeight;
+
+ CamParams(int id, String name, int width, int height) {
+ mId = id;
+ mName = name;
+ mWidth = width;
+ mHeight = height;
+ }
+ }
+
+ protected static class ChromiumCameraInfo {
+ private final int mId;
+ private final Camera.CameraInfo mCameraInfo;
+ // Special devices have more cameras than usual. Those devices are
+ // identified by model & device. Currently only the Tango is supported.
+ // Note that these devices have no Camera.CameraInfo.
+ private static final String[][] s_SPECIAL_DEVICE_LIST = {
+ {"Peanut", "peanut"},
+ };
+ private static final String TAG = "ChromiumCameraInfo";
+
+ private static boolean isSpecialDevice() {
+ for (String[] device : s_SPECIAL_DEVICE_LIST) {
+ if (device[0].contentEquals(android.os.Build.MODEL) &&
+ device[1].contentEquals(android.os.Build.DEVICE)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private ChromiumCameraInfo(int index) {
+ mId = index;
+ mCameraInfo = isSpecialCamera(index) ? null : getCameraInfo(mId);
+ }
+
+ @CalledByNative("ChromiumCameraInfo")
+ private static int getNumberOfCameras() {
+ if (isSpecialDevice()) {
+ Log.d(TAG, "Special device: " + android.os.Build.MODEL);
+ return Camera.getNumberOfCameras() +
+ VideoCaptureTango.numberOfCameras();
+ } else {
+ return Camera.getNumberOfCameras();
+ }
+ }
+
+ @CalledByNative("ChromiumCameraInfo")
+ private static ChromiumCameraInfo getAt(int index) {
+ return new ChromiumCameraInfo(index);
+ }
+
+ @CalledByNative("ChromiumCameraInfo")
+ private int getId() {
+ return mId;
+ }
+
+ @CalledByNative("ChromiumCameraInfo")
+ private String getDeviceName() {
+ if (isSpecialCamera(mId)) {
+ return VideoCaptureTango.getCamParams(
+ mId - Camera.getNumberOfCameras()).mName;
+ } else {
+ if (mCameraInfo == null) {
+ return "";
+ }
+ Log.d(TAG, "Camera enumerated: " + (mCameraInfo.facing ==
+ Camera.CameraInfo.CAMERA_FACING_FRONT ? "front" :
+ "back"));
+ return "camera " + mId + ", facing " + (mCameraInfo.facing ==
+ Camera.CameraInfo.CAMERA_FACING_FRONT ? "front" :
+ "back");
+ }
+ }
+
+ @CalledByNative("ChromiumCameraInfo")
+ private int getOrientation() {
+ if (isSpecialCamera(mId)) {
+ return Camera.CameraInfo.CAMERA_FACING_BACK;
+ } else {
+ return (mCameraInfo == null ? 0 : mCameraInfo.orientation);
+ }
+ }
+
+ private Camera.CameraInfo getCameraInfo(int id) {
+ Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
+ try {
+ Camera.getCameraInfo(id, cameraInfo);
+ } catch (RuntimeException ex) {
+ Log.e(TAG, "getCameraInfo: Camera.getCameraInfo: " + ex);
+ return null;
+ }
+ return cameraInfo;
+ }
+ }
+
+ protected static boolean isSpecialCamera(int id) {
+ return id >= Camera.getNumberOfCameras();
+ }
+
+ // Factory methods.
+ @CalledByNative
+ static VideoCapture createVideoCapture(
+ Context context, int id, long nativeVideoCaptureDeviceAndroid) {
+ if (isSpecialCamera(id)) {
+ return new VideoCaptureTango(context,
+ id - Camera.getNumberOfCameras(),
+ nativeVideoCaptureDeviceAndroid);
+ } else {
+ return new VideoCaptureAndroid(context, id,
+ nativeVideoCaptureDeviceAndroid);
+ }
+ }
+
+ @CalledByNative
+ static VideoCapture.CaptureFormat[] getDeviceSupportedFormats(int id) {
+ return isSpecialCamera(id) ?
+ VideoCaptureTango.getDeviceSupportedFormats(
+ id - Camera.getNumberOfCameras()) :
+ VideoCaptureAndroid.getDeviceSupportedFormats(id);
+ }
+
+ @CalledByNative
+ static int getCaptureFormatWidth(VideoCapture.CaptureFormat format) {
+ return format.getWidth();
+ }
+
+ @CalledByNative
+ static int getCaptureFormatHeight(VideoCapture.CaptureFormat format) {
+ return format.getHeight();
+ }
+
+ @CalledByNative
+ static int getCaptureFormatFramerate(VideoCapture.CaptureFormat format) {
+ return format.getFramerate();
+ }
+
+ @CalledByNative
+ static int getCaptureFormatPixelFormat(VideoCapture.CaptureFormat format) {
+ return format.getPixelFormat();
+ }
+}
diff --git a/media/base/android/java/src/org/chromium/media/VideoCaptureTango.java b/media/base/android/java/src/org/chromium/media/VideoCaptureTango.java
new file mode 100644
index 0000000..e3e263d
--- /dev/null
+++ b/media/base/android/java/src/org/chromium/media/VideoCaptureTango.java
@@ -0,0 +1,193 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.media;
+
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.hardware.Camera;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * This class extends the VideoCapture base class for manipulating a Tango
+ * device's cameras, namely the associated Depth (z-Buffer), Fisheye and back-
+ * facing 4MP video capture devices. These devices are differentiated via the
+ * |id| passed on constructor, according to the index correspondence in
+ * |s_CAM_PARAMS|; all devices |id| are index 0 towards the parent VideoCapture.
+ **/
+public class VideoCaptureTango extends VideoCapture {
+ private ByteBuffer mFrameBuffer = null;
+ private final int mTangoCameraId;
+ // The indexes must coincide with the s_CAM_PARAMS used below.
+ private static final int DEPTH_CAMERA_ID = 0;
+ private static final int FISHEYE_CAMERA_ID = 1;
+ private static final int FOURMP_CAMERA_ID = 2;
+ private static final VideoCaptureFactory.CamParams s_CAM_PARAMS[] = {
+ new VideoCaptureFactory.CamParams(DEPTH_CAMERA_ID, "depth", 320, 240),
+ new VideoCaptureFactory.CamParams(FISHEYE_CAMERA_ID, "fisheye", 640, 480),
+ new VideoCaptureFactory.CamParams(FOURMP_CAMERA_ID, "4MP", 1280, 720)};
+
+ // SuperFrame size definitions. Note that total size is the amount of lines
+ // multiplied by 3/2 due to Chroma components following.
+ private static final int SF_WIDTH = 1280;
+ private static final int SF_HEIGHT = 1168;
+ private static final int SF_FULL_HEIGHT = SF_HEIGHT * 3 / 2;
+ private static final int SF_LINES_HEADER = 16;
+ private static final int SF_LINES_FISHEYE = 240;
+ private static final int SF_LINES_RESERVED = 80; // Spec says 96.
+ private static final int SF_LINES_DEPTH = 60;
+ private static final int SF_LINES_DEPTH_PADDED = 112; // Spec says 96.
+ private static final int SF_LINES_BIGIMAGE = 720;
+ private static final int SF_OFFSET_4MP_CHROMA = 112;
+
+ private static final byte CHROMA_ZERO_LEVEL = 127;
+ private static final String TAG = "VideoCaptureTango";
+
+ static int numberOfCameras() {
+ return s_CAM_PARAMS.length;
+ }
+
+ static VideoCaptureFactory.CamParams getCamParams(int index) {
+ if (index >= s_CAM_PARAMS.length) return null;
+ return s_CAM_PARAMS[index];
+ }
+
+ static CaptureFormat[] getDeviceSupportedFormats(int id) {
+ ArrayList<CaptureFormat> formatList = new ArrayList<CaptureFormat>();
+ if (id == DEPTH_CAMERA_ID) {
+ formatList.add(new CaptureFormat(320, 180, 5, ImageFormat.YV12));
+ } else if (id == FISHEYE_CAMERA_ID) {
+ formatList.add(new CaptureFormat(640, 480, 30, ImageFormat.YV12));
+ } else if (id == FOURMP_CAMERA_ID) {
+ formatList.add(new CaptureFormat(1280, 720, 20, ImageFormat.YV12));
+ }
+ return formatList.toArray(new CaptureFormat[formatList.size()]);
+ }
+
+ VideoCaptureTango(Context context,
+ int id,
+ long nativeVideoCaptureDeviceAndroid) {
+ // All Tango cameras are like the back facing one for the generic
+ // VideoCapture code.
+ super(context, 0, nativeVideoCaptureDeviceAndroid);
+ mTangoCameraId = id;
+ }
+
+ @Override
+ protected void setCaptureParameters(
+ int width,
+ int height,
+ int frameRate,
+ Camera.Parameters cameraParameters) {
+ mCaptureFormat = new CaptureFormat(s_CAM_PARAMS[mTangoCameraId].mWidth,
+ s_CAM_PARAMS[mTangoCameraId].mHeight,
+ frameRate,
+ ImageFormat.YV12);
+ // Connect Tango SuperFrame mode. Available sf modes are "all",
+ // "big-rgb", "small-rgb", "depth", "ir".
+ cameraParameters.set("sf-mode", "all");
+ }
+
+ @Override
+ protected void allocateBuffers() {
+ mFrameBuffer = ByteBuffer.allocateDirect(
+ mCaptureFormat.mWidth * mCaptureFormat.mHeight * 3 / 2);
+ // Prefill Chroma to their zero-equivalent for the cameras that only
+ // provide Luma component.
+ Arrays.fill(mFrameBuffer.array(), CHROMA_ZERO_LEVEL);
+ }
+
+ @Override
+ protected void setPreviewCallback(Camera.PreviewCallback cb) {
+ mCamera.setPreviewCallback(cb);
+ }
+
+ @Override
+ public void onPreviewFrame(byte[] data, Camera camera) {
+ mPreviewBufferLock.lock();
+ try {
+ if (!mIsRunning) {
+ return;
+ }
+ if (data.length == SF_WIDTH * SF_FULL_HEIGHT) {
+ int rotation = getDeviceOrientation();
+ if (rotation != mDeviceOrientation) {
+ mDeviceOrientation = rotation;
+ }
+ if (mCameraFacing == Camera.CameraInfo.CAMERA_FACING_BACK) {
+ rotation = 360 - rotation;
+ }
+ rotation = (mCameraOrientation + rotation) % 360;
+
+ if (mTangoCameraId == DEPTH_CAMERA_ID) {
+ int sizeY = SF_WIDTH * SF_LINES_DEPTH;
+ int startY =
+ SF_WIDTH * (SF_LINES_HEADER + SF_LINES_FISHEYE +
+ SF_LINES_RESERVED);
+ // Depth is composed of 16b samples in which only 12b are
+ // used. Throw away lowest 4 resolution bits. Android
+ // platforms are big endian, LSB in lowest address. In this
+ // case Chroma components are unused. No need to write them
+ // explicitly since they're filled to 128 on creation.
+ byte depthsample;
+ for (int j = startY; j < startY + 2 * sizeY; j += 2) {
+ depthsample = (byte)((data[j + 1] << 4) |
+ ((data[j] & 0xF0) >> 4));
+ mFrameBuffer.put(depthsample);
+ }
+ for (int j = 0;
+ j < mCaptureFormat.mWidth * mCaptureFormat.mHeight -
+ sizeY;
+ ++j)
+ mFrameBuffer.put((byte)0);
+ } else if (mTangoCameraId == FISHEYE_CAMERA_ID) {
+ int sizeY = SF_WIDTH * SF_LINES_FISHEYE;
+ int startY = SF_WIDTH * SF_LINES_HEADER;
+ // Fisheye is black and white so Chroma components are
+ // unused. No need to write them explicitly since they're
+ // filled to 128 on creation.
+ ByteBuffer.wrap(data, startY, sizeY)
+ .get(mFrameBuffer.array(), 0, sizeY);
+ } else if (mTangoCameraId == FOURMP_CAMERA_ID) {
+ int startY =
+ SF_WIDTH * (SF_LINES_HEADER + SF_LINES_FISHEYE +
+ SF_LINES_RESERVED + SF_LINES_DEPTH_PADDED);
+ int sizeY = SF_WIDTH * SF_LINES_BIGIMAGE;
+
+ // The spec is completely inaccurate on the location, sizes
+ // and format of these channels.
+ int startU = SF_WIDTH * (SF_HEIGHT + SF_OFFSET_4MP_CHROMA);
+ int sizeU = SF_WIDTH * SF_LINES_BIGIMAGE / 4;
+ int startV = (SF_WIDTH * SF_HEIGHT * 5 / 4) +
+ SF_WIDTH * SF_OFFSET_4MP_CHROMA;
+ int sizeV = SF_WIDTH * SF_LINES_BIGIMAGE / 4;
+
+ // Equivalent to the following |for| loop but much faster:
+ // for (int i = START; i < START + SIZE; ++i)
+ // mFrameBuffer.put(data[i]);
+ ByteBuffer.wrap(data, startY, sizeY)
+ .get(mFrameBuffer.array(), 0, sizeY);
+ ByteBuffer.wrap(data, startU, sizeU)
+ .get(mFrameBuffer.array(), sizeY, sizeU);
+ ByteBuffer.wrap(data, startV, sizeV)
+ .get(mFrameBuffer.array(), sizeY + sizeU, sizeV);
+ } else {
+ Log.e(TAG, "Unknown camera, #id: " + mTangoCameraId);
+ return;
+ }
+ mFrameBuffer.rewind(); // Important!
+ nativeOnFrameAvailable(mNativeVideoCaptureDeviceAndroid,
+ mFrameBuffer.array(),
+ mFrameBuffer.capacity(),
+ rotation);
+ }
+ } finally {
+ mPreviewBufferLock.unlock();
+ }
+ }
+}
diff --git a/media/base/android/media_jni_registrar.cc b/media/base/android/media_jni_registrar.cc
index fe0ff67..c9b8489 100644
--- a/media/base/android/media_jni_registrar.cc
+++ b/media/base/android/media_jni_registrar.cc
@@ -18,6 +18,7 @@
#include "media/midi/usb_midi_device_android.h"
#include "media/midi/usb_midi_device_factory_android.h"
#include "media/video/capture/android/video_capture_device_android.h"
+#include "media/video/capture/android/video_capture_device_factory_android.h"
namespace media {
@@ -40,6 +41,8 @@ static base::android::RegistrationMethod kMediaRegisteredMethods[] = {
UsbMidiDeviceFactoryAndroid::RegisterUsbMidiDeviceFactory },
{ "VideoCaptureDevice",
VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice },
+ { "VideoCaptureDeviceFactory",
+ VideoCaptureDeviceFactoryAndroid::RegisterVideoCaptureDeviceFactory },
{ "WebAudioMediaCodecBridge",
WebAudioMediaCodecBridge::RegisterWebAudioMediaCodecBridge },
};
diff --git a/media/media.gyp b/media/media.gyp
index 3a3ad2c..856c751 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -450,6 +450,8 @@
'midi/usb_midi_output_stream.h',
'video/capture/android/video_capture_device_android.cc',
'video/capture/android/video_capture_device_android.h',
+ 'video/capture/android/video_capture_device_factory_android.cc',
+ 'video/capture/android/video_capture_device_factory_android.h',
'video/capture/fake_video_capture_device.cc',
'video/capture/fake_video_capture_device.h',
'video/capture/file_video_capture_device.cc',
@@ -1555,6 +1557,7 @@
'type': 'none',
'sources': [
'base/android/java/src/org/chromium/media/VideoCapture.java',
+ 'base/android/java/src/org/chromium/media/VideoCaptureFactory.java',
],
'variables': {
'jni_gen_package': 'media',
diff --git a/media/video/capture/android/video_capture_device_android.cc b/media/video/capture/android/video_capture_device_android.cc
index dff396f..426be13 100644
--- a/media/video/capture/android/video_capture_device_android.cc
+++ b/media/video/capture/android/video_capture_device_android.cc
@@ -7,12 +7,10 @@
#include <string>
#include "base/android/jni_android.h"
-#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
#include "jni/VideoCapture_jni.h"
-#include "media/base/video_util.h"
+#include "media/video/capture/android/video_capture_device_factory_android.h"
using base::android::AttachCurrentThread;
using base::android::CheckException;
@@ -23,82 +21,17 @@ using base::android::ScopedJavaLocalRef;
namespace media {
-// static
+//static
void VideoCaptureDevice::GetDeviceNames(Names* device_names) {
- device_names->clear();
-
- JNIEnv* env = AttachCurrentThread();
-
- int num_cameras = Java_ChromiumCameraInfo_getNumberOfCameras(env);
- DVLOG(1) << "VideoCaptureDevice::GetDeviceNames: num_cameras=" << num_cameras;
- if (num_cameras <= 0)
- return;
-
- for (int camera_id = num_cameras - 1; camera_id >= 0; --camera_id) {
- ScopedJavaLocalRef<jobject> ci =
- Java_ChromiumCameraInfo_getAt(env, camera_id);
-
- Name name(
- base::android::ConvertJavaStringToUTF8(
- Java_ChromiumCameraInfo_getDeviceName(env, ci.obj())),
- base::StringPrintf("%d", Java_ChromiumCameraInfo_getId(env, ci.obj())));
- device_names->push_back(name);
-
- DVLOG(1) << "VideoCaptureDevice::GetDeviceNames: camera device_name="
- << name.name()
- << ", unique_id="
- << name.id()
- << ", orientation "
- << Java_ChromiumCameraInfo_getOrientation(env, ci.obj());
- }
+ VideoCaptureDeviceFactoryAndroid::GetDeviceNames(device_names);
}
// static
-void VideoCaptureDevice::GetDeviceSupportedFormats(const Name& device,
+void VideoCaptureDevice::GetDeviceSupportedFormats(
+ const Name& device,
VideoCaptureFormats* capture_formats) {
- int id;
- if (!base::StringToInt(device.id(), &id))
- return;
- JNIEnv* env = AttachCurrentThread();
- base::android::ScopedJavaLocalRef<jobjectArray> collected_formats =
- Java_VideoCapture_getDeviceSupportedFormats(env, id);
- if (collected_formats.is_null())
- return;
-
- jsize num_formats = env->GetArrayLength(collected_formats.obj());
- for (int i = 0; i < num_formats; ++i) {
- base::android::ScopedJavaLocalRef<jobject> format(
- env, env->GetObjectArrayElement(collected_formats.obj(), i));
-
- VideoPixelFormat pixel_format = media::PIXEL_FORMAT_UNKNOWN;
- switch (media::Java_CaptureFormat_getPixelFormat(env, format.obj())) {
- case VideoCaptureDeviceAndroid::ANDROID_IMAGEFORMAT_YV12:
- pixel_format = media::PIXEL_FORMAT_YV12;
- break;
- case VideoCaptureDeviceAndroid::ANDROID_IMAGEFORMAT_NV21:
- pixel_format = media::PIXEL_FORMAT_NV21;
- break;
- default:
- break;
- }
- VideoCaptureFormat capture_format(
- gfx::Size(media::Java_CaptureFormat_getWidth(env, format.obj()),
- media::Java_CaptureFormat_getHeight(env, format.obj())),
- media::Java_CaptureFormat_getFramerate(env, format.obj()),
- pixel_format);
- capture_formats->push_back(capture_format);
- DVLOG(1) << device.name() << " resolution: "
- << capture_format.frame_size.ToString() << ", fps: "
- << capture_format.frame_rate << ", pixel format: "
- << capture_format.pixel_format;
- }
-}
-
-const std::string VideoCaptureDevice::Name::GetModel() const {
- // Android cameras are not typically USB devices, and this method is currently
- // only used for USB model identifiers, so this implementation just indicates
- // an unknown device model.
- return "";
+ VideoCaptureDeviceFactoryAndroid::GetDeviceSupportedFormats(device,
+ capture_formats);
}
// static
@@ -120,6 +53,13 @@ bool VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice(JNIEnv* env) {
return RegisterNativesImpl(env);
}
+const std::string VideoCaptureDevice::Name::GetModel() const {
+ // Android cameras are not typically USB devices, and this method is currently
+ // only used for USB model identifiers, so this implementation just indicates
+ // an unknown device model.
+ return "";
+}
+
VideoCaptureDeviceAndroid::VideoCaptureDeviceAndroid(const Name& device_name)
: state_(kIdle), got_first_frame_(false), device_name_(device_name) {}
@@ -132,12 +72,8 @@ bool VideoCaptureDeviceAndroid::Init() {
if (!base::StringToInt(device_name_.id(), &id))
return false;
- JNIEnv* env = AttachCurrentThread();
-
- j_capture_.Reset(Java_VideoCapture_createVideoCapture(
- env, base::android::GetApplicationContext(), id,
- reinterpret_cast<intptr_t>(this)));
-
+ j_capture_.Reset(VideoCaptureDeviceFactoryAndroid::createVideoCaptureAndroid(
+ id, reinterpret_cast<intptr_t>(this)));
return true;
}
@@ -155,12 +91,12 @@ void VideoCaptureDeviceAndroid::AllocateAndStart(
JNIEnv* env = AttachCurrentThread();
- jboolean ret =
- Java_VideoCapture_allocate(env,
- j_capture_.obj(),
- params.requested_format.frame_size.width(),
- params.requested_format.frame_size.height(),
- params.requested_format.frame_rate);
+ jboolean ret = Java_VideoCapture_allocate(
+ env,
+ j_capture_.obj(),
+ params.requested_format.frame_size.width(),
+ params.requested_format.frame_size.height(),
+ params.requested_format.frame_rate);
if (!ret) {
SetErrorState("failed to allocate");
return;
diff --git a/media/video/capture/android/video_capture_device_android.h b/media/video/capture/android/video_capture_device_android.h
index 4a59b8b..958036f 100644
--- a/media/video/capture/android/video_capture_device_android.h
+++ b/media/video/capture/android/video_capture_device_android.h
@@ -73,7 +73,7 @@ class MEDIA_EXPORT VideoCaptureDeviceAndroid : public VideoCaptureDevice {
VideoCaptureFormat capture_format_;
// Java VideoCaptureAndroid instance.
- base::android::ScopedJavaGlobalRef<jobject> j_capture_;
+ base::android::ScopedJavaLocalRef<jobject> j_capture_;
DISALLOW_IMPLICIT_CONSTRUCTORS(VideoCaptureDeviceAndroid);
};
diff --git a/media/video/capture/android/video_capture_device_factory_android.cc b/media/video/capture/android/video_capture_device_factory_android.cc
new file mode 100644
index 0000000..a473cb9
--- /dev/null
+++ b/media/video/capture/android/video_capture_device_factory_android.cc
@@ -0,0 +1,111 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/video/capture/android/video_capture_device_factory_android.h"
+
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "jni/VideoCaptureFactory_jni.h"
+
+using base::android::AttachCurrentThread;
+using base::android::ScopedJavaLocalRef;
+
+namespace media {
+
+// static
+void VideoCaptureDeviceFactoryAndroid::GetDeviceNames(
+ VideoCaptureDevice::Names* device_names) {
+ device_names->clear();
+
+ JNIEnv* env = AttachCurrentThread();
+
+ int num_cameras = Java_ChromiumCameraInfo_getNumberOfCameras(env);
+ DVLOG(1) << "VideoCaptureDevice::GetDeviceNames: num_cameras=" << num_cameras;
+ if (num_cameras <= 0)
+ return;
+
+ for (int camera_id = num_cameras - 1; camera_id >= 0; --camera_id) {
+ ScopedJavaLocalRef<jobject> ci =
+ Java_ChromiumCameraInfo_getAt(env, camera_id);
+
+ VideoCaptureDevice::Name name(
+ base::android::ConvertJavaStringToUTF8(
+ Java_ChromiumCameraInfo_getDeviceName(env, ci.obj())),
+ base::StringPrintf("%d", Java_ChromiumCameraInfo_getId(env, ci.obj())));
+ device_names->push_back(name);
+
+ DVLOG(1) << "VideoCaptureDeviceFactoryAndroid::GetDeviceNames: camera"
+ << "device_name=" << name.name() << ", unique_id=" << name.id()
+ << ", orientation "
+ << Java_ChromiumCameraInfo_getOrientation(env, ci.obj());
+ }
+}
+
+// static
+void VideoCaptureDeviceFactoryAndroid::GetDeviceSupportedFormats(
+ const VideoCaptureDevice::Name& device,
+ VideoCaptureFormats* capture_formats) {
+ int id;
+ if (!base::StringToInt(device.id(), &id))
+ return;
+ JNIEnv* env = AttachCurrentThread();
+ base::android::ScopedJavaLocalRef<jobjectArray> collected_formats =
+ Java_VideoCaptureFactory_getDeviceSupportedFormats(env, id);
+ if (collected_formats.is_null())
+ return;
+
+ jsize num_formats = env->GetArrayLength(collected_formats.obj());
+ for (int i = 0; i < num_formats; ++i) {
+ base::android::ScopedJavaLocalRef<jobject> format(
+ env, env->GetObjectArrayElement(collected_formats.obj(), i));
+
+ VideoPixelFormat pixel_format = media::PIXEL_FORMAT_UNKNOWN;
+ switch (media::Java_VideoCaptureFactory_getCaptureFormatPixelFormat(
+ env, format.obj())) {
+ case ANDROID_IMAGEFORMAT_YV12:
+ pixel_format = media::PIXEL_FORMAT_YV12;
+ break;
+ case ANDROID_IMAGEFORMAT_NV21:
+ pixel_format = media::PIXEL_FORMAT_NV21;
+ break;
+ default:
+ break;
+ }
+ VideoCaptureFormat capture_format(
+ gfx::Size(media::Java_VideoCaptureFactory_getCaptureFormatWidth(env,
+ format.obj()),
+ media::Java_VideoCaptureFactory_getCaptureFormatHeight(env,
+ format.obj())),
+ media::Java_VideoCaptureFactory_getCaptureFormatFramerate(env,
+ format.obj()),
+ pixel_format);
+ capture_formats->push_back(capture_format);
+ DVLOG(1) << device.name() << " resolution: "
+ << capture_format.frame_size.ToString() << ", fps: "
+ << capture_format.frame_rate << ", pixel format: "
+ << capture_format.pixel_format;
+ }
+}
+
+//static
+ScopedJavaLocalRef<jobject>
+VideoCaptureDeviceFactoryAndroid::createVideoCaptureAndroid(
+ int id,
+ jlong nativeVideoCaptureDeviceAndroid) {
+ return (Java_VideoCaptureFactory_createVideoCapture(
+ AttachCurrentThread(),
+ base::android::GetApplicationContext(),
+ id,
+ nativeVideoCaptureDeviceAndroid));
+}
+
+// static
+bool VideoCaptureDeviceFactoryAndroid::RegisterVideoCaptureDeviceFactory(
+ JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace media
diff --git a/media/video/capture/android/video_capture_device_factory_android.h b/media/video/capture/android/video_capture_device_factory_android.h
new file mode 100644
index 0000000..b52d4f0
--- /dev/null
+++ b/media/video/capture/android/video_capture_device_factory_android.h
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_VIDEO_CAPTURE_ANDROID_VIDEO_CAPTURE_DEVICE_FACTORY_ANDROID_H_
+#define MEDIA_VIDEO_CAPTURE_ANDROID_VIDEO_CAPTURE_DEVICE_FACTORY_ANDROID_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "media/video/capture/video_capture_device.h"
+
+namespace media {
+
+// VideoCaptureDeviceFactory on Android. This class implements the static
+// VideoCapture methods and the factory of VideoCaptureAndroid.
+class MEDIA_EXPORT VideoCaptureDeviceFactoryAndroid {
+ public:
+ // Automatically generated enum to interface with Java world.
+ enum AndroidImageFormat {
+#define DEFINE_ANDROID_IMAGEFORMAT(name, value) name = value,
+#include "media/video/capture/android/imageformat_list.h"
+#undef DEFINE_ANDROID_IMAGEFORMAT
+ };
+
+ virtual ~VideoCaptureDeviceFactoryAndroid();
+
+ static void GetDeviceNames(VideoCaptureDevice::Names* device_names);
+ static void GetDeviceSupportedFormats(
+ const VideoCaptureDevice::Name& device,
+ VideoCaptureFormats* capture_formats);
+ static bool RegisterVideoCaptureDeviceFactory(JNIEnv* env);
+ static base::android::ScopedJavaLocalRef<jobject> createVideoCaptureAndroid(
+ int id,
+ jlong nativeVideoCaptureDeviceAndroid);
+};
+
+} // namespace media
+
+#endif // MEDIA_VIDEO_CAPTURE_ANDROID_VIDEO_CAPTURE_DEVICE_FACTORY_ANDROID_H_