1 /* 2 * Copyright 2019 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.impl; 18 19 import android.hardware.camera2.CameraCaptureSession; 20 import android.hardware.camera2.CameraDevice; 21 import android.hardware.camera2.CameraDevice.StateCallback; 22 import android.hardware.camera2.CaptureRequest; 23 import android.hardware.camera2.params.InputConfiguration; 24 import android.hardware.camera2.params.SessionConfiguration; 25 import android.util.Range; 26 import android.util.Size; 27 28 import androidx.camera.core.DynamicRange; 29 import androidx.camera.core.Logger; 30 import androidx.camera.core.MirrorMode; 31 import androidx.camera.core.impl.stabilization.StabilizationMode; 32 import androidx.camera.core.internal.HighSpeedFpsModifier; 33 import androidx.camera.core.internal.compat.workaround.SurfaceSorter; 34 35 import com.google.auto.value.AutoValue; 36 37 import org.jspecify.annotations.NonNull; 38 import org.jspecify.annotations.Nullable; 39 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.Collection; 43 import java.util.Collections; 44 import java.util.LinkedHashSet; 45 import java.util.List; 46 import java.util.Set; 47 import java.util.concurrent.atomic.AtomicBoolean; 48 49 /** 50 * Configurations needed for a capture session. 51 * 52 * <p>The SessionConfig contains all the {@link android.hardware.camera2} parameters that are 53 * required to initialize a {@link android.hardware.camera2.CameraCaptureSession} and issue a {@link 54 * CaptureRequest}. 55 */ 56 public final class SessionConfig { 57 /** Regular session type. */ 58 public static final int SESSION_TYPE_REGULAR = SessionConfiguration.SESSION_REGULAR; 59 /** High-speed session type. */ 60 public static final int SESSION_TYPE_HIGH_SPEED = SessionConfiguration.SESSION_HIGH_SPEED; 61 /** The default session type. */ 62 public static final int DEFAULT_SESSION_TYPE = SESSION_TYPE_REGULAR; 63 // Current supported session template values and the bigger index in the list, the 64 // priority is higher. 65 private static final List<Integer> SUPPORTED_TEMPLATE_PRIORITY = Arrays.asList( 66 CameraDevice.TEMPLATE_PREVIEW, 67 // TODO(230673983): Based on the framework assumptions, we prioritize video capture 68 // and disable ZSL (fallback to regular) if both use cases are bound. 69 CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG, 70 CameraDevice.TEMPLATE_RECORD 71 ); 72 /** The set of {@link OutputConfig} that data from the camera will be put into. */ 73 private final List<OutputConfig> mOutputConfigs; 74 /** The {@link OutputConfig} for the postview. */ 75 private final OutputConfig mPostviewOutputConfig; 76 /** The state callback for a {@link CameraDevice}. */ 77 private final List<CameraDevice.StateCallback> mDeviceStateCallbacks; 78 /** The state callback for a {@link CameraCaptureSession}. */ 79 private final List<CameraCaptureSession.StateCallback> mSessionStateCallbacks; 80 /** The callbacks used in single requests. */ 81 private final List<CameraCaptureCallback> mSingleCameraCaptureCallbacks; 82 private final ErrorListener mErrorListener; 83 /** The configuration for building the {@link CaptureRequest} used for repeating requests. */ 84 private final CaptureConfig mRepeatingCaptureConfig; 85 /** The type of the session */ 86 private final int mSessionType; 87 88 /** 89 * Immutable class to store an input configuration that is used to create a reprocessable 90 * capture session. 91 */ 92 private @Nullable InputConfiguration mInputConfiguration; 93 94 /** 95 * The output configuration associated with the {@link DeferrableSurface} that will be used to 96 * create the configuration needed to open a camera session. In camera2 this will be used to 97 * create the corresponding {@link android.hardware.camera2.params.OutputConfiguration}. 98 */ 99 @SuppressWarnings("AutoValueImmutableFields") // avoid extra dependency for ImmutableList. 100 @AutoValue 101 public abstract static class OutputConfig { 102 public static final int SURFACE_GROUP_ID_NONE = -1; 103 104 /** 105 * Returns the surface associated with the {@link OutputConfig}. 106 */ getSurface()107 public abstract @NonNull DeferrableSurface getSurface(); 108 109 /** 110 * Returns the shared surfaces. If non-empty, surface sharing will be enabled and the 111 * shared surfaces will share the same memory buffer as the main surface returned in 112 * {@link #getSurface()}. 113 */ getSharedSurfaces()114 public abstract @NonNull List<DeferrableSurface> getSharedSurfaces(); 115 116 /** 117 * Returns the physical camera ID. By default it would be null. For cameras consisting of 118 * multiple physical cameras, this allows output to be redirected to specific physical 119 * camera. 120 */ getPhysicalCameraId()121 public abstract @Nullable String getPhysicalCameraId(); 122 123 /** 124 * Returns the mirror mode. 125 * 126 * @return {@link MirrorMode} 127 */ 128 @MirrorMode.Mirror getMirrorMode()129 public abstract int getMirrorMode(); 130 131 /** 132 * Returns the surface group ID. Default value is {@link #SURFACE_GROUP_ID_NONE} meaning 133 * it doesn't belong to any surface group. A surface group ID is used to identify which 134 * surface group this output surface belongs to. Output streams with the same 135 * non-negative group ID won't receive the camera output simultaneously therefore it 136 * could reduce the overall memory footprint. 137 */ getSurfaceGroupId()138 public abstract int getSurfaceGroupId(); 139 140 /** 141 * Returns the dynamic range for this output configuration. 142 * 143 * <p>The dynamic range will determine the dynamic range encoding and profile for pixels in 144 * the surfaces associated with this output configuration. 145 * 146 * <p>If not set, this defaults to {@link DynamicRange#SDR}. 147 */ getDynamicRange()148 public abstract @NonNull DynamicRange getDynamicRange(); 149 150 /** 151 * Creates the {@link Builder} instance with specified {@link DeferrableSurface}. 152 */ builder(@onNull DeferrableSurface surface)153 public static @NonNull Builder builder(@NonNull DeferrableSurface surface) { 154 return new AutoValue_SessionConfig_OutputConfig.Builder() 155 .setSurface(surface) 156 .setSharedSurfaces(Collections.emptyList()) 157 .setPhysicalCameraId(null) 158 .setMirrorMode(MirrorMode.MIRROR_MODE_UNSPECIFIED) 159 .setSurfaceGroupId(SURFACE_GROUP_ID_NONE) 160 .setDynamicRange(DynamicRange.SDR); 161 } 162 163 /** 164 * Builder to create the {@link OutputConfig} 165 */ 166 @AutoValue.Builder 167 public abstract static class Builder { 168 /** 169 * Sets the surface associated with the {@link OutputConfig}. 170 */ setSurface(@onNull DeferrableSurface surface)171 public abstract @NonNull Builder setSurface(@NonNull DeferrableSurface surface); 172 173 /** 174 * Sets the shared surfaces. After being set, surface sharing will be enabled and the 175 * shared surfaces will share the same memory buffer as the main surface returned in 176 * {@link #getSurface()}. 177 */ setSharedSurfaces( @onNull List<DeferrableSurface> surface)178 public abstract @NonNull Builder setSharedSurfaces( 179 @NonNull List<DeferrableSurface> surface); 180 181 /** 182 * Sets the physical camera ID. For cameras consisting of multiple physical cameras, 183 * this allows output to be redirected to specific physical camera. 184 */ setPhysicalCameraId(@ullable String cameraId)185 public abstract @NonNull Builder setPhysicalCameraId(@Nullable String cameraId); 186 187 /** 188 * Sets the mirror mode. It specifies mirroring mode for 189 * {@link android.hardware.camera2.params.OutputConfiguration}. 190 * @see android.hardware.camera2.params.OutputConfiguration#setMirrorMode(int) 191 */ setMirrorMode(@irrorMode.Mirror int mirrorMode)192 public abstract @NonNull Builder setMirrorMode(@MirrorMode.Mirror int mirrorMode); 193 194 /** 195 * Sets the surface group ID. A surface group ID is used to identify which surface group 196 * this output surface belongs to. Output streams with the same non-negative group ID 197 * won't receive the camera output simultaneously therefore it could be used to reduce 198 * the overall memory footprint. 199 */ setSurfaceGroupId(int surfaceGroupId)200 public abstract @NonNull Builder setSurfaceGroupId(int surfaceGroupId); 201 202 /** 203 * Returns the dynamic range for this output configuration. 204 * 205 * <p>The dynamic range will determine the dynamic range encoding and profile for 206 * pixels in the surfaces associated with this output configuration. 207 */ setDynamicRange(@onNull DynamicRange dynamicRange)208 public abstract @NonNull Builder setDynamicRange(@NonNull DynamicRange dynamicRange); 209 210 /** 211 * Creates the instance. 212 */ build()213 public abstract @NonNull OutputConfig build(); 214 } 215 } 216 217 /** 218 * Private constructor for a SessionConfig. 219 * 220 * <p>In practice, the {@link SessionConfig.BaseBuilder} will be used to construct a 221 * SessionConfig. 222 * 223 * @param outputConfigs The list of {@link OutputConfig} where data will be put into. 224 * @param deviceStateCallbacks The state callbacks for a {@link CameraDevice}. 225 * @param sessionStateCallbacks The state callbacks for a {@link CameraCaptureSession}. 226 * @param repeatingCaptureConfig The configuration for building the {@link CaptureRequest}. 227 * @param inputConfiguration The input configuration to create a reprocessable capture 228 * session. 229 * @param sessionType The session type for the {@link CameraCaptureSession}. 230 */ SessionConfig( List<OutputConfig> outputConfigs, List<StateCallback> deviceStateCallbacks, List<CameraCaptureSession.StateCallback> sessionStateCallbacks, List<CameraCaptureCallback> singleCameraCaptureCallbacks, CaptureConfig repeatingCaptureConfig, @Nullable ErrorListener errorListener, @Nullable InputConfiguration inputConfiguration, int sessionType, @Nullable OutputConfig postviewOutputConfig)231 SessionConfig( 232 List<OutputConfig> outputConfigs, 233 List<StateCallback> deviceStateCallbacks, 234 List<CameraCaptureSession.StateCallback> sessionStateCallbacks, 235 List<CameraCaptureCallback> singleCameraCaptureCallbacks, 236 CaptureConfig repeatingCaptureConfig, 237 @Nullable ErrorListener errorListener, 238 @Nullable InputConfiguration inputConfiguration, 239 int sessionType, 240 @Nullable OutputConfig postviewOutputConfig) { 241 mOutputConfigs = outputConfigs; 242 mDeviceStateCallbacks = Collections.unmodifiableList(deviceStateCallbacks); 243 mSessionStateCallbacks = Collections.unmodifiableList(sessionStateCallbacks); 244 mSingleCameraCaptureCallbacks = 245 Collections.unmodifiableList(singleCameraCaptureCallbacks); 246 mErrorListener = errorListener; 247 mRepeatingCaptureConfig = repeatingCaptureConfig; 248 mInputConfiguration = inputConfiguration; 249 mSessionType = sessionType; 250 mPostviewOutputConfig = postviewOutputConfig; 251 } 252 253 /** Returns an instance of a session configuration with minimal configurations. */ defaultEmptySessionConfig()254 public static @NonNull SessionConfig defaultEmptySessionConfig() { 255 return new SessionConfig( 256 new ArrayList<OutputConfig>(), 257 new ArrayList<CameraDevice.StateCallback>(0), 258 new ArrayList<CameraCaptureSession.StateCallback>(0), 259 new ArrayList<CameraCaptureCallback>(0), 260 new CaptureConfig.Builder().build(), 261 /* errorListener */ null, 262 /* inputConfiguration */ null, 263 DEFAULT_SESSION_TYPE, 264 /* postviewOutputConfig */ null); 265 } 266 getInputConfiguration()267 public @Nullable InputConfiguration getInputConfiguration() { 268 return mInputConfiguration; 269 } 270 271 /** 272 * Returns all {@link DeferrableSurface}s that are used to configure the session. It includes 273 * both the {@link DeferrableSurface} of the all {@link OutputConfig}s and its shared 274 * surfaces. 275 */ getSurfaces()276 public @NonNull List<DeferrableSurface> getSurfaces() { 277 List<DeferrableSurface> deferrableSurfaces = new ArrayList<>(); 278 for (OutputConfig outputConfig : mOutputConfigs) { 279 deferrableSurfaces.add(outputConfig.getSurface()); 280 for (DeferrableSurface sharedSurface : outputConfig.getSharedSurfaces()) { 281 deferrableSurfaces.add(sharedSurface); 282 } 283 } 284 return Collections.unmodifiableList(deferrableSurfaces); 285 } 286 getOutputConfigs()287 public @NonNull List<OutputConfig> getOutputConfigs() { 288 return mOutputConfigs; 289 } 290 getPostviewOutputConfig()291 public @Nullable OutputConfig getPostviewOutputConfig() { 292 return mPostviewOutputConfig; 293 } 294 getImplementationOptions()295 public @NonNull Config getImplementationOptions() { 296 return mRepeatingCaptureConfig.getImplementationOptions(); 297 } 298 getTemplateType()299 public int getTemplateType() { 300 return mRepeatingCaptureConfig.getTemplateType(); 301 } 302 getSessionType()303 public int getSessionType() { 304 return mSessionType; 305 } 306 getExpectedFrameRateRange()307 public @NonNull Range<Integer> getExpectedFrameRateRange() { 308 return mRepeatingCaptureConfig.getExpectedFrameRateRange(); 309 } 310 311 /** Obtains all registered {@link CameraDevice.StateCallback} callbacks. */ getDeviceStateCallbacks()312 public @NonNull List<CameraDevice.StateCallback> getDeviceStateCallbacks() { 313 return mDeviceStateCallbacks; 314 } 315 316 /** Obtains all registered {@link CameraCaptureSession.StateCallback} callbacks. */ getSessionStateCallbacks()317 public @NonNull List<CameraCaptureSession.StateCallback> getSessionStateCallbacks() { 318 return mSessionStateCallbacks; 319 } 320 321 /** Obtains all registered {@link CameraCaptureCallback} callbacks for repeating requests. */ getRepeatingCameraCaptureCallbacks()322 public @NonNull List<CameraCaptureCallback> getRepeatingCameraCaptureCallbacks() { 323 return mRepeatingCaptureConfig.getCameraCaptureCallbacks(); 324 } 325 326 /** Obtains the registered {@link ErrorListener} callback. */ getErrorListener()327 public @Nullable ErrorListener getErrorListener() { 328 return mErrorListener; 329 } 330 331 /** Obtains all registered {@link CameraCaptureCallback} callbacks for single requests. */ getSingleCameraCaptureCallbacks()332 public @NonNull List<CameraCaptureCallback> getSingleCameraCaptureCallbacks() { 333 return mSingleCameraCaptureCallbacks; 334 } 335 getRepeatingCaptureConfig()336 public @NonNull CaptureConfig getRepeatingCaptureConfig() { 337 return mRepeatingCaptureConfig; 338 } 339 340 /** Returns the one which has higher priority. */ getHigherPriorityTemplateType(int type1, int type2)341 public static int getHigherPriorityTemplateType(int type1, int type2) { 342 return SUPPORTED_TEMPLATE_PRIORITY.indexOf(type1) 343 >= SUPPORTED_TEMPLATE_PRIORITY.indexOf(type2) ? type1 : type2; 344 } 345 346 public enum SessionError { 347 /** 348 * A {@link DeferrableSurface} contained in the config needs to be reset. 349 * 350 * <p>The surface is no longer valid, for example the surface has already been closed. 351 */ 352 SESSION_ERROR_SURFACE_NEEDS_RESET, 353 /** An unknown error has occurred. */ 354 SESSION_ERROR_UNKNOWN 355 } 356 357 /** 358 * Callback for errors that occur when accessing the session config. 359 */ 360 public interface ErrorListener { 361 /** 362 * Called when an error has occurred. 363 * 364 * @param sessionConfig The {@link SessionConfig} that generated the error. 365 * @param error The error that was generated. 366 */ onError(@onNull SessionConfig sessionConfig, @NonNull SessionError error)367 void onError(@NonNull SessionConfig sessionConfig, @NonNull SessionError error); 368 } 369 370 /** 371 * A closeable ErrorListener that onError callback won't be invoked after it is closed. 372 */ 373 public static final class CloseableErrorListener implements ErrorListener { 374 private final AtomicBoolean mIsClosed = new AtomicBoolean(false); 375 private final ErrorListener mErrorListener; 376 CloseableErrorListener(@onNull ErrorListener errorListener)377 public CloseableErrorListener(@NonNull ErrorListener errorListener) { 378 mErrorListener = errorListener; 379 } 380 381 @Override onError(@onNull SessionConfig sessionConfig, @NonNull SessionError error)382 public void onError(@NonNull SessionConfig sessionConfig, @NonNull SessionError error) { 383 if (!mIsClosed.get()) { 384 mErrorListener.onError(sessionConfig, error); 385 } 386 } 387 388 /** 389 * Closes the ErrorListener to not invoke the onError callback function. 390 */ close()391 public void close() { 392 mIsClosed.set(true); 393 } 394 } 395 396 /** 397 * Interface for unpacking a configuration into a SessionConfig.Builder 398 * 399 * <p>TODO(b/120949879): This will likely be removed once SessionConfig is refactored to 400 * remove camera2 dependencies. 401 */ 402 public interface OptionUnpacker { 403 404 /** 405 * Apply the options from the config onto the builder 406 * 407 * @param resolution the suggested resolution 408 * @param config the set of options to apply 409 * @param builder the builder on which to apply the options 410 */ unpack( @onNull Size resolution, @NonNull UseCaseConfig<?> config, SessionConfig.@NonNull Builder builder)411 void unpack( 412 @NonNull Size resolution, 413 @NonNull UseCaseConfig<?> config, 414 SessionConfig.@NonNull Builder builder); 415 } 416 417 /** 418 * Base builder for easy modification/rebuilding of a {@link SessionConfig}. 419 */ 420 static class BaseBuilder { 421 // Use LinkedHashSet to preserve the adding order for bug fixing and testing. 422 final Set<OutputConfig> mOutputConfigs = new LinkedHashSet<>(); 423 final CaptureConfig.Builder mCaptureConfigBuilder = new CaptureConfig.Builder(); 424 final List<CameraDevice.StateCallback> mDeviceStateCallbacks = new ArrayList<>(); 425 final List<CameraCaptureSession.StateCallback> mSessionStateCallbacks = new ArrayList<>(); 426 final List<CameraCaptureCallback> mSingleCameraCaptureCallbacks = new ArrayList<>(); 427 @Nullable ErrorListener mErrorListener; 428 @Nullable InputConfiguration mInputConfiguration; 429 int mSessionType = DEFAULT_SESSION_TYPE; 430 @Nullable OutputConfig mPostviewOutputConfig; 431 } 432 433 /** 434 * Builder for easy modification/rebuilding of a {@link SessionConfig}. 435 */ 436 public static class Builder extends BaseBuilder { 437 /** 438 * Creates a {@link Builder} from a {@link UseCaseConfig}. 439 * 440 * <p>Populates the builder with all the properties defined in the base configuration. 441 */ createFrom( @onNull UseCaseConfig<?> config, @NonNull Size resolution)442 public static @NonNull Builder createFrom( 443 @NonNull UseCaseConfig<?> config, 444 @NonNull Size resolution) { 445 OptionUnpacker unpacker = config.getSessionOptionUnpacker(null); 446 if (unpacker == null) { 447 throw new IllegalStateException( 448 "Implementation is missing option unpacker for " 449 + config.getTargetName(config.toString())); 450 } 451 452 Builder builder = new Builder(); 453 454 // Unpack the configuration into this builder 455 unpacker.unpack(resolution, config, builder); 456 return builder; 457 } 458 459 /** 460 * Set the input configuration for reprocessable capture session. 461 * 462 * @param inputConfiguration The input configuration. 463 */ setInputConfiguration( @ullable InputConfiguration inputConfiguration)464 public @NonNull Builder setInputConfiguration( 465 @Nullable InputConfiguration inputConfiguration) { 466 mInputConfiguration = inputConfiguration; 467 return this; 468 } 469 470 /** 471 * Set the template characteristics of the SessionConfig. 472 * 473 * @param templateType Template constant that must match those defined by {@link 474 * CameraDevice} 475 * <p>TODO(b/120949879): This is camera2 implementation detail that 476 * should be moved 477 */ setTemplateType(int templateType)478 public @NonNull Builder setTemplateType(int templateType) { 479 mCaptureConfigBuilder.setTemplateType(templateType); 480 return this; 481 } 482 483 /** 484 * Sets the session type. 485 */ setSessionType(int sessionType)486 public @NonNull Builder setSessionType(int sessionType) { 487 mSessionType = sessionType; 488 return this; 489 } 490 491 /** 492 * Set the expected frame rate range of the SessionConfig. 493 * 494 * @param expectedFrameRateRange The frame rate range calculated from the UseCases for 495 * {@link CameraDevice} 496 */ setExpectedFrameRateRange( @onNull Range<Integer> expectedFrameRateRange)497 public @NonNull Builder setExpectedFrameRateRange( 498 @NonNull Range<Integer> expectedFrameRateRange) { 499 mCaptureConfigBuilder.setExpectedFrameRateRange(expectedFrameRateRange); 500 return this; 501 } 502 503 /** 504 * Set the preview stabilization mode of the SessionConfig. 505 * @param mode {@link StabilizationMode} 506 */ setPreviewStabilization(@tabilizationMode.Mode int mode)507 public @NonNull Builder setPreviewStabilization(@StabilizationMode.Mode int mode) { 508 if (mode != StabilizationMode.UNSPECIFIED) { 509 mCaptureConfigBuilder.setPreviewStabilization(mode); 510 } 511 return this; 512 } 513 514 /** 515 * Set the video stabilization mode of the SessionConfig. 516 * @param mode {@link StabilizationMode} 517 */ setVideoStabilization(@tabilizationMode.Mode int mode)518 public @NonNull Builder setVideoStabilization(@StabilizationMode.Mode int mode) { 519 if (mode != StabilizationMode.UNSPECIFIED) { 520 mCaptureConfigBuilder.setVideoStabilization(mode); 521 } 522 return this; 523 } 524 525 /** 526 * Adds a tag to the SessionConfig with a key. For tracking the source. 527 */ addTag(@onNull String key, @NonNull Object tag)528 public @NonNull Builder addTag(@NonNull String key, @NonNull Object tag) { 529 mCaptureConfigBuilder.addTag(key, tag); 530 return this; 531 } 532 533 /** 534 * Adds a {@link CameraDevice.StateCallback} callback. 535 */ 536 // TODO(b/120949879): This is camera2 implementation detail that should be moved addDeviceStateCallback( CameraDevice.@onNull StateCallback deviceStateCallback)537 public @NonNull Builder addDeviceStateCallback( 538 CameraDevice.@NonNull StateCallback deviceStateCallback) { 539 if (mDeviceStateCallbacks.contains(deviceStateCallback)) { 540 return this; 541 } 542 mDeviceStateCallbacks.add(deviceStateCallback); 543 return this; 544 } 545 546 /** 547 * Adds all {@link CameraDevice.StateCallback} callbacks. 548 */ addAllDeviceStateCallbacks( @onNull Collection<CameraDevice.StateCallback> deviceStateCallbacks)549 public @NonNull Builder addAllDeviceStateCallbacks( 550 @NonNull Collection<CameraDevice.StateCallback> deviceStateCallbacks) { 551 for (CameraDevice.StateCallback callback : deviceStateCallbacks) { 552 addDeviceStateCallback(callback); 553 } 554 return this; 555 } 556 557 /** 558 * Adds a {@link CameraCaptureSession.StateCallback} callback. 559 */ 560 // TODO(b/120949879): This is camera2 implementation detail that should be moved addSessionStateCallback( CameraCaptureSession.@onNull StateCallback sessionStateCallback)561 public @NonNull Builder addSessionStateCallback( 562 CameraCaptureSession.@NonNull StateCallback sessionStateCallback) { 563 if (mSessionStateCallbacks.contains(sessionStateCallback)) { 564 return this; 565 } 566 mSessionStateCallbacks.add(sessionStateCallback); 567 return this; 568 } 569 570 /** 571 * Adds all {@link CameraCaptureSession.StateCallback} callbacks. 572 */ addAllSessionStateCallbacks( @onNull List<CameraCaptureSession.StateCallback> sessionStateCallbacks)573 public @NonNull Builder addAllSessionStateCallbacks( 574 @NonNull List<CameraCaptureSession.StateCallback> sessionStateCallbacks) { 575 for (CameraCaptureSession.StateCallback callback : sessionStateCallbacks) { 576 addSessionStateCallback(callback); 577 } 578 return this; 579 } 580 581 /** 582 * Adds a {@link CameraCaptureCallback} callback for repeating requests. 583 * <p>This callback does not call for single requests. 584 */ addRepeatingCameraCaptureCallback( @onNull CameraCaptureCallback cameraCaptureCallback)585 public @NonNull Builder addRepeatingCameraCaptureCallback( 586 @NonNull CameraCaptureCallback cameraCaptureCallback) { 587 mCaptureConfigBuilder.addCameraCaptureCallback(cameraCaptureCallback); 588 return this; 589 } 590 591 /** 592 * Adds all {@link CameraCaptureCallback} callbacks. 593 * <p>These callbacks do not call for single requests. 594 */ addAllRepeatingCameraCaptureCallbacks( @onNull Collection<CameraCaptureCallback> cameraCaptureCallbacks)595 public @NonNull Builder addAllRepeatingCameraCaptureCallbacks( 596 @NonNull Collection<CameraCaptureCallback> cameraCaptureCallbacks) { 597 mCaptureConfigBuilder.addAllCameraCaptureCallbacks(cameraCaptureCallbacks); 598 return this; 599 } 600 601 /** 602 * Adds a {@link CameraCaptureCallback} callback for single and repeating requests. 603 * <p>Listeners added here are available in both the 604 * {@link #getRepeatingCameraCaptureCallbacks()} and 605 * {@link #getSingleCameraCaptureCallbacks()} methods. 606 */ addCameraCaptureCallback( @onNull CameraCaptureCallback cameraCaptureCallback)607 public @NonNull Builder addCameraCaptureCallback( 608 @NonNull CameraCaptureCallback cameraCaptureCallback) { 609 mCaptureConfigBuilder.addCameraCaptureCallback(cameraCaptureCallback); 610 if (!mSingleCameraCaptureCallbacks.contains(cameraCaptureCallback)) { 611 mSingleCameraCaptureCallbacks.add(cameraCaptureCallback); 612 } 613 return this; 614 } 615 616 /** 617 * Adds all {@link CameraCaptureCallback} callbacks for single and repeating requests. 618 * <p>Listeners added here are available in both the 619 * {@link #getRepeatingCameraCaptureCallbacks()} and 620 * {@link #getSingleCameraCaptureCallbacks()} methods. 621 */ addAllCameraCaptureCallbacks( @onNull Collection<CameraCaptureCallback> cameraCaptureCallbacks)622 public @NonNull Builder addAllCameraCaptureCallbacks( 623 @NonNull Collection<CameraCaptureCallback> cameraCaptureCallbacks) { 624 for (CameraCaptureCallback c : cameraCaptureCallbacks) { 625 mCaptureConfigBuilder.addCameraCaptureCallback(c); 626 if (!mSingleCameraCaptureCallbacks.contains(c)) { 627 mSingleCameraCaptureCallbacks.add(c); 628 } 629 } 630 return this; 631 } 632 633 /** 634 * Removes a previously added {@link CameraCaptureCallback} callback for single and/or 635 * repeating requests. 636 * 637 * @param cameraCaptureCallback The callback to remove. 638 * @return {@code true} if the callback was successfully removed. {@code false} if the 639 * callback wasn't present in this builder. 640 */ removeCameraCaptureCallback( @onNull CameraCaptureCallback cameraCaptureCallback)641 public boolean removeCameraCaptureCallback( 642 @NonNull CameraCaptureCallback cameraCaptureCallback) { 643 boolean removedFromRepeating = 644 mCaptureConfigBuilder.removeCameraCaptureCallback(cameraCaptureCallback); 645 boolean removedFromSingle = 646 mSingleCameraCaptureCallbacks.remove(cameraCaptureCallback); 647 return removedFromRepeating || removedFromSingle; 648 } 649 650 /** Obtain all {@link CameraCaptureCallback} callbacks for single requests. */ getSingleCameraCaptureCallbacks()651 public @NonNull List<CameraCaptureCallback> getSingleCameraCaptureCallbacks() { 652 return Collections.unmodifiableList(mSingleCameraCaptureCallbacks); 653 } 654 655 /** 656 * Adds all {@link ErrorListener} listeners repeating requests. 657 */ setErrorListener(@onNull ErrorListener errorListener)658 public @NonNull Builder setErrorListener(@NonNull ErrorListener errorListener) { 659 mErrorListener = errorListener; 660 return this; 661 } 662 663 664 /** 665 * Add a surface to the set that the session repeatedly writes data to. 666 * 667 * <p>The dynamic range of this surface will default to {@link DynamicRange#SDR}. To 668 * manually set the dynamic range, use 669 * {@link #addSurface(DeferrableSurface, DynamicRange, String, int)}. 670 */ addSurface(@onNull DeferrableSurface surface)671 public @NonNull Builder addSurface(@NonNull DeferrableSurface surface) { 672 return addSurface(surface, DynamicRange.SDR, null, 673 MirrorMode.MIRROR_MODE_UNSPECIFIED); 674 } 675 676 /** 677 * Add a surface with the provided dynamic range to the set that the session repeatedly 678 * writes data to. 679 */ addSurface(@onNull DeferrableSurface surface, @NonNull DynamicRange dynamicRange, @Nullable String physicalCameraId, @MirrorMode.Mirror int mirrorMode)680 public @NonNull Builder addSurface(@NonNull DeferrableSurface surface, 681 @NonNull DynamicRange dynamicRange, 682 @Nullable String physicalCameraId, 683 @MirrorMode.Mirror int mirrorMode) { 684 OutputConfig outputConfig = OutputConfig.builder(surface) 685 .setPhysicalCameraId(physicalCameraId) 686 .setDynamicRange(dynamicRange) 687 .setMirrorMode(mirrorMode) 688 .build(); 689 mOutputConfigs.add(outputConfig); 690 mCaptureConfigBuilder.addSurface(surface); 691 return this; 692 } 693 694 /** 695 * Adds an {@link OutputConfig} to create the capture session with. The surface set in 696 * the {@link OutputConfig} will be added to the repeating request. 697 */ addOutputConfig(@onNull OutputConfig outputConfig)698 public @NonNull Builder addOutputConfig(@NonNull OutputConfig outputConfig) { 699 mOutputConfigs.add(outputConfig); 700 mCaptureConfigBuilder.addSurface(outputConfig.getSurface()); 701 for (DeferrableSurface sharedSurface : outputConfig.getSharedSurfaces()) { 702 mCaptureConfigBuilder.addSurface(sharedSurface); 703 } 704 return this; 705 } 706 707 /** 708 * Add a surface for the session which only used for single captures. 709 * 710 * <p>The dynamic range of this surface will default to {@link DynamicRange#SDR}. To 711 * manually set the dynamic range, use 712 * {@link #addNonRepeatingSurface(DeferrableSurface, DynamicRange)}. 713 */ addNonRepeatingSurface(@onNull DeferrableSurface surface)714 public @NonNull Builder addNonRepeatingSurface(@NonNull DeferrableSurface surface) { 715 return addNonRepeatingSurface(surface, DynamicRange.SDR); 716 } 717 718 /** 719 * Add a surface with the provided dynamic range for the session which only used for 720 * single captures. 721 */ addNonRepeatingSurface(@onNull DeferrableSurface surface, @NonNull DynamicRange dynamicRange)722 public @NonNull Builder addNonRepeatingSurface(@NonNull DeferrableSurface surface, 723 @NonNull DynamicRange dynamicRange) { 724 OutputConfig outputConfig = OutputConfig.builder(surface) 725 .setDynamicRange(dynamicRange) 726 .build(); 727 mOutputConfigs.add(outputConfig); 728 return this; 729 } 730 731 /** 732 * Sets the postview surface. 733 */ setPostviewSurface(@onNull DeferrableSurface surface)734 public @NonNull Builder setPostviewSurface(@NonNull DeferrableSurface surface) { 735 mPostviewOutputConfig = OutputConfig.builder(surface).build(); 736 return this; 737 } 738 739 /** Remove a surface from the set which the session repeatedly writes to. */ removeSurface(@onNull DeferrableSurface surface)740 public @NonNull Builder removeSurface(@NonNull DeferrableSurface surface) { 741 OutputConfig outputConfigToRemove = null; 742 for (OutputConfig config : mOutputConfigs) { 743 if (config.getSurface().equals(surface)) { 744 outputConfigToRemove = config; 745 break; 746 } 747 } 748 749 if (outputConfigToRemove != null) { 750 mOutputConfigs.remove(outputConfigToRemove); 751 } 752 mCaptureConfigBuilder.removeSurface(surface); 753 return this; 754 } 755 756 /** Clears all surfaces from the set which the session writes to. */ clearSurfaces()757 public @NonNull Builder clearSurfaces() { 758 mOutputConfigs.clear(); 759 mCaptureConfigBuilder.clearSurfaces(); 760 return this; 761 } 762 763 /** Set the {@link Config} for options that are implementation specific. */ setImplementationOptions(@onNull Config config)764 public @NonNull Builder setImplementationOptions(@NonNull Config config) { 765 mCaptureConfigBuilder.setImplementationOptions(config); 766 return this; 767 } 768 769 /** Add a set of {@link Config} to the implementation specific options. */ addImplementationOptions(@onNull Config config)770 public @NonNull Builder addImplementationOptions(@NonNull Config config) { 771 mCaptureConfigBuilder.addImplementationOptions(config); 772 return this; 773 } 774 775 /** 776 * Builds an instance of a SessionConfig that has all the combined parameters of the 777 * SessionConfig that have been added to the Builder. 778 */ build()779 public @NonNull SessionConfig build() { 780 return new SessionConfig( 781 new ArrayList<>(mOutputConfigs), 782 new ArrayList<>(mDeviceStateCallbacks), 783 new ArrayList<>(mSessionStateCallbacks), 784 new ArrayList<>(mSingleCameraCaptureCallbacks), 785 mCaptureConfigBuilder.build(), 786 mErrorListener, 787 mInputConfiguration, 788 mSessionType, 789 mPostviewOutputConfig); 790 } 791 } 792 793 /** 794 * Builder for combining multiple instances of {@link SessionConfig}. This will check if all 795 * the parameters for the {@link SessionConfig} are compatible with each other 796 */ 797 public static final class ValidatingBuilder extends BaseBuilder { 798 private static final String TAG = "ValidatingBuilder"; 799 private final SurfaceSorter mSurfaceSorter = new SurfaceSorter(); 800 private boolean mValid = true; 801 private boolean mTemplateSet = false; 802 private List<ErrorListener> mErrorListeners = new ArrayList<>(); 803 804 /** 805 * Add an implementation option to the ValidatingBuilder's CaptureConfigBuilder. If it 806 * already has an option with the same key, write it over. 807 */ addImplementationOption(Config.@onNull Option<T> option, @NonNull T value)808 public <T> void addImplementationOption(Config.@NonNull Option<T> option, 809 @NonNull T value) { 810 mCaptureConfigBuilder.addImplementationOption(option, value); 811 } 812 813 /** 814 * Add the SessionConfig to the set of SessionConfig that have been aggregated by the 815 * ValidatingBuilder 816 */ add(@onNull SessionConfig sessionConfig)817 public void add(@NonNull SessionConfig sessionConfig) { 818 CaptureConfig captureConfig = sessionConfig.getRepeatingCaptureConfig(); 819 820 // Check template 821 if (captureConfig.getTemplateType() != CaptureConfig.TEMPLATE_TYPE_NONE) { 822 mTemplateSet = true; 823 mCaptureConfigBuilder.setTemplateType( 824 getHigherPriorityTemplateType(captureConfig.getTemplateType(), 825 mCaptureConfigBuilder.getTemplateType())); 826 } 827 828 setOrVerifyExpectFrameRateRange(captureConfig.getExpectedFrameRateRange()); 829 setPreviewStabilizationMode(captureConfig.getPreviewStabilizationMode()); 830 setVideoStabilizationMode(captureConfig.getVideoStabilizationMode()); 831 832 TagBundle tagBundle = sessionConfig.getRepeatingCaptureConfig().getTagBundle(); 833 mCaptureConfigBuilder.addAllTags(tagBundle); 834 835 // Check device state callbacks 836 mDeviceStateCallbacks.addAll(sessionConfig.getDeviceStateCallbacks()); 837 838 // Check session state callbacks 839 mSessionStateCallbacks.addAll(sessionConfig.getSessionStateCallbacks()); 840 841 // Check camera capture callbacks for repeating requests. 842 mCaptureConfigBuilder.addAllCameraCaptureCallbacks( 843 sessionConfig.getRepeatingCameraCaptureCallbacks()); 844 845 // Check camera capture callbacks for single requests. 846 mSingleCameraCaptureCallbacks.addAll(sessionConfig.getSingleCameraCaptureCallbacks()); 847 848 if (sessionConfig.getErrorListener() != null) { 849 mErrorListeners.add(sessionConfig.getErrorListener()); 850 } 851 852 // Check input configuration for reprocessable capture session. 853 if (sessionConfig.getInputConfiguration() != null) { 854 mInputConfiguration = sessionConfig.getInputConfiguration(); 855 } 856 857 // Check surfaces 858 mOutputConfigs.addAll(sessionConfig.getOutputConfigs()); 859 860 // Check capture request surfaces 861 mCaptureConfigBuilder.getSurfaces().addAll(captureConfig.getSurfaces()); 862 863 if (!getSurfaces().containsAll(mCaptureConfigBuilder.getSurfaces())) { 864 String errorMessage = 865 "Invalid configuration due to capture request surfaces are not a subset " 866 + "of surfaces"; 867 Logger.d(TAG, errorMessage); 868 mValid = false; 869 } 870 871 if (sessionConfig.getSessionType() != mSessionType 872 && sessionConfig.getSessionType() != DEFAULT_SESSION_TYPE 873 && mSessionType != DEFAULT_SESSION_TYPE) { 874 String errorMessage = 875 "Invalid configuration due to that two non-default session types are set"; 876 Logger.d(TAG, errorMessage); 877 mValid = false; 878 } else { 879 if (sessionConfig.getSessionType() != DEFAULT_SESSION_TYPE) { 880 mSessionType = sessionConfig.getSessionType(); 881 } 882 } 883 884 if (sessionConfig.mPostviewOutputConfig != null) { 885 if (mPostviewOutputConfig != sessionConfig.mPostviewOutputConfig 886 && mPostviewOutputConfig != null) { 887 String errorMessage = 888 "Invalid configuration due to that two different postview output " 889 + "configs are set"; 890 Logger.d(TAG, errorMessage); 891 mValid = false; 892 } else { 893 mPostviewOutputConfig = sessionConfig.mPostviewOutputConfig; 894 } 895 } 896 897 // The conflicting of options is handled in addImplementationOptions where it could 898 // throw an IllegalArgumentException if the conflict cannot be resolved. 899 mCaptureConfigBuilder.addImplementationOptions( 900 captureConfig.getImplementationOptions()); 901 } 902 setOrVerifyExpectFrameRateRange( @onNull Range<Integer> expectedFrameRateRange)903 private void setOrVerifyExpectFrameRateRange( 904 @NonNull Range<Integer> expectedFrameRateRange) { 905 if (expectedFrameRateRange.equals(StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED)) { 906 return; 907 } 908 909 if (mCaptureConfigBuilder.getExpectedFrameRateRange().equals( 910 StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED)) { 911 mCaptureConfigBuilder.setExpectedFrameRateRange(expectedFrameRateRange); 912 return; 913 } 914 915 if (!mCaptureConfigBuilder.getExpectedFrameRateRange().equals(expectedFrameRateRange)) { 916 mValid = false; 917 Logger.d(TAG, "Different ExpectedFrameRateRange values"); 918 } 919 } 920 setPreviewStabilizationMode(@tabilizationMode.Mode int mode)921 private void setPreviewStabilizationMode(@StabilizationMode.Mode int mode) { 922 if (mode != StabilizationMode.UNSPECIFIED) { 923 mCaptureConfigBuilder.setPreviewStabilization(mode); 924 } 925 } 926 setVideoStabilizationMode(@tabilizationMode.Mode int mode)927 private void setVideoStabilizationMode(@StabilizationMode.Mode int mode) { 928 if (mode != StabilizationMode.UNSPECIFIED) { 929 mCaptureConfigBuilder.setVideoStabilization(mode); 930 } 931 } 932 getSurfaces()933 private List<DeferrableSurface> getSurfaces() { 934 List<DeferrableSurface> surfaces = new ArrayList<>(); 935 for (OutputConfig outputConfig : mOutputConfigs) { 936 surfaces.add(outputConfig.getSurface()); 937 for (DeferrableSurface sharedSurface : outputConfig.getSharedSurfaces()) { 938 surfaces.add(sharedSurface); 939 } 940 } 941 return surfaces; 942 } 943 944 /** Clears all surfaces from the set which the session writes to. */ clearSurfaces()945 public void clearSurfaces() { 946 mOutputConfigs.clear(); 947 mCaptureConfigBuilder.clearSurfaces(); 948 } 949 950 /** Check if the set of SessionConfig that have been combined are valid */ isValid()951 public boolean isValid() { 952 return mTemplateSet && mValid; 953 } 954 955 /** 956 * Builds an instance of a SessionConfig that has all the combined parameters of the 957 * SessionConfig that have been added to the ValidatingBuilder. 958 */ build()959 public @NonNull SessionConfig build() { 960 if (!mValid) { 961 throw new IllegalArgumentException("Unsupported session configuration combination"); 962 } 963 964 List<OutputConfig> outputConfigs = new ArrayList<>(mOutputConfigs); 965 mSurfaceSorter.sort(outputConfigs); 966 967 if (mSessionType == SESSION_TYPE_HIGH_SPEED) { 968 // HighSpeedFpsModifier may modify the expected frame rate range for 969 // mCaptureConfigBuilder. 970 new HighSpeedFpsModifier().modifyFpsForPreviewOnlyRepeating(outputConfigs, 971 mCaptureConfigBuilder); 972 } 973 974 ErrorListener errorListener = null; 975 // Creates an error listener to notify errors to the underlying error listeners. 976 if (!mErrorListeners.isEmpty()) { 977 errorListener = (sessionConfig, error) -> { 978 for (ErrorListener listener: mErrorListeners) { 979 listener.onError(sessionConfig, error); 980 } 981 }; 982 } 983 984 return new SessionConfig( 985 outputConfigs, 986 new ArrayList<>(mDeviceStateCallbacks), 987 new ArrayList<>(mSessionStateCallbacks), 988 new ArrayList<>(mSingleCameraCaptureCallbacks), 989 mCaptureConfigBuilder.build(), 990 errorListener, 991 mInputConfiguration, 992 mSessionType, 993 mPostviewOutputConfig); 994 } 995 } 996 } 997