• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.graphics.ImageFormat;
24 import android.hardware.camera2.CameraCaptureSession;
25 import android.hardware.camera2.CameraDevice;
26 import android.hardware.camera2.utils.HashCodeHelpers;
27 import android.hardware.camera2.utils.SurfaceUtils;
28 import android.os.Parcel;
29 import android.os.Parcelable;
30 import android.util.Log;
31 import android.util.Size;
32 import android.view.Surface;
33 
34 import static com.android.internal.util.Preconditions.*;
35 
36 /**
37  * A class for describing camera output, which contains a {@link Surface} and its specific
38  * configuration for creating capture session.
39  *
40  * @see CameraDevice#createCaptureSessionByOutputConfiguration
41  *
42  */
43 public final class OutputConfiguration implements Parcelable {
44 
45     /**
46      * Rotation constant: 0 degree rotation (no rotation)
47      *
48      * @hide
49      */
50     @SystemApi
51     public static final int ROTATION_0 = 0;
52 
53     /**
54      * Rotation constant: 90 degree counterclockwise rotation.
55      *
56      * @hide
57      */
58     @SystemApi
59     public static final int ROTATION_90 = 1;
60 
61     /**
62      * Rotation constant: 180 degree counterclockwise rotation.
63      *
64      * @hide
65      */
66     @SystemApi
67     public static final int ROTATION_180 = 2;
68 
69     /**
70      * Rotation constant: 270 degree counterclockwise rotation.
71      *
72      * @hide
73      */
74     @SystemApi
75     public static final int ROTATION_270 = 3;
76 
77     /**
78      * Invalid surface group ID.
79      *
80      *<p>An {@link OutputConfiguration} with this value indicates that the included surface
81      *doesn't belong to any surface group.</p>
82      */
83     public static final int SURFACE_GROUP_ID_NONE = -1;
84 
85     /**
86      * Create a new {@link OutputConfiguration} instance with a {@link Surface}.
87      *
88      * @param surface
89      *          A Surface for camera to output to.
90      *
91      * <p>This constructor creates a default configuration, with a surface group ID of
92      * {@value #SURFACE_GROUP_ID_NONE}.</p>
93      *
94      */
OutputConfiguration(@onNull Surface surface)95     public OutputConfiguration(@NonNull Surface surface) {
96         this(SURFACE_GROUP_ID_NONE, surface, ROTATION_0);
97     }
98 
99     /**
100      * Unknown surface source type.
101      */
102     private final int SURFACE_TYPE_UNKNOWN = -1;
103 
104     /**
105      * The surface is obtained from {@link android.view.SurfaceView}.
106      */
107     private final int SURFACE_TYPE_SURFACE_VIEW = 0;
108 
109     /**
110      * The surface is obtained from {@link android.graphics.SurfaceTexture}.
111      */
112     private final int SURFACE_TYPE_SURFACE_TEXTURE = 1;
113 
114     /**
115      * Create a new {@link OutputConfiguration} instance with a {@link Surface},
116      * with a surface group ID.
117      *
118      * <p>
119      * A surface group ID is used to identify which surface group this output surface belongs to. A
120      * surface group is a group of output surfaces that are not intended to receive camera output
121      * buffer streams simultaneously. The {@link CameraDevice} may be able to share the buffers used
122      * by all the surfaces from the same surface group, therefore may reduce the overall memory
123      * footprint. The application should only set the same set ID for the streams that are not
124      * simultaneously streaming. A negative ID indicates that this surface doesn't belong to any
125      * surface group. The default value is {@value #SURFACE_GROUP_ID_NONE}.</p>
126      *
127      * <p>For example, a video chat application that has an adaptive output resolution feature would
128      * need two (or more) output resolutions, to switch resolutions without any output glitches.
129      * However, at any given time, only one output is active to minimize outgoing network bandwidth
130      * and encoding overhead.  To save memory, the application should set the video outputs to have
131      * the same non-negative group ID, so that the camera device can share the same memory region
132      * for the alternating outputs.</p>
133      *
134      * <p>It is not an error to include output streams with the same group ID in the same capture
135      * request, but the resulting memory consumption may be higher than if the two streams were
136      * not in the same surface group to begin with, especially if the outputs have substantially
137      * different dimensions.</p>
138      *
139      * @param surfaceGroupId
140      *          A group ID for this output, used for sharing memory between multiple outputs.
141      * @param surface
142      *          A Surface for camera to output to.
143      *
144      */
OutputConfiguration(int surfaceGroupId, @NonNull Surface surface)145     public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface) {
146         this(surfaceGroupId, surface, ROTATION_0);
147     }
148 
149     /**
150      * Create a new {@link OutputConfiguration} instance.
151      *
152      * <p>This constructor takes an argument for desired camera rotation</p>
153      *
154      * @param surface
155      *          A Surface for camera to output to.
156      * @param rotation
157      *          The desired rotation to be applied on camera output. Value must be one of
158      *          ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degrees,
159      *          application should make sure corresponding surface size has width and height
160      *          transposed relative to the width and height without rotation. For example,
161      *          if application needs camera to capture 1280x720 picture and rotate it by 90 degree,
162      *          application should set rotation to {@code ROTATION_90} and make sure the
163      *          corresponding Surface size is 720x1280. Note that {@link CameraDevice} might
164      *          throw {@code IllegalArgumentException} if device cannot perform such rotation.
165      * @hide
166      */
167     @SystemApi
OutputConfiguration(@onNull Surface surface, int rotation)168     public OutputConfiguration(@NonNull Surface surface, int rotation) {
169         this(SURFACE_GROUP_ID_NONE, surface, rotation);
170     }
171 
172 
173     /**
174      * Create a new {@link OutputConfiguration} instance, with rotation and a group ID.
175      *
176      * <p>This constructor takes an argument for desired camera rotation and for the surface group
177      * ID.  See {@link #OutputConfiguration(int, Surface)} for details of the group ID.</p>
178      *
179      * @param surfaceGroupId
180      *          A group ID for this output, used for sharing memory between multiple outputs.
181      * @param surface
182      *          A Surface for camera to output to.
183      * @param rotation
184      *          The desired rotation to be applied on camera output. Value must be one of
185      *          ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degrees,
186      *          application should make sure corresponding surface size has width and height
187      *          transposed relative to the width and height without rotation. For example,
188      *          if application needs camera to capture 1280x720 picture and rotate it by 90 degree,
189      *          application should set rotation to {@code ROTATION_90} and make sure the
190      *          corresponding Surface size is 720x1280. Note that {@link CameraDevice} might
191      *          throw {@code IllegalArgumentException} if device cannot perform such rotation.
192      * @hide
193      */
194     @SystemApi
OutputConfiguration(int surfaceGroupId, @NonNull Surface surface, int rotation)195     public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface, int rotation) {
196         checkNotNull(surface, "Surface must not be null");
197         checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
198         mSurfaceGroupId = surfaceGroupId;
199         mSurfaceType = SURFACE_TYPE_UNKNOWN;
200         mSurface = surface;
201         mRotation = rotation;
202         mConfiguredSize = SurfaceUtils.getSurfaceSize(surface);
203         mConfiguredFormat = SurfaceUtils.getSurfaceFormat(surface);
204         mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(surface);
205         mConfiguredGenerationId = surface.getGenerationId();
206         mIsDeferredConfig = false;
207     }
208 
209     /**
210      * Create a new {@link OutputConfiguration} instance, with desired Surface size and Surface
211      * source class.
212      * <p>
213      * This constructor takes an argument for desired Surface size and the Surface source class
214      * without providing the actual output Surface. This is used to setup a output configuration
215      * with a deferred Surface. The application can use this output configuration to create a
216      * session.
217      * </p>
218      * <p>
219      * However, the actual output Surface must be set via {@link #setDeferredSurface} and finish the
220      * deferred Surface configuration via {@link CameraCaptureSession#finishDeferredConfiguration}
221      * before submitting a request with this Surface target. The deferred Surface can only be
222      * obtained from either from {@link android.view.SurfaceView} by calling
223      * {@link android.view.SurfaceHolder#getSurface}, or from
224      * {@link android.graphics.SurfaceTexture} via
225      * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}).
226      * </p>
227      *
228      * @param surfaceSize Size for the deferred surface.
229      * @param klass a non-{@code null} {@link Class} object reference that indicates the source of
230      *            this surface. Only {@link android.view.SurfaceHolder SurfaceHolder.class} and
231      *            {@link android.graphics.SurfaceTexture SurfaceTexture.class} are supported.
232      * @hide
233      */
OutputConfiguration(@onNull Size surfaceSize, @NonNull Class<T> klass)234     public <T> OutputConfiguration(@NonNull Size surfaceSize, @NonNull Class<T> klass) {
235         checkNotNull(klass, "surfaceSize must not be null");
236         checkNotNull(klass, "klass must not be null");
237         if (klass == android.view.SurfaceHolder.class) {
238             mSurfaceType = SURFACE_TYPE_SURFACE_VIEW;
239         } else if (klass == android.graphics.SurfaceTexture.class) {
240             mSurfaceType = SURFACE_TYPE_SURFACE_TEXTURE;
241         } else {
242             mSurfaceType = SURFACE_TYPE_UNKNOWN;
243             throw new IllegalArgumentException("Unknow surface source class type");
244         }
245 
246         mSurfaceGroupId = SURFACE_GROUP_ID_NONE;
247         mSurface = null;
248         mRotation = ROTATION_0;
249         mConfiguredSize = surfaceSize;
250         mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE);
251         mConfiguredDataspace = StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
252         mConfiguredGenerationId = 0;
253         mIsDeferredConfig = true;
254     }
255 
256     /**
257      * Check if this configuration has deferred configuration.
258      *
259      * <p>This will return true if the output configuration was constructed with surface deferred.
260      * It will return true even after the deferred surface is set later.</p>
261      *
262      * @return true if this configuration has deferred surface.
263      * @hide
264      */
isDeferredConfiguration()265     public boolean isDeferredConfiguration() {
266         return mIsDeferredConfig;
267     }
268 
269     /**
270      * Set the deferred surface to this OutputConfiguration.
271      *
272      * <p>
273      * The deferred surface must be obtained from either from {@link android.view.SurfaceView} by
274      * calling {@link android.view.SurfaceHolder#getSurface}, or from
275      * {@link android.graphics.SurfaceTexture} via
276      * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}). After the deferred
277      * surface is set, the application must finish the deferred surface configuration via
278      * {@link CameraCaptureSession#finishDeferredConfiguration} before submitting a request with
279      * this surface target.
280      * </p>
281      *
282      * @param surface The deferred surface to be set.
283      * @throws IllegalArgumentException if the Surface is invalid.
284      * @throws IllegalStateException if a Surface was already set to this deferred
285      *         OutputConfiguration.
286      * @hide
287      */
setDeferredSurface(@onNull Surface surface)288     public void setDeferredSurface(@NonNull Surface surface) {
289         checkNotNull(surface, "Surface must not be null");
290         if (mSurface != null) {
291             throw new IllegalStateException("Deferred surface is already set!");
292         }
293 
294         // This will throw IAE is the surface was abandoned.
295         Size surfaceSize = SurfaceUtils.getSurfaceSize(surface);
296         if (!surfaceSize.equals(mConfiguredSize)) {
297             Log.w(TAG, "Deferred surface size " + surfaceSize +
298                     " is different with pre-configured size " + mConfiguredSize +
299                     ", the pre-configured size will be used.");
300         }
301 
302         mSurface = surface;
303     }
304 
305     /**
306      * Create a new {@link OutputConfiguration} instance with another {@link OutputConfiguration}
307      * instance.
308      *
309      * @param other Another {@link OutputConfiguration} instance to be copied.
310      *
311      * @hide
312      */
OutputConfiguration(@onNull OutputConfiguration other)313     public OutputConfiguration(@NonNull OutputConfiguration other) {
314         if (other == null) {
315             throw new IllegalArgumentException("OutputConfiguration shouldn't be null");
316         }
317 
318         this.mSurface = other.mSurface;
319         this.mRotation = other.mRotation;
320         this.mSurfaceGroupId = other.mSurfaceGroupId;
321         this.mSurfaceType = other.mSurfaceType;
322         this.mConfiguredDataspace = other.mConfiguredDataspace;
323         this.mConfiguredFormat = other.mConfiguredFormat;
324         this.mConfiguredSize = other.mConfiguredSize;
325         this.mConfiguredGenerationId = other.mConfiguredGenerationId;
326         this.mIsDeferredConfig = other.mIsDeferredConfig;
327     }
328 
329     /**
330      * Create an OutputConfiguration from Parcel.
331      */
OutputConfiguration(@onNull Parcel source)332     private OutputConfiguration(@NonNull Parcel source) {
333         int rotation = source.readInt();
334         int surfaceSetId = source.readInt();
335         int surfaceType = source.readInt();
336         int width = source.readInt();
337         int height = source.readInt();
338         Surface surface = Surface.CREATOR.createFromParcel(source);
339         checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
340         mSurfaceGroupId = surfaceSetId;
341         mSurface = surface;
342         mRotation = rotation;
343         if (surface != null) {
344             mSurfaceType = SURFACE_TYPE_UNKNOWN;
345             mConfiguredSize = SurfaceUtils.getSurfaceSize(mSurface);
346             mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurface);
347             mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurface);
348             mConfiguredGenerationId = mSurface.getGenerationId();
349             mIsDeferredConfig = true;
350         } else {
351             mSurfaceType = surfaceType;
352             mConfiguredSize = new Size(width, height);
353             mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE);
354             mConfiguredGenerationId = 0;
355             mConfiguredDataspace =
356                     StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
357             mIsDeferredConfig = false;
358         }
359     }
360 
361     /**
362      * Get the {@link Surface} associated with this {@link OutputConfiguration}.
363      *
364      * @return the {@link Surface} associated with this {@link OutputConfiguration}.
365      */
366     @NonNull
getSurface()367     public Surface getSurface() {
368         return mSurface;
369     }
370 
371     /**
372      * Get the rotation associated with this {@link OutputConfiguration}.
373      *
374      * @return the rotation associated with this {@link OutputConfiguration}.
375      *         Value will be one of ROTATION_[0, 90, 180, 270]
376      *
377      * @hide
378      */
379     @SystemApi
getRotation()380     public int getRotation() {
381         return mRotation;
382     }
383 
384     /**
385      * Get the surface group ID associated with this {@link OutputConfiguration}.
386      *
387      * @return the surface group ID associated with this {@link OutputConfiguration}.
388      *         The default value is {@value #SURFACE_GROUP_ID_NONE}.
389      */
getSurfaceGroupId()390     public int getSurfaceGroupId() {
391         return mSurfaceGroupId;
392     }
393 
394     public static final Parcelable.Creator<OutputConfiguration> CREATOR =
395             new Parcelable.Creator<OutputConfiguration>() {
396         @Override
397         public OutputConfiguration createFromParcel(Parcel source) {
398             try {
399                 OutputConfiguration outputConfiguration = new OutputConfiguration(source);
400                 return outputConfiguration;
401             } catch (Exception e) {
402                 Log.e(TAG, "Exception creating OutputConfiguration from parcel", e);
403                 return null;
404             }
405         }
406 
407         @Override
408         public OutputConfiguration[] newArray(int size) {
409             return new OutputConfiguration[size];
410         }
411     };
412 
413     @Override
describeContents()414     public int describeContents() {
415         return 0;
416     }
417 
418     @Override
writeToParcel(Parcel dest, int flags)419     public void writeToParcel(Parcel dest, int flags) {
420         if (dest == null) {
421             throw new IllegalArgumentException("dest must not be null");
422         }
423         dest.writeInt(mRotation);
424         dest.writeInt(mSurfaceGroupId);
425         dest.writeInt(mSurfaceType);
426         dest.writeInt(mConfiguredSize.getWidth());
427         dest.writeInt(mConfiguredSize.getHeight());
428         if (mSurface != null) {
429             mSurface.writeToParcel(dest, flags);
430         }
431     }
432 
433     /**
434      * Check if this {@link OutputConfiguration} is equal to another {@link OutputConfiguration}.
435      *
436      * <p>Two output configurations are only equal if and only if the underlying surfaces, surface
437      * properties (width, height, format, dataspace) when the output configurations are created,
438      * and all other configuration parameters are equal. </p>
439      *
440      * @return {@code true} if the objects were equal, {@code false} otherwise
441      */
442     @Override
equals(Object obj)443     public boolean equals(Object obj) {
444         if (obj == null) {
445             return false;
446         } else if (this == obj) {
447             return true;
448         } else if (obj instanceof OutputConfiguration) {
449             final OutputConfiguration other = (OutputConfiguration) obj;
450             boolean iSSurfaceEqual = mSurface == other.mSurface &&
451                     mConfiguredGenerationId == other.mConfiguredGenerationId ;
452             if (mIsDeferredConfig) {
453                 Log.i(TAG, "deferred config has the same surface");
454                 iSSurfaceEqual = true;
455             }
456             return mRotation == other.mRotation &&
457                    iSSurfaceEqual&&
458                    mConfiguredSize.equals(other.mConfiguredSize) &&
459                    mConfiguredFormat == other.mConfiguredFormat &&
460                    mConfiguredDataspace == other.mConfiguredDataspace &&
461                    mSurfaceGroupId == other.mSurfaceGroupId &&
462                    mSurfaceType == other.mSurfaceType &&
463                    mIsDeferredConfig == other.mIsDeferredConfig;
464         }
465         return false;
466     }
467 
468     /**
469      * {@inheritDoc}
470      */
471     @Override
hashCode()472     public int hashCode() {
473         // Need ensure that the hashcode remains unchanged after set a deferred surface. Otherwise
474         // The deferred output configuration will be lost in the camera streammap after the deferred
475         // surface is set.
476         if (mIsDeferredConfig) {
477             return HashCodeHelpers.hashCode(
478                     mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace,
479                     mSurfaceGroupId, mSurfaceType);
480         }
481 
482         return HashCodeHelpers.hashCode(
483             mRotation, mSurface.hashCode(), mConfiguredGenerationId,
484             mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace, mSurfaceGroupId);
485     }
486 
487     private static final String TAG = "OutputConfiguration";
488     private Surface mSurface;
489     private final int mRotation;
490     private final int mSurfaceGroupId;
491     // Surface source type, this is only used by the deferred surface configuration objects.
492     private final int mSurfaceType;
493 
494     // The size, format, and dataspace of the surface when OutputConfiguration is created.
495     private final Size mConfiguredSize;
496     private final int mConfiguredFormat;
497     private final int mConfiguredDataspace;
498     // Surface generation ID to distinguish changes to Surface native internals
499     private final int mConfiguredGenerationId;
500     // Flag indicating if this config has deferred surface.
501     private final boolean mIsDeferredConfig;
502 }
503