FileDocCategorySizeDatePackage
AndroidCamera2Settings.javaAPI DocAndroid 5.1 API25512Thu Mar 12 22:22:48 GMT 2015com.android.ex.camera2.portability

AndroidCamera2Settings

public class AndroidCamera2Settings extends CameraSettings
The subclass of {@link CameraSettings} for Android Camera 2 API.

Fields Summary
private static final Log.Tag
TAG
private final Builder
mTemplateSettings
private final com.android.ex.camera2.utils.Camera2RequestSettingsSet
mRequestSettings
private final android.graphics.Rect
mActiveArray
Sensor's active array bounds.
private final android.graphics.Rect
mCropRectangle
Crop rectangle for digital zoom (measured WRT the active array).
private android.graphics.Rect
mVisiblePreviewRectangle
Bounds of visible preview portion (measured WRT the active array).
Constructors Summary
public AndroidCamera2Settings(android.hardware.camera2.CameraDevice camera, int template, android.graphics.Rect activeArray, Size preview, Size photo)
Create a settings representation that answers queries of unspecified options in the same way as the provided template would.

The default settings provided by the given template are only ever used for reporting back to the client app (i.e. when it queries an option it didn't explicitly set first). {@link Camera2RequestSettingsSet}s generated by an instance of this class will have any settings not modified using one of that instance's mutators forced to default, so that their effective values when submitting a capture request will be those of the template that is provided to the camera framework at that time.

param
camera Device from which to draw default settings (non-{@code null}).
param
template Specific template to use for the defaults.
param
activeArray Boundary coordinates of the sensor's active array (non-{@code null}).
param
preview Dimensions of preview streams.
param
photo Dimensions of captured images.
throws
IllegalArgumentException If {@code camera} or {@code activeArray} is {@code null}.
throws
CameraAccessException Upon internal framework/driver failure.


                                                                                                                                                                                                                                          
          
                                        
        if (camera == null) {
            throw new NullPointerException("camera must not be null");
        }
        if (activeArray == null) {
            throw new NullPointerException("activeArray must not be null");
        }

        mTemplateSettings = camera.createCaptureRequest(template);
        mRequestSettings = new Camera2RequestSettingsSet();
        mActiveArray = activeArray;
        mCropRectangle = new Rect(0, 0, activeArray.width(), activeArray.height());

        mSizesLocked = false;

        Range<Integer> previewFpsRange = mTemplateSettings.get(CONTROL_AE_TARGET_FPS_RANGE);
        if (previewFpsRange != null) {
            setPreviewFpsRange(previewFpsRange.getLower(), previewFpsRange.getUpper());
        }
        setPreviewSize(preview);
        // TODO: mCurrentPreviewFormat
        setPhotoSize(photo);
        mJpegCompressQuality = queryTemplateDefaultOrMakeOneUp(JPEG_QUALITY, (byte) 0);
        // TODO: mCurrentPhotoFormat
        // NB: We're assuming that templates won't be zoomed in by default.
        mCurrentZoomRatio = CameraCapabilities.ZOOM_RATIO_UNZOOMED;
        // TODO: mCurrentZoomIndex
        mExposureCompensationIndex =
                queryTemplateDefaultOrMakeOneUp(CONTROL_AE_EXPOSURE_COMPENSATION, 0);

        mCurrentFlashMode = flashModeFromRequest();
        Integer currentFocusMode = mTemplateSettings.get(CONTROL_AF_MODE);
        if (currentFocusMode != null) {
            mCurrentFocusMode = AndroidCamera2Capabilities.focusModeFromInt(currentFocusMode);
        }
        Integer currentSceneMode = mTemplateSettings.get(CONTROL_SCENE_MODE);
        if (currentSceneMode != null) {
            mCurrentSceneMode = AndroidCamera2Capabilities.sceneModeFromInt(currentSceneMode);
        }
        Integer whiteBalance = mTemplateSettings.get(CONTROL_AWB_MODE);
        if (whiteBalance != null) {
            mWhiteBalance = AndroidCamera2Capabilities.whiteBalanceFromInt(whiteBalance);
        }

        mVideoStabilizationEnabled = queryTemplateDefaultOrMakeOneUp(
                        CONTROL_VIDEO_STABILIZATION_MODE, CONTROL_VIDEO_STABILIZATION_MODE_OFF) ==
                CONTROL_VIDEO_STABILIZATION_MODE_ON;
        mAutoExposureLocked = queryTemplateDefaultOrMakeOneUp(CONTROL_AE_LOCK, false);
        mAutoWhiteBalanceLocked = queryTemplateDefaultOrMakeOneUp(CONTROL_AWB_LOCK, false);
        // TODO: mRecordingHintEnabled
        // TODO: mGpsData
        android.util.Size exifThumbnailSize = mTemplateSettings.get(JPEG_THUMBNAIL_SIZE);
        if (exifThumbnailSize != null) {
            mExifThumbnailSize =
                    new Size(exifThumbnailSize.getWidth(), exifThumbnailSize.getHeight());
        }
    
public AndroidCamera2Settings(AndroidCamera2Settings other)

        super(other);
        mTemplateSettings = other.mTemplateSettings;
        mRequestSettings = new Camera2RequestSettingsSet(other.mRequestSettings);
        mActiveArray = other.mActiveArray;
        mCropRectangle = new Rect(other.mCropRectangle);
    
Methods Summary
public CameraSettingscopy()

        return new AndroidCamera2Settings(this);
    
private static android.graphics.RecteffectiveCropRectFromRequested(android.graphics.Rect requestedCrop, Size previewSize)
Calculate the effective crop rectangle for this preview viewport; assumes the preview is centered to the sensor and scaled to fit across one of the dimensions without skewing.

Assumes the zoom level of the provided desired crop rectangle.

param
requestedCrop Desired crop rectangle, in active array space.
param
previewSize Size of the preview buffer render target, in pixels (not in sensor space).
return
A rectangle that serves as the preview stream's effective crop region (unzoomed), in sensor space.
throws
NullPointerException If any of the args were {@code null}.

        float aspectRatioArray = requestedCrop.width() * 1.0f / requestedCrop.height();
        float aspectRatioPreview = previewSize.width() * 1.0f / previewSize.height();

        float cropHeight, cropWidth;
        if (aspectRatioPreview < aspectRatioArray) {
            // The new width must be smaller than the height, so scale the width by AR
            cropHeight = requestedCrop.height();
            cropWidth = cropHeight * aspectRatioPreview;
        } else {
            // The new height must be smaller (or equal) than the width, so scale the height by AR
            cropWidth = requestedCrop.width();
            cropHeight = cropWidth / aspectRatioPreview;
        }

        Matrix translateMatrix = new Matrix();
        RectF cropRect = new RectF(/*left*/0, /*top*/0, cropWidth, cropHeight);

        // Now center the crop rectangle so its center is in the center of the active array
        translateMatrix.setTranslate(requestedCrop.exactCenterX(), requestedCrop.exactCenterY());
        translateMatrix.postTranslate(-cropRect.centerX(), -cropRect.centerY());

        translateMatrix.mapRect(/*inout*/cropRect);

        // Round the rect corners towards the nearest integer values
        Rect result = new Rect();
        cropRect.roundOut(result);
        return result;
    
private com.android.ex.camera2.portability.CameraCapabilities.FlashModeflashModeFromRequest()

        Integer autoExposure = mTemplateSettings.get(CONTROL_AE_MODE);
        if (autoExposure != null) {
            switch (autoExposure) {
                case CONTROL_AE_MODE_ON:
                    return FlashMode.OFF;
                case CONTROL_AE_MODE_ON_AUTO_FLASH:
                    return FlashMode.AUTO;
                case CONTROL_AE_MODE_ON_ALWAYS_FLASH: {
                    if (mTemplateSettings.get(FLASH_MODE) == FLASH_MODE_TORCH) {
                        return FlashMode.TORCH;
                    } else {
                        return FlashMode.ON;
                    }
                }
                case CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE:
                    return FlashMode.RED_EYE;
            }
        }
        return null;
    
public com.android.ex.camera2.utils.Camera2RequestSettingsSetgetRequestSettings()

        updateRequestSettingOrForceToDefault(CONTROL_AE_REGIONS,
                legacyAreasToMeteringRectangles(mMeteringAreas));
        updateRequestSettingOrForceToDefault(CONTROL_AF_REGIONS,
                legacyAreasToMeteringRectangles(mFocusAreas));
        updateRequestSettingOrForceToDefault(CONTROL_AE_TARGET_FPS_RANGE,
                new Range(mPreviewFpsRangeMin, mPreviewFpsRangeMax));
        // TODO: mCurrentPreviewFormat
        updateRequestSettingOrForceToDefault(JPEG_QUALITY, mJpegCompressQuality);
        // TODO: mCurrentPhotoFormat
        mRequestSettings.set(SCALER_CROP_REGION, mCropRectangle);
        // TODO: mCurrentZoomIndex
        updateRequestSettingOrForceToDefault(CONTROL_AE_EXPOSURE_COMPENSATION,
                mExposureCompensationIndex);
        updateRequestFlashMode();
        updateRequestFocusMode();
        updateRequestSceneMode();
        updateRequestWhiteBalance();
        updateRequestSettingOrForceToDefault(CONTROL_VIDEO_STABILIZATION_MODE,
                mVideoStabilizationEnabled ?
                        CONTROL_VIDEO_STABILIZATION_MODE_ON : CONTROL_VIDEO_STABILIZATION_MODE_OFF);
        // OIS shouldn't be on if software video stabilization is.
        mRequestSettings.set(LENS_OPTICAL_STABILIZATION_MODE,
                mVideoStabilizationEnabled ? LENS_OPTICAL_STABILIZATION_MODE_OFF :
                        null);
        updateRequestSettingOrForceToDefault(CONTROL_AE_LOCK, mAutoExposureLocked);
        updateRequestSettingOrForceToDefault(CONTROL_AWB_LOCK, mAutoWhiteBalanceLocked);
        // TODO: mRecordingHintEnabled
        updateRequestGpsData();
        if (mExifThumbnailSize != null) {
            updateRequestSettingOrForceToDefault(JPEG_THUMBNAIL_SIZE,
                    new android.util.Size(
                            mExifThumbnailSize.width(), mExifThumbnailSize.height()));
        } else {
            updateRequestSettingOrForceToDefault(JPEG_THUMBNAIL_SIZE, null);
        }

        return mRequestSettings;
    
private android.hardware.camera2.params.MeteringRectangle[]legacyAreasToMeteringRectangles(java.util.List reference)

        MeteringRectangle[] transformed = null;
        if (reference.size() > 0) {
            transformed = new MeteringRectangle[reference.size()];
            for (int index = 0; index < reference.size(); ++index) {
                android.hardware.Camera.Area source = reference.get(index);
                Rect rectangle = source.rect;

                // Old API coordinates were [-1000,1000]; new ones are [0,ACTIVE_ARRAY_SIZE).
                // We're also going from preview image--relative to sensor active array--relative.
                double oldLeft = (rectangle.left + 1000) / 2000.0;
                double oldTop = (rectangle.top + 1000) / 2000.0;
                double oldRight = (rectangle.right + 1000) / 2000.0;
                double oldBottom = (rectangle.bottom + 1000) / 2000.0;
                int left = mCropRectangle.left + toIntConstrained(
                        mCropRectangle.width() * oldLeft, 0, mCropRectangle.width() - 1);
                int top = mCropRectangle.top + toIntConstrained(
                        mCropRectangle.height() * oldTop, 0, mCropRectangle.height() - 1);
                int right = mCropRectangle.left + toIntConstrained(
                        mCropRectangle.width() * oldRight, 0, mCropRectangle.width() - 1);
                int bottom = mCropRectangle.top + toIntConstrained(
                        mCropRectangle.height() * oldBottom, 0, mCropRectangle.height() - 1);
                transformed[index] = new MeteringRectangle(left, top, right - left, bottom - top,
                        source.weight);
            }
        }
        return transformed;
    
private booleanmatchesTemplateDefault(Key setting)

        if (setting == CONTROL_AE_REGIONS) {
            return mMeteringAreas.size() == 0;
        } else if (setting == CONTROL_AF_REGIONS) {
            return mFocusAreas.size() == 0;
        } else if (setting == CONTROL_AE_TARGET_FPS_RANGE) {
            Range<Integer> defaultFpsRange = mTemplateSettings.get(CONTROL_AE_TARGET_FPS_RANGE);
            return (mPreviewFpsRangeMin == 0 && mPreviewFpsRangeMax == 0) ||
                    (defaultFpsRange != null && mPreviewFpsRangeMin == defaultFpsRange.getLower() &&
                            mPreviewFpsRangeMax == defaultFpsRange.getUpper());
        } else if (setting == JPEG_QUALITY) {
            return Objects.equals(mJpegCompressQuality,
                    mTemplateSettings.get(JPEG_QUALITY));
        } else if (setting == CONTROL_AE_EXPOSURE_COMPENSATION) {
            return Objects.equals(mExposureCompensationIndex,
                    mTemplateSettings.get(CONTROL_AE_EXPOSURE_COMPENSATION));
        } else if (setting == CONTROL_VIDEO_STABILIZATION_MODE) {
            Integer videoStabilization = mTemplateSettings.get(CONTROL_VIDEO_STABILIZATION_MODE);
            return (videoStabilization != null &&
                    (mVideoStabilizationEnabled && videoStabilization ==
                            CONTROL_VIDEO_STABILIZATION_MODE_ON) ||
                    (!mVideoStabilizationEnabled && videoStabilization ==
                            CONTROL_VIDEO_STABILIZATION_MODE_OFF));
        } else if (setting == CONTROL_AE_LOCK) {
            return Objects.equals(mAutoExposureLocked, mTemplateSettings.get(CONTROL_AE_LOCK));
        } else if (setting == CONTROL_AWB_LOCK) {
            return Objects.equals(mAutoWhiteBalanceLocked, mTemplateSettings.get(CONTROL_AWB_LOCK));
        } else if (setting == JPEG_THUMBNAIL_SIZE) {
            if (mExifThumbnailSize == null) {
                // It doesn't matter if this is true or false since setting this
                // to null in the request settings will use the default anyway.
                return false;
            }
            android.util.Size defaultThumbnailSize = mTemplateSettings.get(JPEG_THUMBNAIL_SIZE);
            return (mExifThumbnailSize.width() == 0 && mExifThumbnailSize.height() == 0) ||
                    (defaultThumbnailSize != null &&
                            mExifThumbnailSize.width() == defaultThumbnailSize.getWidth() &&
                            mExifThumbnailSize.height() == defaultThumbnailSize.getHeight());
        }
        Log.w(TAG, "Settings implementation checked default of unhandled option key");
        // Since this class isn't equipped to handle it, claim it matches the default to prevent
        // updateRequestSettingOrForceToDefault from going with the user-provided preference
        return true;
    
private TqueryTemplateDefaultOrMakeOneUp(Key key, T defaultDefault)

        T val = mTemplateSettings.get(key);
        if (val != null) {
            return val;
        } else {
            // Spoof the default so matchesTemplateDefault excludes this key from generated sets.
            // This approach beats a simple sentinel because it provides basic boolean support.
            mTemplateSettings.set(key, defaultDefault);
            return defaultDefault;
        }
    
public voidsetZoomRatio(float ratio)

        super.setZoomRatio(ratio);

        // Compute the crop rectangle to be passed to the framework
        mCropRectangle.set(0, 0,
                toIntConstrained(
                        mActiveArray.width() / mCurrentZoomRatio, 0, mActiveArray.width()),
                toIntConstrained(
                        mActiveArray.height() / mCurrentZoomRatio, 0, mActiveArray.height()));
        mCropRectangle.offsetTo((mActiveArray.width() - mCropRectangle.width()) / 2,
                (mActiveArray.height() - mCropRectangle.height()) / 2);

        // Compute the effective crop rectangle to be used for computing focus/metering coordinates
        mVisiblePreviewRectangle =
                effectiveCropRectFromRequested(mCropRectangle, mCurrentPreviewSize);
    
private inttoIntConstrained(double original, int min, int max)

        original = Math.max(original, min);
        original = Math.min(original, max);
        return (int) original;
    
private voidupdateRequestFlashMode()

        Integer aeMode = null;
        Integer flashMode = null;
        if (mCurrentFlashMode != null) {
            switch (mCurrentFlashMode) {
                case AUTO: {
                    aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH;
                    break;
                }
                case OFF: {
                    aeMode = CONTROL_AE_MODE_ON;
                    flashMode = FLASH_MODE_OFF;
                    break;
                }
                case ON: {
                    aeMode = CONTROL_AE_MODE_ON_ALWAYS_FLASH;
                    flashMode = FLASH_MODE_SINGLE;
                    break;
                }
                case TORCH: {
                    flashMode = FLASH_MODE_TORCH;
                    break;
                }
                case RED_EYE: {
                    aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE;
                    break;
                }
                default: {
                    Log.w(TAG, "Unable to convert to API 2 flash mode: " + mCurrentFlashMode);
                    break;
                }
            }
        }
        mRequestSettings.set(CONTROL_AE_MODE, aeMode);
        mRequestSettings.set(FLASH_MODE, flashMode);
    
private voidupdateRequestFocusMode()

        Integer mode = null;
        if (mCurrentFocusMode != null) {
            switch (mCurrentFocusMode) {
                case AUTO: {
                    mode = CONTROL_AF_MODE_AUTO;
                    break;
                }
                case CONTINUOUS_PICTURE: {
                    mode = CONTROL_AF_MODE_CONTINUOUS_PICTURE;
                    break;
                }
                case CONTINUOUS_VIDEO: {
                    mode = CONTROL_AF_MODE_CONTINUOUS_VIDEO;
                    break;
                }
                case EXTENDED_DOF: {
                    mode = CONTROL_AF_MODE_EDOF;
                    break;
                }
                case FIXED: {
                    mode = CONTROL_AF_MODE_OFF;
                    break;
                }
                // TODO: We cannot support INFINITY
                case MACRO: {
                    mode = CONTROL_AF_MODE_MACRO;
                    break;
                }
                default: {
                    Log.w(TAG, "Unable to convert to API 2 focus mode: " + mCurrentFocusMode);
                    break;
                }
            }
        }
        mRequestSettings.set(CONTROL_AF_MODE, mode);
    
private voidupdateRequestGpsData()

        if (mGpsData == null || mGpsData.processingMethod == null) {
            // It's a hack since we always use GPS time stamp but does
            // not use other fields sometimes. Setting processing
            // method to null means the other fields should not be used.
            mRequestSettings.set(JPEG_GPS_LOCATION, null);
        } else {
            Location location = new Location(mGpsData.processingMethod);
            location.setTime(mGpsData.timeStamp);
            location.setAltitude(mGpsData.altitude);
            location.setLatitude(mGpsData.latitude);
            location.setLongitude(mGpsData.longitude);
            mRequestSettings.set(JPEG_GPS_LOCATION, location);
        }
    
private voidupdateRequestSceneMode()

        Integer mode = null;
        if (mCurrentSceneMode != null) {
            switch (mCurrentSceneMode) {
                case AUTO: {
                    mode = CONTROL_SCENE_MODE_DISABLED;
                    break;
                }
                case ACTION: {
                    mode = CONTROL_SCENE_MODE_ACTION;
                    break;
                }
                case BARCODE: {
                    mode = CONTROL_SCENE_MODE_BARCODE;
                    break;
                }
                case BEACH: {
                    mode = CONTROL_SCENE_MODE_BEACH;
                    break;
                }
                case CANDLELIGHT: {
                    mode = CONTROL_SCENE_MODE_CANDLELIGHT;
                    break;
                }
                case FIREWORKS: {
                    mode = CONTROL_SCENE_MODE_FIREWORKS;
                    break;
                }
                case HDR: {
                    mode = LegacyVendorTags.CONTROL_SCENE_MODE_HDR;
                    break;
                }
                case LANDSCAPE: {
                    mode = CONTROL_SCENE_MODE_LANDSCAPE;
                    break;
                }
                case NIGHT: {
                    mode = CONTROL_SCENE_MODE_NIGHT;
                    break;
                }
                // TODO: We cannot support NIGHT_PORTRAIT
                case PARTY: {
                    mode = CONTROL_SCENE_MODE_PARTY;
                    break;
                }
                case PORTRAIT: {
                    mode = CONTROL_SCENE_MODE_PORTRAIT;
                    break;
                }
                case SNOW: {
                    mode = CONTROL_SCENE_MODE_SNOW;
                    break;
                }
                case SPORTS: {
                    mode = CONTROL_SCENE_MODE_SPORTS;
                    break;
                }
                case STEADYPHOTO: {
                    mode = CONTROL_SCENE_MODE_STEADYPHOTO;
                    break;
                }
                case SUNSET: {
                    mode = CONTROL_SCENE_MODE_SUNSET;
                    break;
                }
                case THEATRE: {
                    mode = CONTROL_SCENE_MODE_THEATRE;
                    break;
                }
                default: {
                    Log.w(TAG, "Unable to convert to API 2 scene mode: " + mCurrentSceneMode);
                    break;
                }
            }
        }
        mRequestSettings.set(CONTROL_SCENE_MODE, mode);
    
private voidupdateRequestSettingOrForceToDefault(Key setting, T possibleChoice)

        mRequestSettings.set(setting, matchesTemplateDefault(setting) ? null : possibleChoice);
    
private voidupdateRequestWhiteBalance()

        Integer mode = null;
        if (mWhiteBalance != null) {
            switch (mWhiteBalance) {
                case AUTO: {
                    mode = CONTROL_AWB_MODE_AUTO;
                    break;
                }
                case CLOUDY_DAYLIGHT: {
                    mode = CONTROL_AWB_MODE_CLOUDY_DAYLIGHT;
                    break;
                }
                case DAYLIGHT: {
                    mode = CONTROL_AWB_MODE_DAYLIGHT;
                    break;
                }
                case FLUORESCENT: {
                    mode = CONTROL_AWB_MODE_FLUORESCENT;
                    break;
                }
                case INCANDESCENT: {
                    mode = CONTROL_AWB_MODE_INCANDESCENT;
                    break;
                }
                case SHADE: {
                    mode = CONTROL_AWB_MODE_SHADE;
                    break;
                }
                case TWILIGHT: {
                    mode = CONTROL_AWB_MODE_TWILIGHT;
                    break;
                }
                case WARM_FLUORESCENT: {
                    mode = CONTROL_AWB_MODE_WARM_FLUORESCENT;
                    break;
                }
                default: {
                    Log.w(TAG, "Unable to convert to API 2 white balance: " + mWhiteBalance);
                    break;
                }
            }
        }
        mRequestSettings.set(CONTROL_AWB_MODE, mode);