用CameraX进行预览,拍照,录像

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

猜你喜欢

转载自blog.csdn.net/mozushixin_1/article/details/108075167