Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / media / base / android / java / src / org / chromium / media / VideoCaptureAndroid.java
blob1fb2f7604429b2ad523c8378802c27eef1cd14a7
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;
16 /**
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.
21 **/
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) {
32 mModel = model;
33 mDevice = device;
34 mMinWidth = minWidth;
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 = {
48 "SAMSUNG-SGH-I747",
49 "ODROID-U2",
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) {
84 Camera camera;
85 try {
86 camera = Camera.open(id);
87 } catch (RuntimeException ex) {
88 Log.e(TAG, "Camera.open: " + ex);
89 return null;
91 Camera.Parameters parameters = getCameraParameters(camera);
92 if (parameters == null) {
93 return 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) {
109 int pixelFormat =
110 AndroidImageFormatList.ANDROID_IMAGEFORMAT_UNKNOWN;
111 if (previewFormat == ImageFormat.YV12) {
112 pixelFormat = AndroidImageFormatList.ANDROID_IMAGEFORMAT_YV12;
113 } else if (previewFormat == ImageFormat.NV21) {
114 continue;
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,
135 size.height,
136 (fpsRange[0] + 999) / 1000,
137 pixelFormat));
141 camera.release();
142 return formatList.toArray(new CaptureFormat[formatList.size()]);
145 VideoCaptureAndroid(Context context,
146 int id,
147 long nativeVideoCaptureDeviceAndroid) {
148 super(context, id, nativeVideoCaptureDeviceAndroid);
151 @Override
152 protected void setCaptureParameters(
153 int width,
154 int height,
155 int frameRate,
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);
164 @Override
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);
174 @Override
175 protected void setPreviewCallback(Camera.PreviewCallback cb) {
176 mCamera.setPreviewCallbackWithBuffer(cb);
179 @Override
180 public void onPreviewFrame(byte[] data, Camera camera) {
181 mPreviewBufferLock.lock();
182 try {
183 if (!mIsRunning) {
184 return;
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);
198 } finally {
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().