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
.hardware
.Camera
;
10 import android
.hardware
.Camera
.Size
;
11 import android
.util
.Log
;
13 import java
.util
.ArrayList
;
14 import java
.util
.List
;
17 * This class extends the VideoCapture base class for manipulating normal video
18 * capture devices in Android, including receiving copies of preview frames via
19 * Java-allocated buffers. It also includes class BuggyDeviceHack to deal with
20 * troublesome devices.
22 public class VideoCaptureAndroid
extends VideoCapture
{
24 // Some devices don't support YV12 format correctly, even with JELLY_BEAN or
25 // newer OS. To work around the issues on those devices, we have to request
26 // NV21. Some other devices have troubles with certain capture resolutions
27 // under a given one: for those, the resolution is swapped with a known
28 // good. Both are supposed to be temporary hacks.
29 private static class BuggyDeviceHack
{
30 private static class IdAndSizes
{
31 IdAndSizes(String model
, String device
, int minWidth
, int minHeight
) {
35 mMinHeight
= minHeight
;
37 public final String mModel
;
38 public final String mDevice
;
39 public final int mMinWidth
;
40 public final int mMinHeight
;
43 private static final IdAndSizes s_CAPTURESIZE_BUGGY_DEVICE_LIST
[] = {
44 new IdAndSizes("Nexus 7", "flo", 640, 480)
47 private static final String
[] s_COLORSPACE_BUGGY_DEVICE_LIST
= {
52 static void applyMinDimensions(CaptureFormat format
) {
53 // NOTE: this can discard requested aspect ratio considerations.
54 for (IdAndSizes buggyDevice
: s_CAPTURESIZE_BUGGY_DEVICE_LIST
) {
55 if (buggyDevice
.mModel
.contentEquals(android
.os
.Build
.MODEL
) &&
56 buggyDevice
.mDevice
.contentEquals(android
.os
.Build
.DEVICE
)) {
57 format
.mWidth
= (buggyDevice
.mMinWidth
> format
.mWidth
)
58 ? buggyDevice
.mMinWidth
: format
.mWidth
;
59 format
.mHeight
= (buggyDevice
.mMinHeight
> format
.mHeight
)
60 ? buggyDevice
.mMinHeight
: format
.mHeight
;
65 static int getImageFormat() {
66 if (android
.os
.Build
.VERSION
.SDK_INT
< android
.os
.Build
.VERSION_CODES
.JELLY_BEAN
) {
67 return ImageFormat
.NV21
;
70 for (String buggyDevice
: s_COLORSPACE_BUGGY_DEVICE_LIST
) {
71 if (buggyDevice
.contentEquals(android
.os
.Build
.MODEL
)) {
72 return ImageFormat
.NV21
;
75 return ImageFormat
.YV12
;
79 private int mExpectedFrameSize
;
80 private static final int NUM_CAPTURE_BUFFERS
= 3;
81 private static final String TAG
= "VideoCaptureAndroid";
83 static CaptureFormat
[] getDeviceSupportedFormats(int id
) {
86 camera
= Camera
.open(id
);
87 } catch (RuntimeException ex
) {
88 Log
.e(TAG
, "Camera.open: " + ex
);
91 Camera
.Parameters parameters
= getCameraParameters(camera
);
92 if (parameters
== null) {
96 ArrayList
<CaptureFormat
> formatList
= new ArrayList
<CaptureFormat
>();
97 // getSupportedPreview{Formats,FpsRange,PreviewSizes}() returns Lists
98 // with at least one element, but when the camera is in bad state, they
99 // can return null pointers; in that case we use a 0 entry, so we can
100 // retrieve as much information as possible.
101 List
<Integer
> pixelFormats
= parameters
.getSupportedPreviewFormats();
102 if (pixelFormats
== null) {
103 pixelFormats
= new ArrayList
<Integer
>();
105 if (pixelFormats
.size() == 0) {
106 pixelFormats
.add(ImageFormat
.UNKNOWN
);
108 for (Integer previewFormat
: pixelFormats
) {
110 AndroidImageFormatList
.ANDROID_IMAGEFORMAT_UNKNOWN
;
111 if (previewFormat
== ImageFormat
.YV12
) {
112 pixelFormat
= AndroidImageFormatList
.ANDROID_IMAGEFORMAT_YV12
;
113 } else if (previewFormat
== ImageFormat
.NV21
) {
117 List
<int[]> listFpsRange
= parameters
.getSupportedPreviewFpsRange();
118 if (listFpsRange
== null) {
119 listFpsRange
= new ArrayList
<int[]>();
121 if (listFpsRange
.size() == 0) {
122 listFpsRange
.add(new int[] {0, 0});
124 for (int[] fpsRange
: listFpsRange
) {
125 List
<Camera
.Size
> supportedSizes
=
126 parameters
.getSupportedPreviewSizes();
127 if (supportedSizes
== null) {
128 supportedSizes
= new ArrayList
<Camera
.Size
>();
130 if (supportedSizes
.size() == 0) {
131 supportedSizes
.add(camera
.new Size(0, 0));
133 for (Camera
.Size size
: supportedSizes
) {
134 formatList
.add(new CaptureFormat(size
.width
,
136 (fpsRange
[0] + 999) / 1000,
142 return formatList
.toArray(new CaptureFormat
[formatList
.size()]);
145 VideoCaptureAndroid(Context context
,
147 long nativeVideoCaptureDeviceAndroid
) {
148 super(context
, id
, nativeVideoCaptureDeviceAndroid
);
152 protected void setCaptureParameters(
156 Camera
.Parameters cameraParameters
) {
157 mCaptureFormat
= new CaptureFormat(
158 width
, height
, frameRate
, BuggyDeviceHack
.getImageFormat());
159 // Hack to avoid certain capture resolutions under a minimum one,
160 // see http://crbug.com/305294.
161 BuggyDeviceHack
.applyMinDimensions(mCaptureFormat
);
165 protected void allocateBuffers() {
166 mExpectedFrameSize
= mCaptureFormat
.mWidth
* mCaptureFormat
.mHeight
*
167 ImageFormat
.getBitsPerPixel(mCaptureFormat
.mPixelFormat
) / 8;
168 for (int i
= 0; i
< NUM_CAPTURE_BUFFERS
; i
++) {
169 byte[] buffer
= new byte[mExpectedFrameSize
];
170 mCamera
.addCallbackBuffer(buffer
);
175 protected void setPreviewCallback(Camera
.PreviewCallback cb
) {
176 mCamera
.setPreviewCallbackWithBuffer(cb
);
180 public void onPreviewFrame(byte[] data
, Camera camera
) {
181 mPreviewBufferLock
.lock();
186 if (data
.length
== mExpectedFrameSize
) {
187 int rotation
= getDeviceOrientation();
188 if (rotation
!= mDeviceOrientation
) {
189 mDeviceOrientation
= rotation
;
191 if (mCameraFacing
== Camera
.CameraInfo
.CAMERA_FACING_BACK
) {
192 rotation
= 360 - rotation
;
194 rotation
= (mCameraOrientation
+ rotation
) % 360;
195 nativeOnFrameAvailable(mNativeVideoCaptureDeviceAndroid
,
196 data
, mExpectedFrameSize
, rotation
);
199 mPreviewBufferLock
.unlock();
200 if (camera
!= null) {
201 camera
.addCallbackBuffer(data
);
206 // TODO(wjia): investigate whether reading from texture could give better
207 // performance and frame rate, using onFrameAvailable().