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 static androidx.camera.core.impl.UseCaseConfig.OPTION_PREVIEW_STABILIZATION_MODE; 20 import static androidx.camera.core.impl.UseCaseConfig.OPTION_VIDEO_STABILIZATION_MODE; 21 22 import android.hardware.camera2.CameraCaptureSession; 23 import android.hardware.camera2.CameraDevice; 24 import android.hardware.camera2.CaptureRequest; 25 import android.util.Range; 26 import android.view.Surface; 27 28 import androidx.camera.core.impl.stabilization.StabilizationMode; 29 30 import org.jspecify.annotations.NonNull; 31 import org.jspecify.annotations.Nullable; 32 33 import java.util.ArrayList; 34 import java.util.Collection; 35 import java.util.Collections; 36 import java.util.HashSet; 37 import java.util.List; 38 import java.util.Objects; 39 import java.util.Set; 40 41 /** 42 * Configurations needed for a capture request. 43 * 44 * <p>The CaptureConfig contains all the {@link android.hardware.camera2} parameters that are 45 * required to issue a {@link CaptureRequest}. 46 */ 47 public final class CaptureConfig { 48 /** Indicates template type is not set. */ 49 public static final int TEMPLATE_TYPE_NONE = -1; 50 51 /** 52 * Request that the implementation rotate the image. 53 * 54 * <p> Currently only applicable for {@link androidx.camera.core.ImageProxy} which are of 55 * JPEG format. 56 * 57 * Option: camerax.core.rotation 58 */ 59 public static final Config.Option<Integer> OPTION_ROTATION = 60 Config.Option.create("camerax.core.captureConfig.rotation", int.class); 61 62 /** 63 * Sets the compression quality of the captured JPEG image. 64 * 65 * See {@link CaptureRequest#JPEG_QUALITY}. 66 * 67 * Option: camerax.core.captureConfig.jpegQuality 68 */ 69 public static final Config.Option<Integer> OPTION_JPEG_QUALITY = 70 Config.Option.create("camerax.core.captureConfig.jpegQuality", Integer.class); 71 72 /** 73 * Option: camerax.core.camera.resolvedFrameRate 74 * 75 * <p> The frame rate that is resolved for all use cases based on supported surface/stream spec 76 * combinations. 77 */ 78 private static final Config.Option<Range<Integer>> OPTION_RESOLVED_FRAME_RATE = 79 Config.Option.create("camerax.core.captureConfig.resolvedFrameRate", Range.class); 80 81 /** Key to get/set the CaptureConfig ID from the TagBundle */ 82 public static final String CAPTURE_CONFIG_ID_TAG_KEY = "CAPTURE_CONFIG_ID_KEY"; 83 84 public static final int DEFAULT_ID = -1; 85 86 /** The set of {@link Surface} that data from the camera will be put into. */ 87 final List<DeferrableSurface> mSurfaces; 88 89 final Config mImplementationOptions; 90 91 /** 92 * The templates used for configuring a {@link CaptureRequest}. This must match the constants 93 * defined by {@link CameraDevice} 94 */ 95 final int mTemplateType; 96 97 final boolean mPostviewEnabled; 98 99 /** The camera capture callback for a {@link CameraCaptureSession}. */ 100 final List<CameraCaptureCallback> mCameraCaptureCallbacks; 101 102 /** True if this capture request needs a repeating surface */ 103 private final boolean mUseRepeatingSurface; 104 105 /** The tag collection for associating capture result with capture request. */ 106 private final @NonNull TagBundle mTagBundle; 107 108 /** 109 * The camera capture result for reprocessing capture request. 110 */ 111 private final @Nullable CameraCaptureResult mCameraCaptureResult; 112 113 /** 114 * Private constructor for a CaptureConfig. 115 * 116 * <p>In practice, the {@link CaptureConfig.Builder} will be used to construct a CaptureConfig. 117 * 118 * @param surfaces The set of {@link Surface} where data will be put into. 119 * @param implementationOptions The generic parameters to be passed to the 120 * {@link CameraInternal} class. 121 * @param templateType The template for parameters of the CaptureRequest. This 122 * must match the 123 * constants defined by {@link CameraDevice}. 124 * @param cameraCaptureCallbacks All camera capture callbacks. 125 * @param cameraCaptureResult The {@link CameraCaptureResult} for reprocessing capture 126 * request. 127 */ CaptureConfig( List<DeferrableSurface> surfaces, Config implementationOptions, int templateType, boolean postviewEnabled, List<CameraCaptureCallback> cameraCaptureCallbacks, boolean useRepeatingSurface, @NonNull TagBundle tagBundle, @Nullable CameraCaptureResult cameraCaptureResult)128 CaptureConfig( 129 List<DeferrableSurface> surfaces, 130 Config implementationOptions, 131 int templateType, 132 boolean postviewEnabled, 133 List<CameraCaptureCallback> cameraCaptureCallbacks, 134 boolean useRepeatingSurface, 135 @NonNull TagBundle tagBundle, 136 @Nullable CameraCaptureResult cameraCaptureResult) { 137 mSurfaces = surfaces; 138 mImplementationOptions = implementationOptions; 139 mTemplateType = templateType; 140 mCameraCaptureCallbacks = Collections.unmodifiableList(cameraCaptureCallbacks); 141 mUseRepeatingSurface = useRepeatingSurface; 142 mTagBundle = tagBundle; 143 mCameraCaptureResult = cameraCaptureResult; 144 mPostviewEnabled = postviewEnabled; 145 } 146 147 /** Returns an instance of a capture configuration with minimal configurations. */ defaultEmptyCaptureConfig()148 public static @NonNull CaptureConfig defaultEmptyCaptureConfig() { 149 return new CaptureConfig.Builder().build(); 150 } 151 152 /** 153 * Returns an instance of {@link CameraCaptureResult} for reprocessing capture request. 154 * 155 * @return {@link CameraCaptureResult}. 156 */ getCameraCaptureResult()157 public @Nullable CameraCaptureResult getCameraCaptureResult() { 158 return mCameraCaptureResult; 159 } 160 161 /** Get all the surfaces that the request will write data to. */ getSurfaces()162 public @NonNull List<DeferrableSurface> getSurfaces() { 163 return Collections.unmodifiableList(mSurfaces); 164 } 165 getImplementationOptions()166 public @NonNull Config getImplementationOptions() { 167 return mImplementationOptions; 168 } 169 170 /** 171 * Gets the template type. 172 * 173 * <p>If not set, returns {@link #TEMPLATE_TYPE_NONE}. 174 */ getTemplateType()175 public int getTemplateType() { 176 return mTemplateType; 177 } 178 179 /** 180 * Returns the ID of the {@link CaptureConfig} that identifies which {@link CaptureConfig} is 181 * triggering the {@link CameraCaptureCallback} callback methods upon its submission. 182 * 183 * <p>The ID will be passed in every methods in {@link CameraCaptureCallback}. Callers have 184 * to set the ID explicitly otherwise it returns {@link #DEFAULT_ID} by default. 185 */ getId()186 public int getId() { 187 Object id = mTagBundle.getTag(CAPTURE_CONFIG_ID_TAG_KEY); 188 if (id == null) { 189 return DEFAULT_ID; 190 } 191 return (int) id; 192 } 193 getExpectedFrameRateRange()194 public @NonNull Range<Integer> getExpectedFrameRateRange() { 195 return Objects.requireNonNull( 196 mImplementationOptions.retrieveOption(OPTION_RESOLVED_FRAME_RATE, 197 StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED)); 198 } 199 200 @StabilizationMode.Mode getPreviewStabilizationMode()201 public int getPreviewStabilizationMode() { 202 return Objects.requireNonNull(mImplementationOptions.retrieveOption( 203 UseCaseConfig.OPTION_PREVIEW_STABILIZATION_MODE, StabilizationMode.UNSPECIFIED)); 204 } 205 206 @StabilizationMode.Mode getVideoStabilizationMode()207 public int getVideoStabilizationMode() { 208 return Objects.requireNonNull( 209 mImplementationOptions.retrieveOption(OPTION_VIDEO_STABILIZATION_MODE, 210 StabilizationMode.UNSPECIFIED)); 211 } 212 isPostviewEnabled()213 public boolean isPostviewEnabled() { 214 return mPostviewEnabled; 215 } 216 isUseRepeatingSurface()217 public boolean isUseRepeatingSurface() { 218 return mUseRepeatingSurface; 219 } 220 221 /** Obtains all registered {@link CameraCaptureCallback} callbacks. */ getCameraCaptureCallbacks()222 public @NonNull List<CameraCaptureCallback> getCameraCaptureCallbacks() { 223 return mCameraCaptureCallbacks; 224 } 225 getTagBundle()226 public @NonNull TagBundle getTagBundle() { 227 return mTagBundle; 228 } 229 230 /** 231 * Interface for unpacking a configuration into a CaptureConfig.Builder 232 */ 233 public interface OptionUnpacker { 234 235 /** 236 * Apply the options from the config onto the builder 237 * 238 * @param config the set of options to apply 239 * @param builder the builder on which to apply the options 240 */ unpack(@onNull UseCaseConfig<?> config, CaptureConfig.@NonNull Builder builder)241 void unpack(@NonNull UseCaseConfig<?> config, CaptureConfig.@NonNull Builder builder); 242 } 243 244 /** 245 * Builder for easy modification/rebuilding of a {@link CaptureConfig}. 246 */ 247 public static final class Builder { 248 private final Set<DeferrableSurface> mSurfaces = new HashSet<>(); 249 private MutableConfig mImplementationOptions = MutableOptionsBundle.create(); 250 private int mTemplateType = TEMPLATE_TYPE_NONE; 251 private boolean mPostviewEnabled = false; 252 private List<CameraCaptureCallback> mCameraCaptureCallbacks = new ArrayList<>(); 253 private boolean mUseRepeatingSurface = false; 254 private MutableTagBundle mMutableTagBundle = MutableTagBundle.create(); 255 private @Nullable CameraCaptureResult mCameraCaptureResult; 256 Builder()257 public Builder() { 258 } 259 Builder(CaptureConfig base)260 private Builder(CaptureConfig base) { 261 mSurfaces.addAll(base.mSurfaces); 262 mImplementationOptions = MutableOptionsBundle.from(base.mImplementationOptions); 263 mTemplateType = base.mTemplateType; 264 mCameraCaptureCallbacks.addAll(base.getCameraCaptureCallbacks()); 265 mUseRepeatingSurface = base.isUseRepeatingSurface(); 266 mMutableTagBundle = MutableTagBundle.from(base.getTagBundle()); 267 mPostviewEnabled = base.mPostviewEnabled; 268 } 269 270 /** 271 * Creates a {@link Builder} from a {@link UseCaseConfig}. 272 * 273 * <p>Populates the builder with all the properties defined in the base configuration. 274 */ createFrom(@onNull UseCaseConfig<?> config)275 public static @NonNull Builder createFrom(@NonNull UseCaseConfig<?> config) { 276 OptionUnpacker unpacker = config.getCaptureOptionUnpacker(null); 277 if (unpacker == null) { 278 throw new IllegalStateException( 279 "Implementation is missing option unpacker for " 280 + config.getTargetName(config.toString())); 281 } 282 283 Builder builder = new Builder(); 284 285 // Unpack the configuration into this builder 286 unpacker.unpack(config, builder); 287 return builder; 288 } 289 290 /** Create a {@link Builder} from a {@link CaptureConfig} */ from(@onNull CaptureConfig base)291 public static @NonNull Builder from(@NonNull CaptureConfig base) { 292 return new Builder(base); 293 } 294 295 /** 296 * Set the {@link CameraCaptureResult} for reprocessable capture request. 297 * 298 * @param cameraCaptureResult {@link CameraCaptureResult}. 299 */ setCameraCaptureResult(@onNull CameraCaptureResult cameraCaptureResult)300 public void setCameraCaptureResult(@NonNull CameraCaptureResult cameraCaptureResult) { 301 mCameraCaptureResult = cameraCaptureResult; 302 } 303 getTemplateType()304 public int getTemplateType() { 305 return mTemplateType; 306 } 307 getExpectedFrameRateRange()308 public @Nullable Range<Integer> getExpectedFrameRateRange() { 309 return mImplementationOptions.retrieveOption(OPTION_RESOLVED_FRAME_RATE, 310 StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED); 311 } 312 313 /** 314 * Set the template characteristics of the CaptureConfig. 315 * 316 * @param templateType Template constant that must match those defined by {@link 317 * CameraDevice} 318 */ setTemplateType(int templateType)319 public void setTemplateType(int templateType) { 320 mTemplateType = templateType; 321 } 322 323 /** 324 * Set the expected frame rate range of the CaptureConfig. 325 * @param expectedFrameRateRange The frame rate range calculated from the UseCases for 326 * {@link CameraDevice} 327 */ setExpectedFrameRateRange(@onNull Range<Integer> expectedFrameRateRange)328 public void setExpectedFrameRateRange(@NonNull Range<Integer> expectedFrameRateRange) { 329 addImplementationOption(OPTION_RESOLVED_FRAME_RATE, expectedFrameRateRange); 330 } 331 332 /** 333 * Set the preview stabilization mode of the CaptureConfig. 334 * @param mode {@link StabilizationMode} 335 */ setPreviewStabilization(@tabilizationMode.Mode int mode)336 public void setPreviewStabilization(@StabilizationMode.Mode int mode) { 337 if (mode != StabilizationMode.UNSPECIFIED) { 338 addImplementationOption(OPTION_PREVIEW_STABILIZATION_MODE, mode); 339 } 340 } 341 342 /** 343 * Set the video stabilization mode of the CaptureConfig. 344 * @param mode {@link StabilizationMode} 345 */ setVideoStabilization(@tabilizationMode.Mode int mode)346 public void setVideoStabilization(@StabilizationMode.Mode int mode) { 347 if (mode != StabilizationMode.UNSPECIFIED) { 348 addImplementationOption(OPTION_VIDEO_STABILIZATION_MODE, mode); 349 } 350 } 351 setPostviewEnabled(boolean postviewEnabled)352 public void setPostviewEnabled(boolean postviewEnabled) { 353 mPostviewEnabled = postviewEnabled; 354 } 355 356 /** 357 * Adds a {@link CameraCaptureCallback} callback. 358 */ addCameraCaptureCallback(@onNull CameraCaptureCallback cameraCaptureCallback)359 public void addCameraCaptureCallback(@NonNull CameraCaptureCallback cameraCaptureCallback) { 360 if (mCameraCaptureCallbacks.contains(cameraCaptureCallback)) { 361 return; 362 } 363 mCameraCaptureCallbacks.add(cameraCaptureCallback); 364 } 365 366 /** 367 * Adds all {@link CameraCaptureCallback} callbacks. 368 */ addAllCameraCaptureCallbacks( @onNull Collection<CameraCaptureCallback> cameraCaptureCallbacks)369 public void addAllCameraCaptureCallbacks( 370 @NonNull Collection<CameraCaptureCallback> cameraCaptureCallbacks) { 371 for (CameraCaptureCallback c : cameraCaptureCallbacks) { 372 addCameraCaptureCallback(c); 373 } 374 } 375 376 /** 377 * Removes a previously added {@link CameraCaptureCallback} callback. 378 * @param cameraCaptureCallback The callback to remove. 379 * @return {@code true} if the callback was successfully removed. {@code false} if the 380 * callback wasn't present in this builder. 381 */ removeCameraCaptureCallback( @onNull CameraCaptureCallback cameraCaptureCallback)382 public boolean removeCameraCaptureCallback( 383 @NonNull CameraCaptureCallback cameraCaptureCallback) { 384 return mCameraCaptureCallbacks.remove(cameraCaptureCallback); 385 } 386 387 /** Add a surface that the request will write data to. */ addSurface(@onNull DeferrableSurface surface)388 public void addSurface(@NonNull DeferrableSurface surface) { 389 mSurfaces.add(surface); 390 } 391 392 /** Remove a surface that the request will write data to. */ removeSurface(@onNull DeferrableSurface surface)393 public void removeSurface(@NonNull DeferrableSurface surface) { 394 mSurfaces.remove(surface); 395 } 396 397 /** Remove all the surfaces that the request will write data to. */ clearSurfaces()398 public void clearSurfaces() { 399 mSurfaces.clear(); 400 } 401 402 /** Gets the surfaces attached to the request. */ getSurfaces()403 public @NonNull Set<DeferrableSurface> getSurfaces() { 404 return mSurfaces; 405 } 406 setImplementationOptions(@onNull Config config)407 public void setImplementationOptions(@NonNull Config config) { 408 mImplementationOptions = MutableOptionsBundle.from(config); 409 } 410 411 /** Add a set of implementation specific options to the request. */ 412 @SuppressWarnings("unchecked") addImplementationOptions(@onNull Config config)413 public void addImplementationOptions(@NonNull Config config) { 414 for (Config.Option<?> option : config.listOptions()) { 415 @SuppressWarnings("unchecked") // Options/values are being copied directly 416 Config.Option<Object> objectOpt = (Config.Option<Object>) option; 417 418 Object existValue = mImplementationOptions.retrieveOption(objectOpt, null); 419 Object newValue = config.retrieveOption(objectOpt); 420 if (existValue instanceof MultiValueSet) { 421 ((MultiValueSet) existValue).addAll(((MultiValueSet) newValue).getAllItems()); 422 } else { 423 if (newValue instanceof MultiValueSet) { 424 newValue = ((MultiValueSet) newValue).clone(); 425 } 426 mImplementationOptions.insertOption(objectOpt, 427 config.getOptionPriority(option), newValue); 428 } 429 } 430 } 431 432 /** Add a single implementation option to the request. */ addImplementationOption(Config.@onNull Option<T> option, @NonNull T value)433 public <T> void addImplementationOption(Config.@NonNull Option<T> option, 434 @NonNull T value) { 435 mImplementationOptions.insertOption(option, value); 436 } 437 getImplementationOptions()438 public @NonNull Config getImplementationOptions() { 439 return mImplementationOptions; 440 } 441 isUseRepeatingSurface()442 public boolean isUseRepeatingSurface() { 443 return mUseRepeatingSurface; 444 } 445 setUseRepeatingSurface(boolean useRepeatingSurface)446 public void setUseRepeatingSurface(boolean useRepeatingSurface) { 447 mUseRepeatingSurface = useRepeatingSurface; 448 } 449 450 /** Gets a tag's value by a key. */ getTag(@onNull String key)451 public @Nullable Object getTag(@NonNull String key) { 452 return mMutableTagBundle.getTag(key); 453 } 454 455 /** 456 * Sets a tag with a key to CaptureConfig. 457 */ addTag(@onNull String key, @NonNull Object tag)458 public void addTag(@NonNull String key, @NonNull Object tag) { 459 mMutableTagBundle.putTag(key, tag); 460 } 461 462 /** 463 * Sets the ID of the {@link CaptureConfig} that helps identify which 464 * {@link CaptureConfig} is triggering the {@link CameraCaptureCallback} callback methods 465 * upon its submission. 466 * 467 * <p>The ID will be passed in every methods in {@link CameraCaptureCallback}. To ensure 468 * it uniquely identifies the {@link CaptureConfig}, set a unique ID for every 469 * CaptureConfig. 470 */ setId(int id)471 public void setId(int id) { 472 mMutableTagBundle.putTag(CAPTURE_CONFIG_ID_TAG_KEY, id); 473 } 474 /** 475 * Adds a TagBundle to CaptureConfig. 476 */ addAllTags(@onNull TagBundle bundle)477 public void addAllTags(@NonNull TagBundle bundle) { 478 mMutableTagBundle.addTagBundle(bundle); 479 } 480 481 /** 482 * Builds an instance of a CaptureConfig that has all the combined parameters of the 483 * CaptureConfig that have been added to the Builder. 484 */ build()485 public @NonNull CaptureConfig build() { 486 return new CaptureConfig( 487 new ArrayList<>(mSurfaces), 488 OptionsBundle.from(mImplementationOptions), 489 mTemplateType, 490 mPostviewEnabled, 491 new ArrayList<>(mCameraCaptureCallbacks), 492 mUseRepeatingSurface, 493 TagBundle.from(mMutableTagBundle), 494 mCameraCaptureResult); 495 } 496 } 497 } 498