Article directory
- Introduction
- The main function
- how to use
-
- 1. Add dependencies to the local project
- 2. Get permission
- 3. Initialize the UVC business class, set the UVC camera status callback, and set the Surface monitoring callback of TextureView or SurfaceView
- 4. Release the UVC business class (including canceling the UVC camera status callback, stopping Camera preview, closing Camera, etc.)
- 5. Picture capture
- 6. Record video
- 7. Change camera preview parameters (including frame format, width, height, FPS)
- 8. Adjust some camera control parameters such as contrast, brightness, hue, saturation, white balance, etc.
- 9. Rotate the camera 90 degrees, 180 degrees, 270 degrees, set the camera preview mirror
- 10. Set up multiple previews
- 11. Set multiple cameras (USB2.0 is limited by bandwidth, it may not be possible to connect multiple cameras at the same time)
- Other APIs
- Download Demo APK
- reference
Introduction
UVCAndroid is a general-purpose development library for Android UVC cameras.
GitHub source address : https://github.com/shiyinghan/UVCAndroid
The main function
The main functions include:
(1) Support USB Camera device detection, real-time preview of the screen;
(2) Support snapping pictures in jpg format, and can set the picture compression quality ;
(3) Support recording mp4 format video, can block audio, and can set video and audio (
4) Support obtaining the resolution supported by the camera, and resolution switching; (5 )
Support the preview to automatically identify the resolution of various cameras; (
6) Support rotating the camera 90 degrees, 180 degrees, 270 degrees ;
( 7) Support adjustment of some camera control parameters such as contrast, brightness, hue, saturation, white balance, etc.;
(8) Support multiple previews and multiple cameras;
(9) Support Android5.0+;
how to use
1. Add dependencies to the local project
The first step is to add the mavenCentral repository to the project gradle file
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()
}
}
The second step is to add dependencies to the gradle file of the app Module
dependencies {
implementation 'com.herohan:UVCAndroid:1.0.4'
}
2. Get permission
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"
>
3. Initialize the UVC business class, set the UVC camera status callback, and set the Surface monitoring callback of TextureView or SurfaceView
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. Release the UVC business class (including canceling the UVC camera status callback, stopping Camera preview, closing Camera, etc.)
Release CameraHelper(including canceling UVC Camera state callback, stopping Camera preview, etc.)
mCameraHelper.release();
5. Picture capture
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. Record video
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. Change camera preview parameters (including frame format, width, height, FPS)
Set camera preview parameters (including frame format, width, height, FPS)
//停止相机预览
mCameraHelper.stopPreview();
//设置摄像机预览参数
mCameraHelper.setPreviewSize(size);
//开始相机预览
mCameraHelper.startPreview();
//需要自适应摄像头分辨率的话,设置新的宽高比
mCameraViewMain.setAspectRatio(mPreviewWidth, mPreviewHeight);
8. Adjust some camera control parameters such as contrast, brightness, hue, saturation, white balance, etc.
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. Rotate the camera 90 degrees, 180 degrees, 270 degrees, set the camera preview mirror
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 up multiple previews
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. Set multiple cameras (USB2.0 is limited by bandwidth, it may not be possible to connect multiple cameras at the same time)
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);
Other APIs
method | illustrate |
---|---|
getDeviceList() | Get all UVC devices currently detected |
getSupportedFormatList() | Get the Format list supported by the current camera |
getSupportedSizeList() | Get the Size list supported by the current camera |
getPreviewSize() | Get the preview size currently used by the camera |
setButtonCallback() | Set button event callback |
setFrameCallback() | Set the real-time preview image data callback ( please call it in the onDeviceOpen or onCameraOpen callback function of StateCallback, the method can refer to SetFrameCallbackActivity in the demo ), support the format UVCCamera.PIXEL_FORMAT_YUV; PIXEL_FORMAT_NV12; PIXEL_FORMAT_NV21; PIXEL_FORMAT_RGB565; PIXEL_FORMAT_RGBX and other formats |
openCamera(Size size) | Open the current camera with the specified format |
closeCamera() | close current camera |
isRecording() | Is it recording |
isCameraOpened() | Whether the current camera has been opened |
Download Demo APK
Download demo APK
app-release.apk