1 /* 2 * Copyright 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.camera.core.internal; 18 19 import static androidx.camera.core.CameraEffect.IMAGE_CAPTURE; 20 import static androidx.camera.core.CameraEffect.PREVIEW; 21 import static androidx.camera.core.CameraEffect.VIDEO_CAPTURE; 22 import static androidx.camera.core.DynamicRange.BIT_DEPTH_10_BIT; 23 import static androidx.camera.core.DynamicRange.ENCODING_SDR; 24 import static androidx.camera.core.DynamicRange.ENCODING_UNSPECIFIED; 25 import static androidx.camera.core.ImageCapture.OUTPUT_FORMAT_JPEG_ULTRA_HDR; 26 import static androidx.camera.core.ImageCapture.OUTPUT_FORMAT_RAW; 27 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_OUTPUT_FORMAT; 28 import static androidx.camera.core.impl.StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED; 29 import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE; 30 import static androidx.camera.core.impl.UseCaseConfig.OPTION_TARGET_HIGH_SPEED_FRAME_RATE; 31 import static androidx.camera.core.impl.utils.TransformUtils.rectToSize; 32 import static androidx.camera.core.processing.TargetUtils.getNumberOfTargets; 33 import static androidx.camera.core.streamsharing.StreamSharing.getCaptureTypes; 34 import static androidx.camera.core.streamsharing.StreamSharing.isStreamSharing; 35 import static androidx.core.util.Preconditions.checkArgument; 36 import static androidx.core.util.Preconditions.checkNotNull; 37 import static androidx.core.util.Preconditions.checkState; 38 39 import static java.util.Collections.emptyList; 40 import static java.util.Objects.requireNonNull; 41 42 import android.graphics.Matrix; 43 import android.graphics.Rect; 44 import android.graphics.RectF; 45 import android.graphics.SurfaceTexture; 46 import android.util.Log; 47 import android.util.Pair; 48 import android.util.Range; 49 import android.util.Size; 50 import android.view.Surface; 51 52 import androidx.annotation.GuardedBy; 53 import androidx.annotation.VisibleForTesting; 54 import androidx.camera.core.Camera; 55 import androidx.camera.core.CameraControl; 56 import androidx.camera.core.CameraEffect; 57 import androidx.camera.core.CameraInfo; 58 import androidx.camera.core.CameraSelector; 59 import androidx.camera.core.CompositionSettings; 60 import androidx.camera.core.DynamicRange; 61 import androidx.camera.core.ImageCapture; 62 import androidx.camera.core.Logger; 63 import androidx.camera.core.Preview; 64 import androidx.camera.core.UseCase; 65 import androidx.camera.core.ViewPort; 66 import androidx.camera.core.concurrent.CameraCoordinator; 67 import androidx.camera.core.impl.AdapterCameraInfo; 68 import androidx.camera.core.impl.AdapterCameraInternal; 69 import androidx.camera.core.impl.AttachedSurfaceInfo; 70 import androidx.camera.core.impl.CameraConfig; 71 import androidx.camera.core.impl.CameraConfigs; 72 import androidx.camera.core.impl.CameraControlInternal; 73 import androidx.camera.core.impl.CameraDeviceSurfaceManager; 74 import androidx.camera.core.impl.CameraInfoInternal; 75 import androidx.camera.core.impl.CameraInternal; 76 import androidx.camera.core.impl.CameraMode; 77 import androidx.camera.core.impl.Config; 78 import androidx.camera.core.impl.Identifier; 79 import androidx.camera.core.impl.MutableOptionsBundle; 80 import androidx.camera.core.impl.PreviewConfig; 81 import androidx.camera.core.impl.SessionConfig; 82 import androidx.camera.core.impl.SessionProcessor; 83 import androidx.camera.core.impl.StreamSpec; 84 import androidx.camera.core.impl.SurfaceConfig; 85 import androidx.camera.core.impl.UseCaseConfig; 86 import androidx.camera.core.impl.UseCaseConfigFactory; 87 import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType; 88 import androidx.camera.core.impl.stabilization.StabilizationMode; 89 import androidx.camera.core.impl.utils.executor.CameraXExecutors; 90 import androidx.camera.core.internal.compat.workaround.StreamSharingForceEnabler; 91 import androidx.camera.core.streamsharing.StreamSharing; 92 import androidx.core.util.Preconditions; 93 94 import com.google.auto.value.AutoValue; 95 96 import org.jspecify.annotations.NonNull; 97 import org.jspecify.annotations.Nullable; 98 99 import java.util.ArrayList; 100 import java.util.Arrays; 101 import java.util.Collection; 102 import java.util.Collections; 103 import java.util.HashMap; 104 import java.util.HashSet; 105 import java.util.LinkedHashSet; 106 import java.util.List; 107 import java.util.Map; 108 import java.util.Objects; 109 import java.util.Set; 110 111 /** 112 * A {@link CameraInternal} adapter which checks that the UseCases to make sure that the resolutions 113 * and image formats can be supported. 114 */ 115 public final class CameraUseCaseAdapter implements Camera { 116 private final @NonNull AdapterCameraInternal mCameraInternal; 117 private final @Nullable AdapterCameraInternal mSecondaryCameraInternal; 118 private final CameraDeviceSurfaceManager mCameraDeviceSurfaceManager; 119 private final UseCaseConfigFactory mUseCaseConfigFactory; 120 121 private static final String TAG = "CameraUseCaseAdapter"; 122 123 private final CameraId mId; 124 125 // UseCases from the app. This does not include internal UseCases created by CameraX. 126 @GuardedBy("mLock") 127 private final List<UseCase> mAppUseCases = new ArrayList<>(); 128 // UseCases sent to the camera including internal UseCases created by CameraX. 129 @GuardedBy("mLock") 130 private final List<UseCase> mCameraUseCases = new ArrayList<>(); 131 132 @GuardedBy("mLock") 133 private final CameraCoordinator mCameraCoordinator; 134 135 @GuardedBy("mLock") 136 private @Nullable ViewPort mViewPort; 137 138 @GuardedBy("mLock") 139 private @NonNull List<CameraEffect> mEffects = emptyList(); 140 141 @GuardedBy("mLock") 142 private @NonNull Range<Integer> mTargetHighSpeedFps = FRAME_RATE_RANGE_UNSPECIFIED; 143 144 // Additional configs to apply onto the UseCases when added to this Camera 145 @GuardedBy("mLock") 146 private final @NonNull CameraConfig mCameraConfig; 147 148 private final Object mLock = new Object(); 149 150 // This indicates whether or not the UseCases that have been added to this adapter has 151 // actually been attached to the CameraInternal instance. 152 @GuardedBy("mLock") 153 private boolean mAttached = true; 154 155 // This holds the cached Interop config from CameraControlInternal. 156 @GuardedBy("mLock") 157 private Config mInteropConfig = null; 158 159 // The placeholder UseCase created to meet combination criteria for Extensions. e.g. When 160 // Extensions require both Preview and ImageCapture and app only provides one of them, 161 // CameraX will create the other and track it with this variable. 162 @GuardedBy("mLock") 163 private @Nullable UseCase mPlaceholderForExtensions; 164 // Current StreamSharing parent UseCase if exists. 165 @GuardedBy("mLock") 166 private @Nullable StreamSharing mStreamSharing; 167 168 private final @NonNull CompositionSettings mCompositionSettings; 169 private final @NonNull CompositionSettings mSecondaryCompositionSettings; 170 private final StreamSharingForceEnabler mStreamSharingForceEnabler = 171 new StreamSharingForceEnabler(); 172 173 /** 174 * Create a new {@link CameraUseCaseAdapter} instance. 175 * 176 * @param camera The camera that is wrapped. 177 * @param cameraCoordinator Camera coordinator that exposes concurrent camera mode. 178 * @param cameraDeviceSurfaceManager A class that checks for whether a specific camera 179 * can support the set of Surface with set resolutions. 180 * @param useCaseConfigFactory UseCase config factory that exposes configuration for 181 * each UseCase. 182 */ CameraUseCaseAdapter(@onNull CameraInternal camera, @NonNull CameraCoordinator cameraCoordinator, @NonNull CameraDeviceSurfaceManager cameraDeviceSurfaceManager, @NonNull UseCaseConfigFactory useCaseConfigFactory)183 public CameraUseCaseAdapter(@NonNull CameraInternal camera, 184 @NonNull CameraCoordinator cameraCoordinator, 185 @NonNull CameraDeviceSurfaceManager cameraDeviceSurfaceManager, 186 @NonNull UseCaseConfigFactory useCaseConfigFactory) { 187 this(camera, 188 null, 189 new AdapterCameraInfo(camera.getCameraInfoInternal(), 190 CameraConfigs.defaultConfig()), 191 null, 192 CompositionSettings.DEFAULT, 193 CompositionSettings.DEFAULT, 194 cameraCoordinator, 195 cameraDeviceSurfaceManager, 196 useCaseConfigFactory); 197 } 198 199 /** 200 * Create a new {@link CameraUseCaseAdapter} instance. 201 * 202 * @param camera The camera that is wrapped. 203 * @param secondaryCamera The secondary camera that is wrapped. 204 * @param adapterCameraInfo The {@link AdapterCameraInfo} that contains the extra 205 * information to configure the {@link CameraInternal} when 206 * attaching the uses cases of this adapter to the camera. 207 * @param secondaryAdapterCameraInfo The {@link AdapterCameraInfo} of secondary camera. 208 * @param compositionSettings The composition settings that will be used to configure the 209 * camera. 210 * @param secondaryCompositionSettings The composition settings that will be used to configure 211 * the secondary camera. 212 * @param cameraCoordinator Camera coordinator that exposes concurrent camera mode. 213 * @param cameraDeviceSurfaceManager A class that checks for whether a specific camera 214 * can support the set of Surface with set resolutions. 215 * @param useCaseConfigFactory UseCase config factory that exposes configuration for 216 * each UseCase. 217 */ CameraUseCaseAdapter( @onNull CameraInternal camera, @Nullable CameraInternal secondaryCamera, @NonNull AdapterCameraInfo adapterCameraInfo, @Nullable AdapterCameraInfo secondaryAdapterCameraInfo, @NonNull CompositionSettings compositionSettings, @NonNull CompositionSettings secondaryCompositionSettings, @NonNull CameraCoordinator cameraCoordinator, @NonNull CameraDeviceSurfaceManager cameraDeviceSurfaceManager, @NonNull UseCaseConfigFactory useCaseConfigFactory)218 public CameraUseCaseAdapter( 219 @NonNull CameraInternal camera, 220 @Nullable CameraInternal secondaryCamera, 221 @NonNull AdapterCameraInfo adapterCameraInfo, 222 @Nullable AdapterCameraInfo secondaryAdapterCameraInfo, 223 @NonNull CompositionSettings compositionSettings, 224 @NonNull CompositionSettings secondaryCompositionSettings, 225 @NonNull CameraCoordinator cameraCoordinator, 226 @NonNull CameraDeviceSurfaceManager cameraDeviceSurfaceManager, 227 @NonNull UseCaseConfigFactory useCaseConfigFactory) { 228 mCameraConfig = adapterCameraInfo.getCameraConfig(); 229 mCameraInternal = new AdapterCameraInternal(camera, adapterCameraInfo); 230 if (secondaryCamera != null && secondaryAdapterCameraInfo != null) { 231 mSecondaryCameraInternal = new AdapterCameraInternal(secondaryCamera, 232 secondaryAdapterCameraInfo); 233 } else { 234 mSecondaryCameraInternal = null; 235 } 236 mCompositionSettings = compositionSettings; 237 mSecondaryCompositionSettings = secondaryCompositionSettings; 238 mCameraCoordinator = cameraCoordinator; 239 mCameraDeviceSurfaceManager = cameraDeviceSurfaceManager; 240 mUseCaseConfigFactory = useCaseConfigFactory; 241 mId = generateCameraId(adapterCameraInfo, secondaryAdapterCameraInfo); 242 } 243 244 /** 245 * Generate a identifier for the {@link AdapterCameraInfo}. 246 */ generateCameraId( @onNull AdapterCameraInfo primaryCameraInfo, @Nullable AdapterCameraInfo secondaryCameraInfo)247 public static @NonNull CameraId generateCameraId( 248 @NonNull AdapterCameraInfo primaryCameraInfo, 249 @Nullable AdapterCameraInfo secondaryCameraInfo) { 250 return CameraId.create( 251 primaryCameraInfo.getCameraId() 252 + (secondaryCameraInfo == null ? "" : secondaryCameraInfo.getCameraId()), 253 primaryCameraInfo.getCameraConfig().getCompatibilityId()); 254 } 255 256 /** 257 * Returns the identifier for this {@link CameraUseCaseAdapter}. 258 */ getCameraId()259 public @NonNull CameraId getCameraId() { 260 return mId; 261 } 262 263 /** 264 * Returns true if the {@link CameraUseCaseAdapter} is an equivalent camera. 265 */ isEquivalent(@onNull CameraUseCaseAdapter cameraUseCaseAdapter)266 public boolean isEquivalent(@NonNull CameraUseCaseAdapter cameraUseCaseAdapter) { 267 return getCameraId().equals(cameraUseCaseAdapter.getCameraId()); 268 } 269 270 /** 271 * Set the viewport that will be used for the {@link UseCase} attached to the camera. 272 */ setViewPort(@ullable ViewPort viewPort)273 public void setViewPort(@Nullable ViewPort viewPort) { 274 synchronized (mLock) { 275 mViewPort = viewPort; 276 } 277 } 278 279 /** 280 * Set the effects that will be used for the {@link UseCase} attached to the camera. 281 */ setEffects(@ullable List<CameraEffect> effects)282 public void setEffects(@Nullable List<CameraEffect> effects) { 283 synchronized (mLock) { 284 mEffects = effects; 285 } 286 } 287 288 /** 289 * Set the target high speed frame rate that will be used for the {@link UseCase} attached to 290 * the camera. 291 */ setTargetHighSpeedFrameRate(@onNull Range<Integer> frameRate)292 public void setTargetHighSpeedFrameRate(@NonNull Range<Integer> frameRate) { 293 synchronized (mLock) { 294 mTargetHighSpeedFps = frameRate; 295 } 296 } 297 298 /** 299 * Add the specified collection of {@link UseCase} to the adapter with dual camera support. 300 * 301 * @throws CameraException Thrown if the combination of newly added UseCases and the 302 * currently added UseCases exceed the capability of the camera. 303 */ addUseCases(@onNull Collection<UseCase> appUseCasesToAdd)304 public void addUseCases(@NonNull Collection<UseCase> appUseCasesToAdd) throws CameraException { 305 synchronized (mLock) { 306 // Configure the CameraConfig when binding 307 mCameraInternal.setExtendedConfig(mCameraConfig); 308 if (mSecondaryCameraInternal != null) { 309 mSecondaryCameraInternal.setExtendedConfig(mCameraConfig); 310 } 311 Set<UseCase> appUseCases = new LinkedHashSet<>(mAppUseCases); 312 //TODO(b/266641900): must be LinkedHashSet otherwise ExistingActivityLifecycleTest 313 // fails due to a camera-pipe integration bug. 314 appUseCases.addAll(appUseCasesToAdd); 315 try { 316 updateUseCases(appUseCases, 317 mSecondaryCameraInternal != null, mSecondaryCameraInternal != null); 318 } catch (IllegalArgumentException e) { 319 throw new CameraException(e); 320 } 321 } 322 } 323 324 /** 325 * Remove the specified collection of {@link UseCase} from the adapter. 326 */ removeUseCases(@onNull Collection<UseCase> useCasesToRemove)327 public void removeUseCases(@NonNull Collection<UseCase> useCasesToRemove) { 328 synchronized (mLock) { 329 Set<UseCase> appUseCases = new LinkedHashSet<>(mAppUseCases); 330 appUseCases.removeAll(useCasesToRemove); 331 updateUseCases(appUseCases, 332 mSecondaryCameraInternal != null, mSecondaryCameraInternal != null); 333 } 334 } 335 336 /** 337 * Updates the states based the new app UseCases. 338 */ updateUseCases(@onNull Collection<UseCase> appUseCases)339 void updateUseCases(@NonNull Collection<UseCase> appUseCases) { 340 updateUseCases(appUseCases, /*applyStreamSharing*/false, /*isDualCamera*/false); 341 } 342 343 /** 344 * Updates the states based the new app UseCases. 345 * 346 * <p> This method calculates the new camera UseCases based on the input and the current state, 347 * attach/detach the camera UseCases, and save the updated state in following member variables: 348 * {@link #mCameraUseCases}, {@link #mAppUseCases} and {@link #mPlaceholderForExtensions}. 349 * 350 * @throws IllegalArgumentException if the UseCase combination is not supported. In that case, 351 * it will not update the internal states. 352 */ updateUseCases(@onNull Collection<UseCase> appUseCases, boolean applyStreamSharing, boolean isDualCamera)353 void updateUseCases(@NonNull Collection<UseCase> appUseCases, 354 boolean applyStreamSharing, 355 boolean isDualCamera) { 356 synchronized (mLock) { 357 checkUnsupportedFeatureCombinationAndThrow(appUseCases); 358 359 // Force enable StreamSharing for Extensions to support VideoCapture. This means that 360 // applyStreamSharing is set to true when the use case combination contains 361 // VideoCapture and Extensions is enabled. 362 if (!applyStreamSharing && shouldForceEnableStreamSharing(appUseCases)) { 363 updateUseCases(appUseCases, /*applyStreamSharing*/true, isDualCamera); 364 return; 365 } 366 367 // Calculate camera UseCases and keep the result in local variables in case they don't 368 // meet the stream combination rules. 369 StreamSharing streamSharing = createOrReuseStreamSharing(appUseCases, 370 applyStreamSharing); 371 UseCase placeholderForExtensions = calculatePlaceholderForExtensions(appUseCases, 372 streamSharing); 373 Collection<UseCase> cameraUseCases = 374 calculateCameraUseCases(appUseCases, placeholderForExtensions, streamSharing); 375 376 // Calculate the action items. 377 List<UseCase> cameraUseCasesToAttach = new ArrayList<>(cameraUseCases); 378 cameraUseCasesToAttach.removeAll(mCameraUseCases); 379 List<UseCase> cameraUseCasesToKeep = new ArrayList<>(cameraUseCases); 380 cameraUseCasesToKeep.retainAll(mCameraUseCases); 381 List<UseCase> cameraUseCasesToDetach = new ArrayList<>(mCameraUseCases); 382 cameraUseCasesToDetach.removeAll(cameraUseCases); 383 384 // Calculate suggested resolutions. This step throws exception if the camera UseCases 385 // fails the supported stream combination rules. 386 Map<UseCase, ConfigPair> configs = getConfigs(cameraUseCasesToAttach, 387 mCameraConfig.getUseCaseConfigFactory(), mUseCaseConfigFactory, 388 mTargetHighSpeedFps); 389 Map<UseCase, StreamSpec> primaryStreamSpecMap; 390 Map<UseCase, StreamSpec> secondaryStreamSpecMap = Collections.emptyMap(); 391 try { 392 primaryStreamSpecMap = calculateSuggestedStreamSpecs( 393 getCameraMode(), 394 mCameraInternal.getCameraInfoInternal(), cameraUseCasesToAttach, 395 cameraUseCasesToKeep, configs); 396 if (mSecondaryCameraInternal != null) { 397 secondaryStreamSpecMap = calculateSuggestedStreamSpecs( 398 getCameraMode(), 399 requireNonNull(mSecondaryCameraInternal).getCameraInfoInternal(), 400 cameraUseCasesToAttach, 401 cameraUseCasesToKeep, configs); 402 } 403 // TODO(b/265704882): enable stream sharing for LEVEL_3 and high preview 404 // resolution. Throw exception here if (applyStreamSharing == false), both video 405 // and preview are used and preview resolution is lower than user configuration. 406 } catch (IllegalArgumentException exception) { 407 // TODO(b/270187871): instead of catch and retry, we can check UseCase 408 // combination directly with #isUseCasesCombinationSupported(). However 409 // calculateSuggestedStreamSpecs() is currently slow. We will do it after it's 410 // optimized 411 // Only allow StreamSharing for non-concurrent mode. 412 if (!applyStreamSharing && !hasExtension() 413 && mCameraCoordinator.getCameraOperatingMode() 414 != CameraCoordinator.CAMERA_OPERATING_MODE_CONCURRENT) { 415 // Try again and see if StreamSharing resolves the issue. 416 updateUseCases(appUseCases, /*applyStreamSharing*/true, isDualCamera); 417 return; 418 } else { 419 // If StreamSharing already on or not enabled, throw exception. 420 throw exception; 421 } 422 } 423 424 // Update properties. 425 updateViewPortAndSensorToBufferMatrix(primaryStreamSpecMap, cameraUseCases); 426 updateEffects(mEffects, cameraUseCases, appUseCases); 427 428 // Detach unused UseCases. 429 for (UseCase useCase : cameraUseCasesToDetach) { 430 useCase.unbindFromCamera(mCameraInternal); 431 } 432 mCameraInternal.detachUseCases(cameraUseCasesToDetach); 433 434 // Detach unused UseCases for secondary camera. 435 if (mSecondaryCameraInternal != null) { 436 for (UseCase useCase : cameraUseCasesToDetach) { 437 useCase.unbindFromCamera(requireNonNull(mSecondaryCameraInternal)); 438 } 439 requireNonNull(mSecondaryCameraInternal) 440 .detachUseCases(cameraUseCasesToDetach); 441 } 442 443 // Update StreamSpec for UseCases to keep. 444 if (cameraUseCasesToDetach.isEmpty()) { 445 // Only do this if we are not removing UseCase, because updating SessionConfig 446 // when removing UseCases may lead to flickering. 447 for (UseCase useCase : cameraUseCasesToKeep) { 448 // Assume secondary camera will not have implementation options in dual camera. 449 if (primaryStreamSpecMap.containsKey(useCase)) { 450 StreamSpec newStreamSpec = primaryStreamSpecMap.get(useCase); 451 Config config = newStreamSpec.getImplementationOptions(); 452 if (config != null && hasImplementationOptionChanged(newStreamSpec, 453 useCase.getSessionConfig())) { 454 useCase.updateSuggestedStreamSpecImplementationOptions(config); 455 if (mAttached) { 456 mCameraInternal.onUseCaseUpdated(useCase); 457 if (mSecondaryCameraInternal != null) { 458 requireNonNull(mSecondaryCameraInternal) 459 .onUseCaseUpdated(useCase); 460 } 461 } 462 } 463 } 464 } 465 } 466 467 // Attach new UseCases. 468 for (UseCase useCase : cameraUseCasesToAttach) { 469 ConfigPair configPair = requireNonNull(configs.get(useCase)); 470 if (mSecondaryCameraInternal != null) { 471 useCase.bindToCamera(mCameraInternal, 472 requireNonNull(mSecondaryCameraInternal), 473 configPair.mExtendedConfig, 474 configPair.mCameraConfig); 475 useCase.updateSuggestedStreamSpec( 476 Preconditions.checkNotNull(primaryStreamSpecMap.get(useCase)), 477 secondaryStreamSpecMap.get(useCase)); 478 } else { 479 useCase.bindToCamera(mCameraInternal, 480 null, 481 configPair.mExtendedConfig, 482 configPair.mCameraConfig); 483 useCase.updateSuggestedStreamSpec( 484 Preconditions.checkNotNull(primaryStreamSpecMap.get(useCase)), 485 null); 486 } 487 } 488 if (mAttached) { 489 mCameraInternal.attachUseCases(cameraUseCasesToAttach); 490 if (mSecondaryCameraInternal != null) { 491 requireNonNull(mSecondaryCameraInternal) 492 .attachUseCases(cameraUseCasesToAttach); 493 } 494 } 495 496 // Once UseCases are detached/attached, notify the camera. 497 for (UseCase useCase : cameraUseCasesToAttach) { 498 useCase.notifyState(); 499 } 500 501 // The changes are successful. Update the states of this class. 502 mAppUseCases.clear(); 503 mAppUseCases.addAll(appUseCases); 504 mCameraUseCases.clear(); 505 mCameraUseCases.addAll(cameraUseCases); 506 mPlaceholderForExtensions = placeholderForExtensions; 507 mStreamSharing = streamSharing; 508 } 509 } 510 shouldForceEnableStreamSharing(@onNull Collection<UseCase> appUseCases)511 private boolean shouldForceEnableStreamSharing(@NonNull Collection<UseCase> appUseCases) { 512 if (hasExtension() && hasVideoCapture(appUseCases)) { 513 return true; 514 } 515 516 return mStreamSharingForceEnabler.shouldForceEnableStreamSharing( 517 mCameraInternal.getCameraInfoInternal().getCameraId(), appUseCases); 518 } 519 520 /** 521 * Return true if the given StreamSpec has any option with a different value than that 522 * of the given sessionConfig. 523 */ hasImplementationOptionChanged( StreamSpec streamSpec, SessionConfig sessionConfig)524 private static boolean hasImplementationOptionChanged( 525 StreamSpec streamSpec, 526 SessionConfig sessionConfig) { 527 Config newStreamSpecOptions = streamSpec.getImplementationOptions(); 528 Config sessionConfigOptions = sessionConfig.getImplementationOptions(); 529 if (newStreamSpecOptions.listOptions().size() 530 != sessionConfig.getImplementationOptions().listOptions().size()) { 531 return true; 532 } 533 for (Config.Option<?> newOption : newStreamSpecOptions.listOptions()) { 534 if (!sessionConfigOptions.containsOption(newOption) 535 || !Objects.equals(sessionConfigOptions.retrieveOption(newOption), 536 newStreamSpecOptions.retrieveOption(newOption))) { 537 return true; 538 } 539 } 540 return false; 541 } 542 getCameraMode()543 private @CameraMode.Mode int getCameraMode() { 544 synchronized (mLock) { 545 if (mCameraCoordinator.getCameraOperatingMode() 546 == CameraCoordinator.CAMERA_OPERATING_MODE_CONCURRENT) { 547 return CameraMode.CONCURRENT_CAMERA; 548 } 549 } 550 551 // TODO(b/271199876): return ULTRA_HIGH_RESOLUTION_CAMERA when it can be enabled via 552 // Camera2Interop 553 554 return CameraMode.DEFAULT; 555 } 556 hasExtension()557 private boolean hasExtension() { 558 synchronized (mLock) { 559 return mCameraConfig.getSessionProcessor(null) != null; 560 } 561 } 562 563 /** 564 * Returns {@link UseCase}s qualified for {@link StreamSharing}. 565 */ getStreamSharingChildren(@onNull Collection<UseCase> appUseCases, boolean forceSharingToPreviewAndVideo)566 private @NonNull Set<UseCase> getStreamSharingChildren(@NonNull Collection<UseCase> appUseCases, 567 boolean forceSharingToPreviewAndVideo) { 568 Set<UseCase> children = new HashSet<>(); 569 int sharingTargets = getSharingTargets(forceSharingToPreviewAndVideo); 570 for (UseCase useCase : appUseCases) { 571 checkArgument(!isStreamSharing(useCase), "Only support one level of sharing for now."); 572 if (useCase.isEffectTargetsSupported(sharingTargets)) { 573 children.add(useCase); 574 } 575 } 576 return children; 577 } 578 579 @CameraEffect.Targets getSharingTargets(boolean forceSharingToPreviewAndVideo)580 private int getSharingTargets(boolean forceSharingToPreviewAndVideo) { 581 synchronized (mLock) { 582 // Find the only effect that has more than one targets. 583 CameraEffect sharingEffect = null; 584 for (CameraEffect effect : mEffects) { 585 if (getNumberOfTargets(effect.getTargets()) > 1) { 586 checkState(sharingEffect == null, "Can only have one sharing effect."); 587 sharingEffect = effect; 588 } 589 } 590 int sharingTargets = sharingEffect == null ? 0 : sharingEffect.getTargets(); 591 592 // Share stream to preview and video capture if the device requires it. 593 if (forceSharingToPreviewAndVideo) { 594 sharingTargets |= PREVIEW | VIDEO_CAPTURE; 595 } 596 return sharingTargets; 597 } 598 } 599 600 /** 601 * Creates a new {@link StreamSharing} or returns the existing one. 602 * 603 * <p>Returns the existing {@link StreamSharing} if the children have not changed. 604 * Otherwise, create a new {@link StreamSharing} and return. 605 * 606 * <p>Returns null when there is no need to share the stream, or the combination of children 607 * UseCase are invalid(e.g. contains more than 1 UseCase per type). 608 */ createOrReuseStreamSharing( @onNull Collection<UseCase> appUseCases, boolean forceSharingToPreviewAndVideo)609 private @Nullable StreamSharing createOrReuseStreamSharing( 610 @NonNull Collection<UseCase> appUseCases, boolean forceSharingToPreviewAndVideo) { 611 synchronized (mLock) { 612 Set<UseCase> newChildren = getStreamSharingChildren(appUseCases, 613 forceSharingToPreviewAndVideo); 614 if (newChildren.size() < 2) { 615 // No need to share the stream for 1 or less children. Except the case that 616 // StreamSharing is enabled for Extensions to support VideoCapture. 617 if (!(hasExtension() && hasVideoCapture(newChildren))) { 618 return null; 619 } 620 } 621 if (mStreamSharing != null && mStreamSharing.getChildren().equals(newChildren)) { 622 // Returns the current instance if the new children equals the old. 623 return requireNonNull(mStreamSharing); 624 } 625 626 if (!isStreamSharingChildrenCombinationValid(newChildren)) { 627 return null; 628 } 629 630 return new StreamSharing(mCameraInternal, 631 mSecondaryCameraInternal, 632 mCompositionSettings, 633 mSecondaryCompositionSettings, 634 newChildren, 635 mUseCaseConfigFactory); 636 } 637 } 638 639 /** 640 * Returns true if the children are valid for {@link StreamSharing}. 641 */ isStreamSharingChildrenCombinationValid( @onNull Collection<UseCase> children)642 static boolean isStreamSharingChildrenCombinationValid( 643 @NonNull Collection<UseCase> children) { 644 int[] validChildrenTypes = {PREVIEW, VIDEO_CAPTURE, IMAGE_CAPTURE}; 645 Set<Integer> childrenTypes = new HashSet<>(); 646 // Loop through all children and add supported types. 647 for (UseCase child : children) { 648 for (int type : validChildrenTypes) { 649 if (child.isEffectTargetsSupported(type)) { 650 if (childrenTypes.contains(type)) { 651 // Return false if there are 2 use case supporting the same type. 652 return false; 653 } 654 childrenTypes.add(type); 655 } 656 } 657 } 658 return true; 659 } 660 661 /** 662 * Returns {@link UseCase} that connects to the camera. 663 */ calculateCameraUseCases(@onNull Collection<UseCase> appUseCases, @Nullable UseCase placeholderForExtensions, @Nullable StreamSharing streamSharing)664 static Collection<UseCase> calculateCameraUseCases(@NonNull Collection<UseCase> appUseCases, 665 @Nullable UseCase placeholderForExtensions, 666 @Nullable StreamSharing streamSharing) { 667 List<UseCase> useCases = new ArrayList<>(appUseCases); 668 if (placeholderForExtensions != null) { 669 useCases.add(placeholderForExtensions); 670 } 671 if (streamSharing != null) { 672 useCases.add(streamSharing); 673 useCases.removeAll(streamSharing.getChildren()); 674 } 675 return useCases; 676 } 677 678 /** 679 * Returns the UseCases currently associated with the adapter. 680 * 681 * <p> The UseCases may or may not be actually attached to the underlying 682 * {@link CameraInternal} instance. 683 */ getUseCases()684 public @NonNull List<UseCase> getUseCases() { 685 synchronized (mLock) { 686 return new ArrayList<>(mAppUseCases); 687 } 688 } 689 690 @VisibleForTesting getCameraUseCases()691 public @NonNull Collection<UseCase> getCameraUseCases() { 692 synchronized (mLock) { 693 return new ArrayList<>(mCameraUseCases); 694 } 695 } 696 697 /** 698 * Attach the UseCases to the {@link CameraInternal} camera so that the UseCases can receive 699 * data if they are active. 700 * 701 * <p> This will start the underlying {@link CameraInternal} instance. 702 * 703 * <p> This will restore the cached Interop config to the {@link CameraInternal}. 704 */ attachUseCases()705 public void attachUseCases() { 706 synchronized (mLock) { 707 if (!mAttached) { 708 // Ensure the current opening camera has the right camera config. 709 if (!mCameraUseCases.isEmpty()) { 710 mCameraInternal.setExtendedConfig(mCameraConfig); 711 if (mSecondaryCameraInternal != null) { 712 mSecondaryCameraInternal.setExtendedConfig(mCameraConfig); 713 } 714 } 715 mCameraInternal.attachUseCases(mCameraUseCases); 716 if (mSecondaryCameraInternal != null) { 717 mSecondaryCameraInternal.attachUseCases(mCameraUseCases); 718 } 719 restoreInteropConfig(); 720 721 // Notify to update the use case's active state because it may be cleared if the 722 // use case was ever detached from a camera previously. 723 for (UseCase useCase : mCameraUseCases) { 724 useCase.notifyState(); 725 } 726 727 mAttached = true; 728 } 729 } 730 } 731 732 /** 733 * When in active resuming mode, it will actively retry opening the camera periodically to 734 * resume regardless of the camera availability if the camera is interrupted in 735 * OPEN/OPENING/PENDING_OPEN state. 736 * 737 * When not in actively resuming mode, it will retry opening camera only when camera 738 * becomes available. 739 */ setActiveResumingMode(boolean enabled)740 public void setActiveResumingMode(boolean enabled) { 741 mCameraInternal.setActiveResumingMode(enabled); 742 } 743 744 /** 745 * Detach the UseCases from the {@link CameraInternal} so that the UseCases stop receiving data. 746 * 747 * <p> This will stop the underlying {@link CameraInternal} instance. 748 * 749 * <p> This will cache the Interop config from the {@link CameraInternal}. 750 */ detachUseCases()751 public void detachUseCases() { 752 synchronized (mLock) { 753 if (mAttached) { 754 mCameraInternal.detachUseCases(new ArrayList<>(mCameraUseCases)); 755 if (mSecondaryCameraInternal != null) { 756 mSecondaryCameraInternal.detachUseCases(new ArrayList<>(mCameraUseCases)); 757 } 758 cacheInteropConfig(); 759 mAttached = false; 760 } 761 } 762 } 763 764 /** 765 * Restores the cached InteropConfig to the camera. 766 */ restoreInteropConfig()767 private void restoreInteropConfig() { 768 synchronized (mLock) { 769 if (mInteropConfig != null) { 770 mCameraInternal.getCameraControlInternal().addInteropConfig(mInteropConfig); 771 } 772 } 773 } 774 775 /** 776 * Caches and clears the InteropConfig from the camera. 777 */ cacheInteropConfig()778 private void cacheInteropConfig() { 779 synchronized (mLock) { 780 CameraControlInternal cameraControlInternal = 781 mCameraInternal.getCameraControlInternal(); 782 mInteropConfig = cameraControlInternal.getInteropConfig(); 783 cameraControlInternal.clearInteropConfig(); 784 } 785 } 786 calculateSuggestedStreamSpecs( @ameraMode.Mode int cameraMode, @NonNull CameraInfoInternal cameraInfoInternal, @NonNull Collection<UseCase> newUseCases, @NonNull Collection<UseCase> currentUseCases, @NonNull Map<UseCase, ConfigPair> configPairMap)787 private Map<UseCase, StreamSpec> calculateSuggestedStreamSpecs( 788 @CameraMode.Mode int cameraMode, 789 @NonNull CameraInfoInternal cameraInfoInternal, 790 @NonNull Collection<UseCase> newUseCases, 791 @NonNull Collection<UseCase> currentUseCases, 792 @NonNull Map<UseCase, ConfigPair> configPairMap) { 793 List<AttachedSurfaceInfo> existingSurfaces = new ArrayList<>(); 794 String cameraId = cameraInfoInternal.getCameraId(); 795 Map<UseCase, StreamSpec> suggestedStreamSpecs = new HashMap<>(); 796 Map<AttachedSurfaceInfo, UseCase> surfaceInfoUseCaseMap = new HashMap<>(); 797 798 // Get resolution for current use cases. 799 for (UseCase useCase : currentUseCases) { 800 SurfaceConfig surfaceConfig = 801 mCameraDeviceSurfaceManager.transformSurfaceConfig( 802 cameraMode, 803 cameraId, 804 useCase.getImageFormat(), 805 useCase.getAttachedSurfaceResolution()); 806 AttachedSurfaceInfo attachedSurfaceInfo = AttachedSurfaceInfo.create(surfaceConfig, 807 useCase.getImageFormat(), useCase.getAttachedSurfaceResolution(), 808 Preconditions.checkNotNull(useCase.getAttachedStreamSpec()).getDynamicRange(), 809 getCaptureTypes(useCase), 810 useCase.getAttachedStreamSpec().getImplementationOptions(), 811 useCase.getCurrentConfig().getTargetFrameRate(null), 812 Preconditions.checkNotNull( 813 useCase.getCurrentConfig().getTargetHighSpeedFrameRate( 814 FRAME_RATE_RANGE_UNSPECIFIED))); 815 existingSurfaces.add(attachedSurfaceInfo); 816 surfaceInfoUseCaseMap.put(attachedSurfaceInfo, useCase); 817 suggestedStreamSpecs.put(useCase, useCase.getAttachedStreamSpec()); 818 } 819 820 // Calculate resolution for new use cases. 821 if (!newUseCases.isEmpty()) { 822 Map<UseCaseConfig<?>, UseCase> configToUseCaseMap = new HashMap<>(); 823 Map<UseCaseConfig<?>, List<Size>> configToSupportedSizesMap = new HashMap<>(); 824 Rect sensorRect; 825 try { 826 sensorRect = mCameraInternal.getCameraControlInternal().getSensorRect(); 827 } catch (NullPointerException e) { 828 // TODO(b/274531208): Remove the unnecessary SENSOR_INFO_ACTIVE_ARRAY_SIZE NPE 829 // check related code only which is used for robolectric tests 830 sensorRect = null; 831 } 832 SupportedOutputSizesSorter supportedOutputSizesSorter = new SupportedOutputSizesSorter( 833 cameraInfoInternal, 834 sensorRect != null ? rectToSize(sensorRect) : null); 835 boolean isPreviewStabilizationOn = false; 836 for (UseCase useCase : newUseCases) { 837 ConfigPair configPair = configPairMap.get(useCase); 838 // Combine with default configuration. 839 UseCaseConfig<?> combinedUseCaseConfig = 840 useCase.mergeConfigs(cameraInfoInternal, configPair.mExtendedConfig, 841 configPair.mCameraConfig); 842 configToUseCaseMap.put(combinedUseCaseConfig, useCase); 843 configToSupportedSizesMap.put(combinedUseCaseConfig, 844 supportedOutputSizesSorter.getSortedSupportedOutputSizes( 845 combinedUseCaseConfig)); 846 847 if (useCase.getCurrentConfig() instanceof PreviewConfig) { 848 isPreviewStabilizationOn = 849 ((PreviewConfig) useCase.getCurrentConfig()) 850 .getPreviewStabilizationMode() == StabilizationMode.ON; 851 } 852 } 853 854 // Get suggested stream specifications and update the use case session configuration 855 Pair<Map<UseCaseConfig<?>, StreamSpec>, Map<AttachedSurfaceInfo, StreamSpec>> 856 streamSpecMaps = 857 mCameraDeviceSurfaceManager.getSuggestedStreamSpecs( 858 cameraMode, 859 cameraId, existingSurfaces, 860 configToSupportedSizesMap, 861 isPreviewStabilizationOn, 862 hasVideoCapture(newUseCases)); 863 864 for (Map.Entry<UseCaseConfig<?>, UseCase> entry : configToUseCaseMap.entrySet()) { 865 suggestedStreamSpecs.put(entry.getValue(), 866 streamSpecMaps.first.get(entry.getKey())); 867 } 868 for (Map.Entry<AttachedSurfaceInfo, StreamSpec> entry : 869 streamSpecMaps.second.entrySet()) { 870 if (surfaceInfoUseCaseMap.containsKey(entry.getKey())) { 871 suggestedStreamSpecs.put(surfaceInfoUseCaseMap.get(entry.getKey()), 872 entry.getValue()); 873 } 874 } 875 } 876 return suggestedStreamSpecs; 877 } 878 879 @VisibleForTesting updateEffects(@onNull List<CameraEffect> effects, @NonNull Collection<UseCase> cameraUseCases, @NonNull Collection<UseCase> appUseCases)880 static void updateEffects(@NonNull List<CameraEffect> effects, 881 @NonNull Collection<UseCase> cameraUseCases, 882 @NonNull Collection<UseCase> appUseCases) { 883 // Match camera UseCases first. Apply the effect early in the pipeline if possible. 884 List<CameraEffect> unusedEffects = setEffectsOnUseCases(effects, cameraUseCases); 885 886 // Match unused effects with app only UseCases. 887 List<UseCase> appOnlyUseCases = new ArrayList<>(appUseCases); 888 appOnlyUseCases.removeAll(cameraUseCases); 889 unusedEffects = setEffectsOnUseCases(unusedEffects, appOnlyUseCases); 890 891 if (unusedEffects.size() > 0) { 892 Logger.w(TAG, "Unused effects: " + unusedEffects); 893 } 894 } 895 896 /** 897 * Sets effects on the given {@link UseCase} list and returns unused effects. 898 */ setEffectsOnUseCases( @onNull List<CameraEffect> effects, @NonNull Collection<UseCase> useCases)899 private static @NonNull List<CameraEffect> setEffectsOnUseCases( 900 @NonNull List<CameraEffect> effects, @NonNull Collection<UseCase> useCases) { 901 List<CameraEffect> unusedEffects = new ArrayList<>(effects); 902 for (UseCase useCase : useCases) { 903 useCase.setEffect(null); 904 for (CameraEffect effect : effects) { 905 if (useCase.isEffectTargetsSupported(effect.getTargets())) { 906 checkState(useCase.getEffect() == null, 907 useCase + " already has effect" + useCase.getEffect()); 908 useCase.setEffect(effect); 909 unusedEffects.remove(effect); 910 } 911 } 912 } 913 return unusedEffects; 914 } 915 updateViewPortAndSensorToBufferMatrix( @onNull Map<UseCase, StreamSpec> suggestedStreamSpecMap, @NonNull Collection<UseCase> useCases)916 private void updateViewPortAndSensorToBufferMatrix( 917 @NonNull Map<UseCase, StreamSpec> suggestedStreamSpecMap, 918 @NonNull Collection<UseCase> useCases) { 919 synchronized (mLock) { 920 if (mViewPort != null && !useCases.isEmpty()) { 921 Integer lensFacing = mCameraInternal.getCameraInfoInternal().getLensFacing(); 922 boolean isFrontCamera; 923 if (lensFacing == null) { 924 // TODO(b/122975195): If the lens facing is null, it's probably an external 925 // camera. We treat it as like a front camera with unverified behaviors. Will 926 // have to define this later. 927 Logger.w(TAG, "The lens facing is null, probably an external."); 928 isFrontCamera = true; 929 } else { 930 isFrontCamera = lensFacing == CameraSelector.LENS_FACING_FRONT; 931 } 932 // Calculate crop rect if view port is provided. 933 Map<UseCase, Rect> cropRectMap = ViewPorts.calculateViewPortRects( 934 mCameraInternal.getCameraControlInternal().getSensorRect(), 935 isFrontCamera, 936 mViewPort.getAspectRatio(), 937 mCameraInternal.getCameraInfoInternal().getSensorRotationDegrees( 938 mViewPort.getRotation()), 939 mViewPort.getScaleType(), 940 mViewPort.getLayoutDirection(), 941 suggestedStreamSpecMap); 942 for (UseCase useCase : useCases) { 943 useCase.setViewPortCropRect( 944 Preconditions.checkNotNull(cropRectMap.get(useCase))); 945 } 946 } 947 948 // Regardless of having ViewPort, SensorToBufferTransformMatrix must be set correctly 949 // in order for get the correct meteringPoint coordinates. 950 for (UseCase useCase : useCases) { 951 useCase.setSensorToBufferTransformMatrix( 952 calculateSensorToBufferTransformMatrix( 953 mCameraInternal.getCameraControlInternal().getSensorRect(), 954 Preconditions.checkNotNull( 955 suggestedStreamSpecMap.get(useCase)).getResolution())); 956 } 957 } 958 } 959 calculateSensorToBufferTransformMatrix( @onNull Rect fullSensorRect, @NonNull Size useCaseSize)960 private static @NonNull Matrix calculateSensorToBufferTransformMatrix( 961 @NonNull Rect fullSensorRect, 962 @NonNull Size useCaseSize) { 963 checkArgument( 964 fullSensorRect.width() > 0 && fullSensorRect.height() > 0, 965 "Cannot compute viewport crop rects zero sized sensor rect."); 966 RectF fullSensorRectF = new RectF(fullSensorRect); 967 Matrix sensorToUseCaseTransformation = new Matrix(); 968 RectF srcRect = new RectF(0, 0, useCaseSize.getWidth(), 969 useCaseSize.getHeight()); 970 sensorToUseCaseTransformation.setRectToRect(srcRect, fullSensorRectF, 971 Matrix.ScaleToFit.CENTER); 972 sensorToUseCaseTransformation.invert(sensorToUseCaseTransformation); 973 return sensorToUseCaseTransformation; 974 } 975 976 // Pair of UseCase configs. One for the extended config applied on top of the use case and 977 // the camera default which applied underneath the use case's config. 978 private static class ConfigPair { ConfigPair(UseCaseConfig<?> extendedConfig, UseCaseConfig<?> cameraConfig)979 ConfigPair(UseCaseConfig<?> extendedConfig, UseCaseConfig<?> cameraConfig) { 980 mExtendedConfig = extendedConfig; 981 mCameraConfig = cameraConfig; 982 } 983 984 UseCaseConfig<?> mExtendedConfig; 985 UseCaseConfig<?> mCameraConfig; 986 } 987 988 /** 989 * Gets a map of the configs for the use cases from the respective factories. 990 */ getConfigs(@onNull Collection<UseCase> useCases, @NonNull UseCaseConfigFactory extendedFactory, @NonNull UseCaseConfigFactory cameraFactory, @NonNull Range<Integer> targetHighSpeedFps)991 private static Map<UseCase, ConfigPair> getConfigs(@NonNull Collection<UseCase> useCases, 992 @NonNull UseCaseConfigFactory extendedFactory, 993 @NonNull UseCaseConfigFactory cameraFactory, 994 @NonNull Range<Integer> targetHighSpeedFps) { 995 Map<UseCase, ConfigPair> configs = new HashMap<>(); 996 for (UseCase useCase : useCases) { 997 UseCaseConfig<?> extendedConfig; 998 if (isStreamSharing(useCase)) { 999 extendedConfig = generateExtendedStreamSharingConfigFromPreview(extendedFactory, 1000 (StreamSharing) useCase); 1001 } else { 1002 extendedConfig = useCase.getDefaultConfig(false, extendedFactory); 1003 } 1004 UseCaseConfig<?> cameraConfig = useCase.getDefaultConfig(true, cameraFactory); 1005 cameraConfig = attachUseCaseSharedConfigs(useCase, cameraConfig, targetHighSpeedFps); 1006 configs.put(useCase, new ConfigPair(extendedConfig, cameraConfig)); 1007 } 1008 return configs; 1009 } 1010 1011 @NonNull attachUseCaseSharedConfigs( @onNull UseCase useCase, @Nullable UseCaseConfig<?> useCaseConfig, @NonNull Range<Integer> targetHighSpeedFps)1012 private static UseCaseConfig<?> attachUseCaseSharedConfigs( 1013 @NonNull UseCase useCase, 1014 @Nullable UseCaseConfig<?> useCaseConfig, 1015 @NonNull Range<Integer> targetHighSpeedFps) { 1016 MutableOptionsBundle mutableConfig = useCaseConfig != null 1017 ? MutableOptionsBundle.from(useCaseConfig) : MutableOptionsBundle.create(); 1018 1019 mutableConfig.insertOption(OPTION_TARGET_HIGH_SPEED_FRAME_RATE, targetHighSpeedFps); 1020 1021 return useCase.getUseCaseConfigBuilder(mutableConfig).getUseCaseConfig(); 1022 } 1023 generateExtendedStreamSharingConfigFromPreview( @onNull UseCaseConfigFactory extendedFactory, @NonNull StreamSharing streamSharing)1024 private static UseCaseConfig<?> generateExtendedStreamSharingConfigFromPreview( 1025 @NonNull UseCaseConfigFactory extendedFactory, @NonNull StreamSharing streamSharing) { 1026 Preview preview = new Preview.Builder().build(); 1027 Config previewConfig = preview.getDefaultConfig(false, extendedFactory); 1028 if (previewConfig == null) { 1029 return null; 1030 } 1031 1032 // Remove OPTION_TARGET_CLASS, since its value would be "Preview". 1033 MutableOptionsBundle mutableConfig = MutableOptionsBundle.from(previewConfig); 1034 mutableConfig.removeOption(TargetConfig.OPTION_TARGET_CLASS); 1035 1036 return streamSharing.getUseCaseConfigBuilder(mutableConfig).getUseCaseConfig(); 1037 } 1038 1039 /** 1040 * Checks for any unsupported feature combinations and throws an exception if found. 1041 * 1042 * @throws IllegalArgumentException if any feature combination is not supported. 1043 */ checkUnsupportedFeatureCombinationAndThrow(@onNull Collection<UseCase> useCases)1044 private void checkUnsupportedFeatureCombinationAndThrow(@NonNull Collection<UseCase> useCases) 1045 throws IllegalArgumentException { 1046 // TODO(b/309900490): since there are other places (e.g. SupportedSurfaceCombination in 1047 // camera2) that feature combination constraints are enforced, it would be nice if they 1048 // followed a similar pattern for checking constraints. 1049 if (hasExtension()) { 1050 if (hasNonSdrConfig(useCases)) { 1051 throw new IllegalArgumentException("Extensions are only supported for use with " 1052 + "standard dynamic range."); 1053 } 1054 1055 if (hasRawImageCapture(useCases)) { 1056 throw new IllegalArgumentException("Extensions are not supported for use with " 1057 + "Raw image capture."); 1058 } 1059 } 1060 1061 // TODO(b/322311893): throw exception to block feature combination of effect with Ultra 1062 // HDR, until ImageProcessor and SurfaceProcessor can support JPEG/R format. 1063 synchronized (mLock) { 1064 if (!mEffects.isEmpty() && (hasUltraHdrImageCapture(useCases) 1065 || hasRawImageCapture(useCases))) { 1066 throw new IllegalArgumentException("Ultra HDR image and Raw capture does not " 1067 + "support for use with CameraEffect."); 1068 } 1069 } 1070 } 1071 hasNonSdrConfig(@onNull Collection<UseCase> useCases)1072 private static boolean hasNonSdrConfig(@NonNull Collection<UseCase> useCases) { 1073 for (UseCase useCase : useCases) { 1074 DynamicRange dynamicRange = useCase.getCurrentConfig().getDynamicRange(); 1075 if (isNotSdr(dynamicRange)) { 1076 return true; 1077 } 1078 } 1079 return false; 1080 } 1081 isNotSdr(@onNull DynamicRange dynamicRange)1082 private static boolean isNotSdr(@NonNull DynamicRange dynamicRange) { 1083 boolean is10Bit = dynamicRange.getBitDepth() == BIT_DEPTH_10_BIT; 1084 boolean isHdr = dynamicRange.getEncoding() != ENCODING_SDR 1085 && dynamicRange.getEncoding() != ENCODING_UNSPECIFIED; 1086 1087 return is10Bit || isHdr; 1088 } 1089 hasUltraHdrImageCapture(@onNull Collection<UseCase> useCases)1090 private static boolean hasUltraHdrImageCapture(@NonNull Collection<UseCase> useCases) { 1091 for (UseCase useCase : useCases) { 1092 if (!isImageCapture(useCase)) { 1093 continue; 1094 } 1095 1096 UseCaseConfig<?> config = useCase.getCurrentConfig(); 1097 if (config.containsOption(OPTION_OUTPUT_FORMAT) && checkNotNull( 1098 config.retrieveOption(OPTION_OUTPUT_FORMAT)) == OUTPUT_FORMAT_JPEG_ULTRA_HDR) { 1099 return true; 1100 } 1101 1102 } 1103 return false; 1104 } 1105 hasRawImageCapture(@onNull Collection<UseCase> useCases)1106 private static boolean hasRawImageCapture(@NonNull Collection<UseCase> useCases) { 1107 for (UseCase useCase : useCases) { 1108 if (!isImageCapture(useCase)) { 1109 continue; 1110 } 1111 1112 UseCaseConfig<?> config = useCase.getCurrentConfig(); 1113 if (config.containsOption(OPTION_OUTPUT_FORMAT) 1114 && (checkNotNull(config.retrieveOption(OPTION_OUTPUT_FORMAT)) 1115 == OUTPUT_FORMAT_RAW)) { 1116 return true; 1117 } 1118 1119 } 1120 return false; 1121 } 1122 1123 /** 1124 * An identifier for a {@link CameraUseCaseAdapter}. 1125 * 1126 * <p>This identifies the actual camera instances that are wrapped by the 1127 * CameraUseCaseAdapter and is used to determine if 2 different instances of 1128 * CameraUseCaseAdapter are actually equivalent. 1129 */ 1130 @AutoValue 1131 public abstract static class CameraId { 1132 /** Creates a identifier for a {@link CameraUseCaseAdapter}. */ create(@onNull String cameraIdString, @NonNull Identifier cameraConfigId)1133 public static @NonNull CameraId create(@NonNull String cameraIdString, 1134 @NonNull Identifier cameraConfigId) { 1135 return new AutoValue_CameraUseCaseAdapter_CameraId(cameraIdString, cameraConfigId); 1136 } 1137 1138 /** Gets the camera ID string. */ getCameraIdString()1139 public abstract @NonNull String getCameraIdString(); 1140 /** Gets the camera configuration. */ getCameraConfigId()1141 public abstract @NonNull Identifier getCameraConfigId(); 1142 } 1143 1144 /** 1145 * An exception thrown when the {@link CameraUseCaseAdapter} errors in one of its operations. 1146 */ 1147 public static final class CameraException extends Exception { CameraException()1148 public CameraException() { 1149 super(); 1150 } 1151 CameraException(@onNull String message)1152 public CameraException(@NonNull String message) { 1153 super(message); 1154 } 1155 CameraException(@onNull Throwable cause)1156 public CameraException(@NonNull Throwable cause) { 1157 super(cause); 1158 } 1159 } 1160 1161 //////////////////////////////////////////////////////////////////////////////////////////////// 1162 // Camera interface 1163 //////////////////////////////////////////////////////////////////////////////////////////////// 1164 @Override getCameraControl()1165 public @NonNull CameraControl getCameraControl() { 1166 return mCameraInternal.getCameraControl(); 1167 } 1168 1169 @Override getCameraInfo()1170 public @NonNull CameraInfo getCameraInfo() { 1171 return mCameraInternal.getCameraInfo(); 1172 } 1173 getSecondaryCameraInfo()1174 public @Nullable CameraInfo getSecondaryCameraInfo() { 1175 return mSecondaryCameraInternal != null ? mSecondaryCameraInternal.getCameraInfo() : null; 1176 } 1177 1178 @Override getExtendedConfig()1179 public @NonNull CameraConfig getExtendedConfig() { 1180 synchronized (mLock) { 1181 return mCameraConfig; 1182 } 1183 } 1184 1185 @Override isUseCasesCombinationSupported(boolean withStreamSharing, UseCase @NonNull ... useCases)1186 public boolean isUseCasesCombinationSupported(boolean withStreamSharing, 1187 UseCase @NonNull ... useCases) { 1188 Collection<UseCase> useCasesToVerify = Arrays.asList(useCases); 1189 if (withStreamSharing) { 1190 StreamSharing streamSharing = createOrReuseStreamSharing(useCasesToVerify, true); 1191 useCasesToVerify = calculateCameraUseCases(useCasesToVerify, null, streamSharing); 1192 } 1193 synchronized (mLock) { 1194 // If the UseCases exceed the resolutions then it will throw an exception 1195 try { 1196 Map<UseCase, ConfigPair> configs = getConfigs(useCasesToVerify, 1197 mCameraConfig.getUseCaseConfigFactory(), mUseCaseConfigFactory, 1198 FRAME_RATE_RANGE_UNSPECIFIED); 1199 calculateSuggestedStreamSpecs( 1200 getCameraMode(), 1201 mCameraInternal.getCameraInfoInternal(), 1202 useCasesToVerify, emptyList(), configs); 1203 } catch (IllegalArgumentException e) { 1204 return false; 1205 } 1206 1207 return true; 1208 } 1209 } 1210 1211 /** 1212 * Calculate the internal created placeholder UseCase for Extensions. 1213 * 1214 * @param appUseCases UseCase provided by the app. 1215 */ calculatePlaceholderForExtensions( @onNull Collection<UseCase> appUseCases, @Nullable StreamSharing streamSharing)1216 private @Nullable UseCase calculatePlaceholderForExtensions( 1217 @NonNull Collection<UseCase> appUseCases, @Nullable StreamSharing streamSharing) { 1218 synchronized (mLock) { 1219 // Replace children with StreamSharing before calculation. 1220 List<UseCase> useCasesToCheck = new ArrayList<>(appUseCases); 1221 if (streamSharing != null) { 1222 useCasesToCheck.add(streamSharing); 1223 useCasesToCheck.removeAll(streamSharing.getChildren()); 1224 } 1225 1226 // Perform calculation. 1227 UseCase placeholder = null; 1228 if (isCoexistingPreviewImageCaptureRequired()) { 1229 if (isExtraPreviewRequired(useCasesToCheck)) { 1230 if (isPreview(mPlaceholderForExtensions)) { 1231 placeholder = mPlaceholderForExtensions; 1232 } else { 1233 placeholder = createExtraPreview(); 1234 } 1235 } else if (isExtraImageCaptureRequired(useCasesToCheck)) { 1236 if (isImageCapture(mPlaceholderForExtensions)) { 1237 placeholder = mPlaceholderForExtensions; 1238 } else { 1239 placeholder = createExtraImageCapture(); 1240 } 1241 } 1242 } 1243 return placeholder; 1244 } 1245 } 1246 isCoexistingPreviewImageCaptureRequired()1247 private boolean isCoexistingPreviewImageCaptureRequired() { 1248 synchronized (mLock) { 1249 return mCameraConfig.getUseCaseCombinationRequiredRule() 1250 == CameraConfig.REQUIRED_RULE_COEXISTING_PREVIEW_AND_IMAGE_CAPTURE; 1251 } 1252 } 1253 1254 /** 1255 * Returns true if the input use case list contains a {@link ImageCapture} but does not 1256 * contain a {@link Preview}. 1257 * 1258 * <p> Note that {@link StreamSharing} provides preview output surface to 1259 * {@link SessionProcessor} and is therefore considered a {@link Preview}. 1260 */ isExtraPreviewRequired(@onNull Collection<UseCase> useCases)1261 private static boolean isExtraPreviewRequired(@NonNull Collection<UseCase> useCases) { 1262 boolean hasPreviewOrStreamSharing = false; 1263 boolean hasImageCapture = false; 1264 1265 for (UseCase useCase : useCases) { 1266 if (isPreview(useCase) || isStreamSharing(useCase)) { 1267 hasPreviewOrStreamSharing = true; 1268 } else if (isImageCapture(useCase)) { 1269 hasImageCapture = true; 1270 } 1271 } 1272 1273 return hasImageCapture && !hasPreviewOrStreamSharing; 1274 } 1275 1276 /** 1277 * Returns true if the input use case list contains a {@link Preview} but does not contain an 1278 * {@link ImageCapture}. 1279 * 1280 * <p> Note that {@link StreamSharing} provides preview output surface to 1281 * {@link SessionProcessor} and is therefore considered a {@link Preview}. 1282 */ isExtraImageCaptureRequired(@onNull Collection<UseCase> useCases)1283 private static boolean isExtraImageCaptureRequired(@NonNull Collection<UseCase> useCases) { 1284 boolean hasPreviewOrStreamSharing = false; 1285 boolean hasImageCapture = false; 1286 1287 for (UseCase useCase : useCases) { 1288 if (isPreview(useCase) || isStreamSharing(useCase)) { 1289 hasPreviewOrStreamSharing = true; 1290 } else if (isImageCapture(useCase)) { 1291 hasImageCapture = true; 1292 } 1293 } 1294 1295 return hasPreviewOrStreamSharing && !hasImageCapture; 1296 } 1297 hasVideoCapture(@onNull Collection<UseCase> useCases)1298 private static boolean hasVideoCapture(@NonNull Collection<UseCase> useCases) { 1299 for (UseCase useCase : useCases) { 1300 if (isVideoCapture(useCase)) { 1301 return true; 1302 } 1303 } 1304 return false; 1305 } 1306 isVideoCapture(@ullable UseCase useCase)1307 private static boolean isVideoCapture(@Nullable UseCase useCase) { 1308 if (useCase != null) { 1309 if (useCase.getCurrentConfig().containsOption(OPTION_CAPTURE_TYPE)) { 1310 return useCase.getCurrentConfig().getCaptureType() == CaptureType.VIDEO_CAPTURE; 1311 } else { 1312 Log.e(TAG, useCase + " UseCase does not have capture type."); 1313 } 1314 1315 } 1316 return false; 1317 } 1318 isPreview(@ullable UseCase useCase)1319 private static boolean isPreview(@Nullable UseCase useCase) { 1320 return useCase instanceof Preview; 1321 } 1322 isImageCapture(@ullable UseCase useCase)1323 private static boolean isImageCapture(@Nullable UseCase useCase) { 1324 return useCase instanceof ImageCapture; 1325 } 1326 createExtraPreview()1327 private Preview createExtraPreview() { 1328 Preview preview = new Preview.Builder().setTargetName("Preview-Extra").build(); 1329 1330 // Sets a SurfaceProvider to provide the needed surface and release it 1331 preview.setSurfaceProvider((surfaceRequest) -> { 1332 SurfaceTexture surfaceTexture = new SurfaceTexture(0); 1333 surfaceTexture.setDefaultBufferSize(surfaceRequest.getResolution().getWidth(), 1334 surfaceRequest.getResolution().getHeight()); 1335 surfaceTexture.detachFromGLContext(); 1336 Surface surface = new Surface(surfaceTexture); 1337 surfaceRequest.provideSurface(surface, 1338 CameraXExecutors.directExecutor(), 1339 (surfaceResponse) -> { 1340 surface.release(); 1341 surfaceTexture.release(); 1342 }); 1343 }); 1344 1345 return preview; 1346 } 1347 createExtraImageCapture()1348 private ImageCapture createExtraImageCapture() { 1349 return new ImageCapture.Builder().setTargetName("ImageCapture-Extra").build(); 1350 } 1351 } 1352