Android Camere开发入门(2):Camera2的使用
Camera2 API简介
在上一篇文章《Android Camere开发入门(1):Camera1的使用》中,我们了解了如何在Android应用中使用Camera API来访问设备的摄像头。然而,随着Android设备的发展和硬件的提升,我们需要使用更强大、更灵活的API来满足日益增长的需求。这就是我们今天要介绍的Camera2 API。
Camera2 API是Android 5.0(API 21)引入的一种全新的相机API,它提供了对设备摄像头的全面控制,包括预览、捕获、图像格式、图像质量、视频录制等。相比于Camera API,Camera2 API更加强大和灵活,可以更好地满足开发者的需求。
本文将介绍如何使用Camera2 API来进行相机操作,包括预览、拍照和录制视频等功能。
获取相机权限
首先,我们需要在AndroidManifest.xml文件中添加相机权限的申请,以便应用能够访问设备的相机。
<uses-permission android:name="android.permission.CAMERA" />
这里使用了一个第三方权限申请框架,在build.gradle添加依赖
implementation 'com.github.getActivity:XXPermissions:18.0'
在Activity中去申请权限,当用户拒绝时给出相应提示。
private String[] permissionArray = {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE};
XXPermissions.with(this).permission(permissionArray).request((permissions, allGranted) -> {
if (!allGranted) {
Toast.makeText(Camera2Activity.this, "请务必开启所有权限", Toast.LENGTH_LONG).show();
return;
}
//获取权限成功
initCamera2();
});
创建CameraManager实例
在Camera2中,我们需要首先创建一个CameraManager实例,它用于获取和管理设备上的相机资源。
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
获取可用的相机列表
通过CameraManager实例,我们可以获取设备上所有可用的相机列表。
String[] cameraIdList = cameraManager.getCameraIdList();
打开相机
选择一个相机ID,然后使用CameraManager打开相机。在这之前,需要实现一个CameraDevice.StateCallback来处理打开相机的结果。
cameraManager.openCamera(cameraIdList[1], new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice cameraDevice) {
// 相机打开成功,可以进行后续操作
createCaptureSession(cameraDevice);
}
@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
// 相机断开连接
mCameraDevice.close();
mCameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice cameraDevice, int error) {
// 打开相机发生错误
mCameraDevice.close();
mCameraDevice = null;
}
}, null);
创建预览会话
一旦相机打开成功,我们可以通过CameraDevice创建一个预览会话。预览会话是实现相机预览的关键。
这里和Camera1不太一样,预览不管使用的是SurfaceView还是TextureView最后都是以Surface的方式进行添加,且Camera可以同时添加多个Surface通道。
private CameraDevice mCameraDevice;
private CameraCaptureSession captureSession;
private int imageWidth = 1920;
private int imageHeight = 1080;
private ImageReader mImageReader;
private void createCaptureSession(CameraDevice cameraDevice) {
mCameraDevice = cameraDevice;
List<Surface> surfaces = new ArrayList<>();
// 预览Surface
SurfaceHolder surfaceHolder = binding.camera2SurfaceView.getHolder();
Surface previewSurface = surfaceHolder.getSurface();
surfaces.add(previewSurface);
// 创建ImageReader对象(拍照)
mImageReader = ImageReader.newInstance(imageWidth, imageHeight, ImageFormat.JPEG, 1);
mImageReader.setOnImageAvailableListener(mImageReaderListener, null);
surfaces.add(mImageReader.getSurface());
//添加录制Surface
initRecording();
surfaces.add(mMediaRecorder.getSurface());
try {
cameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
try {
// 预览会话已创建成功,可以开始预览
captureSession = session;
// 创建预览请求
CaptureRequest.Builder previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewRequestBuilder.addTarget(previewSurface); // 设置预览目标Surface
// 开启连续预览
captureSession.setRepeatingRequest(previewRequestBuilder.build(), null, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
// 预览会话创建失败
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
拍照和录制视频
在预览会话创建成功之后,我们可以通过创建不同类型的CaptureRequest来实现拍照和录制视频功能。
//给一个按钮点击事件进行拍照
binding.camera2TakePicture.setOnClickListener(v -> takePicture());
private void takePicture() {
try {
// 创建拍照请求
CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(mImageReader.getSurface());
// 设置自动对焦
captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_AUTO);
// 设置闪光灯
captureBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
// 获取设备方向
int rotation = getWindowManager().getDefaultDisplay().getRotation();
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, rotation);
captureSession.capture(captureBuilder.build(), null, null);
// 播放拍照音效或显示闪光灯动画等
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private ImageReader.OnImageAvailableListener mImageReaderListener = reader -> {
// 获取到拍照的图像数据
Image image = reader.acquireLatestImage();
// 获取图片的字节数组
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
// 保存图片到相册
saveImageToGallery(data);
// 释放图像资源
image.close();
};
private void saveImageToGallery(byte[] data) {
// 定义图片的保存路径和文件名
String fileName = "IMG_" + System.currentTimeMillis() + ".jpg";
String filePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath() + File.separator + fileName;
// 创建文件输出流
try {
FileOutputStream fos = new FileOutputStream(filePath);
fos.write(data);
fos.close();
// 通知图库更新
MediaScannerConnection.scanFile(this, new String[]{filePath}, null, null);
// 在某些设备上,可能需要发送广播通知才能使图片立即出现在相册中
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(new File(filePath))));
// 显示保存成功的提示
Toast.makeText(this, "图片保存成功", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
// 显示保存失败的提示
Toast.makeText(this, "图片保存失败", Toast.LENGTH_SHORT).show();
}
}
给一个按钮进行开启和关闭录制
binding.camera2Recording.setOnClickListener(v -> {
if (!isRecording) startRecordingVideo();
else stopRecordingVideo();
binding.camera2Recording.setText(isRecording ? "关闭录制" : "开启录制");
});
private MediaRecorder mMediaRecorder;
private int videoWidth = 1920;
private int videoHeight = 1080;
private File recorderFile;
private String recorderPath;
private boolean isRecording = false;
private void initRecording() {
recorderFile = new File(Camera2Activity.this.getExternalFilesDir(null).getAbsolutePath() + "/video/");
if (!recorderFile.exists()) {
recorderFile.mkdir();
}
recorderPath = recorderFile.getAbsolutePath() + "/" + System.currentTimeMillis() + ".mp4";
Log.e(TAG, "视频路径:" + recorderPath);
mMediaRecorder = new MediaRecorder();
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setOutputFile(recorderPath);
mMediaRecorder.setVideoEncodingBitRate(10000000);
mMediaRecorder.setVideoFrameRate(30);
mMediaRecorder.setVideoSize(videoWidth, videoHeight);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
// 开始录制
try {
mMediaRecorder.prepare();
mMediaRecorder.start();
} catch (IOException e) {
e.printStackTrace();
}
}
private void startRecordingVideo() {
// 创建录制视频请求
try {
CaptureRequest.Builder recordRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
recordRequestBuilder.addTarget(mMediaRecorder.getSurface()); // 设置录制目标Surface
captureSession.setRepeatingRequest(recordRequestBuilder.build(), null, null); // 开始录制视频
isRecording = true;
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void stopRecordingVideo() {
// 停止录制视频
try {
mMediaRecorder.stop();
} catch (IllegalStateException e) {
e.printStackTrace();
} finally {
mMediaRecorder.reset();
mMediaRecorder.release();
mMediaRecorder = null;
}
isRecording = false;
// 关闭相机预览会话
// if (captureSession != null) {
// captureSession.close();
// captureSession = null;
// }
//图库更新
addToGallery(recorderPath);
}
private void addToGallery(String videoFilePath) {
// 发送广播通知图库更新
MediaScannerConnection.scanFile(this, new String[]{videoFilePath}, null,
(path, uri) -> {
// 添加到相册成功的回调
Toast.makeText(this, "视频已保存至相册", Toast.LENGTH_SHORT).show();
});
}
总结
这些只是Camera2 API的基本用法,它还提供了更多的功能和选项,如自动对焦、闪光灯控制、镜头切换等。你可以在官方文档中进一步了解这些功能。
通过上述步骤,我们可以实现相机的预览、拍照和录制视频等基本功能。
参考链接
-
Android官方文档:https://developer.android.com/reference/android/hardware/camera2
-
Google Sample代码:https://github.com/android/camera-samples/tree/main/Camera2Basic
-
Codelab教程:https://codelabs.developers.google.com/codelabs/camerax-getting-started
DEMO
gitee:https://gitee.com/yunianvh/camera-demo
github:https://github.com/yunianvh/CameraDemo
编辑:玉念聿辉
感谢查阅