1 /*
2  * Copyright (C) 2017 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 
18 package android.hardware.camera2.params;
19 
20 import static com.android.internal.util.Preconditions.*;
21 
22 import android.annotation.CallbackExecutor;
23 import android.annotation.FlaggedApi;
24 import android.annotation.IntDef;
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.annotation.SuppressLint;
28 import android.graphics.ColorSpace;
29 import android.hardware.camera2.CameraCaptureSession;
30 import android.hardware.camera2.CameraCharacteristics;
31 import android.hardware.camera2.CameraDevice;
32 import android.hardware.camera2.CameraDevice.CameraDeviceSetup;
33 import android.hardware.camera2.CaptureRequest;
34 import android.hardware.camera2.impl.CameraMetadataNative;
35 import android.hardware.camera2.params.InputConfiguration;
36 import android.hardware.camera2.params.OutputConfiguration;
37 import android.hardware.camera2.utils.HashCodeHelpers;
38 import android.media.ImageReader;
39 import android.os.Parcel;
40 import android.os.Parcelable;
41 
42 import com.android.internal.camera.flags.Flags;
43 
44 import java.lang.annotation.Retention;
45 import java.lang.annotation.RetentionPolicy;
46 import java.util.ArrayList;
47 import java.util.Collections;
48 import java.util.List;
49 import java.util.concurrent.Executor;
50 
51 /**
52  * A helper class that aggregates all supported arguments for capture session initialization.
53  */
54 public final class SessionConfiguration implements Parcelable {
55     private static final String TAG = "SessionConfiguration";
56 
57     /**
58      * A regular session type containing instances of {@link OutputConfiguration} running
59      * at regular non high speed FPS ranges and optionally {@link InputConfiguration} for
60      * reprocessable sessions.
61      *
62      * @see CameraDevice#createCaptureSession(SessionConfiguration)
63      * @see CameraDevice#createReprocessableCaptureSession
64      */
65     public static final int SESSION_REGULAR = CameraDevice.SESSION_OPERATION_MODE_NORMAL;
66 
67     /**
68      * A high speed session type that can only contain instances of {@link OutputConfiguration}.
69      * The outputs can run using high speed FPS ranges. Calls to {@link #setInputConfiguration}
70      * are not supported.
71      * <p>
72      * When using this type, the CameraCaptureSession returned by
73      * {@link android.hardware.camera2.CameraCaptureSession.StateCallback} can be cast to a
74      * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession} to access the extra
75      * methods for constrained high speed recording.
76      * </p>
77      *
78      * @see CameraDevice#createConstrainedHighSpeedCaptureSession
79      */
80     public static final int SESSION_HIGH_SPEED =
81         CameraDevice.SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED;
82 
83     /**
84      * First vendor-specific session mode
85      * @hide
86      */
87     public static final int SESSION_VENDOR_START =
88         CameraDevice.SESSION_OPERATION_MODE_VENDOR_START;
89 
90      /** @hide */
91     @Retention(RetentionPolicy.SOURCE)
92     @IntDef(prefix = {"SESSION_"}, value =
93             {SESSION_REGULAR,
94              SESSION_HIGH_SPEED })
95     public @interface SessionMode {};
96 
97     // Camera capture session related parameters.
98     private List<OutputConfiguration> mOutputConfigurations;
99     private CameraCaptureSession.StateCallback mStateCallback;
100     private int mSessionType;
101     private Executor mExecutor = null;
102     private InputConfiguration mInputConfig = null;
103     private CaptureRequest mSessionParameters = null;
104     private int mColorSpace;
105 
106     /**
107      * Create a new {@link SessionConfiguration}.
108      *
109      * @param sessionType The session type.
110      * @param outputs A list of output configurations for the capture session.
111      * @param executor The executor which should be used to invoke the callback. In general it is
112      *                 recommended that camera operations are not done on the main (UI) thread.
113      * @param cb A state callback interface implementation.
114      *
115      * @see #SESSION_REGULAR
116      * @see #SESSION_HIGH_SPEED
117      * @see CameraDevice#createCaptureSession(SessionConfiguration)
118      */
SessionConfiguration(@essionMode int sessionType, @NonNull List<OutputConfiguration> outputs, @NonNull @CallbackExecutor Executor executor, @NonNull CameraCaptureSession.StateCallback cb)119     public SessionConfiguration(@SessionMode int sessionType,
120             @NonNull List<OutputConfiguration> outputs,
121             @NonNull @CallbackExecutor Executor executor,
122             @NonNull CameraCaptureSession.StateCallback cb) {
123         mSessionType = sessionType;
124         mOutputConfigurations = Collections.unmodifiableList(new ArrayList<>(outputs));
125         mStateCallback = cb;
126         mExecutor = executor;
127     }
128 
129     /**
130      * Create a new {@link SessionConfiguration} with sessionType and output configurations.
131      *
132      * <p>The SessionConfiguration objects created by this constructor can be used by
133      * {@link CameraDeviceSetup#isSessionConfigurationSupported} and {@link
134      * CameraDeviceSetup#getSessionCharacteristics} to query a camera device's feature
135      * combination support and session specific characteristics. For the SessionConfiguration
136      * object to be used to create a capture session, {@link #setStateCallback} must be called to
137      * specify the state callback function, and any incomplete OutputConfigurations must be
138      * completed via {@link OutputConfiguration#addSurface} or
139      * {@link OutputConfiguration#setSurfacesForMultiResolutionOutput} as appropriate.</p>
140      *
141      * @param sessionType The session type.
142      * @param outputs A list of output configurations for the capture session.
143      *
144      * @see #SESSION_REGULAR
145      * @see #SESSION_HIGH_SPEED
146      * @see CameraDevice#createCaptureSession(SessionConfiguration)
147      * @see CameraDeviceSetup#isSessionConfigurationSupported
148      * @see CameraDeviceSetup#getSessionCharacteristics
149      */
150     @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
SessionConfiguration(@essionMode int sessionType, @NonNull List<OutputConfiguration> outputs)151     public SessionConfiguration(@SessionMode int sessionType,
152             @NonNull List<OutputConfiguration> outputs) {
153         mSessionType = sessionType;
154         mOutputConfigurations = Collections.unmodifiableList(new ArrayList<>(outputs));
155     }
156 
157     /**
158      * Create a SessionConfiguration from Parcel.
159      * No support for parcelable 'mStateCallback' and 'mExecutor' yet.
160      */
SessionConfiguration(@onNull Parcel source)161     private SessionConfiguration(@NonNull Parcel source) {
162         int sessionType = source.readInt();
163         int inputWidth = source.readInt();
164         int inputHeight = source.readInt();
165         int inputFormat = source.readInt();
166         boolean isInputMultiResolution = source.readBoolean();
167         ArrayList<OutputConfiguration> outConfigs = new ArrayList<OutputConfiguration>();
168         source.readTypedList(outConfigs, OutputConfiguration.CREATOR);
169         // Ignore the values for hasSessionParameters and settings because we cannot reconstruct
170         // the CaptureRequest object.
171         if (Flags.featureCombinationQuery()) {
172             boolean hasSessionParameters = source.readBoolean();
173             if (hasSessionParameters) {
174                 CameraMetadataNative settings = new CameraMetadataNative();
175                 settings.readFromParcel(source);
176             }
177         }
178 
179         if ((inputWidth > 0) && (inputHeight > 0) && (inputFormat != -1)) {
180             mInputConfig = new InputConfiguration(inputWidth, inputHeight,
181                     inputFormat, isInputMultiResolution);
182         }
183         mSessionType = sessionType;
184         mOutputConfigurations = outConfigs;
185     }
186 
187     public static final @android.annotation.NonNull Parcelable.Creator<SessionConfiguration> CREATOR =
188             new Parcelable.Creator<SessionConfiguration> () {
189         @Override
190         public SessionConfiguration createFromParcel(Parcel source) {
191             return new SessionConfiguration(source);
192         }
193 
194         @Override
195         public SessionConfiguration[] newArray(int size) {
196             return new SessionConfiguration[size];
197         }
198     };
199 
200     @Override
writeToParcel(Parcel dest, int flags)201     public void writeToParcel(Parcel dest, int flags) {
202         if (dest == null) {
203             throw new IllegalArgumentException("dest must not be null");
204         }
205         dest.writeInt(mSessionType);
206         if (mInputConfig != null) {
207             dest.writeInt(mInputConfig.getWidth());
208             dest.writeInt(mInputConfig.getHeight());
209             dest.writeInt(mInputConfig.getFormat());
210             dest.writeBoolean(mInputConfig.isMultiResolution());
211         } else {
212             dest.writeInt(/*inputWidth*/ 0);
213             dest.writeInt(/*inputHeight*/ 0);
214             dest.writeInt(/*inputFormat*/ -1);
215             dest.writeBoolean(/*isMultiResolution*/ false);
216         }
217         dest.writeTypedList(mOutputConfigurations);
218         if (Flags.featureCombinationQuery()) {
219             if (mSessionParameters != null) {
220                 dest.writeBoolean(/*hasSessionParameters*/true);
221                 CameraMetadataNative metadata = mSessionParameters.getNativeCopy();
222                 metadata.writeToParcel(dest, /*flags*/0);
223             } else {
224                 dest.writeBoolean(/*hasSessionParameters*/false);
225             }
226         }
227     }
228 
229     @Override
describeContents()230     public int describeContents() {
231         return 0;
232     }
233 
234     /**
235      * Check if this {@link SessionConfiguration} is equal to another {@link SessionConfiguration}.
236      *
237      * <p>Two output session configurations are only equal if and only if the underlying input
238      * configuration, output configurations, and session type are equal. </p>
239      *
240      * @return {@code true} if the objects were equal, {@code false} otherwise
241      */
242     @Override
equals(@ullable Object obj)243     public boolean equals(@Nullable Object obj) {
244         if (obj == null) {
245             return false;
246         } else if (this == obj) {
247             return true;
248         } else if (obj instanceof SessionConfiguration) {
249             final SessionConfiguration other = (SessionConfiguration) obj;
250             if (mInputConfig != other.mInputConfig || mSessionType != other.mSessionType ||
251                     mOutputConfigurations.size() != other.mOutputConfigurations.size()) {
252                 return false;
253             }
254 
255             for (int i = 0;  i < mOutputConfigurations.size(); i++) {
256                 if (!mOutputConfigurations.get(i).equals(other.mOutputConfigurations.get(i)))
257                     return false;
258             }
259 
260             return true;
261         }
262 
263         return false;
264     }
265 
266     /**
267      * {@inheritDoc}
268      */
269     @Override
hashCode()270     public int hashCode() {
271         return HashCodeHelpers.hashCode(mOutputConfigurations.hashCode(), mInputConfig.hashCode(),
272                 mSessionType);
273     }
274 
275     /**
276      * Retrieve the type of the capture session.
277      *
278      * @return The capture session type.
279      */
getSessionType()280     public @SessionMode int getSessionType() {
281         return mSessionType;
282     }
283 
284     /**
285      * Retrieve the {@link OutputConfiguration} list for the capture session.
286      *
287      * @return A list of output configurations for the capture session.
288      */
getOutputConfigurations()289     public List<OutputConfiguration> getOutputConfigurations() {
290         return mOutputConfigurations;
291     }
292 
293     /**
294      * Retrieve the {@link CameraCaptureSession.StateCallback} for the capture session.
295      *
296      * @return A state callback interface implementation.
297      */
getStateCallback()298     public CameraCaptureSession.StateCallback getStateCallback() {
299         return mStateCallback;
300     }
301 
302     /**
303      * Retrieve the {@link java.util.concurrent.Executor} for the capture session.
304      *
305      * @return The Executor on which the callback will be invoked.
306      */
getExecutor()307     public Executor getExecutor() {
308         return mExecutor;
309     }
310 
311     /**
312      * Sets the {@link InputConfiguration} for a reprocessable session. Input configuration are not
313      * supported for {@link #SESSION_HIGH_SPEED}.
314      *
315      * @param input Input configuration.
316      * @throws UnsupportedOperationException In case it is called for {@link #SESSION_HIGH_SPEED}
317      *                                       type session configuration.
318      */
setInputConfiguration(@onNull InputConfiguration input)319     public void setInputConfiguration(@NonNull InputConfiguration input) {
320         if (mSessionType != SESSION_HIGH_SPEED) {
321             mInputConfig = input;
322         } else {
323             throw new UnsupportedOperationException("Method not supported for high speed session" +
324                     " types");
325         }
326     }
327 
328     /**
329      * Retrieve the {@link InputConfiguration}.
330      *
331      * @return The capture session input configuration.
332      */
getInputConfiguration()333     public InputConfiguration getInputConfiguration() {
334         return mInputConfig;
335     }
336 
337     /**
338      * Sets the session wide camera parameters (see {@link CaptureRequest}). This argument can
339      * be set for every supported session type and will be passed to the camera device as part
340      * of the capture session initialization. Session parameters are a subset of the available
341      * capture request parameters (see {@link CameraCharacteristics#getAvailableSessionKeys})
342      * and their application can introduce internal camera delays. To improve camera performance
343      * it is suggested to change them sparingly within the lifetime of the capture session and
344      * to pass their initial values as part of this method.
345      *
346      * @param params A capture request that includes the initial values for any available
347      *               session wide capture keys. Tags (see {@link CaptureRequest.Builder#setTag}) and
348      *               output targets (see {@link CaptureRequest.Builder#addTarget}) are ignored if
349      *               set. Parameter values not part of
350      *               {@link CameraCharacteristics#getAvailableSessionKeys} will also be ignored. It
351      *               is recommended to build the session parameters using the same template type as
352      *               the initial capture request, so that the session and initial request parameters
353      *               match as much as possible.
354      */
setSessionParameters(CaptureRequest params)355     public void setSessionParameters(CaptureRequest params) {
356         mSessionParameters = params;
357     }
358 
359     /**
360      * Retrieve the session wide camera parameters (see {@link CaptureRequest}).
361      *
362      * @return A capture request that includes the initial values for any available
363      *         session wide capture keys.
364      */
getSessionParameters()365     public CaptureRequest getSessionParameters() {
366         return mSessionParameters;
367     }
368 
369     /**
370      * Set a specific device-supported color space.
371      *
372      * <p>Clients can choose from any profile advertised as supported in
373      * {@link CameraCharacteristics#REQUEST_AVAILABLE_COLOR_SPACE_PROFILES}
374      * queried using {@link ColorSpaceProfiles#getSupportedColorSpaces}.
375      * When set, the colorSpace will override the default color spaces of the output targets,
376      * or the color space implied by the dataSpace passed into an {@link ImageReader}'s
377      * constructor.</p>
378      */
setColorSpace(@onNull ColorSpace.Named colorSpace)379     public void setColorSpace(@NonNull ColorSpace.Named colorSpace) {
380         mColorSpace = colorSpace.ordinal();
381         for (OutputConfiguration outputConfiguration : mOutputConfigurations) {
382             outputConfiguration.setColorSpace(colorSpace);
383         }
384     }
385 
386     /**
387      * Clear the color space, such that the default color space will be used.
388      */
clearColorSpace()389     public void clearColorSpace() {
390         mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
391         for (OutputConfiguration outputConfiguration : mOutputConfigurations) {
392             outputConfiguration.clearColorSpace();
393         }
394     }
395 
396     /**
397      * Return the current color space.
398      *
399      * @return the currently set color space
400      */
401     @SuppressLint("MethodNameUnits")
getColorSpace()402     public @Nullable ColorSpace getColorSpace() {
403         if (mColorSpace != ColorSpaceProfiles.UNSPECIFIED) {
404             return ColorSpace.get(ColorSpace.Named.values()[mColorSpace]);
405         } else {
406             return null;
407         }
408     }
409 
410     /**
411      * Set the state callback and executor.
412      *
413      * <p>This function must be called for the SessionConfiguration object created via {@link
414      * #SessionConfiguration(int, List) SessionConfiguration(int, List&lt;OutputConfiguration&gt;)}
415      * before it's used to create a capture session.</p>
416      *
417      * @param executor The executor which should be used to invoke the callback. In general it is
418      *                 recommended that camera operations are not done on the main (UI) thread.
419      * @param cb A state callback interface implementation.
420      */
421     @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
setStateCallback( @onNull @allbackExecutor Executor executor, @NonNull CameraCaptureSession.StateCallback cb)422     public void setStateCallback(
423             @NonNull @CallbackExecutor Executor executor,
424             @NonNull CameraCaptureSession.StateCallback cb) {
425         mStateCallback = cb;
426         mExecutor = executor;
427     }
428 }
429