Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / media / base / android / java / src / org / chromium / media / VideoCapture.java
index 8182c30..06eae1d 100644 (file)
@@ -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.
 
@@ -6,32 +6,24 @@ package org.chromium.media;
 
 import android.content.Context;
 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;
-import android.view.WindowManager;
 
 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, defines a set of methods that native code
+ * needs to use to configure, start capture, and to be reached by callbacks and
+ * provides some neccesary data type(s) with accessors.
  **/
 @JNINamespace("media")
-public class VideoCapture implements PreviewCallback {
-    static class CaptureFormat {
+public abstract class VideoCapture {
+
+    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,540 +31,86 @@ 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;
-    // True when native code has started capture.
-    private boolean mIsRunning = false;
-
-    private static final int NUM_CAPTURE_BUFFERS = 3;
-    private int mExpectedFrameSize = 0;
-    private int mId = 0;
+    protected CaptureFormat mCaptureFormat = null;
+    protected final Context mContext;
+    protected final 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;
-    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;
-        }
+    protected final long mNativeVideoCaptureDeviceAndroid;
 
-        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) {
-        Log.d(TAG, "allocate: requested (" + width + "x" + height + ")@" +
-                 frameRate + "fps");
-        try {
-            mCamera = Camera.open(mId);
-        } catch (RuntimeException ex) {
-            Log.e(TAG, "allocate: Camera.open: " + ex);
-            return false;
-        }
+    abstract boolean allocate(int width, int height, int frameRate);
 
-        Camera.CameraInfo cameraInfo = getCameraInfo(mId);
-        if (cameraInfo == null) {
-            mCamera.release();
-            mCamera = null;
-            return false;
-        }
-
-        mCameraOrientation = cameraInfo.orientation;
-        mCameraFacing = cameraInfo.facing;
-        mDeviceOrientation = getDeviceOrientation();
-        Log.d(TAG, "allocate: orientation dev=" + mDeviceOrientation +
-                  ", cam=" + mCameraOrientation + ", facing=" + mCameraFacing);
-
-        Camera.Parameters parameters = getCameraParameters(mCamera);
-        if (parameters == null) {
-            mCamera = null;
-            return false;
-        }
-
-        // getSupportedPreviewFpsRange() returns a List with at least one
-        // element, but when camera is in bad state, it can return null pointer.
-        List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange();
-        if (listFpsRange == null || listFpsRange.size() == 0) {
-            Log.e(TAG, "allocate: no fps range found");
-            return false;
-        }
-        int frameRateInMs = frameRate * 1000;
-        // Use the first range as default.
-        int[] fpsMinMax = listFpsRange.get(0);
-        int newFrameRate = (fpsMinMax[0] + 999) / 1000;
-        for (int[] fpsRange : listFpsRange) {
-            if (fpsRange[0] <= frameRateInMs && frameRateInMs <= fpsRange[1]) {
-                fpsMinMax = fpsRange;
-                newFrameRate = frameRate;
-                break;
-            }
-        }
-        frameRate = newFrameRate;
-        Log.d(TAG, "allocate: fps set to " + frameRate);
-
-        // Calculate size.
-        List<Camera.Size> listCameraSize =
-                parameters.getSupportedPreviewSizes();
-        int minDiff = Integer.MAX_VALUE;
-        int matchedWidth = width;
-        int matchedHeight = height;
-        for (Camera.Size size : listCameraSize) {
-            int diff = Math.abs(size.width - width) +
-                       Math.abs(size.height - height);
-            Log.d(TAG, "allocate: supported (" +
-                  size.width + ", " + size.height + "), diff=" + diff);
-            // TODO(wjia): Remove this hack (forcing width to be multiple
-            // of 32) by supporting stride in video frame buffer.
-            // Right now, VideoCaptureController requires compact YV12
-            // (i.e., with no padding).
-            if (diff < minDiff && (size.width % 32 == 0)) {
-                minDiff = diff;
-                matchedWidth = size.width;
-                matchedHeight = size.height;
-            }
-        }
-        if (minDiff == Integer.MAX_VALUE) {
-            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 + ")");
-
-        if (parameters.isVideoStabilizationSupported()) {
-            Log.d(TAG, "Image stabilization supported, currently: "
-                  + parameters.getVideoStabilization() + ", setting it.");
-            parameters.setVideoStabilization(true);
-        } else {
-            Log.d(TAG, "Image stabilization not supported.");
-        }
-        parameters.setPreviewSize(mCaptureFormat.mWidth,
-                                  mCaptureFormat.mHeight);
-        parameters.setPreviewFormat(mCaptureFormat.mPixelFormat);
-        parameters.setPreviewFpsRange(fpsMinMax[0], fpsMinMax[1]);
-        mCamera.setParameters(parameters);
-
-        // Set SurfaceTexture. Android Capture needs a SurfaceTexture even if
-        // it is not going to be used.
-        mGlTextures = new int[1];
-        // Generate one texture pointer and bind it as an external texture.
-        GLES20.glGenTextures(1, mGlTextures, 0);
-        GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mGlTextures[0]);
-        // No mip-mapping with camera source.
-        GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES,
-                GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
-        GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES,
-                GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
-        // Clamp to edge is only option.
-        GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES,
-                GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
-        GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES,
-                GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
-
-        mSurfaceTexture = new SurfaceTexture(mGlTextures[0]);
-        mSurfaceTexture.setOnFrameAvailableListener(null);
+    @CalledByNative
+    abstract int startCapture();
 
-        try {
-            mCamera.setPreviewTexture(mSurfaceTexture);
-        } catch (IOException ex) {
-            Log.e(TAG, "allocate: " + ex);
-            return false;
-        }
+    @CalledByNative
+    abstract int stopCapture();
 
-        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;
+    @CalledByNative
+    abstract void deallocate();
 
-        return true;
-    }
+    // Local hook to allow derived classes to configure and plug capture
+    // buffers if needed.
+    abstract void allocateBuffers();
 
     @CalledByNative
-    public int queryWidth() {
+    public final int queryWidth() {
         return mCaptureFormat.mWidth;
     }
 
     @CalledByNative
-    public int queryHeight() {
+    public final int queryHeight() {
         return mCaptureFormat.mHeight;
     }
 
     @CalledByNative
-    public int queryFrameRate() {
+    public final int queryFrameRate() {
         return mCaptureFormat.mFramerate;
     }
 
     @CalledByNative
-    public int getColorspace() {
+    public final int getColorspace() {
         switch (mCaptureFormat.mPixelFormat) {
             case ImageFormat.YV12:
-                return AndroidImageFormatList.ANDROID_IMAGEFORMAT_YV12;
+                return AndroidImageFormat.YV12;
             case ImageFormat.NV21:
-                return AndroidImageFormatList.ANDROID_IMAGEFORMAT_NV21;
+                return AndroidImageFormat.NV21;
             case ImageFormat.UNKNOWN:
             default:
-                return AndroidImageFormatList.ANDROID_IMAGEFORMAT_UNKNOWN;
-        }
-    }
-
-    @CalledByNative
-    public int startCapture() {
-        if (mCamera == null) {
-            Log.e(TAG, "startCapture: camera is null");
-            return -1;
-        }
-
-        mPreviewBufferLock.lock();
-        try {
-            if (mIsRunning) {
-                return 0;
-            }
-            mIsRunning = true;
-        } finally {
-            mPreviewBufferLock.unlock();
-        }
-        mCamera.setPreviewCallbackWithBuffer(this);
-        try {
-            mCamera.startPreview();
-        } catch (RuntimeException ex) {
-            Log.e(TAG, "startCapture: Camera.startPreview: " + ex);
-            return -1;
-        }
-
-        return 0;
-    }
-
-    @CalledByNative
-    public int stopCapture() {
-        if (mCamera == null) {
-            Log.e(TAG, "stopCapture: camera is null");
-            return 0;
-        }
-
-        mPreviewBufferLock.lock();
-        try {
-            if (!mIsRunning) {
-                return 0;
-            }
-            mIsRunning = false;
-        } finally {
-            mPreviewBufferLock.unlock();
-        }
-
-        mCamera.stopPreview();
-        mCamera.setPreviewCallbackWithBuffer(null);
-        return 0;
-    }
-
-    @CalledByNative
-    public void deallocate() {
-        if (mCamera == null)
-            return;
-
-        stopCapture();
-        try {
-            mCamera.setPreviewTexture(null);
-            if (mGlTextures != null)
-                GLES20.glDeleteTextures(1, mGlTextures, 0);
-            mCaptureFormat = null;
-            mCamera.release();
-            mCamera = null;
-        } catch (IOException ex) {
-            Log.e(TAG, "deallocate: failed to deallocate camera, " + ex);
-            return;
-        }
-    }
-
-    @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);
-            }
+                return AndroidImageFormat.UNKNOWN;
         }
     }
 
-    // TODO(wjia): investigate whether reading from texture could give better
-    // performance and frame rate, using onFrameAvailable().
-
-    private static class ChromiumCameraInfo {
-        private final int mId;
-        private final Camera.CameraInfo mCameraInfo;
-
-        private ChromiumCameraInfo(int index) {
-            mId = index;
-            mCameraInfo = getCameraInfo(mId);
-        }
-
-        @CalledByNative("ChromiumCameraInfo")
-        private static int getNumberOfCameras() {
-            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 (mCameraInfo == null) {
-                return "";
-            }
-            return  "camera " + mId + ", facing " +
-                    (mCameraInfo.facing ==
-                     Camera.CameraInfo.CAMERA_FACING_FRONT ? "front" : "back");
-        }
-
-        @CalledByNative("ChromiumCameraInfo")
-        private int getOrientation() {
-            return (mCameraInfo == null ? 0 : mCameraInfo.orientation);
-        }
-    }
-
-    private native void nativeOnFrameAvailable(
+    // Method for VideoCapture implementations to call back native code.
+    public native void nativeOnFrameAvailable(
             long nativeVideoCaptureDeviceAndroid,
             byte[] data,
             int length,
             int rotation);
-
-    private int getDeviceOrientation() {
-        int orientation = 0;
-        if (mContext != null) {
-            WindowManager wm = (WindowManager) mContext.getSystemService(
-                    Context.WINDOW_SERVICE);
-            switch(wm.getDefaultDisplay().getRotation()) {
-                case Surface.ROTATION_90:
-                    orientation = 90;
-                    break;
-                case Surface.ROTATION_180:
-                    orientation = 180;
-                    break;
-                case Surface.ROTATION_270:
-                    orientation = 270;
-                    break;
-                case Surface.ROTATION_0:
-                default:
-                    orientation = 0;
-                    break;
-            }
-        }
-        return orientation;
-    }
-
-    private static Camera.Parameters getCameraParameters(Camera camera) {
-        Camera.Parameters parameters;
-        try {
-            parameters = camera.getParameters();
-        } catch (RuntimeException ex) {
-            Log.e(TAG, "getCameraParameters: Camera.getParameters: " + ex);
-            camera.release();
-            return null;
-        }
-        return parameters;
-    }
-
-    private static 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;
-    }
 }