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; 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.impl.StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED; 23 import static androidx.camera.core.processing.TargetUtils.checkSupportedTargets; 24 import static androidx.camera.core.processing.TargetUtils.getHumanReadableName; 25 import static androidx.core.util.Preconditions.checkArgument; 26 27 import android.util.Range; 28 29 import androidx.annotation.RestrictTo; 30 import androidx.lifecycle.Lifecycle; 31 32 import org.jspecify.annotations.NonNull; 33 import org.jspecify.annotations.Nullable; 34 35 import java.util.ArrayList; 36 import java.util.Arrays; 37 import java.util.List; 38 import java.util.Locale; 39 40 /** 41 * Represents a collection of {@link UseCase}. 42 * 43 * <p>When the {@link UseCaseGroup} is bound to {@link Lifecycle}, it binds all the 44 * {@link UseCase}s to the same {@link Lifecycle}. {@link UseCase}s inside of a 45 * {@link UseCaseGroup} usually share some common properties like the FOV defined by 46 * {@link ViewPort}. 47 */ 48 public final class UseCaseGroup { 49 private final @Nullable ViewPort mViewPort; 50 private final @NonNull List<UseCase> mUseCases; 51 private final @NonNull List<CameraEffect> mEffects; 52 private final @NonNull Range<Integer> mTargetHighSpeedFrameRate; 53 UseCaseGroup(@ullable ViewPort viewPort, @NonNull List<UseCase> useCases, @NonNull List<CameraEffect> effects)54 UseCaseGroup(@Nullable ViewPort viewPort, @NonNull List<UseCase> useCases, 55 @NonNull List<CameraEffect> effects) { 56 mViewPort = viewPort; 57 mUseCases = useCases; 58 mEffects = effects; 59 mTargetHighSpeedFrameRate = FRAME_RATE_RANGE_UNSPECIFIED; 60 } 61 62 /** 63 * Gets the {@link ViewPort} shared by the {@link UseCase} collection. 64 */ getViewPort()65 public @Nullable ViewPort getViewPort() { 66 return mViewPort; 67 } 68 69 /** 70 * Gets the {@link UseCase}s. 71 */ getUseCases()72 public @NonNull List<UseCase> getUseCases() { 73 return mUseCases; 74 } 75 76 /** 77 * Gets the {@link CameraEffect}s. 78 */ getEffects()79 public @NonNull List<CameraEffect> getEffects() { 80 return mEffects; 81 } 82 83 /** 84 * Gets the target high speed frame rate. 85 */ 86 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) getTargetHighSpeedFrameRate()87 public @NonNull Range<Integer> getTargetHighSpeedFrameRate() { 88 return mTargetHighSpeedFrameRate; 89 } 90 91 /** 92 * A builder for generating {@link UseCaseGroup}. 93 */ 94 public static final class Builder { 95 96 // Allow-list effect targets supported by CameraX. 97 private static final List<Integer> SUPPORTED_TARGETS = Arrays.asList( 98 PREVIEW, 99 VIDEO_CAPTURE, 100 IMAGE_CAPTURE, 101 PREVIEW | VIDEO_CAPTURE, 102 PREVIEW | VIDEO_CAPTURE | IMAGE_CAPTURE); 103 104 private ViewPort mViewPort; 105 private final List<UseCase> mUseCases; 106 private final List<CameraEffect> mEffects; 107 Builder()108 public Builder() { 109 mUseCases = new ArrayList<>(); 110 mEffects = new ArrayList<>(); 111 } 112 113 /** 114 * Sets {@link ViewPort} shared by the {@link UseCase}s. 115 */ setViewPort(@onNull ViewPort viewPort)116 public @NonNull Builder setViewPort(@NonNull ViewPort viewPort) { 117 mViewPort = viewPort; 118 return this; 119 } 120 121 /** 122 * Adds a {@link CameraEffect} to the collection. 123 * 124 * <p>The value of {@link CameraEffect#getTargets()} must be one of the supported values 125 * below: 126 * <ul> 127 * <li>{@link CameraEffect#PREVIEW} 128 * <li>{@link CameraEffect#VIDEO_CAPTURE} 129 * <li>{@link CameraEffect#IMAGE_CAPTURE} 130 * <li>{@link CameraEffect#VIDEO_CAPTURE} | {@link CameraEffect#PREVIEW} 131 * <li>{@link CameraEffect#VIDEO_CAPTURE} | {@link CameraEffect#PREVIEW} | 132 * {@link CameraEffect#IMAGE_CAPTURE} 133 * </ul> 134 * 135 * <p>The targets must be mutually exclusive of each other, otherwise, the {@link #build()} 136 * method will throw {@link IllegalArgumentException}. For example, it's invalid to have 137 * one {@link CameraEffect} with target {@link CameraEffect#PREVIEW} and another 138 * {@link CameraEffect} with target {@link CameraEffect#PREVIEW} | 139 * {@link CameraEffect#VIDEO_CAPTURE}, since they both target {@link Preview}. 140 * 141 * <p>Once added, CameraX will use the {@link CameraEffect}s to process the outputs of 142 * the {@link UseCase}s. 143 */ addEffect(@onNull CameraEffect cameraEffect)144 public @NonNull Builder addEffect(@NonNull CameraEffect cameraEffect) { 145 mEffects.add(cameraEffect); 146 return this; 147 } 148 149 /** 150 * Checks effect targets and throw {@link IllegalArgumentException}. 151 * 152 * <p>Throws exception if the effects 1) contains conflicting targets or 2) contains 153 * effects that is not in the allowlist. 154 */ checkEffectTargets()155 private void checkEffectTargets() { 156 int existingTargets = 0; 157 for (CameraEffect effect : mEffects) { 158 int targets = effect.getTargets(); 159 checkSupportedTargets(SUPPORTED_TARGETS, targets); 160 int overlappingTargets = existingTargets & targets; 161 if (overlappingTargets > 0) { 162 throw new IllegalArgumentException(String.format(Locale.US, 163 "More than one effects has targets %s.", 164 getHumanReadableName(overlappingTargets))); 165 } 166 existingTargets |= targets; 167 } 168 } 169 170 171 /** 172 * Adds {@link UseCase} to the collection. 173 */ addUseCase(@onNull UseCase useCase)174 public @NonNull Builder addUseCase(@NonNull UseCase useCase) { 175 mUseCases.add(useCase); 176 return this; 177 } 178 179 /** 180 * Builds a {@link UseCaseGroup} from the current state. 181 */ build()182 public @NonNull UseCaseGroup build() { 183 checkArgument(!mUseCases.isEmpty(), "UseCase must not be empty."); 184 checkEffectTargets(); 185 return new UseCaseGroup(mViewPort, mUseCases, mEffects); 186 } 187 } 188 } 189