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
18package android.hardware.camera2.params;
19
20import android.annotation.NonNull;
21import android.annotation.Nullable;
22import android.annotation.SystemApi;
23import android.hardware.camera2.CameraDevice;
24import android.hardware.camera2.utils.HashCodeHelpers;
25import android.hardware.camera2.utils.SurfaceUtils;
26import android.os.Parcel;
27import android.os.Parcelable;
28import android.util.Log;
29import android.util.Size;
30import android.view.Surface;
31
32import static com.android.internal.util.Preconditions.*;
33
34/**
35 * A class for describing camera output, which contains a {@link Surface} and its specific
36 * configuration for creating capture session.
37 *
38 * @see CameraDevice#createCaptureSessionByOutputConfiguration
39 *
40 */
41public final class OutputConfiguration implements Parcelable {
42
43    /**
44     * Rotation constant: 0 degree rotation (no rotation)
45     *
46     * @hide
47     */
48    @SystemApi
49    public static final int ROTATION_0 = 0;
50
51    /**
52     * Rotation constant: 90 degree counterclockwise rotation.
53     *
54     * @hide
55     */
56    @SystemApi
57    public static final int ROTATION_90 = 1;
58
59    /**
60     * Rotation constant: 180 degree counterclockwise rotation.
61     *
62     * @hide
63     */
64    @SystemApi
65    public static final int ROTATION_180 = 2;
66
67    /**
68     * Rotation constant: 270 degree counterclockwise rotation.
69     *
70     * @hide
71     */
72    @SystemApi
73    public static final int ROTATION_270 = 3;
74
75    /**
76     * Invalid surface group ID.
77     *
78     *<p>An {@link OutputConfiguration} with this value indicates that the included surface
79     *doesn't belong to any surface group.</p>
80     */
81    public static final int SURFACE_GROUP_ID_NONE = -1;
82
83    /**
84     * Create a new {@link OutputConfiguration} instance with a {@link Surface}.
85     *
86     * @param surface
87     *          A Surface for camera to output to.
88     *
89     * <p>This constructor creates a default configuration, with a surface group ID of
90     * {@value #SURFACE_GROUP_ID_NONE}.</p>
91     *
92     */
93    public OutputConfiguration(@NonNull Surface surface) {
94        this(SURFACE_GROUP_ID_NONE, surface, ROTATION_0);
95    }
96
97    /**
98     * Create a new {@link OutputConfiguration} instance with a {@link Surface},
99     * with a surface group ID.
100     *
101     * <p>
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>
109     *
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>
116     *
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>
121     *
122     * @param surfaceGroupId
123     *          A group ID for this output, used for sharing memory between multiple outputs.
124     * @param surface
125     *          A Surface for camera to output to.
126     *
127     */
128    public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface) {
129        this(surfaceGroupId, surface, ROTATION_0);
130    }
131
132    /**
133     * Create a new {@link OutputConfiguration} instance.
134     *
135     * <p>This constructor takes an argument for desired camera rotation</p>
136     *
137     * @param surface
138     *          A Surface for camera to output to.
139     * @param rotation
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.
148     * @hide
149     */
150    @SystemApi
151    public OutputConfiguration(@NonNull Surface surface, int rotation) {
152        this(SURFACE_GROUP_ID_NONE, surface, rotation);
153    }
154
155
156    /**
157     * Create a new {@link OutputConfiguration} instance, with rotation and a group ID.
158     *
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>
161     *
162     * @param surfaceGroupId
163     *          A group ID for this output, used for sharing memory between multiple outputs.
164     * @param surface
165     *          A Surface for camera to output to.
166     * @param rotation
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.
175     * @hide
176     */
177    @SystemApi
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;
182        mSurface = surface;
183        mRotation = rotation;
184        mConfiguredSize = SurfaceUtils.getSurfaceSize(surface);
185        mConfiguredFormat = SurfaceUtils.getSurfaceFormat(surface);
186        mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(surface);
187        mConfiguredGenerationId = surface.getGenerationId();
188    }
189
190    /**
191     * Create a new {@link OutputConfiguration} instance with another {@link OutputConfiguration}
192     * instance.
193     *
194     * @param other Another {@link OutputConfiguration} instance to be copied.
195     *
196     * @hide
197     */
198    public OutputConfiguration(@NonNull OutputConfiguration other) {
199        if (other == null) {
200            throw new IllegalArgumentException("OutputConfiguration shouldn't be null");
201        }
202
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;
210    }
211
212    /**
213     * Create an OutputConfiguration from Parcel.
214     */
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;
222        mSurface = surface;
223        mRotation = rotation;
224        mConfiguredSize = SurfaceUtils.getSurfaceSize(mSurface);
225        mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurface);
226        mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurface);
227        mConfiguredGenerationId = mSurface.getGenerationId();
228    }
229
230    /**
231     * Get the {@link Surface} associated with this {@link OutputConfiguration}.
232     *
233     * @return the {@link Surface} associated with this {@link OutputConfiguration}.
234     */
235    @NonNull
236    public Surface getSurface() {
237        return mSurface;
238    }
239
240    /**
241     * Get the rotation associated with this {@link OutputConfiguration}.
242     *
243     * @return the rotation associated with this {@link OutputConfiguration}.
244     *         Value will be one of ROTATION_[0, 90, 180, 270]
245     *
246     * @hide
247     */
248    @SystemApi
249    public int getRotation() {
250        return mRotation;
251    }
252
253    /**
254     * Get the surface group ID associated with this {@link OutputConfiguration}.
255     *
256     * @return the surface group ID associated with this {@link OutputConfiguration}.
257     *         The default value is {@value #SURFACE_GROUP_ID_NONE}.
258     */
259    public int getSurfaceGroupId() {
260        return mSurfaceGroupId;
261    }
262
263    public static final Parcelable.Creator<OutputConfiguration> CREATOR =
264            new Parcelable.Creator<OutputConfiguration>() {
265        @Override
266        public OutputConfiguration createFromParcel(Parcel source) {
267            try {
268                OutputConfiguration outputConfiguration = new OutputConfiguration(source);
269                return outputConfiguration;
270            } catch (Exception e) {
271                Log.e(TAG, "Exception creating OutputConfiguration from parcel", e);
272                return null;
273            }
274        }
275
276        @Override
277        public OutputConfiguration[] newArray(int size) {
278            return new OutputConfiguration[size];
279        }
280    };
281
282    @Override
283    public int describeContents() {
284        return 0;
285    }
286
287    @Override
288    public void writeToParcel(Parcel dest, int flags) {
289        if (dest == null) {
290            throw new IllegalArgumentException("dest must not be null");
291        }
292        dest.writeInt(mRotation);
293        dest.writeInt(mSurfaceGroupId);
294        mSurface.writeToParcel(dest, flags);
295    }
296
297    /**
298     * Check if this {@link OutputConfiguration} is equal to another {@link OutputConfiguration}.
299     *
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>
303     *
304     * @return {@code true} if the objects were equal, {@code false} otherwise
305     */
306    @Override
307    public boolean equals(Object obj) {
308        if (obj == null) {
309            return false;
310        } else if (this == obj) {
311            return true;
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;
321        }
322        return false;
323    }
324
325    /**
326     * {@inheritDoc}
327     */
328    @Override
329    public int hashCode() {
330        return HashCodeHelpers.hashCode(
331            mRotation, mSurface.hashCode(), mConfiguredGenerationId,
332            mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace, mSurfaceGroupId);
333    }
334
335    private static final String TAG = "OutputConfiguration";
336    private final Surface mSurface;
337    private final int mRotation;
338    private int mSurfaceGroupId;
339
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;
346}
347