一、前言
从Camera1到Camera2,再到现在的Jetpack中的CameraX,Google一直在致力于让Android应用开发者更加方便的去使用摄像头相关的API,更加简单快速的实现摄像头相关的应用功能;下面就来学习一下使用CameraX实现拍照和录制视频功能;
二、CameraX概述
CameraX是Android Jetpack中的组件,旨在简化相机相关的应用开发工作,但是要注意的是CameraX向下最低兼容到Android 5.0(API 21);CameraX利用了Camera2的功能,但采取了一种具有生命周期感知能力且基于用例(UseCase)的更简单的方式,它还解决了设备兼容性问题。
三、CameraX使用
添加依赖:
在Module的build.gradle中添加CameraX相关的依赖
dependencies {
.....
def camerax_version = "1.0.0-beta07"
// CameraX core library using camera2 implementation
implementation "androidx.camera:camera-camera2:$camerax_version"
// CameraX Lifecycle Library
implementation "androidx.camera:camera-lifecycle:$camerax_version"
// CameraX View class
implementation "androidx.camera:camera-view:1.0.0-alpha14"
}
同时CameraX需要一些属于java8的方法,因此我们需要相应地设置编译选项。在build.gradle文件中android块的末尾,buildTypes之后,添加以下内容:
android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
添加相关权限
添加相机权限,这里因为我们还要实现照片拍摄,视频录制所以除了相机权限,还需要录音权限,以及存储的读写权限;
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
相机预览
相机预览我们肯定是要有一个显示摄像头画面的View,在使用Camera1或Camera2时我们一般都是使用SurfaceView来显示摄像头画面,CameraX中提供PreviewView
作为显示摄像头画面的View
所以我么在布局中添加一个PreviewView
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.camera.view.PreviewView
android:id="@+id/preview_activity_video_recode"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
并且使用Preview相关的API实现摄像头画面的预览
@SuppressLint("RestrictedApi")
private void preview() {
ListenableFuture<ProcessCameraProvider> listenableFuture = ProcessCameraProvider.getInstance(mActivity);
try {
mProcessCameraProvider = listenableFuture.get();
mProcessCameraProvider = listenableFuture.get();
mPreview = new Preview.Builder()
.setTargetResolution(new Size(640, 480))
.build();
mPreview.setSurfaceProvider(mPreviewView.createSurfaceProvider());
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
.build();
imageAnalysis
.setAnalyzer(ContextCompat.getMainExecutor(mActivity),
new ImageAnalysis.Analyzer() {
@Override
public void analyze(@NonNull ImageProxy image) {
Log.e(TAG, "analyze: " + image);
image.close();
}
}
);
mProcessCameraProvider.bindToLifecycle((LifecycleOwner) mActivity, CameraSelector.DEFAULT_BACK_CAMERA,
mPreview,
imageAnalysis
);
} catch (Exception e) {
e.printStackTrace();
}
}
我们这里在实现摄像头画面预览的同时,还添加了一ImageAnalysis 图像分析用例,ImageAnalysis设置一个ImageAnalysis.Analyzer分析器,这个用例是用来实现每一帧画面的数据分析的,每一帧画面都会回调到analyze(ImageProxy image)方法
我们可以从IamgeProxy中拿到每一帧画面的图像数据;
这里要注意在回调方法中使用完IamgeProxy后,一定要调用IamgeProxy.close()方法关闭,否则analyze(ImageProxy image)这个方法只会回调一次,这是个坑一定要注意关闭;
拍摄照片
使用IamgeCapture相关的API实现照片拍摄功能
private void takePhoto() {
ImageCapture imageCapture = new ImageCapture.Builder()
//优化捕获速度,可能降低图片质量
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
//设置宽高比
.setTargetResolution(new Size(640, 480))
//设置初始的旋转角度
.build();
String dirPath = getExternalFilesDir("").getAbsolutePath()
+ File.separator + "TestRecode";
File dirFile = new File(dirPath);
if (!dirFile.exists()) {
boolean mkdir = dirFile.mkdir();
Log.e(TAG, "takePhoto: mkdir:" + mkdir);
}
File file = new File(dirFile, System.currentTimeMillis() + ".jpg");
if (!file.exists()) {
try {
boolean newFile = file.createNewFile();
Log.e(TAG, "takePhoto: newFile:" + newFile);
} catch (IOException e) {
e.printStackTrace();
}
}
ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(file).build();
mProcessCameraProvider.bindToLifecycle((LifecycleOwner) mActivity, CameraSelector.DEFAULT_BACK_CAMERA,
imageCapture
);
imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(mActivity), new ImageCapture.OnImageSavedCallback() {
@Override
public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
Log.e(TAG, "onImageSaved: " + outputFileResults);
Uri savedUri = outputFileResults.getSavedUri();
Log.e(TAG, "onImageSaved: " + savedUri);
if (savedUri == null) {
savedUri = Uri.fromFile(file);
}
Toast.makeText(mActivity, "拍摄成功", Toast.LENGTH_SHORT).show();
}
@Override
public void one rror(@NonNull ImageCaptureException exception) {
Log.e(TAG, "onError: " + exception);
}
});
}
录制视频
使用VideoCapture相关的API实现视频录制的功能
@SuppressLint("RestrictedApi")
private void startRecorder() {
mVideoCapture = new VideoCapture.Builder()
.build();
String dirPath = getExternalFilesDir("").getAbsolutePath()
+ File.separator + "TestRecode";
File dirFile = new File(dirPath);
if (!dirFile.exists()) {
boolean mkdir = dirFile.mkdir();
Log.e(TAG, "startRecorder: mkdir:" + mkdir);
}
File file = new File(dirFile, System.currentTimeMillis() + ".mp4");
if (!file.exists()) {
try {
boolean newFile = file.createNewFile();
Log.e(TAG, "startRecorder: newFile:" + newFile);
} catch (IOException e) {
e.printStackTrace();
}
}
mProcessCameraProvider.bindToLifecycle(
this, CameraSelector.DEFAULT_BACK_CAMERA, mPreview,
mVideoCapture);
mVideoCapture.startRecording(file, ContextCompat.getMainExecutor(mActivity),
new VideoCapture.OnVideoSavedCallback() {
@Override
public void onVideoSaved(@NonNull File file) {
Log.e(TAG, "onVideoSaved: " + file);
Toast.makeText(mActivity, "录制结束", Toast.LENGTH_SHORT).show();
}
@Override
public void one rror(int videoCaptureError, @NonNull String message, @Nullable Throwable cause) {
Log.e(TAG, "onError: " + videoCaptureError + "," + message);
Toast.makeText(mActivity, "录制出错:code:" + videoCaptureError + "," + message, Toast.LENGTH_SHORT).show();
mIsRecording = false;
}
}
);
}
@SuppressLint("RestrictedApi")
private void stopRecorder() {
if (mVideoCapture != null) {
mVideoCapture.stopRecording();
}
}
一开始的时候回调到onError中,videoCaptureError是2,message是MediaMuxer creation failed!,百度也没有找到解决方法,后来发现是视频文件File没有创建成功,所以要注意检查视频文件File有没有创建成功;
当然别忘记了在程序中先要动态申请相关权限;
以上就是使用CameraX实现摄像头预览、拍照、视频录制功能;
Demo源码地址:https://github.com/maqing-programmer/CameraXDemo