CameraX实现预览、拍照、录制视频

 

Android中的相机功能一直以来比较难用,截止目前出现了Camera,Camera2,Camera2支持API 21以上设备,要支持5.0一下还得需要Camera,Camera2比Camera更加复杂,更加灵活和解耦,但是然后用起来比较麻烦,因此CameraX就出现了,目前CameraX还是刚刚起步,还不是很稳定,版本还是1.0.0。CameraX 由两个概念来完成实现  Camera View 和 Camera Core。Camera View 可被单独用于处理基本的相机要求,比如拍照,录视频,生命周期管理以及相机切换等。而核心库能够搭配 Camera View 处理更复杂的 CameraX 实现。

一、Camrea整体架构

  • 预览:接受用于显示预览的 Surface,例如 PreviewView
  • 图片分析:提供 CPU 可访问的缓冲区以进行分析(例如进行机器学习)。
  • 图片拍摄:拍摄并保存照片。

不同用例可以相互组合使用,也可以同时处于活动状态。例如,应用中可以加入预览用例供用户查看进入相机视野的画面,加入图片分析用例来确定照片里的人物是否在微笑,以及包含一个图片拍摄用例以便在人物微笑时拍摄照片。

二、配置

1、依赖

google官方文档最新的CameraX稳定版本是:1.0.0-beta04,这里集成最新的稳定版。

implementation "androidx.camera:camera-core:1.0.0-beta04"
implementation "androidx.camera:camera-camera2:1.0.0-beta04"
implementation "androidx.camera:camera-view:1.0.0-alpha11"
implementation "androidx.camera:camera-lifecycle:1.0.0-beta04"
implementation "androidx.camera:camera-extensions:1.0.0-alpha09"

2、添加布局

    <androidx.camera.view.PreviewView
        android:id="@+id/preview_view"
        android:layout_width="match_parent"
        android:layout_height="240dp"
        app:scaleType="fillCenter" />

app:scaleType表示相机宽高尺寸设置无法满足硬件本身显示条件时候的显示样式,这里是fillCenter表示居中。

 previewView = findViewById(R.id.preview_view);
 previewView.post(new Runnable() {
            @Override
            public void run() {
                //动态获取宽高
                width = previewView.getWidth();
                height = previewView.getHeight();
            }
        });

3、初始化

    private void init() {
        cameraProviderFuture = ProcessCameraProvider.getInstance(MainActivity.this);
        cameraSelector = new CameraSelector.Builder()
                //设置前后摄像头
                .requireLensFacing(CameraSelector.LENS_FACING_BACK)
                .build();
        preview = new Preview.Builder()
                //设置成像宽高,需要和previewView保持一致
                .setTargetAspectRatio(aspectRatio(width, height))
                //旋转角度
                .setTargetRotation(previewView.getDisplay().getRotation())
                .build();
        try {
            cameraProvider = cameraProviderFuture.get();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

这块在preview中还可以设置帧率,线程等等内容。

三、预览

在向应用添加预览时,请使用 PreviewView,这是一种可以剪裁、缩放和旋转以确保正确显示的 View。当相机处于活动状态时,图片预览会流式传输到 PreviewView 中的 Surface。

    private void startPreview() {
        cameraProviderFuture.addListener(new Runnable() {
            @Override
            public void run() {
                try {
                    //预览之前一定要取消
                    cameraProvider.unbindAll();
                    //绑定当前Activity的生命周期
                    cameraProvider.bindToLifecycle(MainActivity.this, cameraSelector, preview);
                    //设置预览 
                   preview.setSurfaceProvider(previewView.createSurfaceProvider());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            //预览线程,主线程
        }, ContextCompat.getMainExecutor(MainActivity.this));
    }

四、拍照

图片拍摄用例旨在拍摄高分辨率的优质照片,不仅提供简单的相机手动控制功能,还提供自动白平衡、自动曝光和自动对焦 (3A) 功能。调用程序负责决定如何使用拍摄的照片,具体包括以下选项:

  • takePicture(Executor, OnImageCapturedCallback):此方法为拍摄的图片提供内存缓冲区。
  • takePicture(OutputFileOptions, Executor, OnImageSavedCallback):此方法将拍摄的图片保存到提供的文件位置。运行 ImageCapture 的可自定义执行程序有两种类型:回调执行程序和 IO 执行程序。

回调执行程序是 takePicture 方法的参数。它用于执行用户提供的 OnImageCapturedCallback。如果调用方选择将图片保存到文件位置,您可以指定执行程序以执行 IO。如需设置 IO 执行程序,请调用 ImageCapture.Builder.setIoExecutor(Executor)。如果执行程序不存在,则默认 CameraX 为任务的内部 IO 执行程序。

    public void takePhoto() {
        ImageCapture imageCapture = new ImageCapture.Builder()
                //旋转角度
                .setTargetRotation(previewView.getDisplay().getRotation())
                //缩短照相时间
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                //宽高
                .setTargetAspectRatio(aspectRatio(width, height))
                .build();
        //图像分析,返回每一帧,CameraX 会生成 YUV_420_888 格式的图片。
        ImageAnalysis   imageAnalyzer = new ImageAnalysis.Builder()
                .setTargetAspectRatio(aspectRatio(width, height))
                .setTargetRotation(previewView.getDisplay().getRotation())
                .build();
        imageAnalyzer.setAnalyzer(cameraBackExecutor, image -> {
                    Log.d(TAG, image.getImageInfo().getTimestamp() + "");
                    image.close();
                }
        );
        //拍照前必须解绑
        cameraProvider.unbindAll();
        //绑定当前Activity生命周期
        cameraProvider.bindToLifecycle(this, cameraSelector, imageCapture, imageAnalyzer);
        //拍照图片保存路径
        File file = getFile(".jpg");
        //配置路径参数
        ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(file).build();
        //开启拍照
        imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(this), new ImageCapture.OnImageSavedCallback() {

            @Override
            public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
                Toast.makeText(MainActivity.this, file.getAbsolutePath(), Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onError(@NonNull ImageCaptureException exception) {
                Toast.makeText(MainActivity.this, exception.getMessage(), Toast.LENGTH_LONG).show();
                Log.e(TAG, "Photo capture failed: ${exc.message}", exception);
            }
        });
    }

参数:setCaptureMode:

  • ​CAPTURE_MODE_MINIMIZE_LATENCY:如需缩短照片拍摄的延迟时间。 

  • CAPTURE_MODE_MAXIMIZE_QUALITY:优化照片质量。

五、录像

    private void takeVideo() {
        VideoCapture videoCapture = new VideoCaptureConfig.Builder()
                //设置宽高
                .setTargetAspectRatio(aspectRatio(width, height))
                //设置旋转角度
                .setTargetRotation(previewView.getDisplay().getRotation())
                .build();
        //录像前必须解绑
        cameraProvider.unbindAll();
        //开启相机预览
        preview.setSurfaceProvider(previewView.createSurfaceProvider());
        //绑定生命周期,这里如果没有参数preview,则只录像,不显示画面
        cameraProvider.bindToLifecycle(this, cameraSelector,preview, videoCapture);
        //视频路径
        File file = getFile(".mp4");
        //开始录像
        videoCapture.startRecording(file, ContextCompat.getMainExecutor(MainActivity.this), new VideoCapture.OnVideoSavedCallback() {
            @Override
            public void onVideoSaved(@NonNull File file) {
                Toast.makeText(MainActivity.this, file.getAbsolutePath(), Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onError(int videoCaptureError, @NonNull String message, @Nullable Throwable cause) {
                Log.d(TAG, "onError: " + message);
            }
        });
        //停止录像,并且回调OnVideoSavedCallback
        btn4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                videoCapture.stopRecording();
                preview.clear();
            }
        });
    }

最后记得动态申请权限,否则会出现录像,拍照失败等。权限代码如下:

    <uses-feature android:name="android.hardware.camera.any" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ||
                ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ||
                ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ||
                ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE,
                            Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO},
                    100);
            return;
        }

运行效果如文章刚刚开始时候所示。

 

 

 

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值