1. 程式人生 > >Android UVC驅動外接攝像頭

Android UVC驅動外接攝像頭

簡單記錄一下開發中遇到的手機驅動外接攝像頭,目前只能針對個別機型,像小米,魅族MX2,ZTE測試過是可行的,Lenovo,VIVO,華為由於關閉了外接裝置,並不支援外接攝像頭。攝像頭要支援UVC軟碟機。另外要注意,攝像頭預覽解析度要是手機/平板解析度和攝像頭支援的解析度交集,Demo中將檢視解析度的程式碼解開(程式碼改為true)可以檢視兩者支援的解析度。還是有很多問題的,距離商用還有一段距離,僅供參考,還請感興趣的大牛聯絡完善。

在平板測試過程中發現效能比較好的品牌平板表現良好,不過遇到一個棘手的問題就是HOME與BACk鍵交叉使用會導致影象不再顯示,手機顯示蠻好,平板表現比較次。

專案建立

本專案在EC下建立取名為UVCDemo,包名:com.serenegiant.uvccamera,我們先把底層庫引入:


如果會NDK的話請研究jni檔案:


資原始檔:


許可權如下:

  1. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  2. <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  3. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  4. <uses-permission android:name
    ="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
  5. <uses-permission android:name="android.permission.CAMERA"/>
上述許可權並不是都要有,由於肯定牽扯到聯網所以要有網路相關的許可權,根據需求增刪(本專案所用如下)
  1. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  2. <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
    />
  3. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  4. <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
  5. <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
  6. <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  7. <uses-permission android:name="android.permission.INTERNET" />
  8. <uses-permission android:name="android.permission.CAMERA"/>
  9. <!-- 遮蔽HOME鍵需要的許可權 -->
  10. <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
  11. <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
  12. <!-- 開機自啟動需要的許可權 -->
  13. <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

核心程式碼


原理是使用UVCCamera來控制、管理與外接裝置的連線,UVCCameraTextureView控制元件進行影象的預覽,USBMonitor進行驅動的連線和斷開 先在需要的地方新增控制元件:
  1. <com.serenegiant.usb.widget.UVCCameraTextureView
  2. android:id=“@+id/camera_view”
  3. android:layout_width=“match_parent”
  4. android:layout_height=“match_parent”
  5. android:layout_centerHorizontal=“true”
  6. android:layout_centerVertical=“true” />
我在CaptureActivity初始化:
  1. final View view = findViewById(R.id.camera_view);
  2. mUVCCameraView = (CameraViewInterface)view;
  3. mUVCCameraView.setAspectRatio(PREVIEW_WIDTH / (float)PREVIEW_HEIGHT);//TODO
  4. mUSBMonitor = new USBMonitor(this, mOnDeviceConnectListener);
  5. mHandler = CameraHandler.createHandler(this, mUVCCameraView);
我通過CameraHandler將事件分發出去,裡面包含了對外接攝像頭的所有操作,拍照啊、錄影啊、釋放資源啊等等 在使用“.so”庫的時候記得新增庫才能使用本地語言
  1. System.loadLibrary(“usb100”);
  2. System.loadLibrary(“uvc”);
  3. System.loadLibrary(“UVCCamera”);
預覽的呼叫等都是通過呼叫native的方法c++實現的
  1. /**
  2. * start preview
  3. */
  4. public void startPreview() {
  5. if (mCtrlBlock != null) {
  6. nativeStartPreview(mNativePtr);
  7. }
  8. }
  9. /**
  10. * stop preview
  11. */
  12. public void stopPreview() {
  13. setFrameCallback(null, 0);
  14. if (mCtrlBlock != null) {
  15. nativeStopPreview(mNativePtr);
  16. }
  17. }
驅動連線方面,我們自定義一個規則
  1. <?xml version=“1.0” encoding=“utf-8”?>
  2. <usb>
  3. <usb-device class=“239” subclass=“2” /> <!– all device of UVC –>
  4. </usb>
驅動列表是通過Android的USBManager獲取得到的
  1. /**
  2. * Returns a HashMap containing all USB devices currently attached.
  3. * USB device name is the key for the returned HashMap.
  4. * The result will be empty if no devices are attached, or if
  5. * USB host mode is inactive or unsupported.
  6. *
  7. * @return HashMap containing all connected USB devices.
  8. */
  9. public HashMap<String,UsbDevice> getDeviceList() {
  10. Bundle bundle = new Bundle();
  11. try {
  12. mService.getDeviceList(bundle);
  13. HashMap<String,UsbDevice> result = new HashMap<String,UsbDevice>();
  14. for (String name : bundle.keySet()) {
  15. result.put(name, (UsbDevice)bundle.get(name));
  16. }
  17. return result;
  18. } catch (RemoteException e) {
  19. Log.e(TAG, “RemoteException in getDeviceList”, e);
  20. return null;
  21. }
  22. }


專案原始碼

UVCCamera
  1. package com.serenegiant.usb;
  2. /*
  3. * UVCCamera
  4. * library and sample to access to UVC web camera on non-rooted Android device
  5. *
  6. * Copyright (c) 2014-2015 saki [email protected]
  7. *
  8. * File name: UVCCamera.java
  9. *
  10. * Licensed under the Apache License, Version 2.0 (the “License”);
  11. * you may not use this file except in compliance with the License.
  12. * You may obtain a copy of the License at
  13. *
  14. * http://www.apache.org/licenses/LICENSE-2.0
  15. *
  16. * Unless required by applicable law or agreed to in writing, software
  17. * distributed under the License is distributed on an “AS IS” BASIS,
  18. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  19. * See the License for the specific language governing permissions and
  20. * limitations under the License.
  21. *
  22. * All files in the folder are under this Apache License, Version 2.0.
  23. * Files in the jni/libjpeg, jni/libusb, jin/libuvc, jni/rapidjson folder may have a different license, see the respective files.
  24. */
  25. import java.util.ArrayList;
  26. import java.util.List;
  27. import org.json.JSONArray;
  28. import org.json.JSONException;
  29. import org.json.JSONObject;
  30. import android.graphics.SurfaceTexture;
  31. import android.hardware.usb.UsbDevice;
  32. import android.text.TextUtils;
  33. import android.util.Log;
  34. import android.view.Surface;
  35. import android.view.SurfaceHolder;
  36. import com.serenegiant.usb.USBMonitor.UsbControlBlock;
  37. public class UVCCamera {
  38. private static final boolean DEBUG = false; // TODO set false when releasing
  39. private static final String TAG = UVCCamera.class.getSimpleName();
  40. private static final String DEFAULT_USBFS = “/dev/bus/usb”;
  41. public static final int DEFAULT_PREVIEW_WIDTH = 1600;//640
  42. public static final int DEFAULT_PREVIEW_HEIGHT = 1200;//480
  43. public static final int DEFAULT_PREVIEW_MODE = 0;
  44. public static final float DEFAULT_BANDWIDTH = 1.0f;
  45. public static final int FRAME_FORMAT_YUYV = 0;
  46. public static final int FRAME_FORMAT_MJPEG = 1;
  47. public static final int PIXEL_FORMAT_RAW = 0;
  48. public static final int PIXEL_FORMAT_YUV = 1;
  49. public static final int PIXEL_FORMAT_RGB565 = 2;
  50. public static final int PIXEL_FORMAT_RGBX = 3;
  51. public static final int PIXEL_FORMAT_YUV420SP = 4;
  52. public static final int PIXEL_FORMAT_NV21 = 5; // = YVU420SemiPlanar
  53. //——————————————————————————–
  54. public static final int CTRL_SCANNING = 0x00000001; // D0: Scanning Mode
  55. public static final int CTRL_AE = 0x00000002; // D1: Auto-Exposure Mode
  56. public static final int CTRL_AE_PRIORITY = 0x00000004; // D2: Auto-Exposure Priority
  57. public static final int CTRL_AE_ABS = 0x00000008; // D3: Exposure Time (Absolute)
  58. public static final int CTRL_AR_REL = 0x00000010; // D4: Exposure Time (Relative)
  59. public static final int CTRL_FOCUS_ABS = 0x00000020; // D5: Focus (Absolute)
  60. public static final int CTRL_FOCUS_REL = 0x00000040; // D6: Focus (Relative)
  61. public static final int CTRL_IRIS_ABS = 0x00000080; // D7: Iris (Absolute)
  62. public static final int CTRL_IRIS_REL = 0x00000100; // D8: Iris (Relative)
  63. public static final int CTRL_ZOOM_ABS = 0x00000200; // D9: Zoom (Absolute)
  64. public static final int CTRL_ZOOM_REL = 0x00000400; // D10: Zoom (Relative)
  65. public static final int CTRL_PANTILT_ABS = 0x00000800; // D11: PanTilt (Absolute)
  66. public static final int CTRL_PANTILT_REL = 0x00001000; // D12: PanTilt (Relative)
  67. public static final int CTRL_ROLL_ABS = 0x00002000; // D13: Roll (Absolute)
  68. public static final int CTRL_ROLL_REL = 0x00004000; // D14: Roll (Relative)
  69. public static final int CTRL_FOCUS_AUTO = 0x00020000; // D17: Focus, Auto
  70. public static final int CTRL_PRIVACY = 0x00040000; // D18: Privacy
  71. public static final int CTRL_FOCUS_SIMPLE = 0x00080000; // D19: Focus, Simple
  72. public static final int CTRL_WINDOW = 0x00100000; // D20: Window
  73. public static final int PU_BRIGHTNESS = 0x80000001; // D0: Brightness
  74. public static final int PU_CONTRAST = 0x80000002; // D1: Contrast
  75. public static final int PU_HUE = 0x80000004; // D2: Hue
  76. public static final int PU_SATURATION = 0x80000008; // D3: Saturation
  77. public static final int PU_SHARPNESS = 0x80000010; // D4: Sharpness
  78. public static final int PU_GAMMA = 0x80000020; // D5: Gamma
  79. public static final int PU_WB_TEMP = 0x80000040; // D6: White Balance Temperature
  80. public static final int PU_WB_COMPO = 0x80000080; // D7: White Balance Component
  81. public static final int PU_BACKLIGHT = 0x80000100; // D8: Backlight Compensation
  82. public static final int PU_GAIN = 0x80000200; // D9: Gain
  83. public static final int PU_POWER_LF = 0x80000400; // D10: Power Line Frequency
  84. public static final int PU_HUE_AUTO = 0x80000800; // D11: Hue, Auto
  85. public static final int PU_WB_TEMP_AUTO = 0x80001000; // D12: White Balance Temperature, Auto
  86. public static final int PU_WB_COMPO_AUTO = 0x80002000; // D13: White Balance Component, Auto
  87. public static final int PU_DIGITAL_MULT = 0x80004000; // D14: Digital Multiplier
  88. public static final int PU_DIGITAL_LIMIT = 0x80008000; // D15: Digital Multiplier Limit
  89. public static final int PU_AVIDEO_STD = 0x80010000; // D16: Analog Video Standard
  90. public static final int PU_AVIDEO_LOCK = 0x80020000; // D17: Analog Video Lock Status
  91. public static final int PU_CONTRAST_AUTO = 0x80040000; // D18: Contrast, Auto
  92. // uvc_status_class from libuvc.h
  93. public static final int STATUS_CLASS_CONTROL = 0x10;
  94. public static final int STATUS_CLASS_CONTROL_CAMERA = 0x11;
  95. public static final int STATUS_CLASS_CONTROL_PROCESSING = 0x12;
  96. // uvc_status_attribute from libuvc.h
  97. public static final int STATUS_ATTRIBUTE_VALUE_CHANGE = 0x00;
  98. public static final int STATUS_ATTRIBUTE_INFO_CHANGE = 0x01;
  99. public static final int STATUS_ATTRIBUTE_FAILURE_CHANGE = 0x02;
  100. public static final int STATUS_ATTRIBUTE_UNKNOWN = 0xff;
  101. private static boolean isLoaded;
  102. static {
  103. if (!isLoaded) {
  104. System.loadLibrary(“usb100”);
  105. System.loadLibrary(“uvc”);
  106. System.loadLibrary(“UVCCamera”);
  107. isLoaded = true;
  108. }
  109. }
  110. private UsbControlBlock mCtrlBlock;
  111. protected long mControlSupports; // カメラコントロールでサポートしている機能フラグ
  112. protected long mProcSupports; // プロセッシングユニットでサポートしている機能フラグ
  113. protected int mCurrentPreviewMode = 0;
  114. protected int mCurrentPreviewWidth = DEFAULT_PREVIEW_WIDTH, mCurrentPreviewHeight = DEFAULT_PREVIEW_HEIGHT;
  115. protected String mSupportedSize;
  116. // these fields from here are accessed from native code and do not change name and remove
  117. protected long mNativePtr;
  118. protected int mScanningModeMin, mScanningModeMax, mScanningModeDef;
  119. protected int mExposureModeMin, mExposureModeMax, mExposureModeDef;
  120. protected int mExposurePriorityMin, mExposurePriorityMax, mExposurePriorityDef;
  121. protected int mExposureMin, mExposureMax, mExposureDef;
  122. protected int mAutoFocusMin, mAutoFocusMax, mAutoFocusDef;
  123. protected int mFocusMin, mFocusMax, mFocusDef;
  124. protected int mFocusRelMin, mFocusRelMax, mFocusRelDef;
  125. protected int mFocusSimpleMin, mFocusSimpleMax, mFocusSimpleDef;
  126. protected int mIrisMin, mIrisMax, mIrisDef;
  127. protected int mIrisRelMin, mIrisRelMax, mIrisRelDef;
  128. protected int mPanMin, mPanMax, mPanDef;
  129. protected int mTiltMin, mTiltMax, mTiltDef;
  130. protected int mRollMin, mRollMax, mRollDef;
  131. protected int mPanRelMin, mPanRelMax, mPanRelDef;
  132. protected int mTiltRelMin, mTiltRelMax, mTiltRelDef;
  133. protected int mRollRelMin, mRollRelMax, mRollRelDef;
  134. protected int mPrivacyMin, mPrivacyMax, mPrivacyDef;
  135. protected int mAutoWhiteBlanceMin, mAutoWhiteBlanceMax, mAutoWhiteBlanceDef;
  136. protected int mAutoWhiteBlanceCompoMin, mAutoWhiteBlanceCompoMax, mAutoWhiteBlanceCompoDef;
  137. protected int mWhiteBlanceMin, mWhiteBlanceMax, mWhiteBlanceDef;
  138. protected int mWhiteBlanceCompoMin, mWhiteBlanceCompoMax, mWhiteBlanceCompoDef;
  139. protected int mWhiteBlanceRelMin, mWhiteBlanceRelMax, mWhiteBlanceRelDef;
  140. protected int mBacklightCompMin, mBacklightCompMax, mBacklightCompDef;
  141. protected int mBrightnessMin, mBrightnessMax, mBrightnessDef;
  142. protected int mContrastMin, mContrastMax, mContrastDef;
  143. protected int mSharpnessMin, mSharpnessMax, mSharpnessDef;
  144. protected int mGainMin, mGainMax, mGainDef;
  145. protected int mGammaMin, mGammaMax, mGammaDef;
  146. protected int mSaturationMin, mSaturationMax, mSaturationDef;
  147. protected int mHueMin, mHueMax, mHueDef;
  148. protected int mZoomMin, mZoomMax, mZoomDef;
  149. protected int mZoomRelMin, mZoomRelMax, mZoomRelDef;
  150. protected int mPowerlineFrequencyMin, mPowerlineFrequencyMax, mPowerlineFrequencyDef;
  151. protected int mMultiplierMin, mMultiplierMax, mMultiplierDef;
  152. protected int mMultiplierLimitMin, mMultiplierLimitMax, mMultiplierLimitDef;
  153. protected int mAnalogVideoStandardMin, mAnalogVideoStandardMax, mAnalogVideoStandardDef;
  154. protected int mAnalogVideoLockStateMin, mAnalogVideoLockStateMax, mAnalogVideoLockStateDef;
  155. // until here
  156. /**
  157. * the sonctructor of this class should be call within the thread that has a looper
  158. * (UI thread or a thread that called Looper.prepare)
  159. */
  160. public UVCCamera() {
  161. mNativePtr = nativeCreate();
  162. mSupportedSize = null;
  163. }
  164. /**
  165. * connect to a UVC camera
  166. * USB permission is necessary before this method is called
  167. * @param ctrlBlock
  168. */
  169. public void open(final UsbControlBlock ctrlBlock) {
  170. mCtrlBlock = ctrlBlock;
  171. nativeConnect(mNativePtr,
  172. mCtrlBlock.getVenderId(), mCtrlBlock.getProductId(),
  173. mCtrlBlock.getFileDescriptor(),
  174. getUSBFSName(mCtrlBlock));
  175. if (mNativePtr != 0 && TextUtils.isEmpty(mSupportedSize)) {
  176. mSupportedSize = nativeGetSupportedSize(mNativePtr);
  177. }
  178. nativeSetPreviewSize(mNativePtr, DEFAULT_PREVIEW_WIDTH, DEFAULT_PREVIEW_HEIGHT, DEFAULT_PREVIEW_MODE, DEFAULT_BANDWIDTH);
  179. }
  180. /**
  181. * set status callback
  182. * @param callback
  183. */
  184. public void setStatusCallback(final IStatusCallback callback) {
  185. if (mNativePtr != 0) {
  186. nativeSetStatusCallback(mNativePtr, callback);
  187. }
  188. }
  189. /**
  190. * set button callback
  191. * @param callback
  192. */
  193. public void setButtonCallback(final IButtonCallback callback) {
  194. if (mNativePtr != 0) {
  195. nativeSetButtonCallback(mNativePtr, callback);
  196. }
  197. }
  198. /**
  199. * close and release UVC camera
  200. */
  201. public void close() {
  202. stopPreview();
  203. if (mNativePtr != 0) {
  204. nativeRelease(mNativePtr);
  205. }
  206. mCtrlBlock = null;
  207. mControlSupports = mProcSupports = 0;
  208. mCurrentPreviewMode = -1;
  209. }
  210. public UsbDevice getDevice() {
  211. return mCtrlBlock != null ? mCtrlBlock.getDevice() : null;
  212. }
  213. public String getDeviceName(){
  214. return mCtrlBlock != null ? mCtrlBlock.getDeviceName() : null;
  215. }
  216. public UsbControlBlock getUsbControlBlock() {
  217. return mCtrlBlock;
  218. }
  219. public synchronized String getSupportedSize() {
  220. return !TextUtils.isEmpty(mSupportedSize) ? mSupportedSize : (mSupportedSize = nativeGetSupportedSize(mNativePtr));
  221. }
  222. public Size getPreviewSize() {
  223. Size result = null;
  224. final List<Size> list = getSupportedSizeList();
  225. for (final Size sz: list) {
  226. if ((sz.width == mCurrentPreviewWidth)
  227. || (sz.height == mCurrentPreviewHeight)) {
  228. result =sz;
  229. break;
  230. }
  231. }
  232. return result;
  233. }
  234. /**