1.CameraX的优势
CameraX 是一个 Jetpack 支持库,旨在帮助您简化相机应用的开发工作。它提供一致且易于使用的 API Surface,适用于大多数 Android 设备,并可向后兼容至 Android 5.0(API 级别 21)。
虽然它利用的是 camera2 的功能,但使用的是更为简单且基于用例的方法,该方法具有生命周期感知能力。它还解决了设备兼容性问题,因此您无需在代码库中添加设备专属代码。这些功能减少了将相机功能添加到应用时需要编写的代码量。
2.添加依赖
//主要工具类
implementation 'androidx.camera:camera-core:1.0.0-alpha10'
//主要的外部接口
implementation 'androidx.camera:camera-camera2:1.0.0-alpha10'
//管理相机的生命周期
implementation 'androidx.camera:camera-lifecycle:1.0.0-alpha10'
//预览图层
implementation 'androidx.camera:camera-view:1.0.0-alpha10'
//可选插件,通过该插件,您可以在支持的设备上添加效果。这些效果包括人像、HDR、夜间模式和美颜。
implementation 'androidx.camera:camera-extensions:1.0.0-alpha10'
各版本接口可能略有差别,但是功能基本保持一致。此外还需要申请相机权限:
<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" />
3.打开摄像头
private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
private PreviewView previewView;
private ImageCapture imageCapture;
private ProcessCameraProvider cameraProvider;
cameraProviderFuture = ProcessCameraProvider.getInstance(activity);
cameraProviderFuture.addListener(() -> {
try {
cameraProvider = cameraProviderFuture.get();
//相机ID CameraSelector.LENS_FACING_FRONT前置;CameraSelector.LENS_FACING_BACK 后置
bindPreview(cameraProvider, cameraId);
} catch (ExecutionException | InterruptedException e) {
Log.e(TAG, "error:" + e);
}
}, ContextCompat.getMainExecutor(activity));
private void bindPreview(@NonNull ProcessCameraProvider cameraProvider, int lensFacing) {
Preview preview = new Preview.Builder()
.setTargetAspectRatio(AspectRatio.RATIO_16_9) //设置宽高比
.setTargetRotation(Surface.ROTATION_0) // 设置旋转角度
.build();
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(lensFacing)
.build();
/**
图像分析可以分为两种模式:阻塞模式和非阻塞模式。通过使用 STRATEGY_BLOCK_PRODUCER 调用
setBackpressureStrategy() 以启用阻塞模式。
在此模式下,执行程序会按顺序从相机接收帧;这意味着,如果 analyze() 方法所用的时间超过单帧在当前帧
速率下的延迟时间,帧便可能不再是最新的帧,因为新帧已被阻止进入流水线,直到该方法返回为止。
通过使用 STRATEGY_KEEP_ONLY_LATEST 调用 setBackpressureStrategy() 以启用非阻塞模式。在此模式
下,执行程序会从相机接收调用 analyze() 方法时的最后一个可用帧。如果此方法所用的时间超过单帧在当前
帧速率下的延迟时间, 可能会跳过某些帧,以便在下一次 analyze() 接收数据时,它会获取相机流水线中的
最后一个可用帧。从 analyze() 返回前,请通过调用 image.close() 关闭图片引用,以避免阻塞其他图像的
生成(导致预览停顿)并避免可能出现的图像丢失。 此方法必须完成分析或创建副本,而不是将图像引用传递
到分析方法以外。*/
//设置宽高比
// 设置旋转角度
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
.setTargetAspectRatio(AspectRatio.RATIO_16_9) //设置宽高比
.setTargetRotation(Surface.ROTATION_0) // 设置旋转角度
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build();
imageAnalysis.setAnalyzer(executors, imageProxy -> {
//预览过程中,如果需要录制视频,则在这里获取。
//将图片转换成yuv(格式YUV_420_888)数据,再进行编码。
//如果处理数据太慢,导致造成阻塞,可以设置监听器,把数据发送到其他线程进行处理
ImageProxy.PlaneProxy[] planes = imageProxy.getPlanes();
byte[] mYuvBytes = new byte[imageProxy.getWidth() * imageProxy.getHeight() * 3 / 2];
// YUV_420_888
ByteBuffer yBuffer = planes[0].getBuffer();
int yLen = imageProxy.getWidth() * imageProxy.getHeight();
yBuffer.get(mYuvBytes, 0, yLen);
ByteBuffer uBuffer = planes[1].getBuffer();
ByteBuffer vBuffer = planes[2].getBuffer();
int pixelStride = planes[1].getPixelStride(); // pixelStride = 2
for (int i = 0; i <= uBuffer.remaining(); i += pixelStride) {
mYuvBytes[yLen++] = uBuffer.get(i);
mYuvBytes[yLen++] = vBuffer.get(i);
}
imageProxy.close();
});
//拍摄图像的配置
imageCapture =
new ImageCapture.Builder()
//CAPTURE_MODE_MAXIMIZE_QUALITY 拍摄高质量图片,图像质量优先于延迟,可能需要更长的时间
//CAPTURE_MODE_MINIMIZE_LATENCY
.setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
.setTargetAspectRatio(AspectRatio.RATIO_16_9) //设置宽高比
.setTargetRotation(Surface.ROTATION_0) // 设置旋转角度
.build();
cameraProvider.unbindAll();
Camera camera = cameraProvider.bindToLifecycle((LifecycleOwner) activity, cameraSelector, imageCapture, preview, imageAnalysis);
//这里previewView是预览图层,需要在布局中实现,然后在这里使用
preview.setSurfaceProvider(previewView.createSurfaceProvider(camera.getCameraInfo()));
}
PreviewView的使用
<androidx.camera.view.PreviewView
android:id="@+id/preview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
4.拍照
拍照回调有两种方式:
1.回调Image,需要保存的话,可以转Bitmap,然后保存
2.直接配置好保存路径,然后接收拍照成功和失败的回调
public void takePicture() {
imageCapture.takePicture(executors, new ImageCapture.OnImageCapturedCallback() {
@Override
public void onCaptureSuccess(@NonNull ImageProxy imageProxy) {
ImageProxy.PlaneProxy[] planes = imageProxy.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
buffer.position(0);
byte[] bytes = new byte[buffer.capacity()];
buffer.get(bytes);
imageProxy.close();
super.onCaptureSuccess(imageProxy);
}
});
/*ImageCapture.Metadata metadata = new ImageCapture.Metadata();
if (mCurrentCameraId == CameraSelector.LENS_FACING_FRONT) {
metadata.setReversedHorizontal(true);
} else {
metadata.setReversedHorizontal(false);
}
File file = new File(Environment.getExternalStorageState(), new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS",
Locale.US).format(System.currentTimeMillis()) + ".jpg");
ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.
Builder(file).setMetadata(metadata).build();
imageCapture.takePicture(outputFileOptions, executors, new ImageCapture.OnImageSavedCallback() {
@Override
public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
Log.e(TAG, "outputFileResults:" + outputFileResults.getSavedUri());
}
@Override
public void onError(@NonNull ImageCaptureException exception) {
Log.e(TAG, "exception:" + exception.toString());
}
});*/
}
供应商扩展需要的同学可以自行在官网查看。
PS:CameraX的官方说明
https://developer.android.google.cn/training/camerax