简介
UVCAndroid基于saki4510t/UVCCamera开发,对原UVCCamera库进行了一部分的重构,升级了原生层的libjpeg-turbo、libusb和libuvc库为较新的版本,拍照和录像API参考了goolge的CameraX库的实现。
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.1'
}
2. 获取权限
Request permissions
List<String> needPermissions = new ArrayList<>();
needPermissions.add(Manifest.permission.CAMERA);
needPermissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
needPermissions.add(Manifest.permission.RECORD_AUDIO);
XXPermissions.with(this)
.permission(needPermissions)
.request((permissions, all) -> {
if(!all){
return;
}
});
<application
...
android:requestLegacyExternalStorage="true"
>
3. 初始化UVC业务类,设置UVC摄像头状态回调,设置TextureView或者SurfaceView的Surface监听回调
Initialize CameraHelper,set UVC Camera state callback
private ICameraHelper mCameraHelper;
private AspectRatioSurfaceView mCameraViewMain;
private ICameraHelper.StateCallback mStateListener;
mStateListener = new ICameraHelper.StateCallback() {
@Override
public void onAttach(UsbDevice device) {
mCameraHelper.selectDevice(device);
}
@Override
public void onDeviceOpen(UsbDevice device, boolean isFirstOpen) {
mCameraHelper.openCamera();
}
@Override
public void onCameraOpen(UsbDevice device) {
mCameraHelper.startPreview();
Size size = mCameraHelper.getPreviewSize();
if (size != null) {
int width = size.width;
int height = size.height;
mCameraViewMain.setAspectRatio(width, height);
}
mCameraHelper.addSurface(mCameraViewMain.getHolder().getSurface(), false);
}
@Override
public void onCameraClose(UsbDevice device) {
if (mCameraHelper != null) {
mCameraHelper.removeSurface(mCameraViewMain.getHolder().getSurface());
}
}
@Override
public void onDeviceClose(UsbDevice device) {
}
@Override
public void onDetach(UsbDevice device) {
}
@Override
public void onCancel(UsbDevice device) {
}
};
mCameraViewMain.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
if (mCameraHelper != null) {
mCameraHelper.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 (mCameraHelper != null) {
mCameraHelper.removeSurface(holder.getSurface());
}
}
});
mCameraHelper = new CameraHelper();
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));
File file = FileUtils.getCaptureFile(this, Environment.DIRECTORY_DCIM, ".jpg");
ImageCapture.OutputFileOptions options =
new ImageCapture.OutputFileOptions.Builder(file).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)
.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();
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 control = mCameraHelper.getUVCControl();
private void setAllControlChangeListener(UVCControl controls) {
isbBrightness.setOnSeekChangeListener(
(MyOnSeekChangeListener) seekParams -> controls.setBrightness(seekParams.progress));
isbContrast.setOnSeekChangeListener(
(MyOnSeekChangeListener) seekParams -> controls.setContrast(seekParams.progress));
cbContrastAuto.setOnCheckedChangeListener((buttonView, isChecked) -> {
controls.setContrastAuto(isChecked);
});
isbHue.setOnSeekChangeListener(
(MyOnSeekChangeListener) seekParams -> controls.setHue(seekParams.progress));
cbHueAuto.setOnCheckedChangeListener((buttonView, isChecked) -> {
controls.setHueAuto(isChecked);
});
isbSaturation.setOnSeekChangeListener(
(MyOnSeekChangeListener) seekParams -> controls.setSaturation(seekParams.progress));
isbSharpness.setOnSeekChangeListener(
(MyOnSeekChangeListener) seekParams -> controls.setSharpness(seekParams.progress));
isbGamma.setOnSeekChangeListener(
(MyOnSeekChangeListener) seekParams -> controls.setGamma(seekParams.progress));
isbWhiteBalance.setOnSeekChangeListener(
(MyOnSeekChangeListener) seekParams -> controls.setWhiteBalance(seekParams.progress));
cbWhiteBalanceAuto.setOnCheckedChangeListener((buttonView, isChecked) -> {
controls.setWhiteBalanceAuto(isChecked);
});
isbBacklightComp.setOnSeekChangeListener(
(MyOnSeekChangeListener) seekParams -> controls.setBacklightComp(seekParams.progress));
isbGain.setOnSeekChangeListener(
(MyOnSeekChangeListener) seekParams -> controls.setGain(seekParams.progress));
isbExposureTime.setOnSeekChangeListener(
(MyOnSeekChangeListener) seekParams -> controls.setExposureTimeAbsolute(seekParams.progress));
cbExposureTimeAuto.setOnCheckedChangeListener((buttonView, isChecked) -> {
controls.setExposureTimeAuto(isChecked);
});
isbIris.setOnSeekChangeListener(
(MyOnSeekChangeListener) seekParams -> controls.setIrisAbsolute(seekParams.progress));
isbFocus.setOnSeekChangeListener(
(MyOnSeekChangeListener) seekParams -> controls.setFocusAbsolute(seekParams.progress));
cbFocusAuto.setOnCheckedChangeListener((buttonView, isChecked) -> {
controls.setFocusAuto(isChecked);
});
isbZoom.setOnSeekChangeListener(
(MyOnSeekChangeListener) seekParams -> controls.setZoomAbsolute(seekParams.progress));
isbPan.setOnSeekChangeListener(
(MyOnSeekChangeListener) seekParams -> controls.setPanAbsolute(seekParams.progress));
isbTilt.setOnSeekChangeListener(
(MyOnSeekChangeListener) seekParams -> controls.setTiltAbsolute(seekParams.progress));
isbRoll.setOnSeekChangeListener(
(MyOnSeekChangeListener) seekParams -> controls.setRollAbsolute(seekParams.progress));
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) {
control.resetBrightness();
control.resetContrast();
control.resetContrastAuto();
control.resetHue();
control.resetHueAuto();
control.resetSaturation();
control.resetSharpness();
control.resetGamma();
control.resetWhiteBalance();
control.resetWhiteBalanceAuto();
control.resetBacklightComp();
control.resetGain();
control.resetExposureTimeAbsolute();
control.resetAutoExposureMode();
control.resetIrisAbsolute();
control.resetFocusAbsolute();
control.resetFocusAuto();
control.resetZoomAbsolute();
control.resetPanAbsolute();
control.resetTiltAbsolute();
control.resetRollAbsolute();
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.设置多个摄像头
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)) {
mCameraHelperLeft.openCamera();
}
}
@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;
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)) {
mCameraHelperRight.openCamera();
}
}
@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;
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 | setFrameCallback() | 设置实时预览图像数据回调,支持格式 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-release.apk
demo-release.apk
|