@capgo/camera-preview 8.5.1 → 8.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +136 -0
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraPreview.java +134 -14
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.java +316 -2
- package/android/src/main/java/app/capgo/capacitor/camera/preview/model/CameraSessionConfiguration.java +9 -0
- package/dist/docs.json +245 -1
- package/dist/esm/definitions.d.ts +96 -0
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +20 -1
- package/dist/esm/web.js +22 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +21 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +21 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapgoCameraPreviewPlugin/CameraController.swift +294 -9
- package/ios/Sources/CapgoCameraPreviewPlugin/Plugin.swift +90 -4
- package/package.json +1 -1
|
@@ -45,6 +45,7 @@ import androidx.annotation.NonNull;
|
|
|
45
45
|
import androidx.annotation.OptIn;
|
|
46
46
|
import androidx.camera.camera2.interop.Camera2CameraControl;
|
|
47
47
|
import androidx.camera.camera2.interop.Camera2CameraInfo;
|
|
48
|
+
import androidx.camera.camera2.interop.Camera2Interop;
|
|
48
49
|
import androidx.camera.camera2.interop.CaptureRequestOptions;
|
|
49
50
|
import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
|
|
50
51
|
import androidx.camera.core.AspectRatio;
|
|
@@ -110,6 +111,7 @@ import java.util.Locale;
|
|
|
110
111
|
import java.util.Map;
|
|
111
112
|
import java.util.Objects;
|
|
112
113
|
import java.util.Set;
|
|
114
|
+
import java.util.TreeSet;
|
|
113
115
|
import java.util.concurrent.ConcurrentHashMap;
|
|
114
116
|
import java.util.concurrent.Executor;
|
|
115
117
|
import java.util.concurrent.ExecutorService;
|
|
@@ -184,6 +186,10 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
184
186
|
private boolean isRunning = false;
|
|
185
187
|
private Size currentPreviewResolution = null;
|
|
186
188
|
private ListenableFuture<FocusMeteringResult> currentFocusFuture = null; // Track current focus operation
|
|
189
|
+
private Integer configuredVideoFrameRate = null;
|
|
190
|
+
private Range<Integer> configuredVideoFrameRateRange = null;
|
|
191
|
+
private Runnable pendingFrameRateBindSuccess;
|
|
192
|
+
private java.util.function.Consumer<String> pendingFrameRateBindError;
|
|
187
193
|
private String currentExposureMode = "CONTINUOUS"; // Default behavior
|
|
188
194
|
// Capture/stop coordination
|
|
189
195
|
private final Object captureLock = new Object();
|
|
@@ -1071,7 +1077,11 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
1071
1077
|
? previewView.getDisplay().getRotation()
|
|
1072
1078
|
: android.view.Surface.ROTATION_0;
|
|
1073
1079
|
|
|
1074
|
-
Preview
|
|
1080
|
+
Preview.Builder previewBuilder = new Preview.Builder()
|
|
1081
|
+
.setResolutionSelector(resolutionSelector)
|
|
1082
|
+
.setTargetRotation(rotation);
|
|
1083
|
+
previewBuilder = applyTargetFpsToPreviewBuilder(previewBuilder);
|
|
1084
|
+
Preview preview = previewBuilder.build();
|
|
1075
1085
|
// Keep reference to preview use case for later re-binding (e.g., when enabling video)
|
|
1076
1086
|
imageCapture = new ImageCapture.Builder()
|
|
1077
1087
|
.setResolutionSelector(resolutionSelector)
|
|
@@ -1139,7 +1149,10 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
1139
1149
|
recorderBuilder.setVideoCapabilitiesSource(Recorder.VIDEO_CAPABILITIES_SOURCE_CODEC_CAPABILITIES);
|
|
1140
1150
|
}
|
|
1141
1151
|
Recorder recorder = recorderBuilder.build();
|
|
1142
|
-
|
|
1152
|
+
VideoCapture.Builder<Recorder> videoCaptureBuilder = new VideoCapture.Builder<>(recorder);
|
|
1153
|
+
videoCaptureBuilder = applyTargetFpsToVideoCaptureBuilder(videoCaptureBuilder);
|
|
1154
|
+
videoCaptureBuilder.setVideoStabilizationEnabled(isVideoStabilizationEnabledForSession());
|
|
1155
|
+
videoCapture = videoCaptureBuilder.build();
|
|
1143
1156
|
}
|
|
1144
1157
|
|
|
1145
1158
|
// Unbind any existing use cases and bind new ones
|
|
@@ -1287,6 +1300,7 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
1287
1300
|
}
|
|
1288
1301
|
|
|
1289
1302
|
isRunning = true;
|
|
1303
|
+
completePendingFrameRateBindSuccess();
|
|
1290
1304
|
Log.d(TAG, "bindCameraUseCases: Camera bound successfully");
|
|
1291
1305
|
if (listener != null) {
|
|
1292
1306
|
if (sessionConfig != null && sessionConfig.isToBack()) {
|
|
@@ -1324,6 +1338,7 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
1324
1338
|
} catch (Exception e) {
|
|
1325
1339
|
// Restore webView background on error
|
|
1326
1340
|
restoreWebViewBackground();
|
|
1341
|
+
completePendingFrameRateBindError("Error binding camera: " + e.getMessage());
|
|
1327
1342
|
if (listener != null) listener.onCameraStartError(this, "Error binding camera: " + e.getMessage());
|
|
1328
1343
|
}
|
|
1329
1344
|
});
|
|
@@ -3355,6 +3370,185 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
3355
3370
|
}
|
|
3356
3371
|
}
|
|
3357
3372
|
|
|
3373
|
+
private void clearConfiguredVideoFrameRate() {
|
|
3374
|
+
configuredVideoFrameRate = null;
|
|
3375
|
+
configuredVideoFrameRateRange = null;
|
|
3376
|
+
}
|
|
3377
|
+
|
|
3378
|
+
private Range<Integer>[] getAdvertisedFpsRanges() throws Exception {
|
|
3379
|
+
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
|
|
3380
|
+
String cameraId = resolveActiveCameraIdForCharacteristics();
|
|
3381
|
+
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
|
|
3382
|
+
return characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
|
|
3383
|
+
}
|
|
3384
|
+
|
|
3385
|
+
private Range<Integer> findAdvertisedFpsRangeForRate(int frameRate, Range<Integer>[] ranges) {
|
|
3386
|
+
if (ranges == null) {
|
|
3387
|
+
return null;
|
|
3388
|
+
}
|
|
3389
|
+
|
|
3390
|
+
Range<Integer> containingRange = null;
|
|
3391
|
+
for (Range<Integer> range : ranges) {
|
|
3392
|
+
if (range.getLower() > frameRate || range.getUpper() < frameRate) {
|
|
3393
|
+
continue;
|
|
3394
|
+
}
|
|
3395
|
+
if (range.getLower().equals(range.getUpper()) && range.getLower() == frameRate) {
|
|
3396
|
+
return range;
|
|
3397
|
+
}
|
|
3398
|
+
containingRange = range;
|
|
3399
|
+
}
|
|
3400
|
+
return containingRange;
|
|
3401
|
+
}
|
|
3402
|
+
|
|
3403
|
+
private Range<Integer> resolveConfiguredVideoFrameRateRange() {
|
|
3404
|
+
if (configuredVideoFrameRate == null) {
|
|
3405
|
+
return null;
|
|
3406
|
+
}
|
|
3407
|
+
try {
|
|
3408
|
+
Range<Integer>[] ranges = getAdvertisedFpsRanges();
|
|
3409
|
+
return findAdvertisedFpsRangeForRate(configuredVideoFrameRate, ranges);
|
|
3410
|
+
} catch (Exception e) {
|
|
3411
|
+
Log.w(TAG, "resolveConfiguredVideoFrameRateRange: Failed to resolve FPS range", e);
|
|
3412
|
+
return null;
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
|
|
3416
|
+
private void completePendingFrameRateBindSuccess() {
|
|
3417
|
+
if (pendingFrameRateBindSuccess == null) {
|
|
3418
|
+
return;
|
|
3419
|
+
}
|
|
3420
|
+
Runnable callback = pendingFrameRateBindSuccess;
|
|
3421
|
+
pendingFrameRateBindSuccess = null;
|
|
3422
|
+
pendingFrameRateBindError = null;
|
|
3423
|
+
callback.run();
|
|
3424
|
+
}
|
|
3425
|
+
|
|
3426
|
+
private void completePendingFrameRateBindError(String message) {
|
|
3427
|
+
if (pendingFrameRateBindError == null) {
|
|
3428
|
+
return;
|
|
3429
|
+
}
|
|
3430
|
+
java.util.function.Consumer<String> callback = pendingFrameRateBindError;
|
|
3431
|
+
pendingFrameRateBindSuccess = null;
|
|
3432
|
+
pendingFrameRateBindError = null;
|
|
3433
|
+
callback.accept(message);
|
|
3434
|
+
}
|
|
3435
|
+
|
|
3436
|
+
@OptIn(markerClass = ExperimentalCamera2Interop.class)
|
|
3437
|
+
private Preview.Builder applyTargetFpsToPreviewBuilder(Preview.Builder builder) {
|
|
3438
|
+
Range<Integer> fpsRange = resolveConfiguredVideoFrameRateRange();
|
|
3439
|
+
if (fpsRange == null) {
|
|
3440
|
+
return builder;
|
|
3441
|
+
}
|
|
3442
|
+
|
|
3443
|
+
Camera2Interop.Extender<Preview> extender = new Camera2Interop.Extender<>(builder);
|
|
3444
|
+
extender.setCaptureRequestOption(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
|
|
3445
|
+
return builder;
|
|
3446
|
+
}
|
|
3447
|
+
|
|
3448
|
+
@OptIn(markerClass = ExperimentalCamera2Interop.class)
|
|
3449
|
+
private VideoCapture.Builder<Recorder> applyTargetFpsToVideoCaptureBuilder(VideoCapture.Builder<Recorder> builder) {
|
|
3450
|
+
Range<Integer> fpsRange = resolveConfiguredVideoFrameRateRange();
|
|
3451
|
+
if (fpsRange == null) {
|
|
3452
|
+
return builder;
|
|
3453
|
+
}
|
|
3454
|
+
|
|
3455
|
+
Camera2Interop.Extender<VideoCapture<Recorder>> extender = new Camera2Interop.Extender<>(builder);
|
|
3456
|
+
extender.setCaptureRequestOption(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
|
|
3457
|
+
return builder;
|
|
3458
|
+
}
|
|
3459
|
+
|
|
3460
|
+
private String resolveActiveCameraIdForCharacteristics() throws Exception {
|
|
3461
|
+
if (currentLogicalDeviceId != null) {
|
|
3462
|
+
return currentLogicalDeviceId;
|
|
3463
|
+
}
|
|
3464
|
+
if (camera != null) {
|
|
3465
|
+
return Camera2CameraInfo.from(camera.getCameraInfo()).getCameraId();
|
|
3466
|
+
}
|
|
3467
|
+
throw new Exception("Camera not initialized");
|
|
3468
|
+
}
|
|
3469
|
+
|
|
3470
|
+
private List<Integer> frameRatesFromFpsRanges(Range<Integer>[] ranges) {
|
|
3471
|
+
TreeSet<Integer> rates = new TreeSet<>();
|
|
3472
|
+
int[] standardRates = new int[] { 24, 25, 30, 50, 60, 120 };
|
|
3473
|
+
|
|
3474
|
+
if (ranges == null) {
|
|
3475
|
+
return new ArrayList<>();
|
|
3476
|
+
}
|
|
3477
|
+
|
|
3478
|
+
for (Range<Integer> range : ranges) {
|
|
3479
|
+
int min = range.getLower();
|
|
3480
|
+
int max = range.getUpper();
|
|
3481
|
+
if (min == max) {
|
|
3482
|
+
rates.add(min);
|
|
3483
|
+
continue;
|
|
3484
|
+
}
|
|
3485
|
+
|
|
3486
|
+
rates.add(min);
|
|
3487
|
+
rates.add(max);
|
|
3488
|
+
for (int standardRate : standardRates) {
|
|
3489
|
+
if (standardRate >= min && standardRate <= max) {
|
|
3490
|
+
rates.add(standardRate);
|
|
3491
|
+
}
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
|
|
3495
|
+
return new ArrayList<>(rates);
|
|
3496
|
+
}
|
|
3497
|
+
|
|
3498
|
+
public List<Integer> getSupportedVideoFrameRates() throws Exception {
|
|
3499
|
+
return frameRatesFromFpsRanges(getAdvertisedFpsRanges());
|
|
3500
|
+
}
|
|
3501
|
+
|
|
3502
|
+
public int getVideoFrameRate() throws Exception {
|
|
3503
|
+
if (configuredVideoFrameRate != null) {
|
|
3504
|
+
return configuredVideoFrameRate;
|
|
3505
|
+
}
|
|
3506
|
+
return 30;
|
|
3507
|
+
}
|
|
3508
|
+
|
|
3509
|
+
public void setVideoFrameRate(int frameRate, Runnable onSuccess, java.util.function.Consumer<String> onError) {
|
|
3510
|
+
mainExecutor.execute(() -> {
|
|
3511
|
+
Integer previousFrameRate = configuredVideoFrameRate;
|
|
3512
|
+
Range<Integer> previousRange = configuredVideoFrameRateRange;
|
|
3513
|
+
try {
|
|
3514
|
+
if (currentRecording != null) {
|
|
3515
|
+
throw new Exception("Cannot change video frame rate while recording");
|
|
3516
|
+
}
|
|
3517
|
+
if (frameRate <= 0) {
|
|
3518
|
+
throw new Exception("frameRate must be greater than 0");
|
|
3519
|
+
}
|
|
3520
|
+
|
|
3521
|
+
List<Integer> supportedRates = getSupportedVideoFrameRates();
|
|
3522
|
+
if (!supportedRates.contains(frameRate)) {
|
|
3523
|
+
throw new Exception("Unsupported frame rate " + frameRate + ". Supported values: " + supportedRates);
|
|
3524
|
+
}
|
|
3525
|
+
|
|
3526
|
+
Range<Integer> advertisedRange = findAdvertisedFpsRangeForRate(frameRate, getAdvertisedFpsRanges());
|
|
3527
|
+
if (advertisedRange == null) {
|
|
3528
|
+
throw new Exception("Unsupported frame rate " + frameRate + " for the active camera");
|
|
3529
|
+
}
|
|
3530
|
+
|
|
3531
|
+
configuredVideoFrameRate = frameRate;
|
|
3532
|
+
configuredVideoFrameRateRange = advertisedRange;
|
|
3533
|
+
if (isRunning && cameraProvider != null) {
|
|
3534
|
+
pendingFrameRateBindSuccess = onSuccess;
|
|
3535
|
+
pendingFrameRateBindError = (message) -> {
|
|
3536
|
+
configuredVideoFrameRate = previousFrameRate;
|
|
3537
|
+
configuredVideoFrameRateRange = previousRange;
|
|
3538
|
+
onError.accept(message);
|
|
3539
|
+
};
|
|
3540
|
+
bindCameraUseCases();
|
|
3541
|
+
return;
|
|
3542
|
+
}
|
|
3543
|
+
onSuccess.run();
|
|
3544
|
+
} catch (Exception e) {
|
|
3545
|
+
configuredVideoFrameRate = previousFrameRate;
|
|
3546
|
+
configuredVideoFrameRateRange = previousRange;
|
|
3547
|
+
onError.accept(e.getMessage());
|
|
3548
|
+
}
|
|
3549
|
+
});
|
|
3550
|
+
}
|
|
3551
|
+
|
|
3358
3552
|
private long showFocusIndicator(float x, float y) {
|
|
3359
3553
|
// If preview is gone (e.g., stopping/closing), bail out safely
|
|
3360
3554
|
if (previewContainer == null) {
|
|
@@ -3646,6 +3840,7 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
3646
3840
|
|
|
3647
3841
|
mainExecutor.execute(() -> {
|
|
3648
3842
|
try {
|
|
3843
|
+
String previousDeviceId = currentDeviceId;
|
|
3649
3844
|
CameraSessionConfiguration previousConfig = sessionConfig;
|
|
3650
3845
|
CameraInfo targetCameraInfo = findAvailableCameraInfoById(deviceId);
|
|
3651
3846
|
String fallbackPosition = resolveFallbackPositionForDeviceId(deviceId);
|
|
@@ -3688,6 +3883,9 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
3688
3883
|
);
|
|
3689
3884
|
copyMutableSessionConfigState(previousConfig, updatedConfig);
|
|
3690
3885
|
updatedConfig.setTargetZoom(1.0f);
|
|
3886
|
+
if (!Objects.equals(deviceId, previousDeviceId)) {
|
|
3887
|
+
clearConfiguredVideoFrameRate();
|
|
3888
|
+
}
|
|
3691
3889
|
sessionConfig = updatedConfig;
|
|
3692
3890
|
|
|
3693
3891
|
Log.d(TAG, "switchToDevice: Updated sessionConfig with deviceId: " + deviceId);
|
|
@@ -3701,6 +3899,7 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
3701
3899
|
|
|
3702
3900
|
public void flipCamera() {
|
|
3703
3901
|
Log.d(TAG, "flipCamera: Flipping camera");
|
|
3902
|
+
clearConfiguredVideoFrameRate();
|
|
3704
3903
|
|
|
3705
3904
|
// Determine current position based on session config and flip it
|
|
3706
3905
|
String currentPosition = sessionConfig.getPosition();
|
|
@@ -4663,6 +4862,30 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
4663
4862
|
}
|
|
4664
4863
|
}
|
|
4665
4864
|
|
|
4865
|
+
public void startRecordVideo(
|
|
4866
|
+
Long maxDurationMillis,
|
|
4867
|
+
Long maxFileSize,
|
|
4868
|
+
Integer frameRate,
|
|
4869
|
+
Runnable onSuccess,
|
|
4870
|
+
java.util.function.Consumer<String> onError
|
|
4871
|
+
) {
|
|
4872
|
+
Runnable startRecording = () -> {
|
|
4873
|
+
try {
|
|
4874
|
+
startRecordVideo(maxDurationMillis, maxFileSize);
|
|
4875
|
+
onSuccess.run();
|
|
4876
|
+
} catch (Exception e) {
|
|
4877
|
+
onError.accept(e.getMessage());
|
|
4878
|
+
}
|
|
4879
|
+
};
|
|
4880
|
+
|
|
4881
|
+
if (frameRate == null) {
|
|
4882
|
+
mainExecutor.execute(startRecording);
|
|
4883
|
+
return;
|
|
4884
|
+
}
|
|
4885
|
+
|
|
4886
|
+
setVideoFrameRate(frameRate, startRecording, onError);
|
|
4887
|
+
}
|
|
4888
|
+
|
|
4666
4889
|
/** @noinspection ResultOfMethodCallIgnored*/
|
|
4667
4890
|
public void startRecordVideo(Long maxDurationMillis, Long maxFileSize) throws Exception {
|
|
4668
4891
|
if (videoCapture == null) {
|
|
@@ -4785,6 +5008,15 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
4785
5008
|
private static final List<String> ALL_VIDEO_QUALITIES = Arrays.asList("low", "medium", "high", "2160p", "1080p", "720p", "480p", "4:3");
|
|
4786
5009
|
|
|
4787
5010
|
private static final List<String> ALL_VIDEO_CODECS = Arrays.asList("avc1", "hvc1");
|
|
5011
|
+
private static final List<String> ALL_VIDEO_STABILIZATION_MODES = Arrays.asList("off", "standard");
|
|
5012
|
+
private static final List<String> IOS_ONLY_VIDEO_STABILIZATION_MODES = Arrays.asList(
|
|
5013
|
+
"cinematic",
|
|
5014
|
+
"cinematicExtended",
|
|
5015
|
+
"previewOptimized",
|
|
5016
|
+
"cinematicExtendedEnhanced",
|
|
5017
|
+
"auto",
|
|
5018
|
+
"lowLatency"
|
|
5019
|
+
);
|
|
4788
5020
|
|
|
4789
5021
|
public String getVideoQualitySetting() {
|
|
4790
5022
|
if (sessionConfig == null) {
|
|
@@ -4864,4 +5096,86 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
4864
5096
|
}
|
|
4865
5097
|
return false;
|
|
4866
5098
|
}
|
|
5099
|
+
|
|
5100
|
+
public boolean isVideoStabilizationSupported() {
|
|
5101
|
+
return isStandardVideoStabilizationSupported();
|
|
5102
|
+
}
|
|
5103
|
+
|
|
5104
|
+
public List<String> getSupportedVideoStabilizationModes() {
|
|
5105
|
+
List<String> modes = new ArrayList<>();
|
|
5106
|
+
modes.add("off");
|
|
5107
|
+
if (isStandardVideoStabilizationSupported()) {
|
|
5108
|
+
modes.add("standard");
|
|
5109
|
+
}
|
|
5110
|
+
return modes;
|
|
5111
|
+
}
|
|
5112
|
+
|
|
5113
|
+
public String getVideoStabilizationModeSetting() {
|
|
5114
|
+
if (sessionConfig == null) {
|
|
5115
|
+
return "off";
|
|
5116
|
+
}
|
|
5117
|
+
return sessionConfig.getVideoStabilizationMode();
|
|
5118
|
+
}
|
|
5119
|
+
|
|
5120
|
+
public void setVideoStabilizationModeSetting(String mode) {
|
|
5121
|
+
if (sessionConfig == null) {
|
|
5122
|
+
throw new IllegalStateException("Camera session is not running");
|
|
5123
|
+
}
|
|
5124
|
+
if (currentRecording != null) {
|
|
5125
|
+
throw new IllegalStateException("Cannot change video stabilization while recording is in progress");
|
|
5126
|
+
}
|
|
5127
|
+
String normalized = mode != null ? mode : "off";
|
|
5128
|
+
if (IOS_ONLY_VIDEO_STABILIZATION_MODES.contains(normalized)) {
|
|
5129
|
+
throw new IllegalArgumentException(
|
|
5130
|
+
"Video stabilization mode '" + normalized + "' is only supported on iOS. Use 'off' or 'standard' on Android."
|
|
5131
|
+
);
|
|
5132
|
+
}
|
|
5133
|
+
if (!ALL_VIDEO_STABILIZATION_MODES.contains(normalized)) {
|
|
5134
|
+
throw new IllegalArgumentException("Unsupported video stabilization mode: " + mode);
|
|
5135
|
+
}
|
|
5136
|
+
if ("standard".equals(normalized) && !isStandardVideoStabilizationSupported()) {
|
|
5137
|
+
throw new IllegalArgumentException("Video stabilization mode 'standard' is not supported on this device");
|
|
5138
|
+
}
|
|
5139
|
+
if (normalized.equals(sessionConfig.getVideoStabilizationMode())) {
|
|
5140
|
+
return;
|
|
5141
|
+
}
|
|
5142
|
+
sessionConfig.setVideoStabilizationMode(normalized);
|
|
5143
|
+
if (sessionConfig.isVideoModeEnabled() && isRunning) {
|
|
5144
|
+
mainExecutor.execute(this::bindCameraUseCases);
|
|
5145
|
+
}
|
|
5146
|
+
}
|
|
5147
|
+
|
|
5148
|
+
private boolean isVideoStabilizationEnabledForSession() {
|
|
5149
|
+
if (sessionConfig == null) {
|
|
5150
|
+
return false;
|
|
5151
|
+
}
|
|
5152
|
+
return "standard".equalsIgnoreCase(sessionConfig.getVideoStabilizationMode());
|
|
5153
|
+
}
|
|
5154
|
+
|
|
5155
|
+
private boolean isStandardVideoStabilizationSupported() {
|
|
5156
|
+
if (camera == null) {
|
|
5157
|
+
return false;
|
|
5158
|
+
}
|
|
5159
|
+
try {
|
|
5160
|
+
String cameraId = Camera2CameraInfo.from(camera.getCameraInfo()).getCameraId();
|
|
5161
|
+
CameraManager cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
|
|
5162
|
+
if (cameraManager == null) {
|
|
5163
|
+
return false;
|
|
5164
|
+
}
|
|
5165
|
+
int[] modes = cameraManager
|
|
5166
|
+
.getCameraCharacteristics(cameraId)
|
|
5167
|
+
.get(CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES);
|
|
5168
|
+
if (modes == null) {
|
|
5169
|
+
return false;
|
|
5170
|
+
}
|
|
5171
|
+
for (int availableMode : modes) {
|
|
5172
|
+
if (availableMode == CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE_ON) {
|
|
5173
|
+
return true;
|
|
5174
|
+
}
|
|
5175
|
+
}
|
|
5176
|
+
} catch (CameraAccessException e) {
|
|
5177
|
+
Log.w(TAG, "Failed to read video stabilization modes", e);
|
|
5178
|
+
}
|
|
5179
|
+
return false;
|
|
5180
|
+
}
|
|
4867
5181
|
}
|
|
@@ -27,6 +27,7 @@ public class CameraSessionConfiguration {
|
|
|
27
27
|
private boolean isCentered = false;
|
|
28
28
|
private String videoQuality;
|
|
29
29
|
private String videoCodec = "avc1";
|
|
30
|
+
private String videoStabilizationMode = "off";
|
|
30
31
|
private boolean enablePhysicalDeviceSelection = false;
|
|
31
32
|
private boolean barcodeScannerEnabled = false;
|
|
32
33
|
|
|
@@ -160,6 +161,14 @@ public class CameraSessionConfiguration {
|
|
|
160
161
|
this.videoCodec = codec != null ? codec : "avc1";
|
|
161
162
|
}
|
|
162
163
|
|
|
164
|
+
public String getVideoStabilizationMode() {
|
|
165
|
+
return videoStabilizationMode;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
public void setVideoStabilizationMode(String mode) {
|
|
169
|
+
this.videoStabilizationMode = mode != null ? mode : "off";
|
|
170
|
+
}
|
|
171
|
+
|
|
163
172
|
public boolean isPhysicalDeviceSelectionEnabled() {
|
|
164
173
|
return enablePhysicalDeviceSelection;
|
|
165
174
|
}
|