Cam APP-HAL流程追踪之demo梳理

一、基础知识

1、Google官网的Cam流程如下图1

Google 流程图-图1

2、Cam的预览、拍照、录像是分开的

Cam的预览、拍照、录像是各自独立的-换句话说可以不开启预览拍照或者录像–后面代码会详细介绍;市场上的成品Cam应用,打开Cam后直接打开了预览,然后可以拍照或者录视频是为了更好的排出效果;

之前电商能找出很多的针孔摄像头专门搞偷拍的-没有预览直接录视频或者拍照,由于出事太多”背后的灰色产业链!”,此类产品全部下架。

二、demo分析

1、权限

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera2.full" />

2、UI布局

  • 设置一个TextureView 用于 预览图片;
  • 设置一个ImageView 用于 显示拍照结果;
  • 设置一个 Button 用于触发拍照功能;

这里介绍一下 为什么用TextureView,而不用SurfaceView、SurfaceTexture、GLSurfaceView或者其他view?

  • View:显示视图,内置画布,提供图形绘制函数、触屏事件、按键事件函数等;必须在UI主线程内更新画面,速度较慢
  • SurfaceView:基于view视图进行拓展的视图类,更适合2D游戏的开发;是view的子类,类似使用双缓机制,在新的线程中更新画面,所以刷新界面速度比view快。其缺点是不能做变形和动画,也不能随屏幕的变化而变化,另外不能在其上面覆盖其它的SurfaceView
  • GLSurfaceView:是surfaceview的子类,在其基础上封装了egl环境管理,以及render线程。专用于3D游戏开发的视图,OpenGL ES专用。
  • TextureView:它也是继承自View,只能运行中硬件加速窗口。它的功能类似于SurfaceView + SurfaceTexture,它内部包含一个SurfaceTexture,它可以让Camera的数据和显示分离,比如需要做二次处理时,如Camera吧采集的数据发送给SurfaceTexture(比如做个美颜),SurfaceTexture处理后传给TextureView显示。TextureView可以做view的变形和动画。一般它是在主线程上做处理(在Android 5.0引入渲染线程后,它是在渲染线程中做的)。
  • https://source.android.google.cn/devices/graphics/arch-st?hl=zh-cn
    SurfaceTexture 是 Surface 和 OpenGL ES (GLES) 纹理的组合。SurfaceTexture 实例用于提供输出到 GLES 纹理的接口。
    SurfaceTexture 包含一个以应用为使用方的 BufferQueue 实例。当生产方将新的缓冲区排入队列时,onFrameAvailable() 回调会通知应用。然后,应用调用 updateTexImage(),这会释放先前占用的缓冲区,从队列中获取新缓冲区并执行 EGL 调用,从而使 GLES 可将此缓冲区作为外部纹理使用。

3、代码

总体流程本来打算自己画的,偶然看到了这个
流程图就直接拿来作参考了原图地址

1、 view初始化

       textureView = findViewById(R.id.texture_view_camera2);
       imageViewThumb = findViewById(R.id.thumb_iv);
       findViewById(R.id.shutter_button).setOnClickListener(this);

2、获取CameraManger、给TextureView设置状态监听

        cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        textureView.setSurfaceTextureListener(this);

3、获取当前机器支持的size的list 然后找到设定比例中的最大值 设置为默认size(一般都封装成util)

    private int firstWidthRatio = 3;//画幅比例宽度比值分子
    private int firstHeightRatio = 4;//画幅比例高度比值分母
    private int firstDeviceWidth;//设备宽度
    private int firstDeviceHeight;//设备高度
    private Size previewSizeStandard;//预览尺寸标准
    private Size photoSizeStandard;//照片尺寸标准


    private void initPictureSize() {
        try {
            for (String camId : cameraManager.getCameraIdList()) {
                cameraId = camId;
                CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
                StreamConfigurationMap streamConfigurationMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);//输出流配置
                Size[] previewSizeList = streamConfigurationMap.getOutputSizes(SurfaceTexture.class);//获取当前相机支持的所有尺寸列表
                previewSizeStandard = SizeUtil.getSuitableSize(previewSizeList, firstWidthRatio, firstHeightRatio, firstDeviceWidth, firstDeviceHeight);

                /*根据画幅比例和设备宽高在支持的所有尺寸列表中过滤出尺寸标准用作照片(ImageReader)(如果获取了多个照片尺寸,选择尺寸最大的(排序))(在设置ImageReader和缩略图的时候使用)(尚未生效)*/
                Size[] photoSizeList = streamConfigurationMap.getOutputSizes(ImageFormat.JPEG);//获取兼容JPEG格式的所有尺寸列表
                photoSizeStandard = SizeUtil.getSuitableSizePhoto(photoSizeList, firstWidthRatio, firstHeightRatio, firstDeviceWidth, firstDeviceHeight);
                /*
                 * 获取到最佳预览尺寸的标准后,我们就可以修改自定义的TextureView的尺寸,避免画面拉伸的情况
                 */
                textureView.setAspectRation(previewSizeStandard.getHeight(), previewSizeStandard.getWidth());
                Log.d(TAG, "previewSizeStandard:=" + previewSizeStandard);
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
    
    /* 返回采样率InSampleSize(int)
     * 不断将尺寸减半,直到小于缩略图的尺寸*/
    public static int getInSampleSize(Size picSize, int reqHeight, int reqWidth) {

        int height = picSize.getWidth();
        int width = picSize.getHeight();
        int inSampleSize = 1;
        if (height > reqHeight || width > reqWidth) {
            int halfHeight = height / 2;
            int halfWidth = width / 2;
            while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }

4、初始化ImageReader

    private void initImageReader() {
        imageReader = ImageReader.newInstance(photoSizeStandard.getWidth(), photoSizeStandard.getHeight(), ImageFormat.JPEG, 2);
        imageReader.setOnImageAvailableListener(this, null);
        Log.d(TAG, "setOnImageAvailableListener X");
        captureSurface = imageReader.getSurface();
    }

5、打开相机和各种回调

    //    申请相机权限
    private void checkPermission() {
        Log.d(TAG, "checkPermission");
        // 检查是否申请了权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
                openCamera();
            } else {
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 1);
            }
        }
    }


    private void openCamera() {
        Log.d(TAG, "openCamera");
        // 4 检查相机权限
        checkPermission();
        // 5 开启相机(传入:要开启的相机ID,和状态回调对象)
        try {
            initCamStateCallBack();
            cameraManager.openCamera(cameraId, camStateCallback, mHandler);
            Log.d(TAG, "openCamera X");
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private void initCamStateCallBack() {
        camStateCallback = new CameraDevice.StateCallback() {
            @Override
            public void onOpened(@NonNull CameraDevice camera) {
                Log.d(TAG, "openCamera onOpened");
                cameraDevice = camera;
                try {
                    SurfaceTexture sTexture = textureView.getSurfaceTexture();
                    sTexture.setDefaultBufferSize(previewSizeStandard.getWidth(), previewSizeStandard.getHeight());
                    previewSurface = new Surface(sTexture);
                    Log.d(TAG, "previewSizeStandard:=" + previewSizeStandard);
                    // 2.2 构建请求对象(设置预览参数,和输出对象)
                    previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); // 设置参数:预览
                    previewRequestBuilder.addTarget(previewSurface); // 设置参数:目标容器
                    captureRequest = previewRequestBuilder.build();
                    CamCaptureSessionStateCallback();
                    // 2.3 创建会话
                 //如果些成cameraDevice.createCaptureSession(Arrays.asList(previewSurface), camCaptureSessionStateCallback, mHandler);虽然也不会报错,但不会走setOnImageAvailableListener的回调
                    cameraDevice.createCaptureSession(Arrays.asList(previewSurface, imageReader.getSurface()), camCaptureSessionStateCallback, mHandler);
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onDisconnected(@NonNull CameraDevice camera) {
                Log.d(TAG, "openCamera onOpened");
            }

            @Override
            public void onError(@NonNull CameraDevice camera, int error) {
                Log.d(TAG, "openCamera onError");
            }
        };
    }
    private void CamCaptureSessionStateCallback() {
        camCaptureSessionStateCallback = new CameraCaptureSession.StateCallback() {
            @Override  //2.3.1  会话准备好了,在里面创建 预览或拍照请求
            public void onConfigured(@NonNull CameraCaptureSession session) {
                cameraCaptureSession = session;
                //预览代码,上文中说到,可以不开启预览拍照,如果把此 session.setRepeatingRequest(captureRequest, null, null);注释掉就不会开启预览,且不影响后续拍照
//                try {
//                    Log.d(TAG, "CamCaptureSessionStateCallback onConfigured");
//                    session.setRepeatingRequest(captureRequest, null, null);
//                } catch (CameraAccessException e) {
//                    e.printStackTrace();
//                }
            }

            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                Log.d(TAG, "CamCaptureSessionStateCallback onConfigureFailed");
            }
        };
    }


    //**********************************TextureView.SurfaceTextureListener*****start*****************************
    @Override
    public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
        Log.d(TAG, "onSurfaceTextureAvailable ");
        previewSurface = new Surface(textureView.getSurfaceTexture());
        openCamera();
    }

    @Override
    public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {
        Log.d(TAG, "onSurfaceTextureSizeChanged ");
    }

    @Override
    public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
        Log.d(TAG, "onSurfaceTextureDestroyed ");
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {
//        Log.d(TAG,"onSurfaceTextureUpdated ");
    }

    //**********************************TextureView.SurfaceTextureListener*****end*****************************


6、按快门拍照

    @Override
    public void onClick(View v) {

        switch (v.getId()) {
            case R.id.shutter_button:
                Log.d(TAG, "onClick shutter_button");

                try {
                    configureCaptureBeforeCapture();
                    cameraCaptureSession.stopRepeating();
                    cameraCaptureSession.capture(captureRequestBuild.build(), captureCallback, mHandler);
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
                break;
        }
    }

    private void configureCaptureBeforeCapture() {
        try {
            captureRequestBuild = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        captureRequestBuild.set(CaptureRequest.JPEG_ORIENTATION, 90);
        captureRequestBuild.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
        //B4.2 配置request的参数 的目标对象
        SurfaceTexture sTexture = textureView.getSurfaceTexture();
        sTexture.setDefaultBufferSize(previewSizeStandard.getWidth(), previewSizeStandard.getHeight());
        captureSurface = new Surface(sTexture);
//        captureRequestBuild.addTarget(captureSurface);//把capture的图片渲染到 TextureView 上  ,如果注释掉此demo不会把图片显示到 TextureView ,
//        captureRequestBuild.addTarget(imageReader.getSurface());//ImageAvailableListener 回调(保存图片用的),注释掉不会走ImageAvailableListener的回调逻辑

    }




    @Override
    public void onImageAvailable(ImageReader reader) {
        Log.d(TAG, "setOnImageAvailableListener onImageAvailable");
        Image image = reader.acquireLatestImage();
        ByteBuffer buffer = image.getPlanes()[0].getBuffer();
        int length = buffer.remaining();
        byte[] bytes = new byte[length];
        buffer.get(bytes);
        image.close();
        BitmapFactory.Options sampler = new BitmapFactory.Options();
        //设置采样率
        sampler.inSampleSize = getInSampleSize(photoSizeStandard, 60, 60);//(缩略图尺寸px转换为int)
        //将byte[]转换为bitmap
        bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, sampler);
        //B2.2 显示图片
        imageViewThumb.setImageBitmap(bitmap);
    }







    private CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {
        @Override
        public void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, long timestamp, long frameNumber) {
            super.onCaptureStarted(session, request, timestamp, frameNumber);
            Log.d(TAG, "onCaptureStarted");
        }

        @Override
        public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
            super.onCaptureProgressed(session, request, partialResult);
            Log.d(TAG, "onCaptureProgressed");
        }

        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
            super.onCaptureCompleted(session, request, result);
            Log.d(TAG, "onCaptureCompleted");
            //拍照完成后是否继续预览
//            try {
//                cameraCaptureSession.setRepeatingRequest(previewRequestBuilder.build(),null,null);
//            } catch (CameraAccessException e) {
//                e.printStackTrace();
//            }
        }

        @Override
        public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
            super.onCaptureFailed(session, request, failure);
            Log.d(TAG, "onCaptureFailed");
        }

        @Override
        public void onCaptureSequenceCompleted(@NonNull CameraCaptureSession session, int sequenceId, long frameNumber) {
            super.onCaptureSequenceCompleted(session, sequenceId, frameNumber);
            Log.d(TAG, "onCaptureSequenceCompleted");
        }

        @Override
        public void onCaptureSequenceAborted(@NonNull CameraCaptureSession session, int sequenceId) {
            super.onCaptureSequenceAborted(session, sequenceId);
            Log.d(TAG, "onCaptureSequenceAborted");
        }

        @Override
        public void onCaptureBufferLost(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull Surface target, long frameNumber) {
            super.onCaptureBufferLost(session, request, target, frameNumber);
            Log.d(TAG, "onCaptureBufferLost");
        }
    };

7、验证预览、拍照是独立的,
方式一:

正常预览时拍照:

05-12 11:43:30.011  8024  8024 D MainActivity: onClick shutter_button
05-12 11:43:30.363  8024  8054 D MainActivity: onCaptureStarted
05-12 11:43:30.376  8024  8054 D MainActivity: onCaptureCompleted
05-12 11:43:30.377  8024  8054 D MainActivity: onCaptureSequenceCompleted
05-12 11:43:30.400  8024  8024 D MainActivity: setOnImageAvailableListener onImageAvailable

当没有onImageAvailable 对调时 不设置 captureRequestBuild.addTarget(imageReader.getSurface());

:~$ alog "runtime|MainActi" -i
05-12 10:36:47.036  6848  6848 D MainActivity: onClick shutter_button
05-12 10:36:47.089  6848  6875 D MainActivity: onCaptureStarted
05-12 10:36:47.093  6848  6875 D MainActivity: onCaptureCompleted
05-12 10:36:47.093  6848  6875 D MainActivity: onCaptureSequenceCompleted

没有 MainActivity: setOnImageAvailableListener onImageAvailable

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
相机API2是Android系统中用于控制相机硬件的标准接口,它提供了更灵活、更高效的相机控制功能。而app-fw-halAndroid系统中的相机HAL模块,它是应用程序和相机驱动程序之间的接口。在使用相机API2拍摄RAW照片时,其流程如下: 1. 打开相机:应用程序使用相机管理器(CameraManager)打开相机设备。 2. 创建相机会话:应用程序使用相机设备的创建会话方法(createCaptureSession)创建一个用于拍摄的会话。在创建会话时,需要指定输出目标,例如预览或拍照。 3. 配置相机:应用程序使用相机设备的创建请求方法(createCaptureRequest)创建一个请求对象,并通过该对象配置相机的参数。例如,设置相机的曝光时间、ISO等参数。 4. 配置输出目标:应用程序通过请求对象的addTarget方法将输出目标与请求对象关联起来。对于RAW照片,需要将输出目标设置为ImageReader。 5. 开始预览:应用程序通过会话对象的setRepeatingRequest方法开始预览。 6. 拍照:应用程序通过请求对象的capture方法拍摄照片。 7. 获取RAW数据:当拍摄完成后,ImageReader会回调onImageAvailable方法,应用程序可以在该方法中获取RAW数据。 8. 处理RAW数据:应用程序可以将RAW数据转换为JPEG格式并保存到文件系统中,或者进行其他的处理操作。 9. 关闭会话:应用程序使用会话对象的close方法关闭会话。 10. 关闭相机:应用程序使用相机设备的close方法关闭相机。 以上就是使用相机API2拍摄RAW照片的流程,其中app-fw-hal作为相机HAL模块提供了应用程序和相机驱动程序之间的接口。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值