From 337d845ecc3bf412a8fc52e33cf4981a3279e582 Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Thu, 28 Nov 2019 19:38:00 +0100 Subject: [PATCH] Make CameraOptions engine aware --- .../cameraview/CameraOptions.java | 314 +++--------------- .../cameraview/engine/Camera1Engine.java | 2 +- .../cameraview/engine/Camera1Options.java | 128 +++++++ .../cameraview/engine/Camera2Engine.java | 35 +- .../cameraview/engine/Camera2Options.java | 160 +++++++++ .../picture/Full1PictureRecorder.java | 1 - .../picture/Full2PictureRecorder.java | 1 - .../picture/Snapshot1PictureRecorder.java | 1 - .../picture/SnapshotGlPictureRecorder.java | 1 - 9 files changed, 346 insertions(+), 297 deletions(-) create mode 100644 cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Options.java create mode 100644 cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Options.java diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java index c68650c2..42440ec4 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java @@ -44,263 +44,26 @@ import java.util.Set; /** * Options telling you what is available and what is not. */ -public class CameraOptions { - - private Set supportedWhiteBalance = new HashSet<>(5); - private Set supportedFacing = new HashSet<>(2); - private Set supportedFlash = new HashSet<>(4); - private Set supportedHdr = new HashSet<>(2); - private Set supportedPictureSizes = new HashSet<>(15); - private Set supportedVideoSizes = new HashSet<>(5); - private Set supportedPictureAspectRatio = new HashSet<>(4); - private Set supportedVideoAspectRatio = new HashSet<>(3); - - private boolean zoomSupported; - private boolean exposureCorrectionSupported; - private float exposureCorrectionMinValue; - private float exposureCorrectionMaxValue; - private boolean autoFocusSupported; - private float previewFrameRateMinValue; - private float previewFrameRateMaxValue; - - - public CameraOptions(@NonNull Camera.Parameters params, int cameraId, boolean flipSizes) { - List strings; - Camera1Mapper mapper = Camera1Mapper.get(); - - // Facing - Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); - for (int i = 0, count = Camera.getNumberOfCameras(); i < count; i++) { - Camera.getCameraInfo(i, cameraInfo); - Facing value = mapper.unmapFacing(cameraInfo.facing); - if (value != null) supportedFacing.add(value); - } - - // WB - strings = params.getSupportedWhiteBalance(); - if (strings != null) { - for (String string : strings) { - WhiteBalance value = mapper.unmapWhiteBalance(string); - if (value != null) supportedWhiteBalance.add(value); - } - } - - // Flash - supportedFlash.add(Flash.OFF); - strings = params.getSupportedFlashModes(); - if (strings != null) { - for (String string : strings) { - Flash value = mapper.unmapFlash(string); - if (value != null) supportedFlash.add(value); - } - } - - // Hdr - supportedHdr.add(Hdr.OFF); - strings = params.getSupportedSceneModes(); - if (strings != null) { - for (String string : strings) { - Hdr value = mapper.unmapHdr(string); - if (value != null) supportedHdr.add(value); - } - } - - // zoom - zoomSupported = params.isZoomSupported(); - - // autofocus - autoFocusSupported = params.getSupportedFocusModes() - .contains(Camera.Parameters.FOCUS_MODE_AUTO); - - // Exposure correction - float step = params.getExposureCompensationStep(); - exposureCorrectionMinValue = (float) params.getMinExposureCompensation() * step; - exposureCorrectionMaxValue = (float) params.getMaxExposureCompensation() * step; - exposureCorrectionSupported = params.getMinExposureCompensation() != 0 - || params.getMaxExposureCompensation() != 0; - - // Picture Sizes - List sizes = params.getSupportedPictureSizes(); - for (Camera.Size size : sizes) { - int width = flipSizes ? size.height : size.width; - int height = flipSizes ? size.width : size.height; - supportedPictureSizes.add(new Size(width, height)); - supportedPictureAspectRatio.add(AspectRatio.of(width, height)); - } - - // Video Sizes - // As a safety measure, remove Sizes bigger than CamcorderProfile.highest - CamcorderProfile profile = CamcorderProfiles.get(cameraId, - new Size(Integer.MAX_VALUE, Integer.MAX_VALUE)); - Size videoMaxSize = new Size(profile.videoFrameWidth, profile.videoFrameHeight); - List vsizes = params.getSupportedVideoSizes(); - if (vsizes != null) { - for (Camera.Size size : vsizes) { - if (size.width <= videoMaxSize.getWidth() - && size.height <= videoMaxSize.getHeight()) { - int width = flipSizes ? size.height : size.width; - int height = flipSizes ? size.width : size.height; - supportedVideoSizes.add(new Size(width, height)); - supportedVideoAspectRatio.add(AspectRatio.of(width, height)); - } - } - } else { - // StackOverflow threads seems to agree that if getSupportedVideoSizes is null, - // previews can be used. - List fallback = params.getSupportedPreviewSizes(); - for (Camera.Size size : fallback) { - if (size.width <= videoMaxSize.getWidth() - && size.height <= videoMaxSize.getHeight()) { - int width = flipSizes ? size.height : size.width; - int height = flipSizes ? size.width : size.height; - supportedVideoSizes.add(new Size(width, height)); - supportedVideoAspectRatio.add(AspectRatio.of(width, height)); - } - } - } - - // Preview FPS - previewFrameRateMinValue = Float.MAX_VALUE; - previewFrameRateMaxValue = -Float.MAX_VALUE; - List fpsRanges = params.getSupportedPreviewFpsRange(); - for (int[] fpsRange : fpsRanges) { - float lower = (float) fpsRange[0] / 1000F; - float upper = (float) fpsRange[1] / 1000F; - previewFrameRateMinValue = Math.min(previewFrameRateMinValue, lower); - previewFrameRateMaxValue = Math.max(previewFrameRateMaxValue, upper); - } - } - - // Camera2Engine constructor. - @RequiresApi(Build.VERSION_CODES.LOLLIPOP) - public CameraOptions(@NonNull CameraManager manager, - @NonNull String cameraId, - boolean flipSizes) throws CameraAccessException { - Camera2Mapper mapper = Camera2Mapper.get(); - CameraCharacteristics cameraCharacteristics = manager.getCameraCharacteristics(cameraId); - - // Facing - for (String cameraId1 : manager.getCameraIdList()) { - CameraCharacteristics cameraCharacteristics1 = manager - .getCameraCharacteristics(cameraId1); - Integer cameraFacing = cameraCharacteristics1.get(CameraCharacteristics.LENS_FACING); - if (cameraFacing != null) { - Facing value = mapper.unmapFacing(cameraFacing); - if (value != null) supportedFacing.add(value); - } - } - - // WB - int[] awbModes = cameraCharacteristics.get( - CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES); - //noinspection ConstantConditions - for (int awbMode : awbModes) { - WhiteBalance value = mapper.unmapWhiteBalance(awbMode); - if (value != null) supportedWhiteBalance.add(value); - } - - // Flash - supportedFlash.add(Flash.OFF); - Boolean hasFlash = cameraCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); - if (hasFlash != null && hasFlash) { - int[] aeModes = cameraCharacteristics.get( - CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES); - //noinspection ConstantConditions - for (int aeMode : aeModes) { - Set flashes = mapper.unmapFlash(aeMode); - supportedFlash.addAll(flashes); - } - } - - // HDR - supportedHdr.add(Hdr.OFF); - int[] sceneModes = cameraCharacteristics.get( - CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES); - //noinspection ConstantConditions - for (int sceneMode : sceneModes) { - Hdr value = mapper.unmapHdr(sceneMode); - if (value != null) supportedHdr.add(value); - } - - // Zoom - Float maxZoom = cameraCharacteristics.get( - CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM); - if(maxZoom != null) { - zoomSupported = maxZoom > 1; - } - - - // AutoFocus - // This now means 3A metering with respect to a specific region of the screen. - // Some controls (AF, AE) have special triggers that might or might not be supported. - // But they can also be on some continuous search mode so that the trigger is not needed. - // What really matters in my opinion is the availability of regions. - Integer afRegions = cameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF); - Integer aeRegions = cameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AE); - Integer awbRegions = cameraCharacteristics.get( - CameraCharacteristics.CONTROL_MAX_REGIONS_AWB); - autoFocusSupported = (afRegions != null && afRegions > 0) - || (aeRegions != null && aeRegions > 0) - || (awbRegions != null && awbRegions > 0); - - // Exposure correction - Range exposureRange = cameraCharacteristics.get( - CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE); - Rational exposureStep = cameraCharacteristics.get( - CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP); - if (exposureRange != null && exposureStep != null && exposureStep.floatValue() != 0) { - exposureCorrectionMinValue = exposureRange.getLower() / exposureStep.floatValue(); - exposureCorrectionMaxValue = exposureRange.getUpper() / exposureStep.floatValue(); - } - exposureCorrectionSupported = exposureCorrectionMinValue != 0 - && exposureCorrectionMaxValue != 0; - - - // Picture Sizes - StreamConfigurationMap streamMap = cameraCharacteristics.get( - CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); - if (streamMap == null) { - throw new RuntimeException("StreamConfigurationMap is null. Should not happen."); - } - android.util.Size[] psizes = streamMap.getOutputSizes(ImageFormat.JPEG); - for (android.util.Size size : psizes) { - int width = flipSizes ? size.getHeight() : size.getWidth(); - int height = flipSizes ? size.getWidth() : size.getHeight(); - supportedPictureSizes.add(new Size(width, height)); - supportedPictureAspectRatio.add(AspectRatio.of(width, height)); - } - - // Video Sizes - // As a safety measure, remove Sizes bigger than CamcorderProfile.highest - CamcorderProfile profile = CamcorderProfiles.get(cameraId, - new Size(Integer.MAX_VALUE, Integer.MAX_VALUE)); - Size videoMaxSize = new Size(profile.videoFrameWidth, profile.videoFrameHeight); - android.util.Size[] vsizes = streamMap.getOutputSizes(MediaRecorder.class); - for (android.util.Size size : vsizes) { - if (size.getWidth() <= videoMaxSize.getWidth() - && size.getHeight() <= videoMaxSize.getHeight()) { - int width = flipSizes ? size.getHeight() : size.getWidth(); - int height = flipSizes ? size.getWidth() : size.getHeight(); - supportedVideoSizes.add(new Size(width, height)); - supportedVideoAspectRatio.add(AspectRatio.of(width, height)); - } - } - - // Preview FPS - Range[] range = cameraCharacteristics.get( - CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES); - if (range != null) { - previewFrameRateMinValue = Float.MAX_VALUE; - previewFrameRateMaxValue = -Float.MAX_VALUE; - for (Range fpsRange : range) { - previewFrameRateMinValue = Math.min(previewFrameRateMinValue, fpsRange.getLower()); - previewFrameRateMaxValue = Math.max(previewFrameRateMaxValue, fpsRange.getUpper()); - } - } else { - previewFrameRateMinValue = 0F; - previewFrameRateMaxValue = 0F; - } - } +public abstract class CameraOptions { + + protected Set supportedWhiteBalance = new HashSet<>(5); + protected Set supportedFacing = new HashSet<>(2); + protected Set supportedFlash = new HashSet<>(4); + protected Set supportedHdr = new HashSet<>(2); + protected Set supportedPictureSizes = new HashSet<>(15); + protected Set supportedVideoSizes = new HashSet<>(5); + protected Set supportedPictureAspectRatio = new HashSet<>(4); + protected Set supportedVideoAspectRatio = new HashSet<>(3); + + protected boolean zoomSupported; + protected boolean exposureCorrectionSupported; + protected float exposureCorrectionMinValue; + protected float exposureCorrectionMaxValue; + protected boolean autoFocusSupported; + protected float previewFrameRateMinValue; + protected float previewFrameRateMaxValue; + + protected CameraOptions() { } /** * Shorthand for getSupported*().contains(value). @@ -308,7 +71,7 @@ public class CameraOptions { * @param control value to check * @return whether it's supported */ - public boolean supports(@NonNull Control control) { + public final boolean supports(@NonNull Control control) { return getSupportedControls(control.getClass()).contains(control); } @@ -319,7 +82,7 @@ public class CameraOptions { * @param action value to be checked * @return whether it's supported */ - public boolean supports(@NonNull GestureAction action) { + public final boolean supports(@NonNull GestureAction action) { switch (action) { case AUTO_FOCUS: return isAutoFocusSupported(); @@ -338,7 +101,8 @@ public class CameraOptions { @SuppressWarnings("unchecked") @NonNull - public Collection getSupportedControls(@NonNull Class controlClass) { + public final Collection getSupportedControls( + @NonNull Class controlClass) { if (controlClass.equals(Audio.class)) { return (Collection) Arrays.asList(Audio.values()); } else if (controlClass.equals(Facing.class)) { @@ -370,7 +134,7 @@ public class CameraOptions { * @return a collection of supported values. */ @NonNull - public Collection getSupportedPictureSizes() { + public final Collection getSupportedPictureSizes() { return Collections.unmodifiableSet(supportedPictureSizes); } @@ -381,7 +145,7 @@ public class CameraOptions { */ @SuppressWarnings("WeakerAccess") @NonNull - public Collection getSupportedPictureAspectRatios() { + public final Collection getSupportedPictureAspectRatios() { return Collections.unmodifiableSet(supportedPictureAspectRatio); } @@ -391,7 +155,7 @@ public class CameraOptions { * @return a collection of supported values. */ @NonNull - public Collection getSupportedVideoSizes() { + public final Collection getSupportedVideoSizes() { return Collections.unmodifiableSet(supportedVideoSizes); } @@ -402,7 +166,7 @@ public class CameraOptions { */ @SuppressWarnings("WeakerAccess") @NonNull - public Collection getSupportedVideoAspectRatios() { + public final Collection getSupportedVideoAspectRatios() { return Collections.unmodifiableSet(supportedVideoAspectRatio); } @@ -414,7 +178,7 @@ public class CameraOptions { * @return a collection of supported values. */ @NonNull - public Collection getSupportedFacing() { + public final Collection getSupportedFacing() { return Collections.unmodifiableSet(supportedFacing); } @@ -428,7 +192,7 @@ public class CameraOptions { * @return a collection of supported values. */ @NonNull - public Collection getSupportedFlash() { + public final Collection getSupportedFlash() { return Collections.unmodifiableSet(supportedFlash); } @@ -443,7 +207,7 @@ public class CameraOptions { * @return a collection of supported values. */ @NonNull - public Collection getSupportedWhiteBalance() { + public final Collection getSupportedWhiteBalance() { return Collections.unmodifiableSet(supportedWhiteBalance); } @@ -456,7 +220,7 @@ public class CameraOptions { */ @SuppressWarnings("WeakerAccess") @NonNull - public Collection getSupportedHdr() { + public final Collection getSupportedHdr() { return Collections.unmodifiableSet(supportedHdr); } @@ -466,7 +230,7 @@ public class CameraOptions { * * @return whether zoom is supported. */ - public boolean isZoomSupported() { + public final boolean isZoomSupported() { return zoomSupported; } @@ -478,7 +242,7 @@ public class CameraOptions { * * @return whether auto focus is supported. */ - public boolean isAutoFocusSupported() { + public final boolean isAutoFocusSupported() { return autoFocusSupported; } @@ -490,7 +254,7 @@ public class CameraOptions { * @see #getExposureCorrectionMaxValue() * @return whether exposure correction is supported. */ - public boolean isExposureCorrectionSupported() { + public final boolean isExposureCorrectionSupported() { return exposureCorrectionSupported; } @@ -500,7 +264,7 @@ public class CameraOptions { * * @return min EV value */ - public float getExposureCorrectionMinValue() { + public final float getExposureCorrectionMinValue() { return exposureCorrectionMinValue; } @@ -511,7 +275,7 @@ public class CameraOptions { * * @return max EV value */ - public float getExposureCorrectionMaxValue() { + public final float getExposureCorrectionMaxValue() { return exposureCorrectionMaxValue; } @@ -520,7 +284,7 @@ public class CameraOptions { * * @return the min value */ - public float getPreviewFrameRateMinValue() { + public final float getPreviewFrameRateMinValue() { return previewFrameRateMinValue; } @@ -529,7 +293,7 @@ public class CameraOptions { * * @return the max value */ - public float getPreviewFrameRateMaxValue() { + public final float getPreviewFrameRateMaxValue() { return previewFrameRateMaxValue; } } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java index 156492f0..6edb1140 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java @@ -157,7 +157,7 @@ public class Camera1Engine extends CameraEngine implements // Set parameters that might have been set before the camera was opened. LOG.i("onStartEngine:", "Applying default parameters."); Camera.Parameters params = mCamera.getParameters(); - mCameraOptions = new CameraOptions(params, mCameraId, + mCameraOptions = new Camera1Options(params, mCameraId, getAngles().flip(Reference.SENSOR, Reference.VIEW)); applyAllParameters(params); mCamera.setParameters(params); diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Options.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Options.java new file mode 100644 index 00000000..e77a8ed5 --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Options.java @@ -0,0 +1,128 @@ +package com.otaliastudios.cameraview.engine; + +import android.hardware.Camera; +import android.media.CamcorderProfile; + +import androidx.annotation.NonNull; + +import com.otaliastudios.cameraview.CameraOptions; +import com.otaliastudios.cameraview.controls.Facing; +import com.otaliastudios.cameraview.controls.Flash; +import com.otaliastudios.cameraview.controls.Hdr; +import com.otaliastudios.cameraview.controls.WhiteBalance; +import com.otaliastudios.cameraview.engine.mappers.Camera1Mapper; +import com.otaliastudios.cameraview.internal.utils.CamcorderProfiles; +import com.otaliastudios.cameraview.size.AspectRatio; +import com.otaliastudios.cameraview.size.Size; + +import java.util.List; + +class Camera1Options extends CameraOptions { + + Camera1Options(@NonNull Camera.Parameters params, int cameraId, boolean flipSizes) { + List strings; + Camera1Mapper mapper = Camera1Mapper.get(); + + // Facing + Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); + for (int i = 0, count = Camera.getNumberOfCameras(); i < count; i++) { + Camera.getCameraInfo(i, cameraInfo); + Facing value = mapper.unmapFacing(cameraInfo.facing); + if (value != null) supportedFacing.add(value); + } + + // WB + strings = params.getSupportedWhiteBalance(); + if (strings != null) { + for (String string : strings) { + WhiteBalance value = mapper.unmapWhiteBalance(string); + if (value != null) supportedWhiteBalance.add(value); + } + } + + // Flash + supportedFlash.add(Flash.OFF); + strings = params.getSupportedFlashModes(); + if (strings != null) { + for (String string : strings) { + Flash value = mapper.unmapFlash(string); + if (value != null) supportedFlash.add(value); + } + } + + // Hdr + supportedHdr.add(Hdr.OFF); + strings = params.getSupportedSceneModes(); + if (strings != null) { + for (String string : strings) { + Hdr value = mapper.unmapHdr(string); + if (value != null) supportedHdr.add(value); + } + } + + // zoom + zoomSupported = params.isZoomSupported(); + + // autofocus + autoFocusSupported = params.getSupportedFocusModes() + .contains(Camera.Parameters.FOCUS_MODE_AUTO); + + // Exposure correction + float step = params.getExposureCompensationStep(); + exposureCorrectionMinValue = (float) params.getMinExposureCompensation() * step; + exposureCorrectionMaxValue = (float) params.getMaxExposureCompensation() * step; + exposureCorrectionSupported = params.getMinExposureCompensation() != 0 + || params.getMaxExposureCompensation() != 0; + + // Picture Sizes + List sizes = params.getSupportedPictureSizes(); + for (Camera.Size size : sizes) { + int width = flipSizes ? size.height : size.width; + int height = flipSizes ? size.width : size.height; + supportedPictureSizes.add(new Size(width, height)); + supportedPictureAspectRatio.add(AspectRatio.of(width, height)); + } + + // Video Sizes + // As a safety measure, remove Sizes bigger than CamcorderProfile.highest + CamcorderProfile profile = CamcorderProfiles.get(cameraId, + new Size(Integer.MAX_VALUE, Integer.MAX_VALUE)); + Size videoMaxSize = new Size(profile.videoFrameWidth, profile.videoFrameHeight); + List vsizes = params.getSupportedVideoSizes(); + if (vsizes != null) { + for (Camera.Size size : vsizes) { + if (size.width <= videoMaxSize.getWidth() + && size.height <= videoMaxSize.getHeight()) { + int width = flipSizes ? size.height : size.width; + int height = flipSizes ? size.width : size.height; + supportedVideoSizes.add(new Size(width, height)); + supportedVideoAspectRatio.add(AspectRatio.of(width, height)); + } + } + } else { + // StackOverflow threads seems to agree that if getSupportedVideoSizes is null, + // previews can be used. + List fallback = params.getSupportedPreviewSizes(); + for (Camera.Size size : fallback) { + if (size.width <= videoMaxSize.getWidth() + && size.height <= videoMaxSize.getHeight()) { + int width = flipSizes ? size.height : size.width; + int height = flipSizes ? size.width : size.height; + supportedVideoSizes.add(new Size(width, height)); + supportedVideoAspectRatio.add(AspectRatio.of(width, height)); + } + } + } + + // Preview FPS + previewFrameRateMinValue = Float.MAX_VALUE; + previewFrameRateMaxValue = -Float.MAX_VALUE; + List fpsRanges = params.getSupportedPreviewFpsRange(); + for (int[] fpsRange : fpsRanges) { + float lower = (float) fpsRange[0] / 1000F; + float upper = (float) fpsRange[1] / 1000F; + previewFrameRateMinValue = Math.min(previewFrameRateMinValue, lower); + previewFrameRateMaxValue = Math.max(previewFrameRateMaxValue, upper); + } + } +} diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java index 5887931e..6d1341d2 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java @@ -387,7 +387,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv LOG.i("createCamera:", "Applying default parameters."); mCameraCharacteristics = mManager.getCameraCharacteristics(mCameraId); boolean flip = getAngles().flip(Reference.SENSOR, Reference.VIEW); - mCameraOptions = new CameraOptions(mManager, mCameraId, flip); + mCameraOptions = new Camera2Options(mManager, mCameraId, flip); createRepeatingRequestBuilder(CameraDevice.TEMPLATE_PREVIEW); } catch (CameraAccessException e) { task.trySetException(createCameraException(e)); @@ -1329,23 +1329,24 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv @Override public void setPictureFormat(final @NonNull PictureFormat pictureFormat) { - LOG.i("setPictureFormat", "changing to", pictureFormat, "posting."); - if (pictureFormat == mPictureFormat) return; - mPictureFormat = pictureFormat; - mHandler.run(new Runnable() { - @Override - public void run() { - LOG.i("setPictureFormat", "changing to", pictureFormat, - "executing. BindState:", getBindState(), - "PreviewState:", getPreviewState()); - if (getBindState() == STATE_STOPPED) { - LOG.i("setPictureFormat", "not bound so won't restart."); - } else { - LOG.i("setPictureFormat", "bound or binding. Calling restartBind()"); - restartBind(); + if (pictureFormat != mPictureFormat) { + mPictureFormat = pictureFormat; + LOG.i("setPictureFormat", "changing to", pictureFormat, "posting."); + mHandler.run(new Runnable() { + @Override + public void run() { + LOG.i("setPictureFormat", "changing to", pictureFormat, + "executing. EngineState:", getEngineState(), + "BindState:", getBindState()); + if (getEngineState() == STATE_STOPPED) { + LOG.i("setPictureFormat", "not started so won't restart."); + } else { + LOG.i("setPictureFormat", "started or starting. Calling restart()"); + restart(); + } } - } - }); + }); + } } //endregion diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Options.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Options.java new file mode 100644 index 00000000..7067fdb4 --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Options.java @@ -0,0 +1,160 @@ +package com.otaliastudios.cameraview.engine; + +import android.graphics.ImageFormat; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.CamcorderProfile; +import android.media.MediaRecorder; +import android.os.Build; +import android.util.Range; +import android.util.Rational; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import com.otaliastudios.cameraview.CameraOptions; +import com.otaliastudios.cameraview.controls.Facing; +import com.otaliastudios.cameraview.controls.Flash; +import com.otaliastudios.cameraview.controls.Hdr; +import com.otaliastudios.cameraview.controls.WhiteBalance; +import com.otaliastudios.cameraview.engine.mappers.Camera2Mapper; +import com.otaliastudios.cameraview.internal.utils.CamcorderProfiles; +import com.otaliastudios.cameraview.size.AspectRatio; +import com.otaliastudios.cameraview.size.Size; + +import java.util.Set; + +@RequiresApi(Build.VERSION_CODES.LOLLIPOP) +class Camera2Options extends CameraOptions { + + Camera2Options(@NonNull CameraManager manager, + @NonNull String cameraId, + boolean flipSizes) throws CameraAccessException { + Camera2Mapper mapper = Camera2Mapper.get(); + CameraCharacteristics cameraCharacteristics = manager.getCameraCharacteristics(cameraId); + + // Facing + for (String cameraId1 : manager.getCameraIdList()) { + CameraCharacteristics cameraCharacteristics1 = manager + .getCameraCharacteristics(cameraId1); + Integer cameraFacing = cameraCharacteristics1.get(CameraCharacteristics.LENS_FACING); + if (cameraFacing != null) { + Facing value = mapper.unmapFacing(cameraFacing); + if (value != null) supportedFacing.add(value); + } + } + + // WB + int[] awbModes = cameraCharacteristics.get( + CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES); + //noinspection ConstantConditions + for (int awbMode : awbModes) { + WhiteBalance value = mapper.unmapWhiteBalance(awbMode); + if (value != null) supportedWhiteBalance.add(value); + } + + // Flash + supportedFlash.add(Flash.OFF); + Boolean hasFlash = cameraCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); + if (hasFlash != null && hasFlash) { + int[] aeModes = cameraCharacteristics.get( + CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES); + //noinspection ConstantConditions + for (int aeMode : aeModes) { + Set flashes = mapper.unmapFlash(aeMode); + supportedFlash.addAll(flashes); + } + } + + // HDR + supportedHdr.add(Hdr.OFF); + int[] sceneModes = cameraCharacteristics.get( + CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES); + //noinspection ConstantConditions + for (int sceneMode : sceneModes) { + Hdr value = mapper.unmapHdr(sceneMode); + if (value != null) supportedHdr.add(value); + } + + // Zoom + Float maxZoom = cameraCharacteristics.get( + CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM); + if(maxZoom != null) { + zoomSupported = maxZoom > 1; + } + + + // AutoFocus + // This now means 3A metering with respect to a specific region of the screen. + // Some controls (AF, AE) have special triggers that might or might not be supported. + // But they can also be on some continuous search mode so that the trigger is not needed. + // What really matters in my opinion is the availability of regions. + Integer afRegions = cameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF); + Integer aeRegions = cameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AE); + Integer awbRegions = cameraCharacteristics.get( + CameraCharacteristics.CONTROL_MAX_REGIONS_AWB); + autoFocusSupported = (afRegions != null && afRegions > 0) + || (aeRegions != null && aeRegions > 0) + || (awbRegions != null && awbRegions > 0); + + // Exposure correction + Range exposureRange = cameraCharacteristics.get( + CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE); + Rational exposureStep = cameraCharacteristics.get( + CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP); + if (exposureRange != null && exposureStep != null && exposureStep.floatValue() != 0) { + exposureCorrectionMinValue = exposureRange.getLower() / exposureStep.floatValue(); + exposureCorrectionMaxValue = exposureRange.getUpper() / exposureStep.floatValue(); + } + exposureCorrectionSupported = exposureCorrectionMinValue != 0 + && exposureCorrectionMaxValue != 0; + + + // Picture Sizes + StreamConfigurationMap streamMap = cameraCharacteristics.get( + CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + if (streamMap == null) { + throw new RuntimeException("StreamConfigurationMap is null. Should not happen."); + } + android.util.Size[] psizes = streamMap.getOutputSizes(ImageFormat.JPEG); + for (android.util.Size size : psizes) { + int width = flipSizes ? size.getHeight() : size.getWidth(); + int height = flipSizes ? size.getWidth() : size.getHeight(); + supportedPictureSizes.add(new Size(width, height)); + supportedPictureAspectRatio.add(AspectRatio.of(width, height)); + } + + // Video Sizes + // As a safety measure, remove Sizes bigger than CamcorderProfile.highest + CamcorderProfile profile = CamcorderProfiles.get(cameraId, + new Size(Integer.MAX_VALUE, Integer.MAX_VALUE)); + Size videoMaxSize = new Size(profile.videoFrameWidth, profile.videoFrameHeight); + android.util.Size[] vsizes = streamMap.getOutputSizes(MediaRecorder.class); + for (android.util.Size size : vsizes) { + if (size.getWidth() <= videoMaxSize.getWidth() + && size.getHeight() <= videoMaxSize.getHeight()) { + int width = flipSizes ? size.getHeight() : size.getWidth(); + int height = flipSizes ? size.getWidth() : size.getHeight(); + supportedVideoSizes.add(new Size(width, height)); + supportedVideoAspectRatio.add(AspectRatio.of(width, height)); + } + } + + // Preview FPS + Range[] range = cameraCharacteristics.get( + CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES); + if (range != null) { + previewFrameRateMinValue = Float.MAX_VALUE; + previewFrameRateMaxValue = -Float.MAX_VALUE; + for (Range fpsRange : range) { + previewFrameRateMinValue = Math.min(previewFrameRateMinValue, fpsRange.getLower()); + previewFrameRateMaxValue = Math.max(previewFrameRateMaxValue, fpsRange.getUpper()); + } + } else { + previewFrameRateMinValue = 0F; + previewFrameRateMaxValue = 0F; + } + } +} diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full1PictureRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full1PictureRecorder.java index 7bad4173..9ec28779 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full1PictureRecorder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full1PictureRecorder.java @@ -61,7 +61,6 @@ public class Full1PictureRecorder extends PictureRecorder { } catch (IOException e) { exifRotation = 0; } - mResult.format = PictureResult.FORMAT_JPEG; mResult.data = data; mResult.rotation = exifRotation; camera.startPreview(); // This is needed, read somewhere in the docs. diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full2PictureRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full2PictureRecorder.java index 0e2c6fae..4811e80b 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full2PictureRecorder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full2PictureRecorder.java @@ -106,7 +106,6 @@ public class Full2PictureRecorder extends PictureRecorder // Just like Camera1, unfortunately, the camera might rotate the image // and put EXIF=0 instead of respecting our EXIF and leave the image unaltered. - mResult.format = PictureResult.FORMAT_JPEG; mResult.rotation = 0; try { ExifInterface exif = new ExifInterface(new ByteArrayInputStream(mResult.data)); diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Snapshot1PictureRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Snapshot1PictureRecorder.java index 5b7cbc19..73a841b5 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Snapshot1PictureRecorder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Snapshot1PictureRecorder.java @@ -81,7 +81,6 @@ public class Snapshot1PictureRecorder extends PictureRecorder { mResult.data = data; mResult.size = new Size(outputRect.width(), outputRect.height()); mResult.rotation = 0; - mResult.format = PictureResult.FORMAT_JPEG; dispatchResult(); } }); diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/SnapshotGlPictureRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/SnapshotGlPictureRecorder.java index 5dd5ce20..cdb81e18 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/SnapshotGlPictureRecorder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/SnapshotGlPictureRecorder.java @@ -238,7 +238,6 @@ public class SnapshotGlPictureRecorder extends PictureRecorder { LOG.i("takeFrame:", "timestampUs:", timestampUs); mViewport.drawFrame(timestampUs, mTextureId, mTransform); if (mHasOverlay) mOverlayDrawer.render(timestampUs); - mResult.format = PictureResult.FORMAT_JPEG; mResult.data = eglSurface.saveFrameTo(Bitmap.CompressFormat.JPEG); // 6. Cleanup