1 /*
2  * Copyright (C) 2015 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.IntDef;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.SystemApi;
26 import android.graphics.ImageFormat;
27 import android.hardware.camera2.CameraCaptureSession;
28 import android.hardware.camera2.CameraCharacteristics;
29 import android.hardware.camera2.CameraDevice;
30 import android.hardware.camera2.CameraMetadata;
31 import android.hardware.camera2.MultiResolutionImageReader;
32 import android.hardware.camera2.params.MultiResolutionStreamInfo;
33 import android.hardware.camera2.utils.HashCodeHelpers;
34 import android.hardware.camera2.utils.SurfaceUtils;
35 import android.media.ImageReader;
36 import android.os.Parcel;
37 import android.os.Parcelable;
38 import android.util.ArraySet;
39 import android.util.Log;
40 import android.util.Size;
41 import android.view.Surface;
42 
43 import java.lang.annotation.Retention;
44 import java.lang.annotation.RetentionPolicy;
45 import java.util.ArrayList;
46 import java.util.Collection;
47 import java.util.Collections;
48 import java.util.List;
49 import java.util.Objects;
50 
51 /**
52  * A class for describing camera output, which contains a {@link Surface} and its specific
53  * configuration for creating capture session.
54  *
55  * <p>There are several ways to instantiate, modify and use OutputConfigurations. The most common
56  * and recommended usage patterns are summarized in the following list:</p>
57  *<ul>
58  * <li>Passing a {@link Surface} to the constructor and using the OutputConfiguration instance as
59  * argument to {@link CameraDevice#createCaptureSessionByOutputConfigurations}. This is the most
60  * frequent usage and clients should consider it first before other more complicated alternatives.
61  * </li>
62  *
63  * <li>Passing only a surface source class as an argument to the constructor. This is usually
64  * followed by a call to create a capture session
65  * (see {@link CameraDevice#createCaptureSessionByOutputConfigurations} and a {@link Surface} add
66  * call {@link #addSurface} with a valid {@link Surface}. The sequence completes with
67  * {@link CameraCaptureSession#finalizeOutputConfigurations}. This is the deferred usage case which
68  * aims to enhance performance by allowing the resource-intensive capture session create call to
69  * execute in parallel with any {@link Surface} initialization, such as waiting for a
70  * {@link android.view.SurfaceView} to be ready as part of the UI initialization.</li>
71  *
72  * <li>The third and most complex usage pattern involves surface sharing. Once instantiated an
73  * OutputConfiguration can be enabled for surface sharing via {@link #enableSurfaceSharing}. This
74  * must be done before creating a new capture session and enables calls to
75  * {@link CameraCaptureSession#updateOutputConfiguration}. An OutputConfiguration with enabled
76  * surface sharing can be modified via {@link #addSurface} or {@link #removeSurface}. The updates
77  * to this OutputConfiguration will only come into effect after
78  * {@link CameraCaptureSession#updateOutputConfiguration} returns without throwing exceptions.
79  * Such updates can be done as long as the session is active. Clients should always consider the
80  * additional requirements and limitations placed on the output surfaces (for more details see
81  * {@link #enableSurfaceSharing}, {@link #addSurface}, {@link #removeSurface},
82  * {@link CameraCaptureSession#updateOutputConfiguration}). A trade-off exists between additional
83  * complexity and flexibility. If exercised correctly surface sharing can switch between different
84  * output surfaces without interrupting any ongoing repeating capture requests. This saves time and
85  * can significantly improve the user experience.</li>
86  *
87  * <li>Surface sharing can be used in combination with deferred surfaces. The rules from both cases
88  * are combined and clients must call {@link #enableSurfaceSharing} before creating a capture
89  * session. Attach and/or remove output surfaces via  {@link #addSurface}/{@link #removeSurface} and
90  * finalize the configuration using {@link CameraCaptureSession#finalizeOutputConfigurations}.
91  * {@link CameraCaptureSession#updateOutputConfiguration} can be called after the configuration
92  * finalize method returns without exceptions.</li>
93  *
94  * <li>If the camera device supports multi-resolution output streams, {@link
95  * CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP} will contain the
96  * formats and their corresponding stream info. The application can use an OutputConfiguration
97  * created with the multi-resolution stream info queried from {@link
98  * MultiResolutionStreamConfigurationMap#getOutputInfo} and
99  * {@link android.hardware.camera2.MultiResolutionImageReader} to capture variable size images.
100  *
101  * </ul>
102  *
103  * <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats except
104  * {@link ImageFormat#JPEG} and {@link ImageFormat#RAW_PRIVATE} can be used for sharing, subject to
105  * device support. On prior API levels, only {@link ImageFormat#PRIVATE} format may be used.</p>
106  *
107  * @see CameraDevice#createCaptureSessionByOutputConfigurations
108  * @see CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP
109  *
110  */
111 public final class OutputConfiguration implements Parcelable {
112 
113     /**
114      * Rotation constant: 0 degree rotation (no rotation)
115      *
116      * @hide
117      */
118     @SystemApi
119     public static final int ROTATION_0 = 0;
120 
121     /**
122      * Rotation constant: 90 degree counterclockwise rotation.
123      *
124      * @hide
125      */
126     @SystemApi
127     public static final int ROTATION_90 = 1;
128 
129     /**
130      * Rotation constant: 180 degree counterclockwise rotation.
131      *
132      * @hide
133      */
134     @SystemApi
135     public static final int ROTATION_180 = 2;
136 
137     /**
138      * Rotation constant: 270 degree counterclockwise rotation.
139      *
140      * @hide
141      */
142     @SystemApi
143     public static final int ROTATION_270 = 3;
144 
145     /**
146      * Invalid surface group ID.
147      *
148      *<p>An {@link OutputConfiguration} with this value indicates that the included surface
149      *doesn't belong to any surface group.</p>
150      */
151     public static final int SURFACE_GROUP_ID_NONE = -1;
152 
153     /** @hide */
154      @Retention(RetentionPolicy.SOURCE)
155      @IntDef(prefix = {"SENSOR_PIXEL_MODE_"}, value =
156          {CameraMetadata.SENSOR_PIXEL_MODE_DEFAULT,
157           CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION})
158      public @interface SensorPixelMode {};
159 
160     /**
161      * Create a new {@link OutputConfiguration} instance with a {@link Surface}.
162      *
163      * @param surface
164      *          A Surface for camera to output to.
165      *
166      * <p>This constructor creates a default configuration, with a surface group ID of
167      * {@value #SURFACE_GROUP_ID_NONE}.</p>
168      *
169      */
OutputConfiguration(@onNull Surface surface)170     public OutputConfiguration(@NonNull Surface surface) {
171         this(SURFACE_GROUP_ID_NONE, surface, ROTATION_0);
172     }
173 
174     /**
175      * Unknown surface source type.
176      */
177     private final int SURFACE_TYPE_UNKNOWN = -1;
178 
179     /**
180      * The surface is obtained from {@link android.view.SurfaceView}.
181      */
182     private final int SURFACE_TYPE_SURFACE_VIEW = 0;
183 
184     /**
185      * The surface is obtained from {@link android.graphics.SurfaceTexture}.
186      */
187     private final int SURFACE_TYPE_SURFACE_TEXTURE = 1;
188 
189     /**
190      * Maximum number of surfaces supported by one {@link OutputConfiguration}.
191      *
192      * <p>The combined number of surfaces added by the constructor and
193      * {@link OutputConfiguration#addSurface} should not exceed this value.</p>
194      *
195      */
196     private static final int MAX_SURFACES_COUNT = 4;
197 
198     /**
199      * Create a new {@link OutputConfiguration} instance with a {@link Surface},
200      * with a surface group ID.
201      *
202      * <p>
203      * A surface group ID is used to identify which surface group this output surface belongs to. A
204      * surface group is a group of output surfaces that are not intended to receive camera output
205      * buffer streams simultaneously. The {@link CameraDevice} may be able to share the buffers used
206      * by all the surfaces from the same surface group, therefore may reduce the overall memory
207      * footprint. The application should only set the same set ID for the streams that are not
208      * simultaneously streaming. A negative ID indicates that this surface doesn't belong to any
209      * surface group. The default value is {@value #SURFACE_GROUP_ID_NONE}.</p>
210      *
211      * <p>For example, a video chat application that has an adaptive output resolution feature would
212      * need two (or more) output resolutions, to switch resolutions without any output glitches.
213      * However, at any given time, only one output is active to minimize outgoing network bandwidth
214      * and encoding overhead.  To save memory, the application should set the video outputs to have
215      * the same non-negative group ID, so that the camera device can share the same memory region
216      * for the alternating outputs.</p>
217      *
218      * <p>It is not an error to include output streams with the same group ID in the same capture
219      * request, but the resulting memory consumption may be higher than if the two streams were
220      * not in the same surface group to begin with, especially if the outputs have substantially
221      * different dimensions.</p>
222      *
223      * @param surfaceGroupId
224      *          A group ID for this output, used for sharing memory between multiple outputs.
225      * @param surface
226      *          A Surface for camera to output to.
227      *
228      */
OutputConfiguration(int surfaceGroupId, @NonNull Surface surface)229     public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface) {
230         this(surfaceGroupId, surface, ROTATION_0);
231     }
232 
233     /**
234      * Set the multi-resolution output flag.
235      *
236      * <p>Specify that this OutputConfiguration is part of a multi-resolution output stream group
237      * used by {@link android.hardware.camera2.MultiResolutionImageReader}.</p>
238      *
239      * <p>This function must only be called for an OutputConfiguration with a non-negative
240      * group ID. And all OutputConfigurations of a MultiResolutionImageReader will have the same
241      * group ID and have this flag set.</p>
242      *
243      * @throws IllegalStateException If surface sharing is enabled via {@link #enableSurfaceSharing}
244      *         call, or no non-negative group ID has been set.
245      * @hide
246      */
setMultiResolutionOutput()247     void setMultiResolutionOutput() {
248         if (mIsShared) {
249             throw new IllegalStateException("Multi-resolution output flag must not be set for " +
250                     "configuration with surface sharing");
251         }
252         if (mSurfaceGroupId == SURFACE_GROUP_ID_NONE) {
253             throw new IllegalStateException("Multi-resolution output flag should only be set for " +
254                     "surface with non-negative group ID");
255         }
256 
257         mIsMultiResolution = true;
258     }
259 
260     /**
261      * Create a new {@link OutputConfiguration} instance.
262      *
263      * <p>This constructor takes an argument for desired camera rotation</p>
264      *
265      * @param surface
266      *          A Surface for camera to output to.
267      * @param rotation
268      *          The desired rotation to be applied on camera output. Value must be one of
269      *          ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degrees,
270      *          application should make sure corresponding surface size has width and height
271      *          transposed relative to the width and height without rotation. For example,
272      *          if application needs camera to capture 1280x720 picture and rotate it by 90 degree,
273      *          application should set rotation to {@code ROTATION_90} and make sure the
274      *          corresponding Surface size is 720x1280. Note that {@link CameraDevice} might
275      *          throw {@code IllegalArgumentException} if device cannot perform such rotation.
276      * @hide
277      */
278     @SystemApi
OutputConfiguration(@onNull Surface surface, int rotation)279     public OutputConfiguration(@NonNull Surface surface, int rotation) {
280         this(SURFACE_GROUP_ID_NONE, surface, rotation);
281     }
282 
283     /**
284      * Create a new {@link OutputConfiguration} instance, with rotation and a group ID.
285      *
286      * <p>This constructor takes an argument for desired camera rotation and for the surface group
287      * ID.  See {@link #OutputConfiguration(int, Surface)} for details of the group ID.</p>
288      *
289      * @param surfaceGroupId
290      *          A group ID for this output, used for sharing memory between multiple outputs.
291      * @param surface
292      *          A Surface for camera to output to.
293      * @param rotation
294      *          The desired rotation to be applied on camera output. Value must be one of
295      *          ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degrees,
296      *          application should make sure corresponding surface size has width and height
297      *          transposed relative to the width and height without rotation. For example,
298      *          if application needs camera to capture 1280x720 picture and rotate it by 90 degree,
299      *          application should set rotation to {@code ROTATION_90} and make sure the
300      *          corresponding Surface size is 720x1280. Note that {@link CameraDevice} might
301      *          throw {@code IllegalArgumentException} if device cannot perform such rotation.
302      * @hide
303      */
304     @SystemApi
OutputConfiguration(int surfaceGroupId, @NonNull Surface surface, int rotation)305     public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface, int rotation) {
306         checkNotNull(surface, "Surface must not be null");
307         checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
308         mSurfaceGroupId = surfaceGroupId;
309         mSurfaceType = SURFACE_TYPE_UNKNOWN;
310         mSurfaces = new ArrayList<Surface>();
311         mSurfaces.add(surface);
312         mRotation = rotation;
313         mConfiguredSize = SurfaceUtils.getSurfaceSize(surface);
314         mConfiguredFormat = SurfaceUtils.getSurfaceFormat(surface);
315         mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(surface);
316         mConfiguredGenerationId = surface.getGenerationId();
317         mIsDeferredConfig = false;
318         mIsShared = false;
319         mPhysicalCameraId = null;
320         mIsMultiResolution = false;
321         mSensorPixelModesUsed = new ArrayList<Integer>();
322     }
323 
324     /**
325      * Create a list of {@link OutputConfiguration} instances for the outputs used by a
326      * {@link android.hardware.camera2.MultiResolutionImageReader}.
327      *
328      * <p>This constructor takes an argument for a
329      * {@link android.hardware.camera2.MultiResolutionImageReader}.</p>
330      *
331      * @param multiResolutionImageReader
332      *          The multi-resolution image reader object.
333      */
createInstancesForMultiResolutionOutput( @onNull MultiResolutionImageReader multiResolutionImageReader)334     public static @NonNull Collection<OutputConfiguration> createInstancesForMultiResolutionOutput(
335             @NonNull MultiResolutionImageReader multiResolutionImageReader)  {
336         checkNotNull(multiResolutionImageReader, "Multi-resolution image reader must not be null");
337 
338         int groupId = MULTI_RESOLUTION_GROUP_ID_COUNTER;
339         MULTI_RESOLUTION_GROUP_ID_COUNTER++;
340         // Skip in case the group id counter overflows to -1, the invalid value.
341         if (MULTI_RESOLUTION_GROUP_ID_COUNTER == -1) {
342             MULTI_RESOLUTION_GROUP_ID_COUNTER++;
343         }
344 
345         ImageReader[] imageReaders = multiResolutionImageReader.getReaders();
346         ArrayList<OutputConfiguration> configs = new ArrayList<OutputConfiguration>();
347         for (int i = 0; i < imageReaders.length; i++) {
348             MultiResolutionStreamInfo streamInfo =
349                     multiResolutionImageReader.getStreamInfoForImageReader(imageReaders[i]);
350 
351             OutputConfiguration config = new OutputConfiguration(
352                     groupId, imageReaders[i].getSurface());
353             config.setPhysicalCameraId(streamInfo.getPhysicalCameraId());
354             config.setMultiResolutionOutput();
355             configs.add(config);
356 
357             // No need to call addSensorPixelModeUsed for ultra high resolution sensor camera,
358             // because regular and max resolution output configurations are used for DEFAULT mode
359             // and MAX_RESOLUTION mode respectively by default.
360         }
361 
362         return configs;
363     }
364 
365     /**
366      * Create a new {@link OutputConfiguration} instance, with desired Surface size and Surface
367      * source class.
368      * <p>
369      * This constructor takes an argument for desired Surface size and the Surface source class
370      * without providing the actual output Surface. This is used to setup an output configuration
371      * with a deferred Surface. The application can use this output configuration to create a
372      * session.
373      * </p>
374      * <p>
375      * However, the actual output Surface must be set via {@link #addSurface} and the deferred
376      * Surface configuration must be finalized via {@link
377      * CameraCaptureSession#finalizeOutputConfigurations} before submitting a request with this
378      * Surface target. The deferred Surface can only be obtained either from {@link
379      * android.view.SurfaceView} by calling {@link android.view.SurfaceHolder#getSurface}, or from
380      * {@link android.graphics.SurfaceTexture} via
381      * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}).
382      * </p>
383      *
384      * @param surfaceSize Size for the deferred surface.
385      * @param klass a non-{@code null} {@link Class} object reference that indicates the source of
386      *            this surface. Only {@link android.view.SurfaceHolder SurfaceHolder.class} and
387      *            {@link android.graphics.SurfaceTexture SurfaceTexture.class} are supported.
388      * @throws IllegalArgumentException if the Surface source class is not supported, or Surface
389      *         size is zero.
390      */
OutputConfiguration(@onNull Size surfaceSize, @NonNull Class<T> klass)391     public <T> OutputConfiguration(@NonNull Size surfaceSize, @NonNull Class<T> klass) {
392         checkNotNull(klass, "surfaceSize must not be null");
393         checkNotNull(klass, "klass must not be null");
394         if (klass == android.view.SurfaceHolder.class) {
395             mSurfaceType = SURFACE_TYPE_SURFACE_VIEW;
396         } else if (klass == android.graphics.SurfaceTexture.class) {
397             mSurfaceType = SURFACE_TYPE_SURFACE_TEXTURE;
398         } else {
399             mSurfaceType = SURFACE_TYPE_UNKNOWN;
400             throw new IllegalArgumentException("Unknow surface source class type");
401         }
402 
403         if (surfaceSize.getWidth() == 0 || surfaceSize.getHeight() == 0) {
404             throw new IllegalArgumentException("Surface size needs to be non-zero");
405         }
406 
407         mSurfaceGroupId = SURFACE_GROUP_ID_NONE;
408         mSurfaces = new ArrayList<Surface>();
409         mRotation = ROTATION_0;
410         mConfiguredSize = surfaceSize;
411         mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE);
412         mConfiguredDataspace = StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
413         mConfiguredGenerationId = 0;
414         mIsDeferredConfig = true;
415         mIsShared = false;
416         mPhysicalCameraId = null;
417         mIsMultiResolution = false;
418         mSensorPixelModesUsed = new ArrayList<Integer>();
419     }
420 
421     /**
422      * Enable multiple surfaces sharing the same OutputConfiguration
423      *
424      * <p>For advanced use cases, a camera application may require more streams than the combination
425      * guaranteed by {@link CameraDevice#createCaptureSession}. In this case, more than one
426      * compatible surface can be attached to an OutputConfiguration so that they map to one
427      * camera stream, and the outputs share memory buffers when possible. Due to buffer sharing
428      * clients should be careful when adding surface outputs that modify their input data. If such
429      * case exists, camera clients should have an additional mechanism to synchronize read and write
430      * access between individual consumers.</p>
431      *
432      * <p>Two surfaces are compatible in the below cases:</p>
433      *
434      * <li> Surfaces with the same size, format, dataSpace, and Surface source class. In this case,
435      * {@link CameraDevice#createCaptureSessionByOutputConfigurations} is guaranteed to succeed.
436      *
437      * <li> Surfaces with the same size, format, and dataSpace, but different Surface source classes
438      * that are generally not compatible. However, on some devices, the underlying camera device is
439      * able to use the same buffer layout for both surfaces. The only way to discover if this is the
440      * case is to create a capture session with that output configuration. For example, if the
441      * camera device uses the same private buffer format between a SurfaceView/SurfaceTexture and a
442      * MediaRecorder/MediaCodec, {@link CameraDevice#createCaptureSessionByOutputConfigurations}
443      * will succeed. Otherwise, it fails with {@link
444      * CameraCaptureSession.StateCallback#onConfigureFailed}.
445      * </ol>
446      *
447      * <p>To enable surface sharing, this function must be called before {@link
448      * CameraDevice#createCaptureSessionByOutputConfigurations} or {@link
449      * CameraDevice#createReprocessableCaptureSessionByConfigurations}. Calling this function after
450      * {@link CameraDevice#createCaptureSessionByOutputConfigurations} has no effect.</p>
451      *
452      * <p>Up to {@link #getMaxSharedSurfaceCount} surfaces can be shared for an OutputConfiguration.
453      * The supported surfaces for sharing must be of type SurfaceTexture, SurfaceView,
454      * MediaRecorder, MediaCodec, or implementation defined ImageReader.</p>
455      *
456      * <p>This function must not be called from OuptutConfigurations created by {@link
457      * #createInstancesForMultiResolutionOutput}.</p>
458      *
459      * @throws IllegalStateException If this OutputConfiguration is created via {@link
460      * #createInstancesForMultiResolutionOutput} to back a MultiResolutionImageReader.
461      */
enableSurfaceSharing()462     public void enableSurfaceSharing() {
463         if (mIsMultiResolution) {
464             throw new IllegalStateException("Cannot enable surface sharing on "
465                     + "multi-resolution output configurations");
466         }
467         mIsShared = true;
468     }
469 
470     /**
471      * Set the id of the physical camera for this OutputConfiguration
472      *
473      * <p>In the case one logical camera is made up of multiple physical cameras, it could be
474      * desirable for the camera application to request streams from individual physical cameras.
475      * This call achieves it by mapping the OutputConfiguration to the physical camera id.</p>
476      *
477      * <p>The valid physical camera ids can be queried by {@link
478      * CameraCharacteristics#getPhysicalCameraIds}.</p>
479      *
480      * <p>Passing in a null physicalCameraId means that the OutputConfiguration is for a logical
481      * stream.</p>
482      *
483      * <p>This function must be called before {@link
484      * CameraDevice#createCaptureSessionByOutputConfigurations} or {@link
485      * CameraDevice#createReprocessableCaptureSessionByConfigurations}. Calling this function
486      * after {@link CameraDevice#createCaptureSessionByOutputConfigurations} or {@link
487      * CameraDevice#createReprocessableCaptureSessionByConfigurations} has no effect.</p>
488      *
489      * <p>As of {@link android.os.Build.VERSION_CODES#S Android 12}, an image buffer from a
490      * physical camera stream can be used for reprocessing to logical camera streams and streams
491      * from the same physical camera if the camera device supports multi-resolution input and output
492      * streams. See {@link CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP}
493      * for details. The behaviors of reprocessing from a non-physical camera stream to a physical
494      * camera stream, and from a physical camera stream to a physical camera stream of different
495      * physical camera, are device-specific and not guaranteed to be supported.</p>
496      *
497      * <p>On prior API levels, the surface belonging to a physical camera OutputConfiguration must
498      * not be used as input or output of a reprocessing request. </p>
499      */
setPhysicalCameraId(@ullable String physicalCameraId)500     public void setPhysicalCameraId(@Nullable String physicalCameraId) {
501         mPhysicalCameraId = physicalCameraId;
502     }
503 
504     /**
505      * Add a sensor pixel mode that this OutputConfiguration will be used in.
506      *
507      * <p> In the case that this output stream configuration (format, width, height) is
508      * available through {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}
509      * configurations and
510      * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION},
511      * configurations, the camera sub-system will assume that this {@link OutputConfiguration} will
512      * be used only with {@link android.hardware.camera2.CaptureRequest}s which has
513      * {@link android.hardware.camera2.CaptureRequest#SENSOR_PIXEL_MODE} set to
514      * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT}.
515      * In such cases, if clients intend to use the
516      * {@link OutputConfiguration}(s) in a {@link android.hardware.camera2.CaptureRequest} with
517      * other sensor pixel modes, they must specify which
518      * {@link android.hardware.camera2.CaptureRequest#SENSOR_PIXEL_MODE}(s) they will use this
519      * {@link OutputConfiguration} with, by calling this method.
520      *
521      * In case this output stream configuration (format, width, height) is only in
522      * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION},
523      * configurations, this output target must only be used with
524      * {@link android.hardware.camera2.CaptureRequest}s which has
525      * {@link android.hardware.camera2.CaptureRequest#SENSOR_PIXEL_MODE} set to
526      * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION} and that
527      * is what the camera sub-system will assume. If clients add
528      * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT} in this
529      * case, session configuration will fail, if this {@link OutputConfiguration} is included.
530      *
531      * In case this output stream configuration (format, width, height) is only in
532      * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP},
533      * configurations, this output target must only be used with
534      * {@link android.hardware.camera2.CaptureRequest}s which has
535      * {@link android.hardware.camera2.CaptureRequest#SENSOR_PIXEL_MODE} set to
536      * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT} and that is what
537      * the camera sub-system will assume. If clients add
538      * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION} in this
539      * case, session configuration will fail, if this {@link OutputConfiguration} is included.
540      *
541      * @param sensorPixelModeUsed The sensor pixel mode this OutputConfiguration will be used with
542      * </p>
543      *
544      */
addSensorPixelModeUsed(@ensorPixelMode int sensorPixelModeUsed)545     public void addSensorPixelModeUsed(@SensorPixelMode int sensorPixelModeUsed) {
546         // Verify that the values are in range.
547         if (sensorPixelModeUsed != CameraMetadata.SENSOR_PIXEL_MODE_DEFAULT &&
548                 sensorPixelModeUsed != CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) {
549             throw new IllegalArgumentException("Not a valid sensor pixel mode " +
550                     sensorPixelModeUsed);
551         }
552 
553         if (mSensorPixelModesUsed.contains(sensorPixelModeUsed)) {
554             // Already added, ignore;
555             return;
556         }
557         mSensorPixelModesUsed.add(sensorPixelModeUsed);
558     }
559 
560     /**
561      * Remove a sensor pixel mode, previously added through addSensorPixelModeUsed, from this
562      * OutputConfiguration.
563      *
564      * <p> Sensor pixel modes added via calls to {@link #addSensorPixelModeUsed} can also be removed
565      * from the OutputConfiguration.</p>
566      *
567      * @param sensorPixelModeUsed The sensor pixel mode to be removed.
568      *
569      * @throws IllegalArgumentException If the sensor pixel mode wasn't previously added
570      *                                  through {@link #addSensorPixelModeUsed}.
571      */
removeSensorPixelModeUsed(@ensorPixelMode int sensorPixelModeUsed)572     public void removeSensorPixelModeUsed(@SensorPixelMode int sensorPixelModeUsed) {
573       if (!mSensorPixelModesUsed.remove(Integer.valueOf(sensorPixelModeUsed))) {
574             throw new IllegalArgumentException("sensorPixelMode " + sensorPixelModeUsed +
575                     "is not part of this output configuration");
576       }
577     }
578 
579     /**
580      * Check if this configuration is for a physical camera.
581      *
582      * <p>This returns true if the output configuration was for a physical camera making up a
583      * logical multi camera via {@link OutputConfiguration#setPhysicalCameraId}.</p>
584      * @hide
585      */
isForPhysicalCamera()586     public boolean isForPhysicalCamera() {
587         return (mPhysicalCameraId != null);
588     }
589 
590     /**
591      * Check if this configuration has deferred configuration.
592      *
593      * <p>This will return true if the output configuration was constructed with surface deferred by
594      * {@link OutputConfiguration#OutputConfiguration(Size, Class)}. It will return true even after
595      * the deferred surface is added later by {@link OutputConfiguration#addSurface}.</p>
596      *
597      * @return true if this configuration has deferred surface.
598      * @hide
599      */
isDeferredConfiguration()600     public boolean isDeferredConfiguration() {
601         return mIsDeferredConfig;
602     }
603 
604     /**
605      * Add a surface to this OutputConfiguration.
606      *
607      * <p> This function can be called before or after {@link
608      * CameraDevice#createCaptureSessionByOutputConfigurations}. If it's called after,
609      * the application must finalize the capture session with
610      * {@link CameraCaptureSession#finalizeOutputConfigurations}. It is possible to call this method
611      * after the output configurations have been finalized only in cases of enabled surface sharing
612      * see {@link #enableSurfaceSharing}. The modified output configuration must be updated with
613      * {@link CameraCaptureSession#updateOutputConfiguration}.</p>
614      *
615      * <p> If the OutputConfiguration was constructed with a deferred surface by {@link
616      * OutputConfiguration#OutputConfiguration(Size, Class)}, the added surface must be obtained
617      * from {@link android.view.SurfaceView} by calling {@link android.view.SurfaceHolder#getSurface},
618      * or from {@link android.graphics.SurfaceTexture} via
619      * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}).</p>
620      *
621      * <p> If the OutputConfiguration was constructed by other constructors, the added
622      * surface must be compatible with the existing surface. See {@link #enableSurfaceSharing} for
623      * details of compatible surfaces.</p>
624      *
625      * <p> If the OutputConfiguration already contains a Surface, {@link #enableSurfaceSharing} must
626      * be called before calling this function to add a new Surface.</p>
627      *
628      * @param surface The surface to be added.
629      * @throws IllegalArgumentException if the Surface is invalid, the Surface's
630      *         dataspace/format doesn't match, or adding the Surface would exceed number of
631      *         shared surfaces supported.
632      * @throws IllegalStateException if the Surface was already added to this OutputConfiguration,
633      *         or if the OutputConfiguration is not shared and it already has a surface associated
634      *         with it.
635      */
addSurface(@onNull Surface surface)636     public void addSurface(@NonNull Surface surface) {
637         checkNotNull(surface, "Surface must not be null");
638         if (mSurfaces.contains(surface)) {
639             throw new IllegalStateException("Surface is already added!");
640         }
641         if (mSurfaces.size() == 1 && !mIsShared) {
642             throw new IllegalStateException("Cannot have 2 surfaces for a non-sharing configuration");
643         }
644         if (mSurfaces.size() + 1 > MAX_SURFACES_COUNT) {
645             throw new IllegalArgumentException("Exceeds maximum number of surfaces");
646         }
647 
648         // This will throw IAE is the surface was abandoned.
649         Size surfaceSize = SurfaceUtils.getSurfaceSize(surface);
650         if (!surfaceSize.equals(mConfiguredSize)) {
651             Log.w(TAG, "Added surface size " + surfaceSize +
652                     " is different than pre-configured size " + mConfiguredSize +
653                     ", the pre-configured size will be used.");
654         }
655 
656         if (mConfiguredFormat != SurfaceUtils.getSurfaceFormat(surface)) {
657             throw new IllegalArgumentException("The format of added surface format doesn't match");
658         }
659 
660         // If the surface format is PRIVATE, do not enforce dataSpace because camera device may
661         // override it.
662         if (mConfiguredFormat != ImageFormat.PRIVATE &&
663                 mConfiguredDataspace != SurfaceUtils.getSurfaceDataspace(surface)) {
664             throw new IllegalArgumentException("The dataspace of added surface doesn't match");
665         }
666 
667         mSurfaces.add(surface);
668     }
669 
670     /**
671      * Remove a surface from this OutputConfiguration.
672      *
673      * <p> Surfaces added via calls to {@link #addSurface} can also be removed from the
674      *  OutputConfiguration. The only notable exception is the surface associated with
675      *  the OutputConfigration see {@link #getSurface} which was passed as part of the constructor
676      *  or was added first in the deferred case
677      *  {@link OutputConfiguration#OutputConfiguration(Size, Class)}.</p>
678      *
679      * @param surface The surface to be removed.
680      *
681      * @throws IllegalArgumentException If the surface is associated with this OutputConfiguration
682      *                                  (see {@link #getSurface}) or the surface didn't get added
683      *                                  with {@link #addSurface}.
684      */
removeSurface(@onNull Surface surface)685     public void removeSurface(@NonNull Surface surface) {
686         if (getSurface() == surface) {
687             throw new IllegalArgumentException(
688                     "Cannot remove surface associated with this output configuration");
689         }
690         if (!mSurfaces.remove(surface)) {
691             throw new IllegalArgumentException("Surface is not part of this output configuration");
692         }
693     }
694 
695     /**
696      * Create a new {@link OutputConfiguration} instance with another {@link OutputConfiguration}
697      * instance.
698      *
699      * @param other Another {@link OutputConfiguration} instance to be copied.
700      *
701      * @hide
702      */
OutputConfiguration(@onNull OutputConfiguration other)703     public OutputConfiguration(@NonNull OutputConfiguration other) {
704         if (other == null) {
705             throw new IllegalArgumentException("OutputConfiguration shouldn't be null");
706         }
707 
708         this.mSurfaces = other.mSurfaces;
709         this.mRotation = other.mRotation;
710         this.mSurfaceGroupId = other.mSurfaceGroupId;
711         this.mSurfaceType = other.mSurfaceType;
712         this.mConfiguredDataspace = other.mConfiguredDataspace;
713         this.mConfiguredFormat = other.mConfiguredFormat;
714         this.mConfiguredSize = other.mConfiguredSize;
715         this.mConfiguredGenerationId = other.mConfiguredGenerationId;
716         this.mIsDeferredConfig = other.mIsDeferredConfig;
717         this.mIsShared = other.mIsShared;
718         this.mPhysicalCameraId = other.mPhysicalCameraId;
719         this.mIsMultiResolution = other.mIsMultiResolution;
720         this.mSensorPixelModesUsed = other.mSensorPixelModesUsed;
721     }
722 
723     /**
724      * Create an OutputConfiguration from Parcel.
725      */
OutputConfiguration(@onNull Parcel source)726     private OutputConfiguration(@NonNull Parcel source) {
727         int rotation = source.readInt();
728         int surfaceSetId = source.readInt();
729         int surfaceType = source.readInt();
730         int width = source.readInt();
731         int height = source.readInt();
732         boolean isDeferred = source.readInt() == 1;
733         boolean isShared = source.readInt() == 1;
734         ArrayList<Surface> surfaces = new ArrayList<Surface>();
735         source.readTypedList(surfaces, Surface.CREATOR);
736         String physicalCameraId = source.readString();
737         boolean isMultiResolutionOutput = source.readInt() == 1;
738         int[] sensorPixelModesUsed = source.createIntArray();
739         checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
740 
741         mSurfaceGroupId = surfaceSetId;
742         mRotation = rotation;
743         mSurfaces = surfaces;
744         mConfiguredSize = new Size(width, height);
745         mIsDeferredConfig = isDeferred;
746         mIsShared = isShared;
747         mSurfaces = surfaces;
748         if (mSurfaces.size() > 0) {
749             mSurfaceType = SURFACE_TYPE_UNKNOWN;
750             mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurfaces.get(0));
751             mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurfaces.get(0));
752             mConfiguredGenerationId = mSurfaces.get(0).getGenerationId();
753         } else {
754             mSurfaceType = surfaceType;
755             mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE);
756             mConfiguredDataspace =
757                     StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
758             mConfiguredGenerationId = 0;
759         }
760         mPhysicalCameraId = physicalCameraId;
761         mIsMultiResolution = isMultiResolutionOutput;
762         mSensorPixelModesUsed = convertIntArrayToIntegerList(sensorPixelModesUsed);
763     }
764 
765     /**
766      * Get the maximum supported shared {@link Surface} count.
767      *
768      * @return the maximum number of surfaces that can be added per each OutputConfiguration.
769      *
770      * @see #enableSurfaceSharing
771      */
getMaxSharedSurfaceCount()772     public int getMaxSharedSurfaceCount() {
773         return MAX_SURFACES_COUNT;
774     }
775 
776     /**
777      * Get the {@link Surface} associated with this {@link OutputConfiguration}.
778      *
779      * If more than one surface is associated with this {@link OutputConfiguration}, return the
780      * first one as specified in the constructor or {@link OutputConfiguration#addSurface}.
781      */
getSurface()782     public @Nullable Surface getSurface() {
783         if (mSurfaces.size() == 0) {
784             return null;
785         }
786 
787         return mSurfaces.get(0);
788     }
789 
790     /**
791      * Get the immutable list of surfaces associated with this {@link OutputConfiguration}.
792      *
793      * @return the list of surfaces associated with this {@link OutputConfiguration} as specified in
794      * the constructor and {@link OutputConfiguration#addSurface}. The list should not be modified.
795      */
796     @NonNull
getSurfaces()797     public List<Surface> getSurfaces() {
798         return Collections.unmodifiableList(mSurfaces);
799     }
800 
801     /**
802      * Get the rotation associated with this {@link OutputConfiguration}.
803      *
804      * @return the rotation associated with this {@link OutputConfiguration}.
805      *         Value will be one of ROTATION_[0, 90, 180, 270]
806      *
807      * @hide
808      */
809     @SystemApi
getRotation()810     public int getRotation() {
811         return mRotation;
812     }
813 
814     /**
815      * Get the surface group ID associated with this {@link OutputConfiguration}.
816      *
817      * @return the surface group ID associated with this {@link OutputConfiguration}.
818      *         The default value is {@value #SURFACE_GROUP_ID_NONE}.
819      */
getSurfaceGroupId()820     public int getSurfaceGroupId() {
821         return mSurfaceGroupId;
822     }
823 
824     public static final @android.annotation.NonNull Parcelable.Creator<OutputConfiguration> CREATOR =
825             new Parcelable.Creator<OutputConfiguration>() {
826         @Override
827         public OutputConfiguration createFromParcel(Parcel source) {
828             return new OutputConfiguration(source);
829         }
830 
831         @Override
832         public OutputConfiguration[] newArray(int size) {
833             return new OutputConfiguration[size];
834         }
835     };
836 
837     @Override
describeContents()838     public int describeContents() {
839         return 0;
840     }
841 
convertIntegerToIntList(List<Integer> integerList)842     private static int[] convertIntegerToIntList(List<Integer> integerList) {
843         int[] integerArray = new int[integerList.size()];
844         for (int i = 0; i < integerList.size(); i++) {
845             integerArray[i] = integerList.get(i);
846         }
847         return integerArray;
848     }
849 
convertIntArrayToIntegerList(int[] intArray)850     private static ArrayList<Integer> convertIntArrayToIntegerList(int[] intArray) {
851         ArrayList<Integer> integerList = new ArrayList<Integer>();
852         if (intArray == null) {
853             return integerList;
854         }
855         for (int i = 0; i < intArray.length; i++) {
856             integerList.add(intArray[i]);
857         }
858         return integerList;
859     }
860 
861     @Override
writeToParcel(Parcel dest, int flags)862     public void writeToParcel(Parcel dest, int flags) {
863         if (dest == null) {
864             throw new IllegalArgumentException("dest must not be null");
865         }
866         dest.writeInt(mRotation);
867         dest.writeInt(mSurfaceGroupId);
868         dest.writeInt(mSurfaceType);
869         dest.writeInt(mConfiguredSize.getWidth());
870         dest.writeInt(mConfiguredSize.getHeight());
871         dest.writeInt(mIsDeferredConfig ? 1 : 0);
872         dest.writeInt(mIsShared ? 1 : 0);
873         dest.writeTypedList(mSurfaces);
874         dest.writeString(mPhysicalCameraId);
875         dest.writeInt(mIsMultiResolution ? 1 : 0);
876         // writeList doesn't seem to work well with Integer list.
877         dest.writeIntArray(convertIntegerToIntList(mSensorPixelModesUsed));
878     }
879 
880     /**
881      * Check if this {@link OutputConfiguration} is equal to another {@link OutputConfiguration}.
882      *
883      * <p>Two output configurations are only equal if and only if the underlying surfaces, surface
884      * properties (width, height, format, dataspace) when the output configurations are created,
885      * and all other configuration parameters are equal. </p>
886      *
887      * @return {@code true} if the objects were equal, {@code false} otherwise
888      */
889     @Override
equals(@ullable Object obj)890     public boolean equals(@Nullable Object obj) {
891         if (obj == null) {
892             return false;
893         } else if (this == obj) {
894             return true;
895         } else if (obj instanceof OutputConfiguration) {
896             final OutputConfiguration other = (OutputConfiguration) obj;
897             if (mRotation != other.mRotation ||
898                     !mConfiguredSize.equals(other.mConfiguredSize) ||
899                     mConfiguredFormat != other.mConfiguredFormat ||
900                     mSurfaceGroupId != other.mSurfaceGroupId ||
901                     mSurfaceType != other.mSurfaceType ||
902                     mIsDeferredConfig != other.mIsDeferredConfig ||
903                     mIsShared != other.mIsShared ||
904                     mConfiguredFormat != other.mConfiguredFormat ||
905                     mConfiguredDataspace != other.mConfiguredDataspace ||
906                     mConfiguredGenerationId != other.mConfiguredGenerationId ||
907                     !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId) ||
908                     mIsMultiResolution != other.mIsMultiResolution)
909                 return false;
910             if (mSensorPixelModesUsed.size() != other.mSensorPixelModesUsed.size()) {
911                 return false;
912             }
913             for (int j = 0; j < mSensorPixelModesUsed.size(); j++) {
914                 if (mSensorPixelModesUsed.get(j) != other.mSensorPixelModesUsed.get(j)) {
915                     return false;
916                 }
917             }
918             int minLen = Math.min(mSurfaces.size(), other.mSurfaces.size());
919             for (int i = 0;  i < minLen; i++) {
920                 if (mSurfaces.get(i) != other.mSurfaces.get(i))
921                     return false;
922             }
923 
924             return true;
925         }
926         return false;
927     }
928 
929     /**
930      * {@inheritDoc}
931      */
932     @Override
hashCode()933     public int hashCode() {
934         // Need ensure that the hashcode remains unchanged after adding a deferred surface. Otherwise
935         // the deferred output configuration will be lost in the camera streammap after the deferred
936         // surface is set.
937         if (mIsDeferredConfig) {
938             return HashCodeHelpers.hashCode(
939                     mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace,
940                     mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0,
941                     mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
942                     mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode());
943         }
944 
945         return HashCodeHelpers.hashCode(
946                 mRotation, mSurfaces.hashCode(), mConfiguredGenerationId,
947                 mConfiguredSize.hashCode(), mConfiguredFormat,
948                 mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0,
949                 mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
950                 mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode());
951     }
952 
953     private static final String TAG = "OutputConfiguration";
954 
955     // A surfaceGroupId counter used for MultiResolutionImageReader. Its value is
956     // incremented everytime {@link createInstancesForMultiResolutionOutput} is called.
957     private static int MULTI_RESOLUTION_GROUP_ID_COUNTER = 0;
958 
959     private ArrayList<Surface> mSurfaces;
960     private final int mRotation;
961     private final int mSurfaceGroupId;
962     // Surface source type, this is only used by the deferred surface configuration objects.
963     private final int mSurfaceType;
964 
965     // The size, format, and dataspace of the surface when OutputConfiguration is created.
966     private final Size mConfiguredSize;
967     private final int mConfiguredFormat;
968     private final int mConfiguredDataspace;
969     // Surface generation ID to distinguish changes to Surface native internals
970     private final int mConfiguredGenerationId;
971     // Flag indicating if this config has deferred surface.
972     private final boolean mIsDeferredConfig;
973     // Flag indicating if this config has shared surfaces
974     private boolean mIsShared;
975     // The physical camera id that this output configuration is for.
976     private String mPhysicalCameraId;
977     // Flag indicating if this config is for a multi-resolution output with a
978     // MultiResolutionImageReader
979     private boolean mIsMultiResolution;
980     // The sensor pixel modes that this OutputConfiguration will use
981     private ArrayList<Integer> mSensorPixelModesUsed;
982 }
983