第11讲 StreamConfigurationMap实战一 - Android Camera2 API

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

更多资源:

资源 描述
在线课程 极客笔记在线课程
知识星球 星球名称:深入浅出Android Camera
星球ID: 17296815
Wechat 极客笔记圈

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;
}

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程

Android Camera2 API