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.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; 31 32 import 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 */ 41 public 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 */ OutputConfiguration(@onNull Surface surface)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 */ OutputConfiguration(int surfaceGroupId, @NonNull Surface surface)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 OutputConfiguration(@onNull Surface surface, int rotation)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 OutputConfiguration(int surfaceGroupId, @NonNull Surface surface, int 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; 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 */ OutputConfiguration(@onNull OutputConfiguration other)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 */ OutputConfiguration(@onNull Parcel source)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 getSurface()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 getRotation()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 */ getSurfaceGroupId()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 describeContents()283 public int describeContents() { 284 return 0; 285 } 286 287 @Override writeToParcel(Parcel dest, int flags)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 equals(Object obj)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 hashCode()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