Use CameraX to preview, take photos, and video

1. Advantages of CameraX

CameraX is a Jetpack support library designed to help you simplify the development of camera applications. It provides a consistent and easy-to-use API Surface, suitable for most Android devices, and is backward compatible to Android 5.0 (API level 21).

Although it uses the functionality of camera2, it uses a simpler and use-case-based approach that has life cycle awareness. It also solves device compatibility issues, so you don’t need to add device-specific code to the code base. These features reduce the amount of code that needs to be written when adding camera functions to the application.

2. Add dependencies

//主要工具类
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'

The interface of each version may be slightly different, but the functions are basically the same. In addition, you need to apply for camera permissions:

<uses-permission android:name="android.permission.CAMERA" />

If you want to take photos and save, you also need storage permissions:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

3. Turn on the camera

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()));
}

Use of PreviewView

<androidx.camera.view.PreviewView
	android:id="@+id/preview"
	android:layout_width="match_parent"
	android:layout_height="match_parent"/>

4. Take pictures

There are two ways to take pictures callback:
1. Call back Image, if you need to save, you can turn to Bitmap, and then save
2. Directly configure the save path, and then receive the callback of the success and failure of the picture.

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());
		}
	});*/
}

Students who need the extension of the supplier can check it on the official website.

PS: The official description of CameraX

https://developer.android.google.cn/training/camerax

Guess you like

Origin blog.csdn.net/mozushixin_1/article/details/108075167