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+;
第一步 添加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'
}
Request permissions
List<String> 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;
}
//摄像头业务操作
});
<application
...
android:requestLegacyExternalStorage="true"
>
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);
Release CameraHelper(including canceling UVC Camera state callback, stopping Camera preview, etc.)
mCameraHelper.release();
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();
}
});
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();
}
});
Set camera preview parameters (including frame format, width, height, FPS)
//停止相机预览
mCameraHelper.stopPreview();
//设置摄像机预览参数
mCameraHelper.setPreviewSize(size);
//开始相机预览
mCameraHelper.startPreview();
//需要自适应摄像头分辨率的话,设置新的宽高比
mCameraViewMain.setAspectRatio(mPreviewWidth, mPreviewHeight);
Size里面的帧格式type只支持UVCCamera.UVC_VS_FRAME_UNCOMPRESSED(YUV格式)和UVCCamera.UVC_VS_FRAME_MJPEG(Mjpeg格式),使用的时候需要摄像头支持(摄像头可能支持YUV格式或者Mjpeg格式)。
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();
}
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));
}
}
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);
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);
方法 | 说明 |
---|---|
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() | 是否已经打开当前摄像头 |
Download demo APK
app-debug.apk
demo-debug.apk
saki4510t/UVCCamera