Android Camere开发入门(2):Camera2的使用

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的基本用法,它还提供了更多的功能和选项,如自动对焦、闪光灯控制、镜头切换等。你可以在官方文档中进一步了解这些功能。

通过上述步骤,我们可以实现相机的预览、拍照和录制视频等基本功能。

参考链接

DEMO


gitee:https://gitee.com/yunianvh/camera-demo
github:https://github.com/yunianvh/CameraDemo

编辑:玉念聿辉
感谢查阅

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玉念聿辉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值