diff --git a/api/current.txt b/api/current.txt index 1c77142a583db..58986e7065efc 100644 --- a/api/current.txt +++ b/api/current.txt @@ -14437,13 +14437,13 @@ package android.hardware.camera2.params { public final class OutputConfiguration implements android.os.Parcelable { ctor public OutputConfiguration(android.view.Surface); + ctor public OutputConfiguration(int, android.view.Surface); method public int describeContents(); method public android.view.Surface getSurface(); - method public int getSurfaceSetId(); - method public void setSurfaceSetId(int); + method public int getSurfaceGroupId(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; - field public static final int SURFACE_SET_ID_INVALID = -1; // 0xffffffff + field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff } public final class RggbChannelVector { diff --git a/api/system-current.txt b/api/system-current.txt index ee3e6d3c99c3b..94784d153f169 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -14845,20 +14845,20 @@ package android.hardware.camera2.params { public final class OutputConfiguration implements android.os.Parcelable { ctor public OutputConfiguration(android.view.Surface); + ctor public OutputConfiguration(int, android.view.Surface); ctor public OutputConfiguration(android.view.Surface, int); - ctor public OutputConfiguration(android.hardware.camera2.params.OutputConfiguration); + ctor public OutputConfiguration(int, android.view.Surface, int); method public int describeContents(); method public int getRotation(); method public android.view.Surface getSurface(); - method public int getSurfaceSetId(); - method public void setSurfaceSetId(int); + method public int getSurfaceGroupId(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; field public static final int ROTATION_0 = 0; // 0x0 field public static final int ROTATION_180 = 2; // 0x2 field public static final int ROTATION_270 = 3; // 0x3 field public static final int ROTATION_90 = 1; // 0x1 - field public static final int SURFACE_SET_ID_INVALID = -1; // 0xffffffff + field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff } public final class RggbChannelVector { diff --git a/api/test-current.txt b/api/test-current.txt index fd34f107d2b0a..f20e15ac7da42 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -14449,13 +14449,13 @@ package android.hardware.camera2.params { public final class OutputConfiguration implements android.os.Parcelable { ctor public OutputConfiguration(android.view.Surface); + ctor public OutputConfiguration(int, android.view.Surface); method public int describeContents(); method public android.view.Surface getSurface(); - method public int getSurfaceSetId(); - method public void setSurfaceSetId(int); + method public int getSurfaceGroupId(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; - field public static final int SURFACE_SET_ID_INVALID = -1; // 0xffffffff + field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff } public final class RggbChannelVector { diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 18a155d787071..b5423392b96e2 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -501,11 +501,8 @@ public class CameraDeviceImpl extends CameraDevice Log.d(TAG, "createCaptureSessionByOutputConfiguration"); } - // OutputConfiguration objects aren't immutable, make a copy before using. - List currentOutputs = new ArrayList(); - for (OutputConfiguration output : outputConfigurations) { - currentOutputs.add(new OutputConfiguration(output)); - } + // OutputConfiguration objects are immutable, but need to have our own array + List currentOutputs = new ArrayList<>(outputConfigurations); createCaptureSessionInternal(null, currentOutputs, callback, handler, /*isConstrainedHighSpeed*/false); diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index cd0c4742d2cc5..61b534bf22deb 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -17,6 +17,8 @@ package android.hardware.camera2.params; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.utils.HashCodeHelpers; @@ -71,12 +73,12 @@ public final class OutputConfiguration implements Parcelable { public static final int ROTATION_270 = 3; /** - * Invalid surface set ID. + * Invalid surface group ID. * *

An {@link OutputConfiguration} with this value indicates that the included surface - *doesn't belong to any surface set.

+ *doesn't belong to any surface group.

*/ - public static final int SURFACE_SET_ID_INVALID = -1; + public static final int SURFACE_GROUP_ID_NONE = -1; /** * Create a new {@link OutputConfiguration} instance with a {@link Surface}. @@ -84,11 +86,47 @@ public final class OutputConfiguration implements Parcelable { * @param surface * A Surface for camera to output to. * - *

This constructor creates a default configuration.

+ *

This constructor creates a default configuration, with a surface group ID of + * {@value #SURFACE_GROUP_ID_NONE}.

* */ - public OutputConfiguration(Surface surface) { - this(surface, ROTATION_0); + public OutputConfiguration(@NonNull Surface surface) { + this(SURFACE_GROUP_ID_NONE, surface, ROTATION_0); + } + + /** + * Create a new {@link OutputConfiguration} instance with a {@link Surface}, + * with a surface group ID. + * + *

+ * A surface group ID is used to identify which surface group this output surface belongs to. A + * surface group is a group of output surfaces that are not intended to receive camera output + * buffer streams simultaneously. The {@link CameraDevice} may be able to share the buffers used + * by all the surfaces from the same surface group, therefore may reduce the overall memory + * footprint. The application should only set the same set ID for the streams that are not + * simultaneously streaming. A negative ID indicates that this surface doesn't belong to any + * surface group. The default value is {@value #SURFACE_GROUP_ID_NONE}.

+ * + *

For example, a video chat application that has an adaptive output resolution feature would + * need two (or more) output resolutions, to switch resolutions without any output glitches. + * However, at any given time, only one output is active to minimize outgoing network bandwidth + * and encoding overhead. To save memory, the application should set the video outputs to have + * the same non-negative group ID, so that the camera device can share the same memory region + * for the alternating outputs.

+ * + *

It is not an error to include output streams with the same group ID in the same capture + * request, but the resulting memory consumption may be higher than if the two streams were + * not in the same surface group to begin with, especially if the outputs have substantially + * different dimensions.

+ * + * @param surfaceGroupId + * A group ID for this output, used for sharing memory between multiple outputs. + * @param surface + * A Surface for camera to output to. + * + */ + public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface) { + this(surfaceGroupId, surface, ROTATION_0); } /** @@ -100,9 +138,9 @@ public final class OutputConfiguration implements Parcelable { * A Surface for camera to output to. * @param rotation * The desired rotation to be applied on camera output. Value must be one of - * ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degree, + * ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degrees, * application should make sure corresponding surface size has width and height - * transposed corresponding to the width and height without rotation. For example, + * transposed relative to the width and height without rotation. For example, * if application needs camera to capture 1280x720 picture and rotate it by 90 degree, * application should set rotation to {@code ROTATION_90} and make sure the * corresponding Surface size is 720x1280. Note that {@link CameraDevice} might @@ -110,15 +148,43 @@ public final class OutputConfiguration implements Parcelable { * @hide */ @SystemApi - public OutputConfiguration(Surface surface, int rotation) { + public OutputConfiguration(@NonNull Surface surface, int rotation) { + this(SURFACE_GROUP_ID_NONE, surface, rotation); + } + + + /** + * Create a new {@link OutputConfiguration} instance, with rotation and a group ID. + * + *

This constructor takes an argument for desired camera rotation and for the surface group + * ID. See {@link #OutputConfiguration(int, Surface)} for details of the group ID.

+ * + * @param surfaceGroupId + * A group ID for this output, used for sharing memory between multiple outputs. + * @param surface + * A Surface for camera to output to. + * @param rotation + * The desired rotation to be applied on camera output. Value must be one of + * ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degrees, + * application should make sure corresponding surface size has width and height + * transposed relative to the width and height without rotation. For example, + * if application needs camera to capture 1280x720 picture and rotate it by 90 degree, + * application should set rotation to {@code ROTATION_90} and make sure the + * corresponding Surface size is 720x1280. Note that {@link CameraDevice} might + * throw {@code IllegalArgumentException} if device cannot perform such rotation. + * @hide + */ + @SystemApi + public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface, int rotation) { checkNotNull(surface, "Surface must not be null"); checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant"); - mSurfaceSetId = SURFACE_SET_ID_INVALID; + mSurfaceGroupId = surfaceGroupId; mSurface = surface; mRotation = rotation; mConfiguredSize = SurfaceUtils.getSurfaceSize(surface); mConfiguredFormat = SurfaceUtils.getSurfaceFormat(surface); mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(surface); + mConfiguredGenerationId = surface.getGenerationId(); } /** @@ -129,35 +195,36 @@ public final class OutputConfiguration implements Parcelable { * * @hide */ - @SystemApi - public OutputConfiguration(OutputConfiguration other) { + public OutputConfiguration(@NonNull OutputConfiguration other) { if (other == null) { throw new IllegalArgumentException("OutputConfiguration shouldn't be null"); } this.mSurface = other.mSurface; this.mRotation = other.mRotation; - this.mSurfaceSetId = other.mSurfaceSetId; + this.mSurfaceGroupId = other.mSurfaceGroupId; this.mConfiguredDataspace = other.mConfiguredDataspace; this.mConfiguredFormat = other.mConfiguredFormat; this.mConfiguredSize = other.mConfiguredSize; + this.mConfiguredGenerationId = other.mConfiguredGenerationId; } /** * Create an OutputConfiguration from Parcel. */ - private OutputConfiguration(Parcel source) { + private OutputConfiguration(@NonNull Parcel source) { int rotation = source.readInt(); int surfaceSetId = source.readInt(); Surface surface = Surface.CREATOR.createFromParcel(source); checkNotNull(surface, "Surface must not be null"); checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant"); - mSurfaceSetId = surfaceSetId; + mSurfaceGroupId = surfaceSetId; mSurface = surface; mRotation = rotation; mConfiguredSize = SurfaceUtils.getSurfaceSize(mSurface); mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurface); mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurface); + mConfiguredGenerationId = mSurface.getGenerationId(); } /** @@ -165,6 +232,7 @@ public final class OutputConfiguration implements Parcelable { * * @return the {@link Surface} associated with this {@link OutputConfiguration}. */ + @NonNull public Surface getSurface() { return mSurface; } @@ -183,35 +251,13 @@ public final class OutputConfiguration implements Parcelable { } /** - * Set the surface set ID to this {@link OutputConfiguration}. + * Get the surface group ID associated with this {@link OutputConfiguration}. * - *

- * A surface set ID is used to identify which surface set this output surface belongs to. A - * surface set is a group of output surfaces that are not intended to receive camera output - * buffer streams simultaneously. The {@link CameraDevice} may be able to share the buffers used - * by all the surfaces from the same surface set, therefore may save the overall memory - * footprint. The application should only set the same set ID for the streams that are not - * simultaneously streaming. A negative ID indicates that this surface doesn't belong to any - * surface set. The default value will be {@value #SURFACE_SET_ID_INVALID}. - *

- * - * @param setId + * @return the surface group ID associated with this {@link OutputConfiguration}. + * The default value is {@value #SURFACE_GROUP_ID_NONE}. */ - public void setSurfaceSetId(int setId) { - if (setId < 0) { - setId = SURFACE_SET_ID_INVALID; - } - mSurfaceSetId = setId; - } - - /** - * Get the surface set Id associated with this {@link OutputConfiguration}. - * - * @return the surface set Id associated with this {@link OutputConfiguration}. - * Value will be one of ROTATION_[0, 90, 180, 270] - */ - public int getSurfaceSetId() { - return mSurfaceSetId; + public int getSurfaceGroupId() { + return mSurfaceGroupId; } public static final Parcelable.Creator CREATOR = @@ -244,7 +290,7 @@ public final class OutputConfiguration implements Parcelable { throw new IllegalArgumentException("dest must not be null"); } dest.writeInt(mRotation); - dest.writeInt(mSurfaceSetId); + dest.writeInt(mSurfaceGroupId); mSurface.writeToParcel(dest, flags); } @@ -265,12 +311,13 @@ public final class OutputConfiguration implements Parcelable { return true; } else if (obj instanceof OutputConfiguration) { final OutputConfiguration other = (OutputConfiguration) obj; - return mSurface == other.mSurface && - mRotation == other.mRotation && + return mRotation == other.mRotation && + mSurface == other.mSurface && + mConfiguredGenerationId == other.mConfiguredGenerationId && mConfiguredSize.equals(other.mConfiguredSize) && mConfiguredFormat == other.mConfiguredFormat && mConfiguredDataspace == other.mConfiguredDataspace && - mSurfaceSetId == other.mSurfaceSetId; + mSurfaceGroupId == other.mSurfaceGroupId; } return false; } @@ -280,16 +327,20 @@ public final class OutputConfiguration implements Parcelable { */ @Override public int hashCode() { - return HashCodeHelpers.hashCode(mSurface.hashCode(), mRotation); + return HashCodeHelpers.hashCode( + mRotation, mSurface.hashCode(), mConfiguredGenerationId, + mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace, mSurfaceGroupId); } private static final String TAG = "OutputConfiguration"; private final Surface mSurface; private final int mRotation; - private int mSurfaceSetId; + private int mSurfaceGroupId; // The size, format, and dataspace of the surface when OutputConfiguration is created. private final Size mConfiguredSize; private final int mConfiguredFormat; private final int mConfiguredDataspace; + // Surface generation ID to distinguish changes to Surface native internals + private final int mConfiguredGenerationId; }