From 141902c6cb1440bcdd7e1c97be12ac68812b5340 Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Fri, 10 Jan 2020 18:55:53 +0100 Subject: [PATCH] Metering improvements (#741) * Increase metering timeout for touch metering * Improve DeviceEncoders to respect max block count * Reorder block * Update emulator to 29.3.4 * Update emulator to 29.3.4 (2) * Improve exposure metering for legacy sensors --- .github/workflows/build.yml | 2 +- cameraview/build.gradle | 4 +- .../cameraview/engine/Camera2Engine.java | 7 ++-- .../engine/meter/ExposureMeter.java | 40 ++++++++++++------- .../cameraview/internal/DeviceEncoders.java | 28 +++++++++++-- 5 files changed, 57 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7f9ce02e..c575244e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,7 +72,7 @@ jobs: disable-animations: true profile: Nexus 5X emulator-options: -no-snapshot -no-window -no-boot-anim -camera-back emulated -camera-front emulated -gpu swiftshader_indirect - emulator-build: 6031357 + emulator-build: 6110076 script: ./.github/workflows/emulator_script.sh - name: Upload emulator tests artifact uses: actions/upload-artifact@v1 diff --git a/cameraview/build.gradle b/cameraview/build.gradle index d2172aa0..b09317ea 100644 --- a/cameraview/build.gradle +++ b/cameraview/build.gradle @@ -145,7 +145,7 @@ bintray { // From official sample https://github.com/bintray/bintray-examples/blob/master/gradle-aar-example/build.gradle task sourcesJar(type: Jar) { - classifier = 'sources' + archiveClassifier.set('sources') from android.sourceSets.main.java.sourceFiles } @@ -167,7 +167,7 @@ task javadoc(type: Javadoc) { } task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' + archiveClassifier.set('javadoc') from javadoc.destinationDir } 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 98f13c82..8a281fce 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java @@ -83,7 +83,8 @@ public class Camera2Engine extends CameraBaseEngine implements ActionHolder { private static final int FRAME_PROCESSING_FORMAT = ImageFormat.YUV_420_888; - @VisibleForTesting static final long METER_TIMEOUT = 2500; + @VisibleForTesting static final long METER_TIMEOUT = 5000; + private static final long METER_TIMEOUT_SHORT = 2500; private final CameraManager mManager; private String mCameraId; @@ -770,7 +771,7 @@ public class Camera2Engine extends CameraBaseEngine implements boolean doMetering) { if (doMetering) { LOG.i("onTakePictureSnapshot:", "doMetering is true. Delaying."); - Action action = Actions.timeout(METER_TIMEOUT, createMeterAction(null)); + Action action = Actions.timeout(METER_TIMEOUT_SHORT, createMeterAction(null)); action.addCallback(new CompletionCallback() { @Override protected void onActionCompleted(@NonNull Action action) { @@ -803,7 +804,7 @@ public class Camera2Engine extends CameraBaseEngine implements protected void onTakePicture(@NonNull final PictureResult.Stub stub, boolean doMetering) { if (doMetering) { LOG.i("onTakePicture:", "doMetering is true. Delaying."); - Action action = Actions.timeout(METER_TIMEOUT, createMeterAction(null)); + Action action = Actions.timeout(METER_TIMEOUT_SHORT, createMeterAction(null)); action.addCallback(new CompletionCallback() { @Override protected void onActionCompleted(@NonNull Action action) { diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/ExposureMeter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/ExposureMeter.java index ed904932..5e7b4581 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/ExposureMeter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/ExposureMeter.java @@ -24,6 +24,9 @@ public class ExposureMeter extends BaseMeter { private static final int STATE_WAITING_PRECAPTURE = 0; private static final int STATE_WAITING_PRECAPTURE_END = 1; + private boolean mSupportsAreas = false; + private boolean mSupportsTrigger = false; + @SuppressWarnings("WeakerAccess") public ExposureMeter(@NonNull List areas, boolean skipIfPossible) { super(areas, skipIfPossible); @@ -32,9 +35,9 @@ public class ExposureMeter extends BaseMeter { @Override protected boolean checkIsSupported(@NonNull ActionHolder holder) { // In our case, this means checking if we support the AE precapture trigger. - boolean isNotLegacy = readCharacteristic( + boolean isLegacy = readCharacteristic( CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL, -1) - != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY; + == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY; Integer aeMode = holder.getBuilder(this).get(CaptureRequest.CONTROL_AE_MODE); boolean isAEOn = aeMode != null && (aeMode == CameraCharacteristics.CONTROL_AE_MODE_ON @@ -43,8 +46,13 @@ public class ExposureMeter extends BaseMeter { || aeMode == CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE || aeMode == 5 /* CameraCharacteristics.CONTROL_AE_MODE_ON_EXTERNAL_FLASH, API 28 */); - boolean result = isNotLegacy && isAEOn; - LOG.i("checkIsSupported:", result); + mSupportsTrigger = !isLegacy; + mSupportsAreas = readCharacteristic(CameraCharacteristics.CONTROL_MAX_REGIONS_AE, + 0) > 0; + boolean result = isAEOn && (mSupportsTrigger || mSupportsAreas); + LOG.i("checkIsSupported:", result, + "trigger:", mSupportsTrigger, + "areas:", mSupportsAreas); return result; } @@ -66,22 +74,26 @@ public class ExposureMeter extends BaseMeter { protected void onStarted(@NonNull ActionHolder holder, @NonNull List areas) { LOG.i("onStarted:", "with areas:", areas); - // Launch the precapture trigger. - holder.getBuilder(this).set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, - CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); - - // Check the regions. - int maxRegions = readCharacteristic(CameraCharacteristics.CONTROL_MAX_REGIONS_AE, - 0); - if (!areas.isEmpty() && maxRegions > 0) { - int max = Math.min(maxRegions, areas.size()); + if (mSupportsAreas && !areas.isEmpty()) { + int max = readCharacteristic(CameraCharacteristics.CONTROL_MAX_REGIONS_AE, 0); + max = Math.min(max, areas.size()); holder.getBuilder(this).set(CaptureRequest.CONTROL_AE_REGIONS, areas.subList(0, max).toArray(new MeteringRectangle[]{})); } + if (mSupportsTrigger) { + holder.getBuilder(this).set( + CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, + CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); + } + // Apply holder.applyBuilder(this); - setState(STATE_WAITING_PRECAPTURE); + if (mSupportsTrigger) { + setState(STATE_WAITING_PRECAPTURE); + } else { + setState(STATE_WAITING_PRECAPTURE_END); + } } @Override diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/DeviceEncoders.java b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/DeviceEncoders.java index 14efcfdc..f670c829 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/DeviceEncoders.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/DeviceEncoders.java @@ -7,6 +7,7 @@ import android.media.MediaCodecInfo; import android.media.MediaCodecList; import android.media.MediaFormat; import android.os.Build; +import android.util.Range; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -196,10 +197,7 @@ public class DeviceEncoders { public int compare(MediaCodecInfo o1, MediaCodecInfo o2) { boolean hw1 = isHardwareEncoder(o1.getName()); boolean hw2 = isHardwareEncoder(o2.getName()); - if (hw1 && hw2) return 0; - if (hw1) return -1; - if (hw2) return 1; - return 0; + return Boolean.compare(hw2, hw1); } }); } @@ -260,6 +258,28 @@ public class DeviceEncoders { " Range:" + mVideoCapabilities.getSupportedHeights()); } + // We cannot change the aspect ratio, but the max block count might also be the + // issue. Try to find a width that contains a height that would accept our AR. + try { + if (!mVideoCapabilities.getSupportedHeightsFor(width).contains(height)) { + int candidateWidth = width; + int minWidth = mVideoCapabilities.getSupportedWidths().getLower(); + int widthAlignment = mVideoCapabilities.getWidthAlignment(); + while (candidateWidth >= minWidth) { + // Reduce by 32 and realign just in case, then check if our AR is now + // supported. If it is, restart from scratch to go through the other checks. + candidateWidth -= 32; + while (candidateWidth % widthAlignment != 0) candidateWidth--; + int candidateHeight = (int) Math.round(candidateWidth / aspect); + if (mVideoCapabilities.getSupportedHeightsFor(candidateWidth) + .contains(candidateHeight)) { + LOG.w("getSupportedVideoSize - restarting with smaller size."); + return getSupportedVideoSize(new Size(candidateWidth, candidateHeight)); + } + } + } + } catch (IllegalArgumentException ignore) {} + // It's still possible that we're unsupported for other reasons. if (!mVideoCapabilities.isSizeSupported(width, height)) { throw new VideoException("Size not supported for unknown reason." +