分享

mtkcamera api2学习

 新用户8389DdzY 2021-09-22

Camera2 中主要的API类

CameraManager类 : 摄像头管理类,用于检测、打开系统摄像头,通过getCameraCharacteristics(cameraId)可以获取摄像头特征。

CameraCharacteristics类:相机特性类,例如,是否支持自动调焦,是否支持zoom,是否支持闪光灯一系列特征。

CameraDevice类: 相机设备,类似早期的camera类。

CameraCaptureSession类:用于创建预览、拍照的Session类。通过它的setRepeatingRequest()方法控制预览界面 , 通过它的capture()方法控制拍照动作或者录像动作。

CameraRequest类:一次捕获的请求,可以设置一些列的参数,用于控制预览和拍照参数,例如:对焦模式,曝光模式,zoom参数等等。

接下来,进一步介绍,Camera2 API中的各种常见类和抽象类。

CameraManager

CameraCharacteristics cameraCharacteristics =manager.getCameraCharacteristics(cameraId);

通过以上代码可以获取摄像头的特征对象,例如: 前后摄像头,分辨率等。

CameraCharacteristics

相机特性类

CameraCharacteristics是一个包含相机参数的对象,可以通过一些key获取对应的values.

以下几种常用的参数:

LENS_FACING:获取摄像头方向。LENS_FACING_FRONT是前摄像头,LENS_FACING_BACK是后摄像头。

SENSOR_ORIENTATION:获取摄像头拍照的方向。

FLASH_INFO_AVAILABLE:获取是否支持闪光灯。

SCALER_AVAILABLE_MAX_DIGITAL_ZOOM:获取最大的数字调焦值,也就是zoom最大值。

LENS_INFO_MINIMUM_FOCUS_DISTANCE:获取最小的调焦距离,某些手机上获取到的该values为null或者0.0。前摄像头大部分有固定焦距,无法调节。

INFO_SUPPORTED_HARDWARE_LEVEL:获取摄像头支持某些特性的程度。

以下手机中支持的若干种程度:

INFO_SUPPORTED_HARDWARE_LEVEL_FULL:全方位的硬件支持,允许手动控制全高清的摄像、支持连拍模式以及其他新特性。

INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED:有限支持,这个需要单独查询。

INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY:所有设备都会支持,也就是和过时的Camera API支持的特性是一致的。

CameraDevice

CameraDevice的createCaptureRequest(int templateType)方法创建CaptureRequest.Builder。

templateType参数有以下几种:

TEMPLATE_PREVIEW :预览

TEMPLATE_RECORD:拍摄视频

TEMPLATE_STILL_CAPTURE:拍照

TEMPLATE_VIDEO_SNAPSHOT:创建视视频录制时截屏的请求

TEMPLATE_ZERO_SHUTTER_LAG:创建一个适用于零快门延迟的请求。在不影响预览帧率的情况下最大化图像质量。

TEMPLATE_MANUAL:创建一个基本捕获请求,这种请求中所有的自动控制都是禁用的(自动曝光,自动白平衡、自动焦点)。

CameraDevice.StateCallback抽象类

该抽象类用于CemeraDevice相机设备状态的回调。

    /**

     * 当相机设备的状态发生改变的时候,将会回调。

     */

    protected final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {

        /**

         * 当相机打开的时候,调用

         * @param cameraDevice

         */

        @Override

        public void onOpened(@NonNull CameraDevice cameraDevice) {

            mCameraDevice = cameraDevice;

            startPreView();

        }

        @Override

        public void onDisconnected(@NonNull CameraDevice cameraDevice) {

            cameraDevice.close();

            mCameraDevice = null;

        }

        /**

         * 发生异常的时候调用

         *

         * 这里释放资源,然后关闭界面

         * @param cameraDevice

         * @param error

         */

        @Override

        public void onError(@NonNull CameraDevice cameraDevice, int error) {

            cameraDevice.close();

            mCameraDevice = null;

        }

        /**

         *当相机被关闭的时候

         */

        @Override

        public void onClosed(@NonNull CameraDevice camera) {

            super.onClosed(camera);

        }

    };

CameraCaptureSession.StateCallback抽象类

该抽象类用于Session过程中状态的回调。

public static abstract class StateCallback {

        //摄像头完成配置,可以处理Capture请求了。

        public abstract void onConfigured(@NonNull CameraCaptureSession session);

        //摄像头配置失败

        public abstract void onConfigureFailed(@NonNull CameraCaptureSession session);

        //摄像头处于就绪状态,当前没有请求需要处理

        public void onReady(@NonNull CameraCaptureSession session) {}

        //摄像头正在处理请求

        public void onActive(@NonNull CameraCaptureSession session) {}

        //请求队列中为空,准备着接受下一个请求。

        public void onCaptureQueueEmpty(@NonNull CameraCaptureSession session) {}

        //会话被关闭

        public void onClosed(@NonNull CameraCaptureSession session) {}

        //Surface准备就绪

        public void onSurfacePrepared(@NonNull CameraCaptureSession session,@NonNull Surface surface) {}

}

接下来,是介绍拍照和录像流程步骤。

使用流程:

1. 打开指定的方向的相机

最先获取CameraManager对象,通过该对象的getCameraIdList()获取到一些列的摄像头参数。

通过循环匹配,获取到指定方向的摄像头,例如后摄像头等。

 CameraManager manager = (CameraManager)getSystemService(Context.CAMERA_SERVICE);

//获取到可用的相机

for (String cameraId : manager.getCameraIdList()) {

      //获取到每个相机的参数对象,包含前后摄像头,分辨率等

     CameraCharacteristics  cameraCharacteristics = manager.getCameraCharacteristics(cameraId);

     //摄像头的方向

     Integer facing = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING);

     if(facing==null){

         continue;

     }

     //匹配方向,指定打开后摄像头

     if(facing!=CameraCharacteristics.LENS_FACING_BACK){

          continue;

     }

     //打开指定的摄像头

    manager.openCamera(mCameraId, stateCallback, workThreadManager.getBackgroundHandler());

    return;

}

当然,实际开发中,还需要获取相机支持的特性(闪光灯,zoom调焦,手动调焦等),和设置摄像头的参数(例如:预览的Size)。

2. 创建预览的界面:

创建 CameraDevice.StateCallback对象,且开启一个相机。当相机开启后,将出现相机预览界面。

CameraDevice.StateCallback对象传入CameraManager中openCamera(mCameraId, stateCallback, workThreadManager.getBackgroundHandler())的第二个参数,用于监听摄像头的状态。

/**

  * 相机设备

  */

 protected CameraDevice mCameraDevice;

 /**

   * 当相机设备的状态发生改变的时候,将会回调。

   */

 protected final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {

        /**

         * 当相机打开的时候,调用

         * @param cameraDevice

         */

        @Override

        public void onOpened(@NonNull CameraDevice cameraDevice) {

            mCameraDevice = cameraDevice;

            createCameraPreviewSession();

        }

       // 省略该状态接口的部分方法

       ...............

 };

 /**

  * 预览请求的Builder

  */

 private CaptureRequest.Builder mPreviewRequestBuilder;

 /**

 * 相机开始预览,创建一个CameraCaptureSession对象

 */

 private void createCameraPreviewSession() {

      // 将CaptureRequest的构建器与Surface对象绑定在一起   

      mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

      // 为相机预览,创建一个CameraCaptureSession对象

     mCameraDevice.createCaptureSession(Arrays.asList(surface, imageReader.getSurface()), stateCallback, null);       

 }

创建完预览的界面后,接下来需要开始刷新。

3. 在预览界面过程中,需要间隔刷新界面

相机预览使用TextureView来实现。创建一个CameraCaptureSession ,通过一个用于预览界面的CaptureRequest,间隔复用给CameraCaptureSession。

 private CameraCaptureSession mCaptureSession;

 CameraCaptureSession.StateCallback stateCallback=new CameraCaptureSession.StateCallback() {

                @Override

                public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {

                //当cameraCaptureSession已经准备完成,开始显示预览界面

                    mCaptureSession = cameraCaptureSession;

                    setCameraCaptureSession();

                }

                //省略该接口的部分方法

                .......

 }

 /**

  * 设置CameraCaptureSession的特征:

  * <p>

  * 自动对焦,闪光灯

  */

 private void setCameraCaptureSession() {

     //设置预览界面的特征,通过mPreviewRequestBuilder.set()方法,例如,闪光灯,zoom调焦等

     ..........

      //为CameraCaptureSession设置间隔的CaptureRequest,用间隔刷新预览界面。

    mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, workThreadManager.getBackgroundHandler());

 }

只要未开始拍照动作或者录像动作,该复用的CaptureRequest会重复的刷新预览界面。

接下来,等待用户点击拍照按钮或者录像按钮,进行拍照动作,或者录像动作。

4. 拍照动作:

首先锁住焦点,通过在相机预览界面个性CaptureRequest。然后,以类似方式,需要运行一个预捕获序列。接下来,可已经准备好捕捉图片。创建一个新的CaptureRequest,且拍照。

  /**

     * 拍照一个静态的图片

     * ,当在CaptureCallback监听器响应的时候调用该方法。

     * <p>

     * 当数字调焦缩放的时候,在写入图片数中也要设置。

     */

private void captureStillPicture() {

    try {

            // 创建一个拍照的CaptureRequest.Builder

            final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);

            captureBuilder.addTarget(imageReader.getSurface());

            //设置一系列的拍照参数,这里省略

            ...........

            //先停止以前的预览状态

           mCaptureSession.stopRepeating();

           mCaptureSession.abortCaptures();

            //执行拍照动作

           mCaptureSession.capture(captureBuilder.build(), captureCallback, null);

    } catch (CameraAccessException e) {

            e.printStackTrace();

    }

}

拍照界面产生的数据只是在手机内存中,图片是一个磁盘文件,还需要一个将拍照产生数据写入文件中的操作类ImageReader。

先是创建ImageReader对象,和设置监听器等一些列参数。

    /**

     * 处理静态图片的输出

     */

    private ImageReader imageReader;

      //对于静态图片,使用可用的最大值来拍摄。

      Size largest = Collections.max(Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), new CompareSizeByArea());

      //设置ImageReader,将大小,图片格式

      imageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, /*maxImages*/2);

      imageReader.setOnImageAvailableListener(onImageAvailableListener, workThreadManager.getBackgroundHandler());   

接下来,将ImageReader的surface配置到captureBuilder对象中captureBuilder.addTarget(imageReader.getSurface());

最后,当拍照完成后,会在该监听状态中回调:

   /**

     * ImageReader的回调监听器

     * <p>

     * onImageAvailable被调用的时候,已经拍照完,准备保存的操作

     * 通常写入磁盘文件中。

     */

    protected final ImageReader.OnImageAvailableListener onImageAvailableListener = (ImageReader reader)

            -> writePictureData(reader.acquireNextImage());

    public void writePictureData(Image image) {

        if (camera2ResultCallBack != null) {

            camera2ResultCallBack.callBack(ObservableBuilder.createWriteCaptureImage(appContext, image));

        }

    }       

    /**

     * 将JPEG图片的数据,写入磁盘中

     *

     * @param context

     * @param mImage

     * @return

     */

    public static Observable<String> createWriteCaptureImage(final Context context, final Image mImage) {

        Observable<String> observable = Observable.create(subscriber -> {

            File file = FileUtils.createPictureDiskFile(context, FileUtils.createBitmapFileName());

            ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();

            byte[] bytes = new byte[buffer.remaining()];

            buffer.get(bytes);

            FileOutputStream output = null;

            try {

                output = new FileOutputStream(file);

                output.write(bytes);

            } catch (IOException e) {

                e.printStackTrace();

            } finally {

                mImage.close();

                if (null != output) {

                    try {

                        output.close();

                    } catch (IOException e) {

                        e.printStackTrace();

                    }

                }

            }

            subscriber.onNext(file.getAbsolutePath());

        });

        return observable;

    }

这里采用RxJava+RxAndroid异步通讯,避免太多回调接口。

5. 录像动作

录像是长时间的动作,录像过程中需要重复性的刷新录制界面。其余的步骤和拍照动作基本类似。

 /**

   * 开始视频录制。

   */

private void startRecordingVideo() {

    try {

            //创建录制的session会话中的请求

            mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);

            //设置录制参数,这里省略

            .........

            // Start a capture session

            // Once the session starts, we can update the UI and start recording

            mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {

                @Override

                public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {

                     mPreviewSession = cameraCaptureSession;

                     Log.i(TAG, " startRecordingVideo  正式开始录制 ");

                     updatePreview();

                }

                //该接口的方法,部分省略

                .............

            }, workThreadManager.getBackgroundHandler());

        } catch (CameraAccessException | IOException e) {

            e.printStackTrace();

        }

}

//录制过程中,不断刷新录制界面

private void updatePreview() {

        try {

            mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), null, workThreadManager.getBackgroundHandler());

        } catch (CameraAccessException e) {

            e.printStackTrace();

        }

}

和拍照类似,将视频数据写入磁盘文件中,也是需要一个操作类 MediaRecorder来实现的。

先是创建该操作类对象,设置一些列参数:

    /**

     * MediaRecorder

     */

private MediaRecorder mMediaRecorder;

 /**

     * 设置媒体录制器的配置参数

     * <p>

     * 音频,视频格式,文件路径,频率,编码格式等等

     *

     * @throws IOException

     */

    private void setUpMediaRecorder() throws IOException {

        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);

        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);

        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);

        mNextVideoAbsolutePath = FileUtils.createVideoDiskFile(appContext, FileUtils.createVideoFileName()).getAbsolutePath();

        mMediaRecorder.setOutputFile(mNextVideoAbsolutePath);

        mMediaRecorder.setVideoEncodingBitRate(10000000);

        //每秒30帧

        mMediaRecorder.setVideoFrameRate(30);

        mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight());

        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);

        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);

        int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();

        switch (mSensorOrientation) {

            case SENSOR_ORIENTATION_DEFAULT_DEGREES:

                mMediaRecorder.setOrientationHint(DEFAULT_ORIENTATIONS.get(rotation));

                break;

            case SENSOR_ORIENTATION_INVERSE_DEGREES:

                mMediaRecorder.setOrientationHint(ORIENTATIONS.get(rotation));

                break;

            default:

                break;

        }

        mMediaRecorder.prepare();

    }

间隔性的随着视频录制而输出数据到文件中。

// 为 MediaRecorder设置Surface

Surface recorderSurface = mMediaRecorder.getSurface();

surfaces.add(recorderSurface);

mPreviewBuilder.addTarget(recorderSurface);

最后,当录制视频结束后,停止输出:

 // 停止录制

 mMediaRecorder.stop();

 mMediaRecorder.reset();

6. 恢复到预览界面:

完成一些列拍照或录像动作后,重新恢复到预览界面。

/**

  * 完成一些列拍照或录像动作后,释放焦点。

  */

private void unlockFocus() {

   try {

         //向session重新发送,预览的间隔性请求,出现预览界面。

         mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, workThreadManager.getBackgroundHandler());

    } catch (CameraAccessException e) {

            e.printStackTrace();

    }

}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多