1e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch// Copyright 2014 The Chromium Authors. All rights reserved.
2e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch// found in the LICENSE file.
4e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
5e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochpackage org.chromium.media;
6e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
7e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochimport android.content.Context;
8e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochimport android.graphics.ImageFormat;
9e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochimport android.util.Log;
10e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
11e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochimport java.util.ArrayList;
12e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochimport java.util.List;
13e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
14e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch/**
15e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch * This class extends the VideoCapture base class for manipulating normal video
16e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch * capture devices in Android, including receiving copies of preview frames via
17e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch * Java-allocated buffers. It also includes class BuggyDeviceHack to deal with
18e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch * troublesome devices.
19e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch **/
201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci@SuppressWarnings("deprecation")
21e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochpublic class VideoCaptureAndroid extends VideoCapture {
22e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
23e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    // Some devices don't support YV12 format correctly, even with JELLY_BEAN or
24e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    // newer OS. To work around the issues on those devices, we have to request
25e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    // NV21. Some other devices have troubles with certain capture resolutions
26e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    // under a given one: for those, the resolution is swapped with a known
27e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    // good. Both are supposed to be temporary hacks.
28e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    private static class BuggyDeviceHack {
29e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        private static class IdAndSizes {
30e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            IdAndSizes(String model, String device, int minWidth, int minHeight) {
31e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                mModel = model;
32e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                mDevice = device;
33e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                mMinWidth = minWidth;
34e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                mMinHeight = minHeight;
35e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            }
36e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            public final String mModel;
37e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            public final String mDevice;
38e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            public final int mMinWidth;
39e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            public final int mMinHeight;
40e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        }
41e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        private static final IdAndSizes CAPTURESIZE_BUGGY_DEVICE_LIST[] = {
43e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            new IdAndSizes("Nexus 7", "flo", 640, 480)
44e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        };
45e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        private static final String[] COLORSPACE_BUGGY_DEVICE_LIST = {
47e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            "SAMSUNG-SGH-I747",
48e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            "ODROID-U2",
49e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        };
50e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
51e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        static void applyMinDimensions(CaptureFormat format) {
52e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            // NOTE: this can discard requested aspect ratio considerations.
536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            for (IdAndSizes buggyDevice : CAPTURESIZE_BUGGY_DEVICE_LIST) {
54e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                if (buggyDevice.mModel.contentEquals(android.os.Build.MODEL) &&
55e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                        buggyDevice.mDevice.contentEquals(android.os.Build.DEVICE)) {
56e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                    format.mWidth = (buggyDevice.mMinWidth > format.mWidth)
57e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                            ? buggyDevice.mMinWidth : format.mWidth;
58e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                    format.mHeight = (buggyDevice.mMinHeight > format.mHeight)
59e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                            ? buggyDevice.mMinHeight : format.mHeight;
60e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                }
61e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            }
62e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        }
63e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
64e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        static int getImageFormat() {
65e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {
66e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                return ImageFormat.NV21;
67e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            }
68e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            for (String buggyDevice : COLORSPACE_BUGGY_DEVICE_LIST) {
70e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                if (buggyDevice.contentEquals(android.os.Build.MODEL)) {
71e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                    return ImageFormat.NV21;
72e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                }
73e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            }
74e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            return ImageFormat.YV12;
75e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        }
76e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    }
77e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
78e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    private int mExpectedFrameSize;
79e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    private static final int NUM_CAPTURE_BUFFERS = 3;
80e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    private static final String TAG = "VideoCaptureAndroid";
81e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
82e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    static CaptureFormat[] getDeviceSupportedFormats(int id) {
831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        android.hardware.Camera camera;
84e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        try {
851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci             camera = android.hardware.Camera.open(id);
86e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        } catch (RuntimeException ex) {
87e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            Log.e(TAG, "Camera.open: " + ex);
88e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            return null;
89e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        }
901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        android.hardware.Camera.Parameters parameters = getCameraParameters(camera);
91e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        if (parameters == null) {
92e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            return null;
93e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        }
94e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
95e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        ArrayList<CaptureFormat> formatList = new ArrayList<CaptureFormat>();
96e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        // getSupportedPreview{Formats,FpsRange,PreviewSizes}() returns Lists
97e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        // with at least one element, but when the camera is in bad state, they
98e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        // can return null pointers; in that case we use a 0 entry, so we can
99e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        // retrieve as much information as possible.
100e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        List<Integer> pixelFormats = parameters.getSupportedPreviewFormats();
101e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        if (pixelFormats == null) {
102e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            pixelFormats = new ArrayList<Integer>();
103e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        }
104e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        if (pixelFormats.size() == 0) {
105e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            pixelFormats.add(ImageFormat.UNKNOWN);
106e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        }
107e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        for (Integer previewFormat : pixelFormats) {
108e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            int pixelFormat =
109e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                    AndroidImageFormatList.ANDROID_IMAGEFORMAT_UNKNOWN;
110e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            if (previewFormat == ImageFormat.YV12) {
111e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                pixelFormat = AndroidImageFormatList.ANDROID_IMAGEFORMAT_YV12;
112e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            } else if (previewFormat == ImageFormat.NV21) {
113e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                continue;
114e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            }
115e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
116e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange();
117e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            if (listFpsRange == null) {
118e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                listFpsRange = new ArrayList<int[]>();
119e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            }
120e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            if (listFpsRange.size() == 0) {
121e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                listFpsRange.add(new int[] {0, 0});
122e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            }
123e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            for (int[] fpsRange : listFpsRange) {
1241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                List<android.hardware.Camera.Size> supportedSizes =
125e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                        parameters.getSupportedPreviewSizes();
126e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                if (supportedSizes == null) {
1271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                    supportedSizes = new ArrayList<android.hardware.Camera.Size>();
128e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                }
129e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                if (supportedSizes.size() == 0) {
130e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                    supportedSizes.add(camera.new Size(0, 0));
131e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                }
1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                for (android.hardware.Camera.Size size : supportedSizes) {
133e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                    formatList.add(new CaptureFormat(size.width,
134e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                                                     size.height,
135116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                                     (fpsRange[1] + 999) / 1000,
136e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                                                     pixelFormat));
137e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                }
138e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            }
139e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        }
140e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        camera.release();
141e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        return formatList.toArray(new CaptureFormat[formatList.size()]);
142e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    }
143e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
144e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    VideoCaptureAndroid(Context context,
145e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                        int id,
146e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                        long nativeVideoCaptureDeviceAndroid) {
147e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        super(context, id, nativeVideoCaptureDeviceAndroid);
148e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    }
149e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
150e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    @Override
151e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    protected void setCaptureParameters(
152e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            int width,
153e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            int height,
154e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            int frameRate,
1551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            android.hardware.Camera.Parameters cameraParameters) {
156e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        mCaptureFormat = new CaptureFormat(
157e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                width, height, frameRate, BuggyDeviceHack.getImageFormat());
158e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        // Hack to avoid certain capture resolutions under a minimum one,
159e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        // see http://crbug.com/305294.
160e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        BuggyDeviceHack.applyMinDimensions(mCaptureFormat);
161e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    }
162e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
163e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    @Override
164e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    protected void allocateBuffers() {
165e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        mExpectedFrameSize = mCaptureFormat.mWidth * mCaptureFormat.mHeight *
166e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                ImageFormat.getBitsPerPixel(mCaptureFormat.mPixelFormat) / 8;
167e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        for (int i = 0; i < NUM_CAPTURE_BUFFERS; i++) {
168e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            byte[] buffer = new byte[mExpectedFrameSize];
169e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            mCamera.addCallbackBuffer(buffer);
170e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        }
171e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    }
172e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
173e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    @Override
1741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    protected void setPreviewCallback(android.hardware.Camera.PreviewCallback cb) {
175e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        mCamera.setPreviewCallbackWithBuffer(cb);
176e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    }
177e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
178e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    @Override
1791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    public void onPreviewFrame(byte[] data, android.hardware.Camera camera) {
180e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        mPreviewBufferLock.lock();
181e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        try {
182e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            if (!mIsRunning) {
183e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                return;
184e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            }
185e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            if (data.length == mExpectedFrameSize) {
186e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                int rotation = getDeviceOrientation();
187e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                if (rotation != mDeviceOrientation) {
188e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                    mDeviceOrientation = rotation;
189e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                }
1901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                if (mCameraFacing == android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK) {
191e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                    rotation = 360 - rotation;
192e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                }
193e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                rotation = (mCameraOrientation + rotation) % 360;
194e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                nativeOnFrameAvailable(mNativeVideoCaptureDeviceAndroid,
195e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                        data, mExpectedFrameSize, rotation);
196e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            }
197e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        } finally {
198e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            mPreviewBufferLock.unlock();
199e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            if (camera != null) {
200e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch                camera.addCallbackBuffer(data);
201e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch            }
202e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        }
203e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    }
204e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
205e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    // TODO(wjia): investigate whether reading from texture could give better
206e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    // performance and frame rate, using onFrameAvailable().
207e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch}
208