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; 8010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)import android.content.pm.PackageManager; 9e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochimport android.util.Log; 10e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 11e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochimport org.chromium.base.CalledByNative; 12e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochimport org.chromium.base.JNINamespace; 13e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch// Needed for jni_generator.py to guess correctly the origin of 14e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch// VideoCapture.CaptureFormat. 15e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochimport org.chromium.media.VideoCapture; 16e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 17e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch/** 18e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch * This class implements a factory of Android Video Capture objects for Chrome. 19e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch * The static createVideoCapture() returns either a "normal" VideoCaptureAndroid 20e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch * or a "special" VideoCaptureTango. Cameras are identified by |id|, where Tango 21e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch * cameras have |id| above the standard ones. Video Capture objects allocated 22e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch * via createVideoCapture() are explicitly owned by the caller. 23e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch * ChromiumCameraInfo is an internal class with some static methods needed from 24e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch * the native side to enumerate devices and collect their names and info. It 25e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch * takes into account the mentioned special devices. 26e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch **/ 27e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch@JNINamespace("media") 281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci@SuppressWarnings("deprecation") 29e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochclass VideoCaptureFactory { 30e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 31010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) static class CamParams { 32e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch final int mId; 33e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch final String mName; 34e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch final int mWidth; 35e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch final int mHeight; 36e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 37e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch CamParams(int id, String name, int width, int height) { 38e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch mId = id; 39e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch mName = name; 40e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch mWidth = width; 41e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch mHeight = height; 42e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 43e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 44e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 45010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) static class ChromiumCameraInfo { 46e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch private final int mId; 471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci private final android.hardware.Camera.CameraInfo mCameraInfo; 48e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch // Special devices have more cameras than usual. Those devices are 49e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch // identified by model & device. Currently only the Tango is supported. 50e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch // Note that these devices have no Camera.CameraInfo. 516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private static final String[][] SPECIAL_DEVICE_LIST = { 52e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch {"Peanut", "peanut"}, 53e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch }; 54e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch private static final String TAG = "ChromiumCameraInfo"; 55e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 56010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) private static int sNumberOfSystemCameras = -1; 57010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 58e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch private static boolean isSpecialDevice() { 596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) for (String[] device : SPECIAL_DEVICE_LIST) { 60e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch if (device[0].contentEquals(android.os.Build.MODEL) && 61e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch device[1].contentEquals(android.os.Build.DEVICE)) { 62e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch return true; 63e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 64e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 65e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch return false; 66e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 67e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 68010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) private static boolean isSpecialCamera(int id) { 69010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) return id >= sNumberOfSystemCameras; 70010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) } 71010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 72010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) private static int toSpecialCameraId(int id) { 73010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) assert isSpecialCamera(id); 74010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) return id - sNumberOfSystemCameras; 75010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) } 76010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 77e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch private ChromiumCameraInfo(int index) { 78e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch mId = index; 79e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch mCameraInfo = isSpecialCamera(index) ? null : getCameraInfo(mId); 80e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 81e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 82e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch @CalledByNative("ChromiumCameraInfo") 83010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) private static int getNumberOfCameras(Context appContext) { 84010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) // Camera.getNumberOfCammeras() will not fail without permission, but the 85010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) // following operation on camera will do. Without permission isn't fatal 86010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) // error in WebView, specially for those application which has no purpose 87010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) // to use camera, but happens to load page required it. 88010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) // So, we output a warning log and pretend system have no camera at all. 89010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) if (sNumberOfSystemCameras == -1) { 90010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) if (PackageManager.PERMISSION_GRANTED == 91010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) appContext.getPackageManager().checkPermission( 92010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) "android.permission.CAMERA", appContext.getPackageName())) { 931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci sNumberOfSystemCameras = android.hardware.Camera.getNumberOfCameras(); 94010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) } else { 95010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) sNumberOfSystemCameras = 0; 96010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) Log.w(TAG, "Missing android.permission.CAMERA permission, " 97010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) + "no system camera available."); 98010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) } 99010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) } 100e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch if (isSpecialDevice()) { 101e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch Log.d(TAG, "Special device: " + android.os.Build.MODEL); 102010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) return sNumberOfSystemCameras + 103e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch VideoCaptureTango.numberOfCameras(); 104e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } else { 105010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) return sNumberOfSystemCameras; 106e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 107e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 108e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 109e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch @CalledByNative("ChromiumCameraInfo") 110e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch private static ChromiumCameraInfo getAt(int index) { 111e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch return new ChromiumCameraInfo(index); 112e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 113e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 114e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch @CalledByNative("ChromiumCameraInfo") 115e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch private int getId() { 116e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch return mId; 117e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 118e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 119e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch @CalledByNative("ChromiumCameraInfo") 120e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch private String getDeviceName() { 121e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch if (isSpecialCamera(mId)) { 122010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) return VideoCaptureTango.getCamParams(toSpecialCameraId(mId)).mName; 123e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } else { 124e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch if (mCameraInfo == null) { 125e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch return ""; 126e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 127e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch Log.d(TAG, "Camera enumerated: " + (mCameraInfo.facing == 1281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT ? "front" : 129e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch "back")); 130e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch return "camera " + mId + ", facing " + (mCameraInfo.facing == 1311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT ? "front" : 132e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch "back"); 133e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 134e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 135e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 136e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch @CalledByNative("ChromiumCameraInfo") 137e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch private int getOrientation() { 138e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch if (isSpecialCamera(mId)) { 1391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK; 140e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } else { 141e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch return (mCameraInfo == null ? 0 : mCameraInfo.orientation); 142e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 143e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 144e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 1451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci private android.hardware.Camera.CameraInfo getCameraInfo(int id) { 1461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci android.hardware.Camera.CameraInfo cameraInfo = 1471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci new android.hardware.Camera.CameraInfo(); 148e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch try { 1491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci android.hardware.Camera.getCameraInfo(id, cameraInfo); 150e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } catch (RuntimeException ex) { 1511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci Log.e(TAG, "getCameraInfo: android.hardware.Camera.getCameraInfo: " + ex); 152e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch return null; 153e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 154e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch return cameraInfo; 155e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 156e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 157e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 158e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch // Factory methods. 159e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch @CalledByNative 160e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch static VideoCapture createVideoCapture( 161e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch Context context, int id, long nativeVideoCaptureDeviceAndroid) { 162010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) if (ChromiumCameraInfo.isSpecialCamera(id)) { 163010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) return new VideoCaptureTango(context, ChromiumCameraInfo.toSpecialCameraId(id), 164e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch nativeVideoCaptureDeviceAndroid); 165e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } else { 166e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch return new VideoCaptureAndroid(context, id, 167e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch nativeVideoCaptureDeviceAndroid); 168e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 169e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 170e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 171e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch @CalledByNative 172e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch static VideoCapture.CaptureFormat[] getDeviceSupportedFormats(int id) { 173010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) return ChromiumCameraInfo.isSpecialCamera(id) ? 174e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch VideoCaptureTango.getDeviceSupportedFormats( 175010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) ChromiumCameraInfo.toSpecialCameraId(id)) : 176e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch VideoCaptureAndroid.getDeviceSupportedFormats(id); 177e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 178e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 179e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch @CalledByNative 180e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch static int getCaptureFormatWidth(VideoCapture.CaptureFormat format) { 181e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch return format.getWidth(); 182e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 183e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 184e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch @CalledByNative 185e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch static int getCaptureFormatHeight(VideoCapture.CaptureFormat format) { 186e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch return format.getHeight(); 187e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 188e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 189e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch @CalledByNative 190e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch static int getCaptureFormatFramerate(VideoCapture.CaptureFormat format) { 191e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch return format.getFramerate(); 192e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 193e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch 194e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch @CalledByNative 195e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch static int getCaptureFormatPixelFormat(VideoCapture.CaptureFormat format) { 196e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch return format.getPixelFormat(); 197e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch } 198e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch} 199