博主免责声明:仅供研究讨论,严禁商用!!!
简单记录一下开发中遇到的手机驱动外接摄像头,目前只能针对个别机型,像小米,魅族MX2,ZTE测试过是可行的,Lenovo,VIVO,华为由于关闭了外接设备,并不支持外接摄像头。还是有很多问题的,距离商用还有一段距离,仅供参考,还请感兴趣的大牛完善。
在平板测试过程中发现性能比较好的品牌平板表现良好i,不过遇到一个棘手的问题就是HOME与BACk键交叉使用会导致图像不再显示,手机显示蛮好,平板表现比较次。
本项目在EC下创建取名为UVCDemo,包名:com.serenegiant.uvccamera,我们先把底层库引入:
如果会NDK的话请研究jni文件:
资源文件:
权限如下:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <uses-permission android:name="android.permission.CAMERA"/>上述权限并不是都要有,由于肯定牵扯到联网所以要有网络相关的权限,根据需求增删(本项目所用如下)
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.CAMERA"/> <!-- 屏蔽HOME键需要的权限 --> <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <!-- 开机自启动需要的权限 --> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<com.serenegiant.usb.widget.UVCCameraTextureView android:id="@+id/camera_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerHorizontal="true" android:layout_centerVertical="true" />我在CaptureActivity初始化:
final View view = findViewById(R.id.camera_view); mUVCCameraView = (CameraViewInterface)view; mUVCCameraView.setAspectRatio(PREVIEW_WIDTH / (float)PREVIEW_HEIGHT);//TODO mUSBMonitor = new USBMonitor(this, mOnDeviceConnectListener); mHandler = CameraHandler.createHandler(this, mUVCCameraView);我通过CameraHandler将事件分发出去,里面包含了对外接摄像头的所有操作,拍照啊、录像啊、释放资源啊等等
System.loadLibrary("usb100"); System.loadLibrary("uvc"); System.loadLibrary("UVCCamera");预览的调用等都是通过调用native的方法c++实现的
/** * start preview */ public void startPreview() { if (mCtrlBlock != null) { nativeStartPreview(mNativePtr); } } /** * stop preview */ public void stopPreview() { setFrameCallback(null, 0); if (mCtrlBlock != null) { nativeStopPreview(mNativePtr); } }驱动连接方面,我们自定义一个规则
<?xml version="1.0" encoding="utf-8"?> <usb> <usb-device class="239" subclass="2" /> <!-- all device of UVC --> </usb>驱动列表是通过Android的USBManager获取得到的
/** * Returns a HashMap containing all USB devices currently attached. * USB device name is the key for the returned HashMap. * The result will be empty if no devices are attached, or if * USB host mode is inactive or unsupported. * * @return HashMap containing all connected USB devices. */ public HashMap<String,UsbDevice> getDeviceList() { Bundle bundle = new Bundle(); try { mService.getDeviceList(bundle); HashMap<String,UsbDevice> result = new HashMap<String,UsbDevice>(); for (String name : bundle.keySet()) { result.put(name, (UsbDevice)bundle.get(name)); } return result; } catch (RemoteException e) { Log.e(TAG, "RemoteException in getDeviceList", e); return null; } }
package com.serenegiant.usb; /* * UVCCamera * library and sample to access to UVC web camera on non-rooted Android device * * Copyright (c) 2014-2015 saki [email protected] * * File name: UVCCamera.java * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * All files in the folder are under this Apache License, Version 2.0. * Files in the jni/libjpeg, jni/libusb, jin/libuvc, jni/rapidjson folder may have a different license, see the respective files. */ import java.util.ArrayList; import java.util.List; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.graphics.SurfaceTexture; import android.hardware.usb.UsbDevice; import android.text.TextUtils; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; import com.serenegiant.usb.USBMonitor.UsbControlBlock; public class UVCCamera { private static final boolean DEBUG = false; // TODO set false when releasing private static final String TAG = UVCCamera.class.getSimpleName(); private static final String DEFAULT_USBFS = "/dev/bus/usb"; public static final int DEFAULT_PREVIEW_WIDTH = 1600;//640 public static final int DEFAULT_PREVIEW_HEIGHT = 1200;//480 public static final int DEFAULT_PREVIEW_MODE = 0; public static final float DEFAULT_BANDWIDTH = 1.0f; public static final int FRAME_FORMAT_YUYV = 0; public static final int FRAME_FORMAT_MJPEG = 1; public static final int PIXEL_FORMAT_RAW = 0; public static final int PIXEL_FORMAT_YUV = 1; public static final int PIXEL_FORMAT_RGB565 = 2; public static final int PIXEL_FORMAT_RGBX = 3; public static final int PIXEL_FORMAT_YUV420SP = 4; public static final int PIXEL_FORMAT_NV21 = 5; // = YVU420SemiPlanar //-------------------------------------------------------------------------------- public static final int CTRL_SCANNING = 0x00000001; // D0: Scanning Mode public static final int CTRL_AE = 0x00000002; // D1: Auto-Exposure Mode public static final int CTRL_AE_PRIORITY = 0x00000004; // D2: Auto-Exposure Priority public static final int CTRL_AE_ABS = 0x00000008; // D3: Exposure Time (Absolute) public static final int CTRL_AR_REL = 0x00000010; // D4: Exposure Time (Relative) public static final int CTRL_FOCUS_ABS = 0x00000020; // D5: Focus (Absolute) public static final int CTRL_FOCUS_REL = 0x00000040; // D6: Focus (Relative) public static final int CTRL_IRIS_ABS = 0x00000080; // D7: Iris (Absolute) public static final int CTRL_IRIS_REL = 0x00000100; // D8: Iris (Relative) public static final int CTRL_ZOOM_ABS = 0x00000200; // D9: Zoom (Absolute) public static final int CTRL_ZOOM_REL = 0x00000400; // D10: Zoom (Relative) public static final int CTRL_PANTILT_ABS = 0x00000800; // D11: PanTilt (Absolute) public static final int CTRL_PANTILT_REL = 0x00001000; // D12: PanTilt (Relative) public static final int CTRL_ROLL_ABS = 0x00002000; // D13: Roll (Absolute) public static final int CTRL_ROLL_REL = 0x00004000; // D14: Roll (Relative) public static final int CTRL_FOCUS_AUTO = 0x00020000; // D17: Focus, Auto public static final int CTRL_PRIVACY = 0x00040000; // D18: Privacy public static final int CTRL_FOCUS_SIMPLE = 0x00080000; // D19: Focus, Simple public static final int CTRL_WINDOW = 0x00100000; // D20: Window public static final int PU_BRIGHTNESS = 0x80000001; // D0: Brightness public static final int PU_CONTRAST = 0x80000002; // D1: Contrast public static final int PU_HUE = 0x80000004; // D2: Hue public static final int PU_SATURATION = 0x80000008; // D3: Saturation public static final int PU_SHARPNESS = 0x80000010; // D4: Sharpness public static final int PU_GAMMA = 0x80000020; // D5: Gamma public static final int PU_WB_TEMP = 0x80000040; // D6: White Balance Temperature public static final int PU_WB_COMPO = 0x80000080; // D7: White Balance Component public static final int PU_BACKLIGHT = 0x80000100; // D8: Backlight Compensation public static final int PU_GAIN = 0x80000200; // D9: Gain public static final int PU_POWER_LF = 0x80000400; // D10: Power Line Frequency public static final int PU_HUE_AUTO = 0x80000800; // D11: Hue, Auto public static final int PU_WB_TEMP_AUTO = 0x80001000; // D12: White Balance Temperature, Auto public static final int PU_WB_COMPO_AUTO = 0x80002000; // D13: White Balance Component, Auto public static final int PU_DIGITAL_MULT = 0x80004000; // D14: Digital Multiplier public static final int PU_DIGITAL_LIMIT = 0x80008000; // D15: Digital Multiplier Limit public static final int PU_AVIDEO_STD = 0x80010000; // D16: Analog Video Standard public static final int PU_AVIDEO_LOCK = 0x80020000; // D17: Analog Video Lock Status public static final int PU_CONTRAST_AUTO = 0x80040000; // D18: Contrast, Auto // uvc_status_class from libuvc.h public static final int STATUS_CLASS_CONTROL = 0x10; public static final int STATUS_CLASS_CONTROL_CAMERA = 0x11; public static final int STATUS_CLASS_CONTROL_PROCESSING = 0x12; // uvc_status_attribute from libuvc.h public static final int STATUS_ATTRIBUTE_VALUE_CHANGE = 0x00; public static final int STATUS_ATTRIBUTE_INFO_CHANGE = 0x01; public static final int STATUS_ATTRIBUTE_FAILURE_CHANGE = 0x02; public static final int STATUS_ATTRIBUTE_UNKNOWN = 0xff; private static boolean isLoaded; static { if (!isLoaded) { System.loadLibrary("usb100"); System.loadLibrary("uvc"); System.loadLibrary("UVCCamera"); isLoaded = true; } } private UsbControlBlock mCtrlBlock; protected long mControlSupports; // カメラコントロールでサポートしている機能フラグ protected long mProcSupports; // プロセッシングユニットでサポートしている機能フラグ protected int mCurrentPreviewMode = 0; protected int mCurrentPreviewWidth = DEFAULT_PREVIEW_WIDTH, mCurrentPreviewHeight = DEFAULT_PREVIEW_HEIGHT; protected String mSupportedSize; // these fields from here are accessed from native code and do not change name and remove protected long mNativePtr; protected int mScanningModeMin, mScanningModeMax, mScanningModeDef; protected int mExposureModeMin, mExposureModeMax, mExposureModeDef; protected int mExposurePriorityMin, mExposurePriorityMax, mExposurePriorityDef; protected int mExposureMin, mExposureMax, mExposureDef; protected int mAutoFocusMin, mAutoFocusMax, mAutoFocusDef; protected int mFocusMin, mFocusMax, mFocusDef; protected int mFocusRelMin, mFocusRelMax, mFocusRelDef; protected int mFocusSimpleMin, mFocusSimpleMax, mFocusSimpleDef; protected int mIrisMin, mIrisMax, mIrisDef; protected int mIrisRelMin, mIrisRelMax, mIrisRelDef; protected int mPanMin, mPanMax, mPanDef; protected int mTiltMin, mTiltMax, mTiltDef; protected int mRollMin, mRollMax, mRollDef; protected int mPanRelMin, mPanRelMax, mPanRelDef; protected int mTiltRelMin, mTiltRelMax, mTiltRelDef; protected int mRollRelMin, mRollRelMax, mRollRelDef; protected int mPrivacyMin, mPrivacyMax, mPrivacyDef; protected int mAutoWhiteBlanceMin, mAutoWhiteBlanceMax, mAutoWhiteBlanceDef; protected int mAutoWhiteBlanceCompoMin, mAutoWhiteBlanceCompoMax, mAutoWhiteBlanceCompoDef; protected int mWhiteBlanceMin, mWhiteBlanceMax, mWhiteBlanceDef; protected int mWhiteBlanceCompoMin, mWhiteBlanceCompoMax, mWhiteBlanceCompoDef; protected int mWhiteBlanceRelMin, mWhiteBlanceRelMax, mWhiteBlanceRelDef; protected int mBacklightCompMin, mBacklightCompMax, mBacklightCompDef; protected int mBrightnessMin, mBrightnessMax, mBrightnessDef; protected int mContrastMin, mContrastMax, mContrastDef; protected int mSharpnessMin, mSharpnessMax, mSharpnessDef; protected int mGainMin, mGainMax, mGainDef; protected int mGammaMin, mGammaMax, mGammaDef; protected int mSaturationMin, mSaturationMax, mSaturationDef; protected int mHueMin, mHueMax, mHueDef; protected int mZoomMin, mZoomMax, mZoomDef; protected int mZoomRelMin, mZoomRelMax, mZoomRelDef; protected int mPowerlineFrequencyMin, mPowerlineFrequencyMax, mPowerlineFrequencyDef; protected int mMultiplierMin, mMultiplierMax, mMultiplierDef; protected int mMultiplierLimitMin, mMultiplierLimitMax, mMultiplierLimitDef; protected int mAnalogVideoStandardMin, mAnalogVideoStandardMax, mAnalogVideoStandardDef; protected int mAnalogVideoLockStateMin, mAnalogVideoLockStateMax, mAnalogVideoLockStateDef; // until here /** * the sonctructor of this class should be call within the thread that has a looper * (UI thread or a thread that called Looper.prepare) */ public UVCCamera() { mNativePtr = nativeCreate(); mSupportedSize = null; } /** * connect to a UVC camera * USB permission is necessary before this method is called * @param ctrlBlock */ public void open(final UsbControlBlock ctrlBlock) { mCtrlBlock = ctrlBlock; nativeConnect(mNativePtr, mCtrlBlock.getVenderId(), mCtrlBlock.getProductId(), mCtrlBlock.getFileDescriptor(), getUSBFSName(mCtrlBlock)); if (mNativePtr != 0 && TextUtils.isEmpty(mSupportedSize)) { mSupportedSize = nativeGetSupportedSize(mNativePtr); } nativeSetPreviewSize(mNativePtr, DEFAULT_PREVIEW_WIDTH, DEFAULT_PREVIEW_HEIGHT, DEFAULT_PREVIEW_MODE, DEFAULT_BANDWIDTH); } /** * set status callback * @param callback */ public void setStatusCallback(final IStatusCallback callback) { if (mNativePtr != 0) { nativeSetStatusCallback(mNativePtr, callback); } } /** * set button callback * @param callback */ public void setButtonCallback(final IButtonCallback callback) { if (mNativePtr != 0) { nativeSetButtonCallback(mNativePtr, callback); } } /** * close and release UVC camera */ public void close() { stopPreview(); if (mNativePtr != 0) { nativeRelease(mNativePtr); } mCtrlBlock = null; mControlSupports = mProcSupports = 0; mCurrentPreviewMode = -1; } public UsbDevice getDevice() { return mCtrlBlock != null ? mCtrlBlock.getDevice() : null; } public String getDeviceName(){ return mCtrlBlock != null ? mCtrlBlock.getDeviceName() : null; } public UsbControlBlock getUsbControlBlock() { return mCtrlBlock; } public synchronized String getSupportedSize() { return !TextUtils.isEmpty(mSupportedSize) ? mSupportedSize : (mSupportedSize = nativeGetSupportedSize(mNativePtr)); } public Size getPreviewSize() { Size result = null; final List<Size> list = getSupportedSizeList(); for (final Size sz: list) { if ((sz.width == mCurrentPreviewWidth) || (sz.height == mCurrentPreviewHeight)) { result =sz; break; } } return result; } /** * Set preview size and preview mode * @param width @param height @param mode 0:yuyv, other:MJPEG */ public void setPreviewSize(final int width, final int height) { setPreviewSize(width, height, mCurrentPreviewMode, 0); } /** * Set preview size and preview mode * @param width @param height @param mode 0:yuyv, other:MJPEG */ public void setPreviewSize(final int width, final int height, final int mode) { setPreviewSize(width, height, mode, 0); } /** * Set preview size and preview mode * @param width @param height @param mode 0:yuyv, other:MJPEG @param bandwidth [0.0f,1.0f] */ public void setPreviewSize(final int width, final int height, final int mode, final float bandwidth) { if ((width == 0) || (height == 0)) throw new IllegalArgumentException("invalid preview size"); if (mNativePtr != 0) { final int result = nativeSetPreviewSize(mNativePtr, width, height, mode, bandwidth); if (result != 0) throw new IllegalArgumentException("Failed to set preview size"); mCurrentPreviewMode = mode; mCurrentPreviewWidth = width; mCurrentPreviewHeight = height; } } public List<Size> getSupportedSizeList() { final int type = (mCurrentPreviewMode > 0) ? 6 : 4; return getSupportedSize(type, mSupportedSize); } public static List<Size> getSupportedSize(final int type, final String supportedSize) { final List<Size> result = new ArrayList<Size>(); if (!TextUtils.isEmpty(supportedSize)) try { final JSONObject json = new JSONObject(supportedSize); final JSONArray formats = json.getJSONArray("formats"); final int format_nums = formats.length(); for (int i = 0; i < format_nums; i++) { final JSONObject format = formats.getJSONObject(i); final int format_type = format.getInt("type"); if ((format_type == type) || (type == -1)) { addSize(format, format_type, result); } } } catch (final JSONException e) { } return result; } private static final void addSize(final JSONObject format, final int type, final List<Size> size_list) throws JSONException { final JSONArray size = format.getJSONArray("size"); final int size_nums = size.length(); for (int j = 0; j < size_nums; j++) { final String[] sz = size.getString(j).split("x"); try { size_list.add(new Size(type, j, Integer.parseInt(sz[0]), Integer.parseInt(sz[1]))); } catch (final Exception e) { break; } } } /** * set preview surface with SurfaceHolder</br> * you can use SurfaceHolder came from SurfaceView/GLSurfaceView * @param holder */ public void setPreviewDisplay(final SurfaceHolder holder) { nativeSetPreviewDisplay(mNativePtr, holder.getSurface()); } /** * set preview surface with SurfaceTexture. * this method require API >= 14 * @param texture */ public void setPreviewTexture(final SurfaceTexture texture) { // API >= 11 final Surface surface = new Surface(texture); // XXX API >= 14 nativeSetPreviewDisplay(mNativePtr, surface); } /** * set preview surface with Surface * @param Surface */ public void setPreviewDisplay(final Surface surface) { nativeSetPreviewDisplay(mNativePtr, surface); } /** * set frame callback * @param callback * @param pixelFormat */ public void setFrameCallback(final IFrameCallback callback, final int pixelFormat) { if (mNativePtr != 0) { nativeSetFrameCallback(mNativePtr, callback, pixelFormat); } } /** * start preview */ public void startPreview() { if (mCtrlBlock != null) { nativeStartPreview(mNativePtr); } } /** * stop preview */ public void stopPreview() { setFrameCallback(null, 0); if (mCtrlBlock != null) { nativeStopPreview(mNativePtr); } } /** * destroy UVCCamera object */ public void destroy() { close(); if (mNativePtr != 0) { nativeDestroy(mNativePtr); mNativePtr = 0; } } // wrong result may return when you call this just after camera open. // it is better to wait several hundreads millseconds. public boolean checkSupportFlag(final long flag) { updateCameraParams(); if ((flag & 0x80000000) == 0x80000000) return ((mProcSupports & flag) == (flag & 0x7ffffffF)); else return (mControlSupports & flag) == flag; } //================================================================================ public synchronized void setAutoFocus(final boolean autoFocus) { if (mNativePtr != 0) { nativeSetAutoFocus(mNativePtr, autoFocus); } } public synchronized boolean getAutoFocus() { boolean result = true; if (mNativePtr != 0) { result = nativeGetAutoFocus(mNativePtr) > 0; } return result; } //================================================================================ /** * @param focus[%] */ public synchronized void setFocus(final int focus) { if (mNativePtr != 0) { final float range = Math.abs(mFocusMax - mFocusMin); if (range > 0) nativeSetFocus(mNativePtr, (int)(focus / 100.f * range) + mFocusMin); } } /** * @param focus_abs * @return forcus[%] */ public synchronized int getFocus(final int focus_abs) { int result = 0; if (mNativePtr != 0) { nativeUpdateFocusLimit(mNativePtr); final float range = Math.abs(mFocusMax - mFocusMin); if (range > 0) { result = (int)((focus_abs - mFocusMin) * 100.f / range); } } return result; } /** * @return focus[%] */ public synchronized int getFocus() { return getFocus(nativeGetFocus(mNativePtr)); } public synchronized void resetFocus() { if (mNativePtr != 0) { nativeSetFocus(mNativePtr, mFocusDef); } } //================================================================================ public synchronized void setAutoWhiteBlance(final boolean autoWhiteBlance) { if (mNativePtr != 0) { nativeSetAutoWhiteBlance(mNativePtr, autoWhiteBlance); } } public synchronized boolean getAutoWhiteBlance() { boolean result = true; if (mNativePtr != 0) { result = nativeGetAutoWhiteBlance(mNativePtr) > 0; } return result; } //================================================================================ /** * @param whiteBlance[%] */ public synchronized void setWhiteBlance(final int whiteBlance) { if (mNativePtr != 0) { final float range = Math.abs(mWhiteBlanceMax - mWhiteBlanceMin); if (range > 0) nativeSetWhiteBlance(mNativePtr, (int)(whiteBlance / 100.f * range) + mWhiteBlanceMin); } } /** * @param whiteBlance_abs * @return whiteBlance[%] */ public synchronized int getWhiteBlance(final int whiteBlance_abs) { int result = 0; if (mNativePtr != 0) { nativeUpdateWhiteBlanceLimit(mNativePtr); final float range = Math.abs(mWhiteBlanceMax - mWhiteBlanceMin); if (range > 0) { result = (int)((whiteBlance_abs - mWhiteBlanceMin) * 100.f / range); } } return result; } /** * @return white blance[%] */ public synchronized int getWhiteBlance() { return getFocus(nativeGetWhiteBlance(mNativePtr)); } public synchronized void resetWhiteBlance() { if (mNativePtr != 0) { nativeSetWhiteBlance(mNativePtr, mWhiteBlanceDef); } } //================================================================================ /** * @param brightness[%] */ public synchronized void setBrightness(final int brightness) { if (mNativePtr != 0) { final float range = Math.abs(mBrightnessMax - mBrightnessMin); if (range > 0) nativeSetBrightness(mNativePtr, (int)(brightness / 100.f * range) + mBrightnessMin); } } /** * @param brightness_abs * @return brightness[%] */ public synchronized int getBrightness(final int brightness_abs) { int result = 0; if (mNativePtr != 0) { nativeUpdateBrightnessLimit(mNativePtr); final float range = Math.abs(mBrightnessMax - mBrightnessMin); if (range > 0) { result = (int)((brightness_abs - mBrightnessMin) * 100.f / range); } } return result; } /** * @return brightness[%] */ public synchronized int getBrightness() { return getBrightness(nativeGetBrightness(mNativePtr)); } public synchronized void resetBrightness() { if (mNativePtr != 0) { nativeSetBrightness(mNativePtr, mBrightnessDef); } } //================================================================================ /** * @param contrast[%] */ public synchronized void setContrast(final int contrast) { if (mNativePtr != 0) { nativeUpdateContrastLimit(mNativePtr); final float range = Math.abs(mContrastMax - mContrastMin); if (range > 0) nativeSetContrast(mNativePtr, (int)(contrast / 100.f * range) + mContrastMin); } } /** * @param contrast_abs * @return contrast[%] */ public synchronized int getContrast(final int contrast_abs) { int result = 0; if (mNativePtr != 0) { final float range = Math.abs(mContrastMax - mContrastMin); if (range > 0) { result = (int)((contrast_abs - mContrastMin) * 100.f / range); } } return result; } /** * @return contrast[%] */ public synchronized int getContrast() { return getContrast(nativeGetContrast(mNativePtr)); } public synchronized void resetContrast() { if (mNativePtr != 0) { nativeSetContrast(mNativePtr, mContrastDef); } } //================================================================================ /** * @param sharpness[%] */ public synchronized void setSharpness(final int sharpness) { if (mNativePtr != 0) { final float range = Math.abs(mSharpnessMax - mSharpnessMin); if (range > 0) nativeSetSharpness(mNativePtr, (int)(sharpness / 100.f * range) + mSharpnessMin); } } /** * @param sharpness_abs * @return sharpness[%] */ public synchronized int getSharpness(final int sharpness_abs) { int result = 0; if (mNativePtr != 0) { nativeUpdateSharpnessLimit(mNativePtr); final float range = Math.abs(mSharpnessMax - mSharpnessMin); if (range > 0) { result = (int)((sharpness_abs - mSharpnessMin) * 100.f / range); } } return result; } /** * @return sharpness[%] */ public synchronized int getSharpness() { return getSharpness(nativeGetSharpness(mNativePtr)); } public synchronized void resetSharpness() { if (mNativePtr != 0) { nativeSetSharpness(mNativePtr, mSharpnessDef); } } //================================================================================ /** * @param gain[%] */ public synchronized void setGain(final int gain) { if (mNativePtr != 0) { final float range = Math.abs(mGainMax - mGainMin); if (range > 0) nativeSetGain(mNativePtr, (int)(gain / 100.f * range) + mGainMin); } } /** * @param gain_abs * @return gain[%] */ public synchronized int getGain(final int gain_abs) { int result = 0; if (mNativePtr != 0) { nativeUpdateGainLimit(mNativePtr); final float range = Math.abs(mGainMax - mGainMin); if (range > 0) { result = (int)((gain_abs - mGainMin) * 100.f / range); } } return result; } /** * @return gain[%] */ public synchronized int getGain() { return getGain(nativeGetGain(mNativePtr)); } public synchronized void resetGain() { if (mNativePtr != 0) { nativeSetGain(mNativePtr, mGainDef); } } //================================================================================ /** * @param gamma[%] */ public synchronized void setGamma(final int gamma) { if (mNativePtr != 0) { final float range = Math.abs(mGammaMax - mGammaMin); if (range > 0) nativeSetGamma(mNativePtr, (int)(gamma / 100.f * range) + mGammaMin); } } /** * @param gamma_abs * @return gamma[%] */ public synchronized int getGamma(final int gamma_abs) { int result = 0; if (mNativePtr != 0) { nativeUpdateGammaLimit(mNativePtr); final float range = Math.abs(mGammaMax - mGammaMin); if (range > 0) { result = (int)((gamma_abs - mGammaMin) * 100.f / range); } } return result; } /** * @return gamma[%] */ public synchronized int getGamma() { return getGamma(nativeGetGamma(mNativePtr)); } public synchronized void resetGamma() { if (mNativePtr != 0) { nativeSetGamma(mNativePtr, mGammaDef); } } //================================================================================ /** * @param saturation[%] */ public synchronized void setSaturation(final int saturation) { if (mNativePtr != 0) { final float range = Math.abs(mSaturationMax - mSaturationMin); if (range > 0) nativeSetSaturation(mNativePtr, (int)(saturation / 100.f * range) + mSaturationMin); } } /** * @param saturation_abs * @return saturation[%] */ public synchronized int getSaturation(final int saturation_abs) { int result = 0; if (mNativePtr != 0) { nativeUpdateSaturationLimit(mNativePtr); final float range = Math.abs(mSaturationMax - mSaturationMin); if (range > 0) { result = (int)((saturation_abs - mSaturationMin) * 100.f / range); } } return result; } /** * @return saturation[%] */ public synchronized int getSaturation() { return getSaturation(nativeGetSaturation(mNativePtr)); } public synchronized void resetSaturation() { if (mNativePtr != 0) { nativeSetSaturation(mNativePtr, mSaturationDef); } } //================================================================================ /** * @param hue[%] */ public synchronized void setHue(final int hue) { if (mNativePtr != 0) { final float range = Math.abs(mHueMax - mHueMin); if (range > 0) nativeSetHue(mNativePtr, (int)(hue / 100.f * range) + mHueMin); } } /** * @param hue_abs * @return hue[%] */ public synchronized int getHue(final int hue_abs) { int result = 0; if (mNativePtr != 0) { nativeUpdateHueLimit(mNativePtr); final float range = Math.abs(mHueMax - mHueMin); if (range > 0) { result = (int)((hue_abs - mHueMin) * 100.f / range); } } return result; } /** * @return hue[%] */ public synchronized int getHue() { return getHue(nativeGetHue(mNativePtr)); } public synchronized void resetHue() { if (mNativePtr != 0) { nativeSetHue(mNativePtr, mSaturationDef); } } //================================================================================ public void setPowerlineFrequency(final int frequency) { if (mNativePtr != 0) nativeSetPowerlineFrequency(mNativePtr, frequency); } public int getPowerlineFrequency() { return nativeGetPowerlineFrequency(mNativePtr); } //================================================================================ /** * this may not work well with some combination of camera and device * @param zoom[%] */ public synchronized void setZoom(final int zoom) { if (mNativePtr != 0) { final float range = Math.abs(mZoomMax - mZoomMin); if (range > 0) { final int z = (int)(zoom / 100.f * range) + mZoomMin; // Log.d(TAG, "setZoom:zoom=" + zoom + " ,value=" + z); nativeSetZoom(mNativePtr, z); } } } /** * @param zoom_abs * @return zoom[%] */ public synchronized int getZoom(final int zoom_abs) { int result = 0; if (mNativePtr != 0) { nativeUpdateZoomLimit(mNativePtr); final float range = Math.abs(mZoomMax - mZoomMin); if (range > 0) { result = (int)((zoom_abs - mZoomMin) * 100.f / range); } } return result; } /** * @return zoom[%] */ public synchronized int getZoom() { return getZoom(nativeGetZoom(mNativePtr)); } public synchronized void resetZoom() { if (mNativePtr != 0) { nativeSetZoom(mNativePtr, mZoomDef); } } //================================================================================ public synchronized void updateCameraParams() { if (mNativePtr != 0) { if ((mControlSupports == 0) || (mProcSupports == 0)) { // サポートしている機能フラグを取得 if (mControlSupports == 0) mControlSupports = nativeGetCtrlSupports(mNativePtr); if (mProcSupports == 0) mProcSupports = nativeGetProcSupports(mNativePtr); // 設定値を取得 if ((mControlSupports != 0) && (mProcSupports != 0)) { nativeUpdateBrightnessLimit(mNativePtr); nativeUpdateContrastLimit(mNativePtr); nativeUpdateSharpnessLimit(mNativePtr); nativeUpdateGainLimit(mNativePtr); nativeUpdateGammaLimit(mNativePtr); nativeUpdateSaturationLimit(mNativePtr); nativeUpdateHueLimit(mNativePtr); nativeUpdateZoomLimit(mNativePtr); nativeUpdateWhiteBlanceLimit(mNativePtr); nativeUpdateFocusLimit(mNativePtr); } if (DEBUG) { dumpControls(mControlSupports); dumpProc(mProcSupports); Log.v(TAG, String.format("Brightness:min=%d,max=%d,def=%d", mBrightnessMin, mBrightnessMax, mBrightnessDef)); Log.v(TAG, String.format("Contrast:min=%d,max=%d,def=%d", mContrastMin, mContrastMax, mContrastDef)); Log.v(TAG, String.format("Sharpness:min=%d,max=%d,def=%d", mSharpnessMin, mSharpnessMax, mSharpnessDef)); Log.v(TAG, String.format("Gain:min=%d,max=%d,def=%d", mGainMin, mGainMax, mGainDef)); Log.v(TAG, String.format("Gamma:min=%d,max=%d,def=%d", mGammaMin, mGammaMax, mGammaDef)); Log.v(TAG, String.format("Saturation:min=%d,max=%d,def=%d", mSaturationMin, mSaturationMax, mSaturationDef)); Log.v(TAG, String.format("Hue:min=%d,max=%d,def=%d", mHueMin, mHueMax, mHueDef)); Log.v(TAG, String.format("Zoom:min=%d,max=%d,def=%d", mZoomMin, mZoomMax, mZoomDef)); Log.v(TAG, String.format("WhiteBlance:min=%d,max=%d,def=%d", mWhiteBlanceMin, mWhiteBlanceMax, mWhiteBlanceDef)); Log.v(TAG, String.format("Focus:min=%d,max=%d,def=%d", mFocusMin, mFocusMax, mFocusDef)); } } } else { mControlSupports = mProcSupports = 0; } } private static final String[] SUPPORTS_CTRL = { "D0: Scanning Mode", "D1: Auto-Exposure Mode", "D2: Auto-Exposure Priority", "D3: Exposure Time (Absolute)", "D4: Exposure Time (Relative)", "D5: Focus (Absolute)", "D6: Focus (Relative)", "D7: Iris (Absolute)", "D8: Iris (Relative)", "D9: Zoom (Absolute)", "D10: Zoom (Relative)", "D11: PanTilt (Absolute)", "D12: PanTilt (Relative)", "D13: Roll (Absolute)", "D14: Roll (Relative)", "D15: Reserved", "D16: Reserved", "D17: Focus, Auto", "D18: Privacy", "D19: Focus, Simple", "D20: Window", "D21: Region of Interest", "D22: Reserved, set to zero", "D23: Reserved, set to zero", }; private static final String[] SUPPORTS_PROC = { "D0: Brightness", "D1: Contrast", "D2: Hue", "D3: Saturation", "D4: Sharpness", "D5: Gamma", "D6: White Balance Temperature", "D7: White Balance Component", "D8: Backlight Compensation", "D9: Gain", "D10: Power Line Frequency", "D11: Hue, Auto", "D12: White Balance Temperature, Auto", "D13: White Balance Component, Auto", "D14: Digital Multiplier", "D15: Digital Multiplier Limit", "D16: Analog Video Standard", "D17: Analog Video Lock Status", "D18: Contrast, Auto", "D19: Reserved. Set to zero", "D20: Reserved. Set to zero", "D21: Reserved. Set to zero", "D22: Reserved. Set to zero", "D23: Reserved. Set to zero", }; private static final void dumpControls(final long controlSupports) { Log.i(TAG, String.format("controlSupports=%x", controlSupports)); for (int i = 0; i < SUPPORTS_CTRL.length; i++) { Log.i(TAG, SUPPORTS_CTRL[i] + ((controlSupports & (0x1 << i)) != 0 ? "=enabled" : "=disabled")); } } private static final void dumpProc(final long procSupports) { Log.i(TAG, String.format("procSupports=%x", procSupports)); for (int i = 0; i < SUPPORTS_PROC.length; i++) { Log.i(TAG, SUPPORTS_PROC[i] + ((procSupports & (0x1 << i)) != 0 ? "=enabled" : "=disabled")); } } private final String getUSBFSName(final UsbControlBlock ctrlBlock) { String result = null; final String name = ctrlBlock.getDeviceName(); final String[] v = !TextUtils.isEmpty(name) ? name.split("/") : null; if ((v != null) && (v.length > 2)) { final StringBuilder sb = new StringBuilder(v[0]); for (int i = 1; i < v.length - 2; i++) sb.append("/").append(v[i]); result = sb.toString(); } if (TextUtils.isEmpty(result)) { Log.w(TAG, "failed to get USBFS path, try to use default path:" + name); result = DEFAULT_USBFS; } return result; } // #nativeCreate and #nativeDestroy are not static methods. private final native long nativeCreate(); private final native void nativeDestroy(final long id_camera); private static final native int nativeConnect(final long id_camera, final int venderId, final int productId, final int fileDescriptor, String usbfs); private static final native int nativeRelease(final long id_camera); private static final native int nativeSetStatusCallback(final long mNativePtr, final IStatusCallback callback); private static final native int nativeSetButtonCallback(final long mNativePtr, final IButtonCallback callback); private static final native int nativeSetPreviewSize(final long id_camera, final int width, final int height, final int mode, final float bandwidth); private static final native String nativeGetSupportedSize(final long id_camera); private static final native int nativeStartPreview(final long id_camera); private static final native int nativeStopPreview(final long id_camera); private static final native int nativeSetPreviewDisplay(final long id_camera, final Surface surface); private static final native int nativeSetFrameCallback(final long mNativePtr, final IFrameCallback callback, final int pixelFormat); //********************************************************************** /** * start movie capturing(this should call while previewing) * @param surface */ public void startCapture(final Surface surface) { if (mCtrlBlock != null && surface != null) { nativeSetCaptureDisplay(mNativePtr, surface); } else throw new NullPointerException("startCapture"); } /** * stop movie capturing */ public void stopCapture() { if (mCtrlBlock != null) { nativeSetCaptureDisplay(mNativePtr, null); } } private static final native int nativeSetCaptureDisplay(final long id_camera, final Surface surface); private static final native long nativeGetCtrlSupports(final long id_camera); private static final native long nativeGetProcSupports(final long id_camera); private final native int nativeUpdateScanningModeLimit(final long id_camera); private static final native int nativeSetScanningMode(final long id_camera, final int scanning_mode); private static final native int nativeGetScanningMode(final long id_camera); private final native int nativeUpdateExposureModeLimit(final long id_camera); private static final native int nativeSetExposureMode(final long id_camera, final int exposureMode); private static final native int nativeGetExposureMode(final long id_camera); private final native int nativeUpdateExposurePriorityLimit(final long id_camera); private static final native int nativeSetExposurePriority(final long id_camera, final int priority); private static final native int nativeGetExposurePriority(final long id_camera); private final native int nativeUpdateExposureLimit(final long id_camera); private static final native int nativeSetExposure(final long id_camera, final int exposure); private static final native int nativeGetExposure(final long id_camera); private final native int nativeUpdateExposureRelLimit(final long id_camera); private static final native int nativeSetExposureRel(final long id_camera, final int exposure_rel); private static final native int nativeGetExposureRel(final long id_camera); private final native int nativeUpdateAutoFocusLimit(final long id_camera); private static final native int nativeSetAutoFocus(final long id_camera, final boolean autofocus); private static final native int nativeGetAutoFocus(final long id_camera); private final native int nativeUpdateFocusLimit(final long id_camera); private static final native int nativeSetFocus(final long id_camera, final int focus); private static final native int nativeGetFocus(final long id_camera); private final native int nativeUpdateFocusRelLimit(final long id_camera); private static final native int nativeSetFocusRel(final long id_camera, final int focus_rel); private static final native int nativeGetFocusRel(final long id_camera); private final native int nativeUpdateIrisLimit(final long id_camera); private static final native int nativeSetIris(final long id_camera, final int iris); private static final native int nativeGetIris(final long id_camera); private final native int nativeUpdateIrisRelLimit(final long id_camera); private static final native int nativeSetIrisRel(final long id_camera, final int iris_rel); private static final native int nativeGetIrisRel(final long id_camera); private final native int nativeUpdatePanLimit(final long id_camera); private static final native int nativeSetPan(final long id_camera, final int pan); private static final native int nativeGetPan(final long id_camera); private final native int nativeUpdatePanRelLimit(final long id_camera); private static final native int nativeSetPanRel(final long id_camera, final int pan_rel); private static final native int nativeGetPanRel(final long id_camera); private final native int nativeUpdateTiltLimit(final long id_camera); private static final native int nativeSetTilt(final long id_camera, final int tilt); private static final native int nativeGetTilt(final long id_camera); private final native int nativeUpdateTiltRelLimit(final long id_camera); private static final native int nativeSetTiltRel(final long id_camera, final int tilt_rel); private static final native int nativeGetTiltRel(final long id_camera); private final native int nativeUpdateRollLimit(final long id_camera); private static final native int nativeSetRoll(final long id_camera, final int roll); private static final native int nativeGetRoll(final long id_camera); private final native int nativeUpdateRollRelLimit(final long id_camera); private static final native int nativeSetRollRel(final long id_camera, final int roll_rel); private static final native int nativeGetRollRel(final long id_camera); private final native int nativeUpdateAutoWhiteBlanceLimit(final long id_camera); private static final native int nativeSetAutoWhiteBlance(final long id_camera, final boolean autoWhiteBlance); private static final native int nativeGetAutoWhiteBlance(final long id_camera); private final native int nativeUpdateAutoWhiteBlanceCompoLimit(final long id_camera); private static final native int nativeSetAutoWhiteBlanceCompo(final long id_camera, final boolean autoWhiteBlanceCompo); private static final native int nativeGetAutoWhiteBlanceCompo(final long id_camera); private final native int nativeUpdateWhiteBlanceLimit(final long id_camera); private static final native int nativeSetWhiteBlance(final long id_camera, final int whiteBlance); private static final native int nativeGetWhiteBlance(final long id_camera); private final native int nativeUpdateWhiteBlanceCompoLimit(final long id_camera); private static final native int nativeSetWhiteBlanceCompo(final long id_camera, final int whiteBlance_compo); private static final native int nativeGetWhiteBlanceCompo(final long id_camera); private final native int nativeUpdateBacklightCompLimit(final long id_camera); private static final native int nativeSetBacklightComp(final long id_camera, final int backlight_comp); private static final native int nativeGetBacklightComp(final long id_camera); private final native int nativeUpdateBrightnessLimit(final long id_camera); private static final native int nativeSetBrightness(final long id_camera, final int brightness); private static final native int nativeGetBrightness(final long id_camera); private final native int nativeUpdateContrastLimit(final long id_camera); private static final native int nativeSetContrast(final long id_camera, final int contrast); private static final native int nativeGetContrast(final long id_camera); private final native int nativeUpdateAutoContrastLimit(final long id_camera); private static final native int nativeSetAutoContrast(final long id_camera, final boolean autocontrast); private static final native int nativeGetAutoContrast(final long id_camera); private final native int nativeUpdateSharpnessLimit(final long id_camera); private static final native int nativeSetSharpness(final long id_camera, final int sharpness); private static final native int nativeGetSharpness(final long id_camera); private final native int nativeUpdateGainLimit(final long id_camera); private static final native int nativeSetGain(final long id_camera, final int gain); private static final native int nativeGetGain(final long id_camera); private final native int nativeUpdateGammaLimit(final long id_camera); private static final native int nativeSetGamma(final long id_camera, final int gamma); private static final native int nativeGetGamma(final long id_camera); private final native int nativeUpdateSaturationLimit(final long id_camera); private static final native int nativeSetSaturation(final long id_camera, final int saturation); private static final native int nativeGetSaturation(final long id_camera); private final native int nativeUpdateHueLimit(final long id_camera); private static final native int nativeSetHue(final long id_camera, final int hue); private static final native int nativeGetHue(final long id_camera); private final native int nativeUpdateAutoHueLimit(final long id_camera); private static final native int nativeSetAutoHue(final long id_camera, final boolean autohue); private static final native int nativeGetAutoHue(final long id_camera); private final native int nativeUpdatePowerlineFrequencyLimit(final long id_camera); private static final native int nativeSetPowerlineFrequency(final long id_camera, final int frequency); private static final native int nativeGetPowerlineFrequency(final long id_camera); private final native int nativeUpdateZoomLimit(final long id_camera); private static final native int nativeSetZoom(final long id_camera, final int zoom); private static final native int nativeGetZoom(final long id_camera); private final native int nativeUpdateZoomRelLimit(final long id_camera); private static final native int nativeSetZoomRel(final long id_camera, final int zoom_rel); private static final native int nativeGetZoomRel(final long id_camera); private final native int nativeUpdateDigitalMultiplierLimit(final long id_camera); private static final native int nativeSetDigitalMultiplier(final long id_camera, final int multiplier); private static final native int nativeGetDigitalMultiplier(final long id_camera); private final native int nativeUpdateDigitalMultiplierLimitLimit(final long id_camera); private static final native int nativeSetDigitalMultiplierLimit(final long id_camera, final int multiplier_limit); private static final native int nativeGetDigitalMultiplierLimit(final long id_camera); private final native int nativeUpdateAnalogVideoStandardLimit(final long id_camera); private static final native int nativeSetAnalogVideoStandard(final long id_camera, final int standard); private static final native int nativeGetAnalogVideoStandard(final long id_camera); private final native int nativeUpdateAnalogVideoLockStateLimit(final long id_camera); private static final native int nativeSetAnalogVideoLoackState(final long id_camera, final int state); private static final native int nativeGetAnalogVideoLoackState(final long id_camera); private final native int nativeUpdatePrivacyLimit(final long id_camera); private static final native int nativeSetPrivacy(final long id_camera, final boolean privacy); private static final native int nativeGetPrivacy(final long id_camera); }
package com.serenegiant.usb.widget; /* * UVCCamera * library and sample to access to UVC web camera on non-rooted Android device * * Copyright (c) 2015 saki [email protected] * * File name: UVCCameraTextureView.java * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * All files in the folder are under this Apache License, Version 2.0. * Files in the jni/libjpeg, jni/libusb, jin/libuvc, jni/rapidjson folder may have a different license, see the respective files. */ import android.content.Context; import android.graphics.Bitmap; import android.graphics.SurfaceTexture; import android.util.AttributeSet; import android.util.Log; import android.view.TextureView; import com.serenegiant.usb.encode.MediaEncoder; /** * change the view size with keeping the specified aspect ratio. * if you set this view with in a FrameLayout and set property "android:layout_gravity="center", * you can show this view in the center of screen and keep the aspect ratio of content * XXX it is better that can set the aspect raton a a xml property */ public class UVCCameraTextureView extends TextureView // API >= 14 implements TextureView.SurfaceTextureListener, CameraViewInterface { private static final boolean DEBUG = true; // TODO set false on release private static final String TAG = "UVCCameraTextureView"; private double mRequestedAspect = -1.0; private boolean mHasSurface; private final Object mCaptureSync = new Object(); private Bitmap mTempBitmap; private boolean mReqesutCaptureStillImage; public UVCCameraTextureView(final Context context) { this(context, null, 0); } public UVCCameraTextureView(final Context context, final AttributeSet attrs) { this(context, attrs, 0); } public UVCCameraTextureView(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); setSurfaceTextureListener(this); } @Override public void onResume() { if (DEBUG) Log.v(TAG, "onResume:"); } @Override public void onPause() { if (DEBUG) Log.v(TAG, "onPause:"); if (mTempBitmap != null) { mTempBitmap.recycle(); mTempBitmap = null; } } @Override public void setAspectRatio(final double aspectRatio) { if (aspectRatio < 0) { throw new IllegalArgumentException(); } if (mRequestedAspect != aspectRatio) { mRequestedAspect = aspectRatio; requestLayout(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mRequestedAspect > 0) { int initialWidth = MeasureSpec.getSize(widthMeasureSpec); int initialHeight = MeasureSpec.getSize(heightMeasureSpec); final int horizPadding = getPaddingLeft() + getPaddingRight(); final int vertPadding = getPaddingTop() + getPaddingBottom(); initialWidth -= horizPadding; initialHeight -= vertPadding; final double viewAspectRatio = (double)initialWidth / initialHeight; final double aspectDiff = mRequestedAspect / viewAspectRatio - 1; if (Math.abs(aspectDiff) > 0.01) { if (aspectDiff > 0) { // width priority decision initialHeight = (int) (initialWidth / mRequestedAspect); } else { // height priority decison initialWidth = (int) (initialHeight * mRequestedAspect); } initialWidth += horizPadding; initialHeight += vertPadding; widthMeasureSpec = MeasureSpec.makeMeasureSpec(initialWidth, MeasureSpec.EXACTLY); heightMeasureSpec = MeasureSpec.makeMeasureSpec(initialHeight, MeasureSpec.EXACTLY); } } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override public void onSurfaceTextureAvailable(final SurfaceTexture surface, final int width, final int height) { if (DEBUG) Log.v(TAG, "onSurfaceTextureAvailable:" + surface); // mRenderHandler = RenderHandler.createHandler(surface); mHasSurface = true; } @Override public void onSurfaceTextureSizeChanged(final SurfaceTexture surface, final int width, final int height) { if (DEBUG) Log.v(TAG, "onSurfaceTextureSizeChanged:" + surface); mTempBitmap = null; } @Override public boolean onSurfaceTextureDestroyed(final SurfaceTexture surface) { if (DEBUG) Log.v(TAG, "onSurfaceTextureDestroyed:" + surface); mHasSurface = false; return true; } @Override public void onSurfaceTextureUpdated(final SurfaceTexture surface) { synchronized (mCaptureSync) { if (mReqesutCaptureStillImage) { mReqesutCaptureStillImage = false; if (mTempBitmap == null) // TODO modify this to change output image size mTempBitmap = getBitmap(1600, 1200);//3264,2448 else getBitmap(mTempBitmap); mCaptureSync.notifyAll(); } } } @Override public boolean hasSurface() { return mHasSurface; } /** * capture preview image as a bitmap * this method blocks current thread until bitmap is ready * if you call this method at almost same time from different thread, * the returned bitmap will be changed while you are processing the bitmap * (because we return same instance of bitmap on each call for memory saving) * if you need to call this method from multiple thread, * you should change this method(copy and return) */ @Override public Bitmap captureStillImage() { synchronized (mCaptureSync) { mReqesutCaptureStillImage = true; try { mCaptureSync.wait(); } catch (final InterruptedException e) { } return mTempBitmap; } } @Override public void setVideoEncoder(final MediaEncoder encoder) { } }
package com.serenegiant.usb; /* * UVCCamera * library and sample to access to UVC web camera on non-rooted Android device * * Copyright (c) 2014-2015 saki [email protected] * * File name: USBMonitor.java * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * All files in the folder are under this Apache License, Version 2.0. * Files in the jni/libjpeg, jni/libusb, jin/libuvc, jni/rapidjson folder may have a different license, see the respective files. */ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbManager; import android.os.Handler; import android.util.Log; import android.util.SparseArray; public final class USBMonitor { private static final boolean DEBUG = false; // TODO set false on production private static final String TAG = "USBMonitor"; private static final String ACTION_USB_PERMISSION_BASE = "com.serenegiant.USB_PERMISSION."; private final String ACTION_USB_PERMISSION = ACTION_USB_PERMISSION_BASE + hashCode(); public static final String ACTION_USB_DEVICE_ATTACHED = "android.hardware.usb.action.USB_DEVICE_ATTACHED"; private final ConcurrentHashMap<UsbDevice, UsbControlBlock> mCtrlBlocks = new ConcurrentHashMap<UsbDevice, UsbControlBlock>(); private final WeakReference<Context> mWeakContext; private final UsbManager mUsbManager; private final OnDeviceConnectListener mOnDeviceConnectListener; private PendingIntent mPermissionIntent = null; private List<DeviceFilter> mDeviceFilters = new ArrayList<DeviceFilter>(); private final Handler mHandler = new Handler(); public interface OnDeviceConnectListener { /** * called when device attached * @param device */ public void onAttach(UsbDevice device); /** * called when device dettach(after onDisconnect) * @param device */ public void onDettach(UsbDevice device); /** * called after device opend * @param device * @param createNew */ public void onConnect(UsbDevice device, UsbControlBlock ctrlBlock, boolean createNew); /** * called when USB device removed or its power off (this callback is called after device closing) * @param device * @param ctrlBlock */ public void onDisconnect(UsbDevice device, UsbControlBlock ctrlBlock); /** * called when canceled or could not get permission from user */ public void onCancel(); } public USBMonitor(final Context context, final OnDeviceConnectListener listener) { if (DEBUG) Log.v(TAG, "USBMonitor:Constructor"); /* if (listener == null) throw new IllegalArgumentException("OnDeviceConnectListener should not null."); */ mWeakContext = new WeakReference<Context>(context); mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE); mOnDeviceConnectListener = listener; if (DEBUG) Log.v(TAG, "USBMonitor:mUsbManager=" + mUsbManager); } public void destroy() { if (DEBUG) Log.i(TAG, "destroy:"); unregister(); final Set<UsbDevice> keys = mCtrlBlocks.keySet(); if (keys != null) { UsbControlBlock ctrlBlock; try { for (final UsbDevice key: keys) { ctrlBlock = mCtrlBlocks.remove(key); ctrlBlock.close(); } } catch (final Exception e) { Log.e(TAG, "destroy:", e); } mCtrlBlocks.clear(); } } /** * register BroadcastReceiver to monitor USB events */ public synchronized void register() { if (mPermissionIntent == null) { if (DEBUG) Log.i(TAG, "register:"); final Context context = mWeakContext.get(); if (context != null) { mPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0); final IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); context.registerReceiver(mUsbReceiver, filter); } mDeviceCounts = 0; mHandler.postDelayed(mDeviceCheckRunnable, 1000); } } /** * unregister BroadcastReceiver */ public synchronized void unregister() { if (mPermissionIntent != null) { if (DEBUG) Log.i(TAG, "unregister:"); final Context context = mWeakContext.get(); if (context != null) { context.unregisterReceiver(mUsbReceiver); } mPermissionIntent = null; } mDeviceCounts = 0; mHandler.removeCallbacks(mDeviceCheckRunnable); } public synchronized boolean isRegistered() { return mPermissionIntent != null; } /** * set device filter * @param filter */ public void setDeviceFilter(final DeviceFilter filter) { mDeviceFilters.clear(); mDeviceFilters.add(filter); } /** * set device filters * @param filters */ public void setDeviceFilter(final List<DeviceFilter> filters) { mDeviceFilters.clear(); mDeviceFilters.addAll(filters); } /** * return the number of connected USB devices that matched device filter * @return */ public int getDeviceCount() { return getDeviceList().size(); } /** * return device list, return empty list if no device matched * @return */ public List<UsbDevice> getDeviceList() { return getDeviceList(mDeviceFilters); } /** * return device list, return empty list if no device matched * @param filters * @return */ public List<UsbDevice> getDeviceList(final List<DeviceFilter> filters) { final HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList(); final List<UsbDevice> result = new ArrayList<UsbDevice>(); if (deviceList != null) { for (final DeviceFilter filter: filters) { final Iterator<UsbDevice> iterator = deviceList.values().iterator(); UsbDevice device; while (iterator.hasNext()) { device = iterator.next(); if ((filter == null) || (filter.matches(device))) { result.add(device); } } } } return result; } /** * return device list, return empty list if no device matched * @param filter * @return */ public List<UsbDevice> getDeviceList(final DeviceFilter filter) { final HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList(); final List<UsbDevice> result = new ArrayList<UsbDevice>(); if (deviceList != null) { final Iterator<UsbDevice> iterator = deviceList.values().iterator(); UsbDevice device; while (iterator.hasNext()) { device = iterator.next(); if ((filter == null) || (filter.matches(device))) { result.add(device); } } } return result; } /** * get USB device list * @return */ public Iterator<UsbDevice> getDevices() { Iterator<UsbDevice> iterator = null; final HashMap<String, UsbDevice> list = mUsbManager.getDeviceList(); if (list != null) iterator = list.values().iterator(); return iterator; } /** * output device list to LogCat */ public final void dumpDevices() { final HashMap<String, UsbDevice> list = mUsbManager.getDeviceList(); if (list != null) { final Set<String> keys = list.keySet(); if (keys != null && keys.size() > 0) { final StringBuilder sb = new StringBuilder(); for (final String key: keys) { final UsbDevice device = list.get(key); final int num_interface = device != null ? device.getInterfaceCount() : 0; sb.setLength(0); for (int i = 0; i < num_interface; i++) { sb.append(String.format("interface%d:%s", i, device.getInterface(i).toString())); } Log.i(TAG, "key=" + key + ":" + device + ":" + sb.toString()); } } else { Log.i(TAG, "no device"); } } else { Log.i(TAG, "no device"); } } /** * return whether the specific Usb device has permission * @param device * @return */ public boolean hasPermission(final UsbDevice device) { return device != null && mUsbManager.hasPermission(device); } /** * request permission to access to USB device * @param device */ public synchronized void requestPermission(final UsbDevice device) { if (DEBUG) Log.v(TAG, "requestPermission:device=" + device); if (mPermissionIntent != null) { if (device != null) { if (mUsbManager.hasPermission(device)) { processConnect(device); } else { mUsbManager.requestPermission(device, mPermissionIntent); } } else { processCancel(device); } } else { processCancel(device); } } /** * BroadcastReceiver for USB permission */ private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { final String action = intent.getAction(); if (ACTION_USB_PERMISSION.equals(action)) { synchronized (USBMonitor.this) { final UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { if (device != null) { processConnect(device); } } else { processCancel(device); } } } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) { final UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); processAttach(device); } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { final UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (device != null) { UsbControlBlock ctrlBlock = null; ctrlBlock = mCtrlBlocks.remove(device); if (ctrlBlock != null) { ctrlBlock.close(); } mDeviceCounts = 0; processDettach(device); } } } }; private volatile int mDeviceCounts = 0; private final Runnable mDeviceCheckRunnable = new Runnable() { @Override public void run() { final int n = getDeviceCount(); if (n != mDeviceCounts) { if (n > mDeviceCounts) { mDeviceCounts = n; if (mOnDeviceConnectListener != null) mOnDeviceConnectListener.onAttach(null); } } mHandler.postDelayed(this, 2000); // confirm every 2 seconds } }; private final void processConnect(final UsbDevice device) { if (DEBUG) Log.v(TAG, "processConnect:"); mHandler.post(new Runnable() { @Override public void run() { UsbControlBlock ctrlBlock; final boolean createNew; ctrlBlock = mCtrlBlocks.get(device); if (ctrlBlock == null) { ctrlBlock = new UsbControlBlock(USBMonitor.this, device); mCtrlBlocks.put(device, ctrlBlock); createNew = true; } else { createNew = false; } if (mOnDeviceConnectListener != null) { final UsbControlBlock ctrlB = ctrlBlock; mOnDeviceConnectListener.onConnect(device, ctrlB, createNew); } } }); } private final void processCancel(final UsbDevice device) { if (DEBUG) Log.v(TAG, "processCancel:"); if (mOnDeviceConnectListener != null) { mHandler.post(new Runnable() { @Override public void run() { mOnDeviceConnectListener.onCancel(); } }); } } private final void processAttach(final UsbDevice device) { if (DEBUG) Log.v(TAG, "processAttach:"); if (mOnDeviceConnectListener != null) { mHandler.post(new Runnable() { @Override public void run() { mOnDeviceConnectListener.onAttach(device); } }); } } private final void processDettach(final UsbDevice device) { if (DEBUG) Log.v(TAG, "processDettach:"); if (mOnDeviceConnectListener != null) { mHandler.post(new Runnable() { @Override public void run() { mOnDeviceConnectListener.onDettach(device); } }); } } public static final class UsbControlBlock { private final WeakReference<USBMonitor> mWeakMonitor; private final WeakReference<UsbDevice> mWeakDevice; protected UsbDeviceConnection mConnection; private final SparseArray<UsbInterface> mInterfaces = new SparseArray<UsbInterface>(); /** * this class needs permission to access USB device before constructing * @param monitor * @param device */ public UsbControlBlock(final USBMonitor monitor, final UsbDevice device) { if (DEBUG) Log.i(TAG, "UsbControlBlock:constructor"); mWeakMonitor = new WeakReference<USBMonitor>(monitor); mWeakDevice = new WeakReference<UsbDevice>(device); mConnection = monitor.mUsbManager.openDevice(device); final String name = device.getDeviceName(); if (mConnection != null) { if (DEBUG) { final int desc = mConnection.getFileDescriptor(); final byte[] rawDesc = mConnection.getRawDescriptors(); Log.i(TAG, "UsbControlBlock:name=" + name + ", desc=" + desc + ", rawDesc=" + rawDesc); } } else { Log.e(TAG, "could not connect to device " + name); } } public UsbDevice getDevice() { return mWeakDevice.get(); } public String getDeviceName() { final UsbDevice device = mWeakDevice.get(); return device != null ? device.getDeviceName() : ""; } public UsbDeviceConnection getUsbDeviceConnection() { return mConnection; } public synchronized int getFileDescriptor() { return mConnection != null ? mConnection.getFileDescriptor() : -1; } public byte[] getRawDescriptors() { return mConnection != null ? mConnection.getRawDescriptors() : null; } public int getVenderId() { final UsbDevice device = mWeakDevice.get(); return device != null ? device.getVendorId() : 0; } public int getProductId() { final UsbDevice device = mWeakDevice.get(); return device != null ? device.getProductId() : 0; } public synchronized String getSerial() { return mConnection != null ? mConnection.getSerial() : null; } /** * open specific interface * @param interfaceIndex * @return */ public synchronized UsbInterface open(final int interfaceIndex) { if (DEBUG) Log.i(TAG, "UsbControlBlock#open:" + interfaceIndex); final UsbDevice device = mWeakDevice.get(); UsbInterface intf = null; intf = mInterfaces.get(interfaceIndex); if (intf == null) { intf = device.getInterface(interfaceIndex); if (intf != null) { synchronized (mInterfaces) { mInterfaces.append(interfaceIndex, intf); } } } return intf; } /** * close specified interface. USB device itself still keep open. * @param interfaceIndex */ public void close(final int interfaceIndex) { UsbInterface intf = null; synchronized (mInterfaces) { intf = mInterfaces.get(interfaceIndex); if (intf != null) { mInterfaces.delete(interfaceIndex); mConnection.releaseInterface(intf); } } } /** * close specified interface. USB device itself still keep open. */ public synchronized void close() { if (DEBUG) Log.i(TAG, "UsbControlBlock#close:"); if (mConnection != null) { final int n = mInterfaces.size(); int key; UsbInterface intf; for (int i = 0; i < n; i++) { key = mInterfaces.keyAt(i); intf = mInterfaces.get(key); mConnection.releaseInterface(intf); } mConnection.close(); mConnection = null; final USBMonitor monitor = mWeakMonitor.get(); if (monitor != null) { if (monitor.mOnDeviceConnectListener != null) { final UsbDevice device = mWeakDevice.get(); monitor.mOnDeviceConnectListener.onDisconnect(device, this); } monitor.mCtrlBlocks.remove(getDevice()); } } } /* @Override protected void finalize() throws Throwable { close(); super.finalize(); } */ } }