1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.chromium.media;
7 import android.content.Context;
8 import android.graphics.ImageFormat;
9 import android.util.Log;
11 import java.util.ArrayList;
12 import java.util.List;
15 * This class extends the VideoCaptureCamera base class for manipulating normal
16 * video capture devices in Android, including receiving copies of preview
17 * frames via Java-allocated buffers. It also includes class BuggyDeviceHack to
18 * deal with troublesome devices.
20 @SuppressWarnings("deprecation")
21 public class VideoCaptureAndroid extends VideoCaptureCamera {
23 // Some devices don't support YV12 format correctly, even with JELLY_BEAN or
24 // newer OS. To work around the issues on those devices, we have to request
25 // NV21. This is supposed to be a temporary hack.
26 private static class BuggyDeviceHack {
27 private static final String[] COLORSPACE_BUGGY_DEVICE_LIST = {
32 static int getImageFormat() {
33 if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {
34 return ImageFormat.NV21;
37 for (String buggyDevice : COLORSPACE_BUGGY_DEVICE_LIST) {
38 if (buggyDevice.contentEquals(android.os.Build.MODEL)) {
39 return ImageFormat.NV21;
42 return ImageFormat.YV12;
46 private int mExpectedFrameSize;
47 private static final int NUM_CAPTURE_BUFFERS = 3;
48 private static final String TAG = "VideoCaptureAndroid";
50 static int getNumberOfCameras() {
51 return android.hardware.Camera.getNumberOfCameras();
54 static String getName(int id) {
55 android.hardware.Camera.CameraInfo cameraInfo = VideoCaptureCamera.getCameraInfo(id);
56 if (cameraInfo == null) return null;
57 return "camera " + id + ", facing " + (cameraInfo.facing
58 == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT ? "front" : "back");
61 static CaptureFormat[] getDeviceSupportedFormats(int id) {
62 android.hardware.Camera camera;
64 camera = android.hardware.Camera.open(id);
65 } catch (RuntimeException ex) {
66 Log.e(TAG, "Camera.open: " + ex);
69 android.hardware.Camera.Parameters parameters = getCameraParameters(camera);
70 if (parameters == null) {
74 ArrayList<CaptureFormat> formatList = new ArrayList<CaptureFormat>();
75 // getSupportedPreview{Formats,FpsRange,PreviewSizes}() returns Lists
76 // with at least one element, but when the camera is in bad state, they
77 // can return null pointers; in that case we use a 0 entry, so we can
78 // retrieve as much information as possible.
79 List<Integer> pixelFormats = parameters.getSupportedPreviewFormats();
80 if (pixelFormats == null) {
81 pixelFormats = new ArrayList<Integer>();
83 if (pixelFormats.size() == 0) {
84 pixelFormats.add(ImageFormat.UNKNOWN);
86 for (Integer previewFormat : pixelFormats) {
87 int pixelFormat = AndroidImageFormat.UNKNOWN;
88 if (previewFormat == ImageFormat.YV12) {
89 pixelFormat = AndroidImageFormat.YV12;
90 } else if (previewFormat == ImageFormat.NV21) {
94 List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange();
95 if (listFpsRange == null) {
96 listFpsRange = new ArrayList<int[]>();
98 if (listFpsRange.size() == 0) {
99 listFpsRange.add(new int[] {0, 0});
101 for (int[] fpsRange : listFpsRange) {
102 List<android.hardware.Camera.Size> supportedSizes =
103 parameters.getSupportedPreviewSizes();
104 if (supportedSizes == null) {
105 supportedSizes = new ArrayList<android.hardware.Camera.Size>();
107 if (supportedSizes.size() == 0) {
108 supportedSizes.add(camera.new Size(0, 0));
110 for (android.hardware.Camera.Size size : supportedSizes) {
111 formatList.add(new CaptureFormat(size.width,
113 (fpsRange[1] + 999) / 1000,
119 return formatList.toArray(new CaptureFormat[formatList.size()]);
122 VideoCaptureAndroid(Context context,
124 long nativeVideoCaptureDeviceAndroid) {
125 super(context, id, nativeVideoCaptureDeviceAndroid);
129 protected void setCaptureParameters(
133 android.hardware.Camera.Parameters cameraParameters) {
134 mCaptureFormat = new CaptureFormat(
135 width, height, frameRate, BuggyDeviceHack.getImageFormat());
139 protected void allocateBuffers() {
140 mExpectedFrameSize = mCaptureFormat.mWidth * mCaptureFormat.mHeight
141 * ImageFormat.getBitsPerPixel(mCaptureFormat.mPixelFormat) / 8;
142 for (int i = 0; i < NUM_CAPTURE_BUFFERS; i++) {
143 byte[] buffer = new byte[mExpectedFrameSize];
144 mCamera.addCallbackBuffer(buffer);
149 protected void setPreviewCallback(android.hardware.Camera.PreviewCallback cb) {
150 mCamera.setPreviewCallbackWithBuffer(cb);
154 public void onPreviewFrame(byte[] data, android.hardware.Camera camera) {
155 mPreviewBufferLock.lock();
160 if (data.length == mExpectedFrameSize) {
161 int rotation = getDeviceOrientation();
162 if (rotation != mDeviceOrientation) {
163 mDeviceOrientation = rotation;
165 if (mCameraFacing == android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK) {
166 rotation = 360 - rotation;
168 rotation = (mCameraOrientation + rotation) % 360;
169 nativeOnFrameAvailable(mNativeVideoCaptureDeviceAndroid,
170 data, mExpectedFrameSize, rotation);
173 mPreviewBufferLock.unlock();
174 if (camera != null) {
175 camera.addCallbackBuffer(data);
180 // TODO(wjia): investigate whether reading from texture could give better
181 // performance and frame rate, using onFrameAvailable().