简介
UVCAndroid是一款用于安卓UVC相机的通用开发库。
GitHub源码地址:https://github.com/shiyinghan/UVCAndroid
主要功能
主要功能包括:
(1) 支持USB Camera设备检测,画面实时预览;
(2) 支持抓拍jpg格式图片,可设置图片压缩质量;
(3) 支持录制mp4格式视频,可屏蔽音频,可设置视频和音频的录制参数;
(4) 支持获取camera支持的分辨率,和分辨率切换;
(5) 支持预览自动识别各种相机的分辨率;
(6) 支持旋转摄像头90度、180度、270度;
(7) 支持调整对比度、亮度、色调、饱和度、白平衡等等一些相机控制参数;
(8) 支持多预览和多摄像头;
(9) 支持Android5.0+;
如何使用
1. 添加依赖到本地工程
第一步 添加mavenCentral仓库到工程gradle文件
Step 1. Add the mavenCentral repository to your build file
Add it in your root build.gradle at the end of repositories:
allprojects { repositories {...mavenCentral() }}
第二步 添加依赖到app Module的gradle文件
dependencies { implementation 'com.herohan:UVCAndroid:1.0.5'}
2. 获取权限
Request permissions
List needPermissions = new ArrayList<>(); needPermissions.add(Manifest.permission.CAMERA); needPermissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);//拍照和录制视频时需要该权限 //needPermissions.add(Manifest.permission.MANAGE_EXTERNAL_STORAGE); //Android 11 使用该权限替代 WRITE_EXTERNAL_STORAGE needPermissions.add(Manifest.permission.RECORD_AUDIO);//录制视频时需要音频时需要该权限//这里使用XXPermissions开源框架获取权限,你也可以使用系统原生的,或者其他开源框架获取权限 XXPermissions.with(this) .permission(needPermissions) .request((permissions, all) -> { if(!all){ return; }//摄像头业务操作 });
3. 初始化UVC业务类,设置UVC摄像头状态回调,设置TextureView或者SurfaceView的Surface监听回调
Initialize CameraHelper,set UVC Camera state callback
private ICameraHelper mCameraHelper; private AspectRatioSurfaceView mCameraViewMain; private ICameraHelper.StateCallback mStateListener;//UVC摄像头状态回调mStateListener = new ICameraHelper.StateCallback() {//插入UVC设备 @Override public void onAttach(UsbDevice device) { //设置为当前设备(如果没有权限,会显示授权对话框) mCameraHelper.selectDevice(device); }//打开UVC设备成功(也就是已经获取到UVC设备的权限) @Override public void onDeviceOpen(UsbDevice device, boolean isFirstOpen) { //打开UVC摄像头 mCameraHelper.openCamera(); }//打开摄像头成功 @Override public void onCameraOpen(UsbDevice device) { //开始预览 mCameraHelper.startPreview();//获取预览使用的Size(包括帧格式、宽度、高度、FPS) Size size = mCameraHelper.getPreviewSize(); if (size != null) { int width = size.width; int height = size.height; //需要自适应摄像头分辨率的话,设置新的宽高比 mCameraViewMain.setAspectRatio(width, height); }//添加预览Surface mCameraHelper.addSurface(mCameraViewMain.getHolder().getSurface(), false); }//关闭摄像头成功 @Override public void onCameraClose(UsbDevice device) { if (mCameraHelper != null) { //移除预览Surface mCameraHelper.removeSurface(mCameraViewMain.getHolder().getSurface()); } }//关闭UVC设备成功 @Override public void onDeviceClose(UsbDevice device) { }//断开UVC设备 @Override public void onDetach(UsbDevice device) { }//用户没有授予访问UVC设备的权限 @Override public void onCancel(UsbDevice device) { } }; //设置SurfaceView的Surface监听回调 mCameraViewMain.getHolder().addCallback(new SurfaceHolder.Callback() { //创建了新的Surface @Override public void surfaceCreated(@NonNull SurfaceHolder holder) { if (mCameraHelper != null) { //添加预览Surface mCameraHelper.addSurface(holder.getSurface(), false); } }//Surface发生了改变 @Override public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) { }//销毁了原来的Surface @Override public void surfaceDestroyed(@NonNull SurfaceHolder holder) { if (mCameraHelper != null) { //移除预览Surface mCameraHelper.removeSurface(holder.getSurface()); } } }); mCameraHelper = new CameraHelper();//设置UVC摄像头状态回调 mCameraHelper.setStateCallback(mStateListener);
4. 释放UVC业务类(包含取消UVC摄像头状态回调,停止Camera预览,关闭Camera等操作)
Release CameraHelper(including canceling UVC Camera state callback, stopping Camera preview, etc.)
mCameraHelper.release();
5. 图片抓拍
Image Capture
//设置视图片抓拍全局参数(非必须,可以不设置,使用默认值)mCameraHelper.setImageCaptureConfig( mCameraHelper.getImageCaptureConfig().setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY));// mCameraHelper.setImageCaptureConfig(// mCameraHelper.getImageCaptureConfig().setJpegCompressionQuality(90));//设置需要保存图片文件 File file = FileUtils.getCaptureFile(this, Environment.DIRECTORY_DCIM, ".jpg"); ImageCapture.OutputFileOptions options = new ImageCapture.OutputFileOptions.Builder(file).build();// ContentValues contentValues = new ContentValues();// contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "NEW_IMAGE");// contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");//// ImageCapture.OutputFileOptions options = new ImageCapture.OutputFileOptions.Builder(// getContentResolver(),// MediaStore.Images.Media.EXTERNAL_CONTENT_URI,// contentValues).build();// ByteArrayOutputStream outputStream = new ByteArrayOutputStream();// ImageCapture.OutputFileOptions options = new ImageCapture.OutputFileOptions.Builder(outputStream).build();//进行图片抓拍 mCameraHelper.takePicture(options, new ImageCapture.OnImageCaptureCallback() { //图片抓拍成功 @Override public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) { Toast.makeText(TakePictureActivity.this, "save \"" + UriHelper.getPath(TakePictureActivity.this, outputFileResults.getSavedUri()) + "\"", Toast.LENGTH_SHORT).show(); }//图片抓拍出现错误 @Override public void onError(int imageCaptureError, @NonNull String message, @Nullable Throwable cause) { Toast.makeText(TakePictureActivity.this, message, Toast.LENGTH_SHORT).show(); } });
6. 录制视频
Video Capture
//设置视频录制全局参数(非必须,可以不设置,使用默认值)mCameraHelper.setVideoCaptureConfig( mCameraHelper.getVideoCaptureConfig() .setAudioCaptureEnable(true) // true:有音频;false:没有音频(默认为true) .setBitRate((int) (1024 * 1024 * 25 * 0.25)) .setVideoFrameRate(25) .setIFrameInterval(1));//设置需要保存视频文件File file = FileUtils.getCaptureFile(this, Environment.DIRECTORY_MOVIES, ".mp4"); VideoCapture.OutputFileOptions options = new VideoCapture.OutputFileOptions.Builder(file).build();// ContentValues contentValues = new ContentValues();// contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "NEW_VIDEO");// contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4");//// VideoCapture.OutputFileOptions options = new VideoCapture.OutputFileOptions.Builder(// getContentResolver(),// MediaStore.Video.Media.EXTERNAL_CONTENT_URI,// contentValues).build();//开始录制 mCameraHelper.startRecording(options, new VideoCapture.OnVideoCaptureCallback() { @Override public void onStart() { }//视频录制成功 @Override public void onVideoSaved(@NonNull VideoCapture.OutputFileResults outputFileResults) { Toast.makeText( RecordVideoActivity.this, "save \"" + UriHelper.getPath(RecordVideoActivity.this, outputFileResults.getSavedUri()) + "\"", Toast.LENGTH_SHORT).show(); }//视频录制出现错误 @Override public void onError(int videoCaptureError, @NonNull String message, @Nullable Throwable cause) { Toast.makeText(RecordVideoActivity.this, message, Toast.LENGTH_LONG).show(); } });
7. 改变摄像机预览参数(包括帧格式、宽度、高度、FPS)
Set camera preview parameters (including frame format, width, height, FPS)
//停止相机预览mCameraHelper.stopPreview();//设置摄像机预览参数mCameraHelper.setPreviewSize(size);//开始相机预览mCameraHelper.startPreview();//需要自适应摄像头分辨率的话,设置新的宽高比mCameraViewMain.setAspectRatio(mPreviewWidth, mPreviewHeight);
8. 调整对比度、亮度、色调、饱和度、白平衡等等一些相机控制参数
Adjust contrast, brightness, hue, saturation, white balance, and other camera controls
//获取UVCControl对象,通过该对象调整相机控制参数UVCControl control = mCameraHelper.getUVCControl();//根据监听器设置各种相机控制参数private void setAllControlChangeListener(UVCControl controls) { // Brightness isbBrightness.setOnSeekChangeListener( (MyOnSeekChangeListener) seekParams -> controls.setBrightness(seekParams.progress)); // Contrast isbContrast.setOnSeekChangeListener( (MyOnSeekChangeListener) seekParams -> controls.setContrast(seekParams.progress)); // Contrast Auto cbContrastAuto.setOnCheckedChangeListener((buttonView, isChecked) -> { controls.setContrastAuto(isChecked); }); // Hue isbHue.setOnSeekChangeListener( (MyOnSeekChangeListener) seekParams -> controls.setHue(seekParams.progress)); // Hue Auto cbHueAuto.setOnCheckedChangeListener((buttonView, isChecked) -> { controls.setHueAuto(isChecked); }); // Saturation isbSaturation.setOnSeekChangeListener( (MyOnSeekChangeListener) seekParams -> controls.setSaturation(seekParams.progress)); // Sharpness isbSharpness.setOnSeekChangeListener( (MyOnSeekChangeListener) seekParams -> controls.setSharpness(seekParams.progress)); // Gamma isbGamma.setOnSeekChangeListener( (MyOnSeekChangeListener) seekParams -> controls.setGamma(seekParams.progress)); // White Balance isbWhiteBalance.setOnSeekChangeListener( (MyOnSeekChangeListener) seekParams -> controls.setWhiteBalance(seekParams.progress)); // White Balance Auto cbWhiteBalanceAuto.setOnCheckedChangeListener((buttonView, isChecked) -> { controls.setWhiteBalanceAuto(isChecked); }); // Backlight Compensation isbBacklightComp.setOnSeekChangeListener( (MyOnSeekChangeListener) seekParams -> controls.setBacklightComp(seekParams.progress)); // Gain isbGain.setOnSeekChangeListener( (MyOnSeekChangeListener) seekParams -> controls.setGain(seekParams.progress)); // Exposure Time isbExposureTime.setOnSeekChangeListener( (MyOnSeekChangeListener) seekParams -> controls.setExposureTimeAbsolute(seekParams.progress)); // Exposure Time Auto cbExposureTimeAuto.setOnCheckedChangeListener((buttonView, isChecked) -> { controls.setExposureTimeAuto(isChecked); }); // Iris isbIris.setOnSeekChangeListener( (MyOnSeekChangeListener) seekParams -> controls.setIrisAbsolute(seekParams.progress)); // Focus isbFocus.setOnSeekChangeListener( (MyOnSeekChangeListener) seekParams -> controls.setFocusAbsolute(seekParams.progress)); // Focus Auto cbFocusAuto.setOnCheckedChangeListener((buttonView, isChecked) -> { controls.setFocusAuto(isChecked); }); // Zoom isbZoom.setOnSeekChangeListener( (MyOnSeekChangeListener) seekParams -> controls.setZoomAbsolute(seekParams.progress)); // Pan isbPan.setOnSeekChangeListener( (MyOnSeekChangeListener) seekParams -> controls.setPanAbsolute(seekParams.progress)); // Tilt isbTilt.setOnSeekChangeListener( (MyOnSeekChangeListener) seekParams -> controls.setTiltAbsolute(seekParams.progress)); // Roll isbRoll.setOnSeekChangeListener( (MyOnSeekChangeListener) seekParams -> controls.setRollAbsolute(seekParams.progress)); // Power Line Frequency rgPowerLineFrequency.setOnCheckedChangeListener((group, checkedId) -> { int value = 0; if (checkedId == R.id.rbPowerLineFrequencyDisable) { value = 0; } else if (checkedId == R.id.rbPowerLineFrequency50Hz) { value = 1; } else if (checkedId == R.id.rbPowerLineFrequency60Hz) { value = 2; } else if (checkedId == R.id.rbPowerLineFrequencyAuto) { value = 3; } controls.setPowerlineFrequency(value); }); }// 重置所有相机控制参数为初试值private void resetAllControlParams(UVCControl control) { // Brightness control.resetBrightness(); // Contrast control.resetContrast(); // Contrast Auto control.resetContrastAuto(); // Hue control.resetHue(); // Hue Auto control.resetHueAuto(); // Saturation control.resetSaturation(); // Sharpness control.resetSharpness(); // Gamma control.resetGamma(); // White Balance control.resetWhiteBalance(); // White Balance Auto control.resetWhiteBalanceAuto(); // Backlight Compensation control.resetBacklightComp(); // Gain control.resetGain(); // Exposure Time control.resetExposureTimeAbsolute(); // Auto-Exposure Mode control.resetAutoExposureMode(); // Iris control.resetIrisAbsolute(); // Focus control.resetFocusAbsolute(); // Focus Auto control.resetFocusAuto(); // Zoom control.resetZoomAbsolute(); // Pan control.resetPanAbsolute(); // Tilt control.resetTiltAbsolute(); // Roll control.resetRollAbsolute(); // Power Line Frequency control.resetPowerlineFrequency(); }
9.旋转摄像头90度、180度、270度,设置摄像头预览镜像
Rotate the camera 90 degrees, 180 degrees, and 270 degrees , set the camera preview mirror
//旋转摄像头private void rotateBy(int angle) { mPreviewRotation += angle; mPreviewRotation %= 360; if (mPreviewRotation < 0) { mPreviewRotation += 360; } if (mCameraHelper != null) { mCameraHelper.setPreviewConfig( mCameraHelper.getPreviewConfig().setRotation(mPreviewRotation)); } }//设置水平镜像显示 private void flipHorizontally() { if (mCameraHelper != null) { mCameraHelper.setPreviewConfig( mCameraHelper.getPreviewConfig().setMirror(MirrorMode.MIRROR_HORIZONTAL)); } }//设置垂直镜像显示 private void flipVertically() { if (mCameraHelper != null) { mCameraHelper.setPreviewConfig( mCameraHelper.getPreviewConfig().setMirror(MirrorMode.MIRROR_VERTICAL)); } }
10.设置多个预览
Set multiple previews
mCameraHelper.addSurface(svCameraViewMain.getHolder().getSurface(), false);mCameraHelper.addSurface(svCameraViewSecond.getHolder().getSurface(), false);mCameraHelper.addSurface(svCameraViewThird.getHolder().getSurface(), false);mCameraHelper.addSurface(svCameraViewFourth.getHolder().getSurface(), false);
11.设置多个摄像头(USB2.0受带宽所限,有可能无法同时连接多个摄像头)
Setting multiple Cameras
private ICameraHelper mCameraHelperLeft; private ICameraHelper mCameraHelperRight; private AspectRatioSurfaceView svCameraViewLeft; private AspectRatioSurfaceView svCameraViewRight;private UsbDevice mUsbDeviceLeft; private UsbDevice mUsbDeviceRight;private final ICameraHelper.StateCallback mStateListenerLeft = new ICameraHelper.StateCallback() {@Override public void onAttach(UsbDevice device) { synchronized (mSync) { if (mUsbDeviceLeft == null && !device.equals(mUsbDeviceRight)) { selectDeviceLeft(device); } } } @Override public void onDeviceOpen(UsbDevice device, boolean isFirstOpen) { if (device.equals(mUsbDeviceLeft)) { UVCParam param = new UVCParam(); param.setQuirks(UVCCamera.UVC_QUIRK_FIX_BANDWIDTH); mCameraHelperLeft.openCamera(param); } } @Override public void onCameraOpen(UsbDevice device) { if (device.equals(mUsbDeviceLeft)) { mCameraHelperLeft.startPreview(); Size size = mCameraHelperLeft.getPreviewSize(); if (size != null) { int width = size.width; int height = size.height; //auto aspect ratio svCameraViewLeft.setAspectRatio(width, height); } mCameraHelperLeft.addSurface(svCameraViewLeft.getHolder().getSurface(), false); mIsCameraLeftConnected = true; } } @Override public void onCameraClose(UsbDevice device) { if (device.equals(mUsbDeviceLeft)) { if (mCameraHelperLeft != null) { mCameraHelperLeft.removeSurface(svCameraViewLeft.getHolder().getSurface()); } mIsCameraLeftConnected = false; } } @Override public void onDeviceClose(UsbDevice device) { } @Override public void onDetach(UsbDevice device) { if (device.equals(mUsbDeviceLeft)) { mUsbDeviceLeft = null; } } @Override public void onCancel(UsbDevice device) { if (device.equals(mUsbDeviceLeft)) { mUsbDeviceLeft = null; } }};private final ICameraHelper.StateCallback mStateListenerRight = new ICameraHelper.StateCallback() {@Override public void onAttach(UsbDevice device) { synchronized (mSync) { if (mUsbDeviceRight == null && !device.equals(mUsbDeviceLeft)) { selectDeviceRight(device); } } } @Override public void onDeviceOpen(UsbDevice device, boolean isFirstOpen) { if (device.equals(mUsbDeviceRight)) { UVCParam param = new UVCParam(); param.setQuirks(UVCCamera.UVC_QUIRK_FIX_BANDWIDTH); mCameraHelperRight.openCamera(param); } } @Override public void onCameraOpen(UsbDevice device) { if (device.equals(mUsbDeviceRight)) { mCameraHelperRight.startPreview(); Size size = mCameraHelperRight.getPreviewSize(); if (size != null) { int width = size.width; int height = size.height; //auto aspect ratio svCameraViewRight.setAspectRatio(width, height); } mCameraHelperRight.addSurface(svCameraViewRight.getHolder().getSurface(), false); mIsCameraRightConnected = true; } } @Override public void onCameraClose(UsbDevice device) { if (device.equals(mUsbDeviceRight)) { if (mCameraHelperRight != null) { mCameraHelperRight.removeSurface(svCameraViewRight.getHolder().getSurface()); } mIsCameraRightConnected = false; } } @Override public void onDeviceClose(UsbDevice device) { } @Override public void onDetach(UsbDevice device) { if (device.equals(mUsbDeviceRight)) { mUsbDeviceRight = null; } } @Override public void onCancel(UsbDevice device) { if (device.equals(mUsbDeviceRight)) { mUsbDeviceRight = null; } }};svCameraViewLeft.getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(@NonNull SurfaceHolder holder) { if (mCameraHelperLeft != null) { mCameraHelperLeft.addSurface(holder.getSurface(), false); } } @Override public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(@NonNull SurfaceHolder holder) { if (mCameraHelperLeft != null) { mCameraHelperLeft.removeSurface(holder.getSurface()); } } });svCameraViewRight.setAspectRatio(DEFAULT_WIDTH, DEFAULT_HEIGHT); svCameraViewRight.getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(@NonNull SurfaceHolder holder) { if (mCameraHelperRight != null) { mCameraHelperRight.addSurface(holder.getSurface(), false); } } @Override public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(@NonNull SurfaceHolder holder) { if (mCameraHelperRight != null) { mCameraHelperRight.removeSurface(holder.getSurface()); } } });mCameraHelperLeft = new CameraHelper();mCameraHelperLeft.setStateCallback(mStateListenerLeft);mCameraHelperRight = new CameraHelper();mCameraHelperRight.setStateCallback(mStateListenerRight);
其他API
方法 | 说明 |
---|---|
getDeviceList() | 获取当前检测到的所有UVC设备 |
getSupportedFormatList() | 获取当前摄像头支持的Format列表 |
getSupportedSizeList() | 获取当前摄像头支持的Size列表 |
getPreviewSize() | 获取当前摄像头正在使用的预览Size |
setButtonCallback() | 设置按钮事件回调 |
setFrameCallback() | 设置实时预览图像数据回调(请在StateCallback的onDeviceOpen或者onCameraOpen回调函数里面调用,使用方法可以参考demo里面的SetFrameCallbackActivity),支持格式 UVCCamera.PIXEL_FORMAT_YUV;PIXEL_FORMAT_NV12;PIXEL_FORMAT_NV21;PIXEL_FORMAT_RGB565;PIXEL_FORMAT_RGBX等格式 |
openCamera(Size size) | 用指定格式打开当前摄像头 |
closeCamera() | 关闭当前摄像头 |
isRecording() | 是否正在录像 |
isCameraOpened() | 是否已经打开当前摄像头 |
下载演示APK
Download demo APK
app-debug.apk
参考
来源地址:https://blog.csdn.net/hanshiying007/article/details/124118486