2 * Copyright (C) 2015 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 package android.hardware.camera2.params;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.hardware.camera2.CameraDevice;
24 import android.hardware.camera2.utils.HashCodeHelpers;
25 import android.hardware.camera2.utils.SurfaceUtils;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 import android.util.Log;
29 import android.util.Size;
30 import android.view.Surface;
32 import static com.android.internal.util.Preconditions.*;
35 * A class for describing camera output, which contains a {@link Surface} and its specific
36 * configuration for creating capture session.
38 * @see CameraDevice#createCaptureSessionByOutputConfiguration
41 public final class OutputConfiguration implements Parcelable {
44 * Rotation constant: 0 degree rotation (no rotation)
49 public static final int ROTATION_0 = 0;
52 * Rotation constant: 90 degree counterclockwise rotation.
57 public static final int ROTATION_90 = 1;
60 * Rotation constant: 180 degree counterclockwise rotation.
65 public static final int ROTATION_180 = 2;
68 * Rotation constant: 270 degree counterclockwise rotation.
73 public static final int ROTATION_270 = 3;
76 * Invalid surface group ID.
78 *<p>An {@link OutputConfiguration} with this value indicates that the included surface
79 *doesn't belong to any surface group.</p>
81 public static final int SURFACE_GROUP_ID_NONE = -1;
84 * Create a new {@link OutputConfiguration} instance with a {@link Surface}.
87 * A Surface for camera to output to.
89 * <p>This constructor creates a default configuration, with a surface group ID of
90 * {@value #SURFACE_GROUP_ID_NONE}.</p>
93 public OutputConfiguration(@NonNull Surface surface) {
94 this(SURFACE_GROUP_ID_NONE, surface, ROTATION_0);
98 * Create a new {@link OutputConfiguration} instance with a {@link Surface},
99 * with a surface group ID.
102 * A surface group ID is used to identify which surface group this output surface belongs to. A
103 * surface group is a group of output surfaces that are not intended to receive camera output
104 * buffer streams simultaneously. The {@link CameraDevice} may be able to share the buffers used
105 * by all the surfaces from the same surface group, therefore may reduce the overall memory
106 * footprint. The application should only set the same set ID for the streams that are not
107 * simultaneously streaming. A negative ID indicates that this surface doesn't belong to any
108 * surface group. The default value is {@value #SURFACE_GROUP_ID_NONE}.</p>
110 * <p>For example, a video chat application that has an adaptive output resolution feature would
111 * need two (or more) output resolutions, to switch resolutions without any output glitches.
112 * However, at any given time, only one output is active to minimize outgoing network bandwidth
113 * and encoding overhead. To save memory, the application should set the video outputs to have
114 * the same non-negative group ID, so that the camera device can share the same memory region
115 * for the alternating outputs.</p>
117 * <p>It is not an error to include output streams with the same group ID in the same capture
118 * request, but the resulting memory consumption may be higher than if the two streams were
119 * not in the same surface group to begin with, especially if the outputs have substantially
120 * different dimensions.</p>
122 * @param surfaceGroupId
123 * A group ID for this output, used for sharing memory between multiple outputs.
125 * A Surface for camera to output to.
128 public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface) {
129 this(surfaceGroupId, surface, ROTATION_0);
133 * Create a new {@link OutputConfiguration} instance.
135 * <p>This constructor takes an argument for desired camera rotation</p>
138 * A Surface for camera to output to.
140 * The desired rotation to be applied on camera output. Value must be one of
141 * ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degrees,
142 * application should make sure corresponding surface size has width and height
143 * transposed relative to the width and height without rotation. For example,
144 * if application needs camera to capture 1280x720 picture and rotate it by 90 degree,
145 * application should set rotation to {@code ROTATION_90} and make sure the
146 * corresponding Surface size is 720x1280. Note that {@link CameraDevice} might
147 * throw {@code IllegalArgumentException} if device cannot perform such rotation.
151 public OutputConfiguration(@NonNull Surface surface, int rotation) {
152 this(SURFACE_GROUP_ID_NONE, surface, rotation);
157 * Create a new {@link OutputConfiguration} instance, with rotation and a group ID.
159 * <p>This constructor takes an argument for desired camera rotation and for the surface group
160 * ID. See {@link #OutputConfiguration(int, Surface)} for details of the group ID.</p>
162 * @param surfaceGroupId
163 * A group ID for this output, used for sharing memory between multiple outputs.
165 * A Surface for camera to output to.
167 * The desired rotation to be applied on camera output. Value must be one of
168 * ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degrees,
169 * application should make sure corresponding surface size has width and height
170 * transposed relative to the width and height without rotation. For example,
171 * if application needs camera to capture 1280x720 picture and rotate it by 90 degree,
172 * application should set rotation to {@code ROTATION_90} and make sure the
173 * corresponding Surface size is 720x1280. Note that {@link CameraDevice} might
174 * throw {@code IllegalArgumentException} if device cannot perform such rotation.
178 public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface, int rotation) {
179 checkNotNull(surface, "Surface must not be null");
180 checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
181 mSurfaceGroupId = surfaceGroupId;
183 mRotation = rotation;
184 mConfiguredSize = SurfaceUtils.getSurfaceSize(surface);
185 mConfiguredFormat = SurfaceUtils.getSurfaceFormat(surface);
186 mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(surface);
187 mConfiguredGenerationId = surface.getGenerationId();
191 * Create a new {@link OutputConfiguration} instance with another {@link OutputConfiguration}
194 * @param other Another {@link OutputConfiguration} instance to be copied.
198 public OutputConfiguration(@NonNull OutputConfiguration other) {
200 throw new IllegalArgumentException("OutputConfiguration shouldn't be null");
203 this.mSurface = other.mSurface;
204 this.mRotation = other.mRotation;
205 this.mSurfaceGroupId = other.mSurfaceGroupId;
206 this.mConfiguredDataspace = other.mConfiguredDataspace;
207 this.mConfiguredFormat = other.mConfiguredFormat;
208 this.mConfiguredSize = other.mConfiguredSize;
209 this.mConfiguredGenerationId = other.mConfiguredGenerationId;
213 * Create an OutputConfiguration from Parcel.
215 private OutputConfiguration(@NonNull Parcel source) {
216 int rotation = source.readInt();
217 int surfaceSetId = source.readInt();
218 Surface surface = Surface.CREATOR.createFromParcel(source);
219 checkNotNull(surface, "Surface must not be null");
220 checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
221 mSurfaceGroupId = surfaceSetId;
223 mRotation = rotation;
224 mConfiguredSize = SurfaceUtils.getSurfaceSize(mSurface);
225 mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurface);
226 mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurface);
227 mConfiguredGenerationId = mSurface.getGenerationId();
231 * Get the {@link Surface} associated with this {@link OutputConfiguration}.
233 * @return the {@link Surface} associated with this {@link OutputConfiguration}.
236 public Surface getSurface() {
241 * Get the rotation associated with this {@link OutputConfiguration}.
243 * @return the rotation associated with this {@link OutputConfiguration}.
244 * Value will be one of ROTATION_[0, 90, 180, 270]
249 public int getRotation() {
254 * Get the surface group ID associated with this {@link OutputConfiguration}.
256 * @return the surface group ID associated with this {@link OutputConfiguration}.
257 * The default value is {@value #SURFACE_GROUP_ID_NONE}.
259 public int getSurfaceGroupId() {
260 return mSurfaceGroupId;
263 public static final Parcelable.Creator<OutputConfiguration> CREATOR =
264 new Parcelable.Creator<OutputConfiguration>() {
266 public OutputConfiguration createFromParcel(Parcel source) {
268 OutputConfiguration outputConfiguration = new OutputConfiguration(source);
269 return outputConfiguration;
270 } catch (Exception e) {
271 Log.e(TAG, "Exception creating OutputConfiguration from parcel", e);
277 public OutputConfiguration[] newArray(int size) {
278 return new OutputConfiguration[size];
283 public int describeContents() {
288 public void writeToParcel(Parcel dest, int flags) {
290 throw new IllegalArgumentException("dest must not be null");
292 dest.writeInt(mRotation);
293 dest.writeInt(mSurfaceGroupId);
294 mSurface.writeToParcel(dest, flags);
298 * Check if this {@link OutputConfiguration} is equal to another {@link OutputConfiguration}.
300 * <p>Two output configurations are only equal if and only if the underlying surfaces, surface
301 * properties (width, height, format, dataspace) when the output configurations are created,
302 * and all other configuration parameters are equal. </p>
304 * @return {@code true} if the objects were equal, {@code false} otherwise
307 public boolean equals(Object obj) {
310 } else if (this == obj) {
312 } else if (obj instanceof OutputConfiguration) {
313 final OutputConfiguration other = (OutputConfiguration) obj;
314 return mRotation == other.mRotation &&
315 mSurface == other.mSurface &&
316 mConfiguredGenerationId == other.mConfiguredGenerationId &&
317 mConfiguredSize.equals(other.mConfiguredSize) &&
318 mConfiguredFormat == other.mConfiguredFormat &&
319 mConfiguredDataspace == other.mConfiguredDataspace &&
320 mSurfaceGroupId == other.mSurfaceGroupId;
329 public int hashCode() {
330 return HashCodeHelpers.hashCode(
331 mRotation, mSurface.hashCode(), mConfiguredGenerationId,
332 mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace, mSurfaceGroupId);
335 private static final String TAG = "OutputConfiguration";
336 private final Surface mSurface;
337 private final int mRotation;
338 private int mSurfaceGroupId;
340 // The size, format, and dataspace of the surface when OutputConfiguration is created.
341 private final Size mConfiguredSize;
342 private final int mConfiguredFormat;
343 private final int mConfiguredDataspace;
344 // Surface generation ID to distinguish changes to Surface native internals
345 private final int mConfiguredGenerationId;