第11讲 StreamConfigurationMap实战一 - Android Camera2 API

本讲是Android Camera专题系列的第11讲,我们介绍Android Camera2 API专题的StreamConfigurationMap实战之如何获取预览Size和拍照Jpeg Size。

视频在线观看:

加入知识星球与更多Camera同学交流

  • 星球名称:深入浅出Android Camera
  • 星球ID: 17296815

GeekCamera2启动时序图

GeekCamera2启动时序图

用StreamConfigurationMap来做什么

在创建Camera Capture Session前,会先通过StreamConfigurationMap获取

  • Camera支持的预览Size列表
  • Camera支持的拍照Size列表
  • Camera支持的录像Size列表

获取预览Size

预览组件

  • SurfaceHolder / SurfaceTexture / ImageReader

需要考虑的因素

  • 屏幕Size、拍照Size

  • 一般提供三种宽高比:全屏、1:1、4:3

实战

  • GeekCamera2 如何选择预览Size

获取Preview Size List核心代码

private void doInitSupportedPreviewSizes(CameraFeatures camera_features,
                                         StreamConfigurationMap configs) throws CameraControllerException{
    android.util.Size [] camera_preview_sizes = configs.getOutputSizes(SurfaceTexture.class);
    camera_features.mSupportedPreviewSizes = new ArrayList<>();
    Point display_size = new Point();
    Activity activity = (Activity)context;
    {
        Display display = activity.getWindowManager().getDefaultDisplay();
        display.getRealSize(display_size);
        // getRealSize() is adjusted based on the current rotation, so should already be landscape format, but it
        // would be good to not assume Open Camera runs in landscape mode (if we ever ran in portrait mode,
        // we'd still want display_size.x > display_size.y as preview resolutions also have width > height)
        if( display_size.x < display_size.y ) {
            //noinspection SuspiciousNameCombination
            display_size.set(display_size.y, display_size.x);
        }
        if( MyDebug.LOG )
            Log.i(TAG, "display_size: " + display_size.x + " x " + display_size.y);
    }
    if( camera_preview_sizes == null ) {
        // camera_preview_sizes is null on Samsung Galaxy Note 10+ and S20 for camera ID 4!
        Log.e(TAG, "no preview sizes returned by getOutputSizes");
        throw new CameraControllerException();
    }
    else {
        for(android.util.Size camera_size : camera_preview_sizes) {
            if( MyDebug.LOG )
                Log.i(TAG, "preview size: " + camera_size.getWidth() + " x " + camera_size.getHeight());
            if( camera_size.getWidth() > display_size.x || camera_size.getHeight() > display_size.y ) {
                // Nexus 6 returns these, even though not supported?! (get green corruption lines if we allow these)
                // Google Camera filters anything larger than height 1080, with a todo saying to use device's measurements
                continue;
            }
            camera_features.mSupportedPreviewSizes.add(new CameraController.Size(camera_size.getWidth(), camera_size.getHeight()));
        }
    }
}

根据Picture Size获取Preview SIze核心代码

public CameraController.Size getOptimalPreviewSize(List<CameraController.Size> sizes) {
    if( MyDebug.LOG )
        Log.d(TAG, "getOptimalPreviewSize()");
    final double ASPECT_TOLERANCE = 0.05;
    if( sizes == null )
        return null;
    if( is_video && video_high_speed ) {
        VideoProfile profile = getVideoProfile();
        if( MyDebug.LOG )
            Log.d(TAG, "video size: " + profile.videoFrameWidth + " x " + profile.videoFrameHeight);
        // preview size must match video resolution for high speed, see doc for CameraDevice.createConstrainedHighSpeedCaptureSession()
        return new CameraController.Size(profile.videoFrameWidth, profile.videoFrameHeight);
    }
    CameraController.Size optimalSize = null;
    double minDiff = Double.MAX_VALUE;
    Point display_size = new Point();
    Activity activity = (Activity)this.getContext();
    {
        Display display = activity.getWindowManager().getDefaultDisplay();
        display.getSize(display_size);
        // getSize() is adjusted based on the current rotation, so should already be landscape format, but:
        // (a) it would be good to not assume Open Camera runs in landscape mode (if we ever ran in portrait mode,
        // we'd still want display_size.x > display_size.y as preview resolutions also have width > height,
        // (b) on some devices (e.g., Nokia 8), when coming back from the Settings when device is held in Preview,
        // display size is returned in portrait format! (To reproduce, enable "Maximise preview size"; or if that's
        // already enabled, change the setting off and on.)
        if( display_size.x < display_size.y ) {
            //noinspection SuspiciousNameCombination
            display_size.set(display_size.y, display_size.x);
        }
        if( MyDebug.LOG )
            Log.d(TAG, "display_size: " + display_size.x + " x " + display_size.y);
    }
    double targetRatio = calculateTargetRatioForPreview(display_size);
    int targetHeight = Math.min(display_size.y, display_size.x);
    if( targetHeight <= 0 ) {
        targetHeight = display_size.y;
    }
    // Try to find the size which matches the aspect ratio, and is closest match to display height
    for(CameraController.Size size : sizes) {
        if( MyDebug.LOG )
            Log.d(TAG, "    supported preview size: " + size.width + ", " + size.height);
        double ratio = (double)size.width / size.height;
        if( Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE )
            continue;
        if( Math.abs(size.height - targetHeight) < minDiff ) {
            optimalSize = size;
            minDiff = Math.abs(size.height - targetHeight);
        }
    }
    if( optimalSize == null ) {
        // can't find match for aspect ratio, so find closest one
        if( MyDebug.LOG )
            Log.d(TAG, "no preview size matches the aspect ratio");
        optimalSize = getClosestSize(sizes, targetRatio, null);
    }
    if( MyDebug.LOG ) {
        Log.d(TAG, "chose optimalSize: " + optimalSize.width + " x " + optimalSize.height);
        Log.d(TAG, "optimalSize ratio: " + ((double)optimalSize.width / optimalSize.height));
    }
    return optimalSize;
}

获取拍照Size

拍照组件

  • ImageReader(JPEG)

需要考虑的点

  • Support Size List要考虑getHighResolutionOutputSizes

  • 拍照使用哪种Format(JPEG,YUV_420_888,RAW等)

  • 宽高比与预览Size保持一致

实战

  • GeekCamera2 如何选择拍照Size

获取Jpeg Size List核心代码

private void doInitSupportedJpegSizes(CameraFeatures camera_features,
                                      StreamConfigurationMap configs) throws CameraControllerException {
    android.util.Size [] camera_picture_sizes = configs.getOutputSizes(ImageFormat.JPEG);
    camera_features.mSupportedPictureSizes = new ArrayList<>();
    if( android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M ) {
        android.util.Size [] camera_picture_sizes_hires = configs.getHighResolutionOutputSizes(ImageFormat.JPEG);
        if( camera_picture_sizes_hires != null ) {
            for(android.util.Size camera_size_hr : camera_picture_sizes_hires) {
                if( MyDebug.LOG )
                    Log.i(TAG, "high resolution picture size: " + camera_size_hr.getWidth() + " x " + camera_size_hr.getHeight());
                // Check not already listed? If it's listed in both, we'll add it later on when scanning camera_picture_sizes
                // (and we don't want to set supports_burst to false for such a resolution).
                boolean found = false;
                for(android.util.Size sz : camera_picture_sizes) {
                    if( sz.equals(camera_size_hr) ) {
                        found = true;
                        break;
                    }
                }
                if( !found ) {
                    if( MyDebug.LOG )
                        Log.i(TAG, "doInitSupportedJpegSizes high resolution [non-burst] picture size: " + camera_size_hr.getWidth() + " x " + camera_size_hr.getHeight());
                    CameraController.Size size = new CameraController.Size(camera_size_hr.getWidth(),
                                                            camera_size_hr.getHeight());
                    size.supports_burst = false;
                    camera_features.mSupportedPictureSizes.add(size);
                }
            }
        }
    }
    if( camera_picture_sizes == null ) {
        // camera_picture_sizes is null on Samsung Galaxy Note 10+ and S20 for camera ID 4!
        Log.e(TAG, "no picture sizes returned by getOutputSizes");
        throw new CameraControllerException();
    }
    else {
        for(android.util.Size camera_size : camera_picture_sizes) {
            if( MyDebug.LOG )
                Log.i(TAG, "doInitSupportedJpegSizes picture size: " + camera_size.getWidth() + " x " + camera_size.getHeight());
            camera_features.mSupportedPictureSizes.add(new CameraController.Size(camera_size.getWidth(),
                                                        camera_size.getHeight()));
        }
    }
    // sizes are usually already sorted from high to low, but sort just in case
    // note some devices do have sizes in a not fully sorted order (e.g., Nokia 8)
    Collections.sort(camera_features.mSupportedPictureSizes, new CameraController.SizeSorter());
    // test high resolution modes not supporting burst:
    //camera_features.picture_sizes.get(0).supports_burst = false;
}
赞(4)
未经允许不得转载:极客笔记 » 第11讲 StreamConfigurationMap实战一

评论 2

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #1

    这个流程图是用什么工具绘制的啊

    maj8个月前 (07-26)回复
    • starUML

      admin8个月前 (07-26)回复
Android Camera2参考
Android Camera2参考概述
Android Camera2参数参考
Android Camera2参数参考概述
Android Camera2指南
Android Camera2指南概述
Android Camera HAL开发
Android Camera HAL开发概述
Android Camera2 API
第2讲 Android Camera 架构介绍第3讲 Camera2 API 概述第4讲 Open和Close Camera流程第5讲 GeekCamera2介绍第6讲 Open和Close Camera代码实现第7讲 Hardware Level详解第8讲 AVAILABLE_CAPABILITIES详解第9讲 StreamConfigurationMap详解一第10讲 StreamConfigurationMap详解二第11讲 StreamConfigurationMap实战一第12讲 StreamConfigurationMap实战二第13讲 为什么需要OutputConfiguration第14讲 OutputConfiguration API详解第15讲 Surface Sharing实战第16讲 Deferred Surface实战第17讲 Reprocessable Capture Session详解一第18讲 Reprocessable Capture Session详解二第19讲 App如何实现ZSL功能第20讲 SessionConfiguration详解第21讲 createCaptureSession详解第22讲 SessionParameter实战第23讲 CaptureRequest详解第24讲 如何操作VendorTag第25讲 获取Physical Camera数据流第26讲 CameraCaptureSession详解第27讲 CameraCaptureSession.CaptureCallback详解第28讲 GeekCamera2连拍实战第29讲 SlowMotion实战第30讲 CaptureResult详解第31讲 AE自动曝光 Part 1第32讲 AE自动曝光 Part 2第33讲 AE自动曝光实战第34讲 AE手动曝光第35讲 AE手动曝光实战第36讲 Flash闪光灯控制第37讲 拍照打闪实战第38讲 通过CropRegion控制Zoom缩放第39讲 通过ZoomRatio控制Zoom缩放第40讲 Digital Zoom缩放实战第41讲 Touch AE实战第42讲 AF自动对焦第43讲 AF自动对焦 第二部分第44讲 AF自动对焦实战第45讲 手动对焦实战第46讲 AWB自动白平衡第47讲 AWB自动白平衡实战第48讲 FD人脸检测第49讲 Android13 Camera2 New APIs介绍