diff --git a/api/current.txt b/api/current.txt index 9f1ab1bfc7962..3b908b61b0906 100644 --- a/api/current.txt +++ b/api/current.txt @@ -14332,7 +14332,7 @@ package android.hardware.camera2 { method public abstract int capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public abstract int captureBurst(java.util.List, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public abstract void close(); - method public abstract void finishDeferredConfiguration(java.util.List) throws android.hardware.camera2.CameraAccessException; + method public abstract void finalizeOutputConfigurations(java.util.List) throws android.hardware.camera2.CameraAccessException; method public abstract android.hardware.camera2.CameraDevice getDevice(); method public abstract android.view.Surface getInputSurface(); method public abstract boolean isReprocessable(); @@ -14973,10 +14973,12 @@ package android.hardware.camera2.params { ctor public OutputConfiguration(android.view.Surface); ctor public OutputConfiguration(int, android.view.Surface); ctor public OutputConfiguration(android.util.Size, java.lang.Class); + method public void addSurface(android.view.Surface); method public int describeContents(); + method public void enableSurfaceSharing(); method public android.view.Surface getSurface(); method public int getSurfaceGroupId(); - method public void setDeferredSurface(android.view.Surface); + method public java.util.List getSurfaces(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff diff --git a/api/system-current.txt b/api/system-current.txt index 5c107aec939cb..04c23b38e8dd3 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -14862,7 +14862,7 @@ package android.hardware.camera2 { method public abstract int capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public abstract int captureBurst(java.util.List, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public abstract void close(); - method public abstract void finishDeferredConfiguration(java.util.List) throws android.hardware.camera2.CameraAccessException; + method public abstract void finalizeOutputConfigurations(java.util.List) throws android.hardware.camera2.CameraAccessException; method public abstract android.hardware.camera2.CameraDevice getDevice(); method public abstract android.view.Surface getInputSurface(); method public abstract boolean isReprocessable(); @@ -15505,11 +15505,13 @@ package android.hardware.camera2.params { ctor public OutputConfiguration(android.view.Surface, int); ctor public OutputConfiguration(int, android.view.Surface, int); ctor public OutputConfiguration(android.util.Size, java.lang.Class); + method public void addSurface(android.view.Surface); method public int describeContents(); + method public void enableSurfaceSharing(); method public int getRotation(); method public android.view.Surface getSurface(); method public int getSurfaceGroupId(); - method public void setDeferredSurface(android.view.Surface); + method public java.util.List getSurfaces(); 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 diff --git a/api/test-current.txt b/api/test-current.txt index 84aa96c89b036..53a9d10f514eb 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -14364,7 +14364,7 @@ package android.hardware.camera2 { method public abstract int capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public abstract int captureBurst(java.util.List, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public abstract void close(); - method public abstract void finishDeferredConfiguration(java.util.List) throws android.hardware.camera2.CameraAccessException; + method public abstract void finalizeOutputConfigurations(java.util.List) throws android.hardware.camera2.CameraAccessException; method public abstract android.hardware.camera2.CameraDevice getDevice(); method public abstract android.view.Surface getInputSurface(); method public abstract boolean isReprocessable(); @@ -15005,10 +15005,12 @@ package android.hardware.camera2.params { ctor public OutputConfiguration(android.view.Surface); ctor public OutputConfiguration(int, android.view.Surface); ctor public OutputConfiguration(android.util.Size, java.lang.Class); + method public void addSurface(android.view.Surface); method public int describeContents(); + method public void enableSurfaceSharing(); method public android.view.Surface getSurface(); method public int getSurfaceGroupId(); - method public void setDeferredSurface(android.view.Surface); + method public java.util.List getSurfaces(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java index 62b7f32c43c00..bcebb7dc594d1 100644 --- a/core/java/android/hardware/camera2/CameraCaptureSession.java +++ b/core/java/android/hardware/camera2/CameraCaptureSession.java @@ -221,8 +221,8 @@ public abstract class CameraCaptureSession implements AutoCloseable { public abstract void tearDown(@NonNull Surface surface) throws CameraAccessException; /** - *

Finish the deferred output configurations where the output Surface was not configured - * before.

+ *

Finalize the output configurations that now have their deferred and/or extra Surfaces + * included.

* *

For camera use cases where a preview and other output configurations need to be * configured, it can take some time for the preview Surface to be ready. For example, if the @@ -235,22 +235,31 @@ public abstract class CameraCaptureSession implements AutoCloseable { * and defer the preview output configuration until the Surface is ready. After the * {@link CameraCaptureSession} is created successfully with this deferred output and other * normal outputs, the application can start submitting requests as long as they do not include - * deferred output Surfaces. Once a deferred Surface is ready, the application can set the - * Surface on the deferred output configuration with the - * {@link OutputConfiguration#setDeferredSurface} method, and then finish the deferred output + * deferred output Surfaces. Once a deferred Surface is ready, the application can add the + * Surface to the deferred output configuration with the + * {@link OutputConfiguration#addSurface} method, and then update the deferred output * configuration via this method, before it can submit capture requests with this output * target.

* - *

The output Surfaces included by this list of deferred + *

This function can also be called in case where multiple surfaces share the same + * OutputConfiguration, and one of the surfaces becomes available after the {@link + * CameraCaptureSession} is created. In that case, the application must first create the + * OutputConfiguration with the available Surface, then enable furture surface sharing via + * {@link OutputConfiguration#enableSurfaceSharing}, before creating the CameraCaptureSession. + * After the CameraCaptureSession is created, and once the extra Surface becomes available, the + * application must then call {@link OutputConfiguration#addSurface} before finalizing the + * configuration with this method.

+ * + *

The output Surfaces included by this list of * {@link OutputConfiguration OutputConfigurations} can be used as {@link CaptureRequest} * targets as soon as this call returns.

* *

This method is not supported by * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}-level devices.

* - * @param deferredOutputConfigs a list of {@link OutputConfiguration OutputConfigurations} that - * have had {@link OutputConfiguration#setDeferredSurface setDeferredSurface} invoked - * with a valid output Surface. + * @param outputConfigs a list of {@link OutputConfiguration OutputConfigurations} that + * have had {@link OutputConfiguration#addSurface addSurface} invoked with a valid + * output Surface after {@link CameraDevice#createCaptureSessionByOutputConfigurations}. * @throws CameraAccessException if the camera device is no longer connected or has encountered * a fatal error. * @throws IllegalStateException if this session is no longer active, either because the session @@ -261,8 +270,8 @@ public abstract class CameraCaptureSession implements AutoCloseable { * source. Or if one of the output configuration was already finished with an * included surface in a prior call. */ - public abstract void finishDeferredConfiguration( - List deferredOutputConfigs) throws CameraAccessException; + public abstract void finalizeOutputConfigurations( + List outputConfigs) throws CameraAccessException; /** *

Submit a request for an image to be captured by the camera device.

diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index 4befb29ae075f..891df6362d29f 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -151,9 +151,9 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession } @Override - public void finishDeferredConfiguration( - List deferredOutputConfigs) throws CameraAccessException { - mDeviceImpl.finishDeferredConfig(deferredOutputConfigs); + public void finalizeOutputConfigurations( + List outputConfigs) throws CameraAccessException { + mDeviceImpl.finalizeOutputConfigs(outputConfigs); } @Override diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java index 01e58f450a4d0..15dbf2620608e 100644 --- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java @@ -258,9 +258,9 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl } @Override - public void finishDeferredConfiguration(List deferredOutputConfigs) + public void finalizeOutputConfigurations(List deferredOutputConfigs) throws CameraAccessException { - mSessionImpl.finishDeferredConfiguration(deferredOutputConfigs); + mSessionImpl.finalizeOutputConfigurations(deferredOutputConfigs); } private class WrapperCallback extends StateCallback { diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index d2aeaea2b37d4..2364ebe0f8916 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -743,14 +743,14 @@ public class CameraDeviceImpl extends CameraDevice } } - public void finishDeferredConfig(List deferredConfigs) + public void finalizeOutputConfigs(List outputConfigs) throws CameraAccessException { - if (deferredConfigs == null || deferredConfigs.size() == 0) { + if (outputConfigs == null || outputConfigs.size() == 0) { throw new IllegalArgumentException("deferred config is null or empty"); } synchronized(mInterfaceLock) { - for (OutputConfiguration config : deferredConfigs) { + for (OutputConfiguration config : outputConfigs) { int streamId = -1; for (int i = 0; i < mConfiguredOutputs.size(); i++) { // Have to use equal here, as createCaptureSessionByOutputConfigurations() and @@ -765,11 +765,11 @@ public class CameraDeviceImpl extends CameraDevice + "session"); } - if (config.getSurface() == null) { - throw new IllegalArgumentException("The deferred config for stream " + streamId - + " must have a non-null surface"); + if (config.getSurfaces().size() == 0) { + throw new IllegalArgumentException("The final config for stream " + streamId + + " must have at least 1 surface"); } - mRemoteDevice.setDeferredConfiguration(streamId, config); + mRemoteDevice.finalizeOutputConfigurations(streamId, config); } } } diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java index d77f60bf0953d..d9f666e543305 100644 --- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java +++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java @@ -215,10 +215,10 @@ public class ICameraDeviceUserWrapper { } } - public void setDeferredConfiguration(int streamId, OutputConfiguration deferredConfig) + public void finalizeOutputConfigurations(int streamId, OutputConfiguration deferredConfig) throws CameraAccessException { try { - mRemoteDevice.setDeferredConfiguration(streamId, deferredConfig); + mRemoteDevice.finalizeOutputConfigurations(streamId, deferredConfig); } catch (Throwable t) { CameraManager.throwAsPublicException(t); throw new UnsupportedOperationException("Unexpected exception", t); diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java index 2a9bf6bace95e..d8ec4df504bbd 100644 --- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java +++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java @@ -578,8 +578,8 @@ public class CameraDeviceUserShim implements ICameraDeviceUser { } @Override - public void setDeferredConfiguration(int steamId, OutputConfiguration config) { - String err = "Set deferred configuration is not supported on legacy devices"; + public void finalizeOutputConfigurations(int steamId, OutputConfiguration config) { + String err = "Finalizing output configuration is not supported on legacy devices"; Log.e(TAG, err); throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err); } diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index 4654fc2b2524c..2d5c4ce1f16d4 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -34,6 +34,7 @@ import android.view.Surface; import java.util.Arrays; import java.util.List; import java.util.Collections; +import java.util.ArrayList; import static com.android.internal.util.Preconditions.*; @@ -115,6 +116,15 @@ public final class OutputConfiguration implements Parcelable { */ private final int SURFACE_TYPE_SURFACE_TEXTURE = 1; + /** + * Maximum number of surfaces supported by one {@link OutputConfiguration}. + * + *

The combined number of surfaces added by the constructor and + * {@link OutputConfiguration#addSurface} should not exceed this value.

+ * + */ + private static final int MAX_SURFACES_COUNT = 2; + /** * Create a new {@link OutputConfiguration} instance with a {@link Surface}, * with a surface group ID. @@ -150,50 +160,6 @@ public final class OutputConfiguration implements Parcelable { this(surfaceGroupId, surface, ROTATION_0); } - /** - * Create a new {@link OutputConfiguration} instance with two surfaces sharing the same stream, - * with a surface group ID. - * - *

For advanced use cases, a camera application may require more streams than the combination - * guaranteed by {@link CameraDevice#createCaptureSession}. In this case, two compatible - * surfaces can be attached to one OutputConfiguration so that they map to one camera stream, - * and buffers are reference counted when being consumed by both surfaces.

- * - *

Two surfaces are compatible in below 2 cases:

- * - *
    - *
  1. Surfaces with the same size, format, dataSpace, and Surface source class. In this case, - * {@link CameraDevice#createCaptureSessionByOutputConfigurations} is guaranteed to succeed. - * - *
  2. Surfaces with the same size, format, and dataSpace, but different Surface - * source classes. However, on some devices, the underlying camera device is able to use the - * same buffer layout for both surfaces. The only way to discover if this is the case is to - * create a capture session with that output configuration. For example, if the camera device - * uses the same private buffer format between a SurfaceView/SurfaceTexture and a - * MediaRecorder/MediaCodec, {@link CameraDevice#createCaptureSessionByOutputConfigurations} - * will succeed. Otherwise, it throws {@code IllegalArgumentException}. - *
- * - * @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 surface2 - * Second surface for camera to output to. - * @throws IllegalArgumentException if the two surfaces have different size, format, or - * dataSpace. - * - * @hide - */ - public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface, - @NonNull Surface surface2) { - this(surfaceGroupId, surface, ROTATION_0, surface2); - - checkNotNull(surface2, "Surface must not be null"); - checkMatchingSurfaces(mConfiguredSize, mConfiguredFormat, mConfiguredDataspace, - mConfiguredGenerationId, surface2); - } - /** * Create a new {@link OutputConfiguration} instance. * @@ -240,68 +206,19 @@ public final class OutputConfiguration implements Parcelable { */ @SystemApi public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface, int rotation) { - this(surfaceGroupId, surface, rotation, null /*surface2*/); - } - - /** - * Create a new {@link OutputConfiguration} instance, with rotation, a group ID, and a secondary - * surface. - * - *

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

- * - *

surface2 should be compatible with surface. See {@link #OutputConfiguration(int, Surface, - * Surface} for details of compatibility between surfaces.

- * - *

Since the rotation is done by the CameraDevice, both surfaces will receive buffers with - * the same rotation applied. This means that if the application needs two compatible surfaces - * to have different rotations, these surfaces cannot be shared within one OutputConfiguration. - *

- * - * @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. - * @param surface2 - * Second surface for camera to output to. - - * @throws IllegalArgumentException if the two surfaces are not compatible to be shared in - * one OutputConfiguration. - * - * @hide - */ - private OutputConfiguration(int surfaceGroupId, @NonNull Surface surface, int rotation, - @Nullable Surface surface2) { checkNotNull(surface, "Surface must not be null"); checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant"); - mSurfaceGroupId = surfaceGroupId; mSurfaceType = SURFACE_TYPE_UNKNOWN; + mSurfaces = new ArrayList(); + mSurfaces.add(surface); mRotation = rotation; mConfiguredSize = SurfaceUtils.getSurfaceSize(surface); mConfiguredFormat = SurfaceUtils.getSurfaceFormat(surface); mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(surface); mConfiguredGenerationId = surface.getGenerationId(); mIsDeferredConfig = false; - - if (surface2 == null) { - mSurfaces = new Surface[1]; - mSurfaces[0] = surface; - } else { - mSurfaces = new Surface[MAX_SURFACES_COUNT]; - mSurfaces[0] = surface; - mSurfaces[1] = surface2; - } + mIsShared = false; } /** @@ -309,16 +226,16 @@ public final class OutputConfiguration implements Parcelable { * source class. *

* This constructor takes an argument for desired Surface size and the Surface source class - * without providing the actual output Surface. This is used to setup a output configuration + * without providing the actual output Surface. This is used to setup an output configuration * with a deferred Surface. The application can use this output configuration to create a * session. *

*

- * However, the actual output Surface must be set via {@link #setDeferredSurface} and finish the - * deferred Surface configuration via {@link CameraCaptureSession#finishDeferredConfiguration} - * before submitting a request with this Surface target. The deferred Surface can only be - * obtained from either from {@link android.view.SurfaceView} by calling - * {@link android.view.SurfaceHolder#getSurface}, or from + * However, the actual output Surface must be set via {@link #addSurface} and the deferred + * Surface configuration must be finalized via {@link + * CameraCaptureSession#finalizeOutputConfigurations} before submitting a request with this + * Surface target. The deferred Surface can only be obtained either from {@link + * android.view.SurfaceView} by calling {@link android.view.SurfaceHolder#getSurface}, or from * {@link android.graphics.SurfaceTexture} via * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}). *

@@ -329,41 +246,68 @@ public final class OutputConfiguration implements Parcelable { * {@link android.graphics.SurfaceTexture SurfaceTexture.class} are supported. */ public OutputConfiguration(@NonNull Size surfaceSize, @NonNull Class klass) { - this(surfaceSize, klass, true /* dummy */); + checkNotNull(klass, "surfaceSize must not be null"); + checkNotNull(klass, "klass must not be null"); + if (klass == android.view.SurfaceHolder.class) { + mSurfaceType = SURFACE_TYPE_SURFACE_VIEW; + } else if (klass == android.graphics.SurfaceTexture.class) { + mSurfaceType = SURFACE_TYPE_SURFACE_TEXTURE; + } else { + mSurfaceType = SURFACE_TYPE_UNKNOWN; + throw new IllegalArgumentException("Unknow surface source class type"); + } - mSurfaces = new Surface[1]; + mSurfaceGroupId = SURFACE_GROUP_ID_NONE; + mSurfaces = new ArrayList(); + mRotation = ROTATION_0; + mConfiguredSize = surfaceSize; + mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE); + mConfiguredDataspace = StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE); + mConfiguredGenerationId = 0; + mIsDeferredConfig = true; + mIsShared = false; } /** - * Create a new {@link OutputConfiguration} instance, with desired Surface size and Surface - * source class for the deferred surface, and a secondary surface. + * Enable multiple surfaces sharing the same OutputConfiguration * - *

This constructor takes an argument for desired surface size and surface source class of - * the deferred surface, and a secondary surface. See {@link #OutputConfiguration(Size, Class)} - * for details of the surface size and surface source class.

+ *

For advanced use cases, a camera application may require more streams than the combination + * guaranteed by {@link CameraDevice#createCaptureSession}. In this case, more than one + * compatible surface can be attached to an OutputConfiguration so that they map to one + * camera stream, and the outputs share memory buffers when possible.

* - *

The deferred surface and secondary surface should be compatible. See - * {@link #OutputConfiguration(int, Surface, Surface)} for details of compatible surfaces. + *

Two surfaces are compatible in the below cases:

* - * @hide + *
  • Surfaces with the same size, format, dataSpace, and Surface source class. In this case, + * {@link CameraDevice#createCaptureSessionByOutputConfigurations} is guaranteed to succeed. + * + *
  • Surfaces with the same size, format, and dataSpace, but different Surface source classes + * that are generally not compatible. However, on some devices, the underlying camera device is + * able to use the same buffer layout for both surfaces. The only way to discover if this is the + * case is to create a capture session with that output configuration. For example, if the + * camera device uses the same private buffer format between a SurfaceView/SurfaceTexture and a + * MediaRecorder/MediaCodec, {@link CameraDevice#createCaptureSessionByOutputConfigurations} + * will succeed. Otherwise, it throws {@code IllegalArgumentException}. + * + * + *

    To enable surface sharing, this function must be called before {@link + * CameraDevice#createCaptureSessionByOutputConfigurations}. Calling this function after {@link + * CameraDevice#createCaptureSessionByOutputConfigurations} has no effect.

    + * + *

    Up to 2 surfaces can be shared for an OutputConfiguration. The supported surfaces for + * sharing must be of type SurfaceTexture, SurfaceView, MediaRecorder, MediaCodec, or + * implementation defined ImageReader.

    */ - public OutputConfiguration(@NonNull Size surfaceSize, @NonNull Class klass, - @NonNull Surface surface2) { - this(surfaceSize, klass, true /* dummy */); - - checkMatchingSurfaces(mConfiguredSize, mConfiguredFormat, mConfiguredDataspace, - mConfiguredGenerationId, surface2); - - mSurfaces = new Surface[MAX_SURFACES_COUNT]; - mSurfaces[0] = null; - mSurfaces[1] = surface2; + public void enableSurfaceSharing() { + mIsShared = true; } /** * Check if this configuration has deferred configuration. * - *

    This will return true if the output configuration was constructed with surface deferred. - * It will return true even after the deferred surface is set later.

    + *

    This will return true if the output configuration was constructed with surface deferred by + * {@link OutputConfiguration#OutputConfiguration(Size, Class)}. It will return true even after + * the deferred surface is added later by {@link OutputConfiguration#addSurface}.

    * * @return true if this configuration has deferred surface. * @hide @@ -373,38 +317,58 @@ public final class OutputConfiguration implements Parcelable { } /** - * Set the deferred surface to this OutputConfiguration. + * Add a surface to this OutputConfiguration. * - *

    - * The deferred surface must be obtained from either from {@link android.view.SurfaceView} by - * calling {@link android.view.SurfaceHolder#getSurface}, or from - * {@link android.graphics.SurfaceTexture} via - * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}). After the deferred - * surface is set, the application must finish the deferred surface configuration via - * {@link CameraCaptureSession#finishDeferredConfiguration} before submitting a request with - * this surface target. + *

    This function can be called before or after {@link + * CameraDevice#createCaptureSessionByOutputConfigurations}. If it's called after, + * the application must finalize the capture session with + * {@link CameraCaptureSession#finalizeOutputConfigurations}. *

    * - * @param surface The deferred surface to be set. - * @throws IllegalArgumentException if the Surface is invalid. - * @throws IllegalStateException if a Surface was already set to this deferred - * OutputConfiguration. + *

    If the OutputConfiguration was constructed with a deferred surface by {@link + * OutputConfiguration#OutputConfiguration(Size, Class)}, the added surface must be obtained + * from {@link android.view.SurfaceView} by calling {@link android.view.SurfaceHolder#getSurface}, + * or from {@link android.graphics.SurfaceTexture} via + * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}).

    + * + *

    If the OutputConfiguration was constructed by other constructors, the added + * surface must be compatible with the existing surface. See {@link #enableSurfaceSharing} for + * details of compatible surfaces.

    + * + *

    If the OutputConfiguration already contains a Surface, {@link #enableSurfaceSharing} must + * be called before calling this function to add a new Surface.

    + * + * @param surface The surface to be added. + * @throws IllegalArgumentException if the Surface is invalid, the Surface's + * size/dataspace/format doesn't match, or adding the Surface would exceed number of + * shared surfaces supported. + * @throws IllegalStateException if the Surface was already added to this OutputConfiguration, + * or if the OutputConfiguration is not shared and it already has a surface associated + * with it. */ - public void setDeferredSurface(@NonNull Surface surface) { + public void addSurface(@NonNull Surface surface) { checkNotNull(surface, "Surface must not be null"); - if (mSurfaces[0] != null) { - throw new IllegalStateException("Deferred surface is already set!"); + if (mSurfaces.contains(surface)) { + throw new IllegalStateException("Surface is already added!"); + } + if (mSurfaces.size() == 1 && !mIsShared) { + throw new IllegalStateException("Cannot have 2 surfaces for a non-sharing configuration"); + } + if (mSurfaces.size() + 1 > MAX_SURFACES_COUNT) { + throw new IllegalArgumentException("Exceeds maximum number of surfaces"); } - // This will throw IAE is the surface was abandoned. - Size surfaceSize = SurfaceUtils.getSurfaceSize(surface); - if (!surfaceSize.equals(mConfiguredSize)) { - Log.w(TAG, "Deferred surface size " + surfaceSize + - " is different with pre-configured size " + mConfiguredSize + - ", the pre-configured size will be used."); + if (!mConfiguredSize.equals(SurfaceUtils.getSurfaceSize(surface))) { + throw new IllegalArgumentException("The size of added surface doesn't match"); + } + if (mConfiguredDataspace != SurfaceUtils.getSurfaceDataspace(surface)) { + throw new IllegalArgumentException("The dataspace of added surface doesn't match"); + } + if (mConfiguredFormat != SurfaceUtils.getSurfaceFormat(surface)) { + throw new IllegalArgumentException("The format of added surface format doesn't match"); } - mSurfaces[0] = surface; + mSurfaces.add(surface); } /** @@ -431,49 +395,6 @@ public final class OutputConfiguration implements Parcelable { this.mIsDeferredConfig = other.mIsDeferredConfig; } - /** - * Private constructor to initialize Configuration based on surface size and class - */ - private OutputConfiguration(@NonNull Size surfaceSize, @NonNull Class klass, - boolean dummy) { - checkNotNull(surfaceSize, "surfaceSize must not be null"); - checkNotNull(klass, "klass must not be null"); - if (klass == android.view.SurfaceHolder.class) { - mSurfaceType = SURFACE_TYPE_SURFACE_VIEW; - } else if (klass == android.graphics.SurfaceTexture.class) { - mSurfaceType = SURFACE_TYPE_SURFACE_TEXTURE; - } else { - mSurfaceType = SURFACE_TYPE_UNKNOWN; - throw new IllegalArgumentException("Unknow surface source class type"); - } - - mSurfaceGroupId = SURFACE_GROUP_ID_NONE; - mRotation = ROTATION_0; - mConfiguredSize = surfaceSize; - mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE); - mConfiguredDataspace = StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE); - mConfiguredGenerationId = 0; - mIsDeferredConfig = true; - } - - /** - * Check if the surface properties match that of the given surface. - * - * @return true if the properties and the surface match. - */ - private void checkMatchingSurfaces(Size size, int format, int dataSpace, int generationId, - @NonNull Surface surface) { - if (!size.equals(SurfaceUtils.getSurfaceSize(surface))) { - throw new IllegalArgumentException("Secondary surface size doesn't match"); - } - if (dataSpace != SurfaceUtils.getSurfaceDataspace(surface)) { - throw new IllegalArgumentException("Secondary surface dataspace doesn't match"); - } - if (format != SurfaceUtils.getSurfaceFormat(surface)) { - throw new IllegalArgumentException("Secondary surface format doesn't match"); - } - } - /** * Create an OutputConfiguration from Parcel. */ @@ -483,27 +404,9 @@ public final class OutputConfiguration implements Parcelable { int surfaceType = source.readInt(); int width = source.readInt(); int height = source.readInt(); - int surfaceCnt = source.readInt(); - - if (surfaceCnt <= 0) { - throw new IllegalArgumentException( - "Surface count in OutputConfiguration must be greater than 0"); - } - if (surfaceCnt > MAX_SURFACES_COUNT) { - throw new IllegalArgumentException( - "Surface count in OutputConfiguration must not be more than " - + MAX_SURFACES_COUNT); - } - - Surface[] surfaces = new Surface[surfaceCnt]; - for (int i = 0; i < surfaceCnt; i++) { - Surface surface = Surface.CREATOR.createFromParcel(source); - surfaces[i] = surface; - - if (surface == null && i > 0) { - throw new IllegalArgumentException("Only the first surface can be deferred"); - } - } + boolean isDeferred = source.readInt() == 1; + ArrayList surfaces = new ArrayList(); + source.readTypedList(surfaces, Surface.CREATOR); checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant"); @@ -511,13 +414,13 @@ public final class OutputConfiguration implements Parcelable { mRotation = rotation; mSurfaces = surfaces; mConfiguredSize = new Size(width, height); - // First surface could be null (being deferred). Use last surface to look up surface - // characteristics. - if (mSurfaces[surfaceCnt-1] != null) { + mIsDeferredConfig = isDeferred; + mSurfaces = surfaces; + if (mSurfaces.size() > 0) { mSurfaceType = SURFACE_TYPE_UNKNOWN; - mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurfaces[surfaceCnt-1]); - mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurfaces[surfaceCnt-1]); - mConfiguredGenerationId = mSurfaces[surfaceCnt-1].getGenerationId(); + mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurfaces.get(0)); + mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurfaces.get(0)); + mConfiguredGenerationId = mSurfaces.get(0).getGenerationId(); } else { mSurfaceType = surfaceType; mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE); @@ -525,38 +428,31 @@ public final class OutputConfiguration implements Parcelable { StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE); mConfiguredGenerationId = 0; } - - if (mSurfaces[0] == null) { - mIsDeferredConfig = true; - } else { - mIsDeferredConfig = false; - } } /** * Get the {@link Surface} associated with this {@link OutputConfiguration}. * - * @return the {@link Surface} associated with this {@link OutputConfiguration}. If more than - * one surface is associated with this {@link OutputConfiguration}, return the first one as - * specified in the constructor. If there is a deferred surface, null will be returned. + * If more than one surface is associated with this {@link OutputConfiguration}, return the + * first one as specified in the constructor or {@link OutputConfiguration#addSurface}. */ public @Nullable Surface getSurface() { - return mSurfaces[0]; + if (mSurfaces.size() == 0) { + return null; + } + + return mSurfaces.get(0); } /** * Get the immutable list of surfaces associated with this {@link OutputConfiguration}. * - * @return the list of surfaces associated with this {@link OutputConfiguration} in the order - * specified in the constructor. If there is a deferred surface in the {@link - * OutputConfiguration}, it is returned as null as first element of the list. The list should - * not be modified. - * - * @hide + * @return the list of surfaces associated with this {@link OutputConfiguration} as specified in + * the constructor and {@link OutputConfiguration#addSurface}. The list should not be modified. */ @NonNull public List getSurfaces() { - return Collections.unmodifiableList(Arrays.asList(mSurfaces)); + return Collections.unmodifiableList(mSurfaces); } /** @@ -616,12 +512,9 @@ public final class OutputConfiguration implements Parcelable { dest.writeInt(mSurfaceType); dest.writeInt(mConfiguredSize.getWidth()); dest.writeInt(mConfiguredSize.getHeight()); - dest.writeInt(mSurfaces.length); - for (int i = 0; i < mSurfaces.length; i++) { - if (mSurfaces[i] != null) { - mSurfaces[i].writeToParcel(dest, flags); - } - } + dest.writeInt(mIsDeferredConfig ? 1 : 0); + dest.writeInt(mIsShared ? 1 : 0); + dest.writeTypedList(mSurfaces); } /** @@ -647,16 +540,15 @@ public final class OutputConfiguration implements Parcelable { mSurfaceGroupId != other.mSurfaceGroupId || mSurfaceType != other.mSurfaceType || mIsDeferredConfig != other.mIsDeferredConfig || + mIsShared != other.mIsShared || mConfiguredFormat != other.mConfiguredFormat || mConfiguredDataspace != other.mConfiguredDataspace || - mSurfaces.length != other.mSurfaces.length || mConfiguredGenerationId != other.mConfiguredGenerationId) return false; - // If deferred, skip the first surface of mSurfaces when comparing. - int minIndex = (mIsDeferredConfig ? 1 : 0); - for (int i = minIndex; i < mSurfaces.length; i++) { - if (mSurfaces[i] != other.mSurfaces[i]) + int minLen = Math.min(mSurfaces.size(), other.mSurfaces.size()); + for (int i = 0; i < minLen; i++) { + if (mSurfaces.get(i) != other.mSurfaces.get(i)) return false; } @@ -670,23 +562,23 @@ public final class OutputConfiguration implements Parcelable { */ @Override public int hashCode() { - // Need ensure that the hashcode remains unchanged after set a deferred surface. Otherwise + // Need ensure that the hashcode remains unchanged after adding a deferred surface. Otherwise // the deferred output configuration will be lost in the camera streammap after the deferred // surface is set. - int minIndex = (mIsDeferredConfig ? 1 : 0); - Surface nonDeferredSurfaces[] = Arrays.copyOfRange(mSurfaces, - minIndex, mSurfaces.length); - int surfaceHash = HashCodeHelpers.hashCodeGeneric(nonDeferredSurfaces); + if (mIsDeferredConfig) { + return HashCodeHelpers.hashCode( + mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace, + mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0); + } return HashCodeHelpers.hashCode( - mRotation, surfaceHash, mConfiguredGenerationId, + mRotation, mSurfaces.hashCode(), mConfiguredGenerationId, mConfiguredSize.hashCode(), mConfiguredFormat, - mConfiguredDataspace, mSurfaceGroupId); + mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0); } private static final String TAG = "OutputConfiguration"; - private static final int MAX_SURFACES_COUNT = 2; - private Surface mSurfaces[]; + private ArrayList mSurfaces; private final int mRotation; private final int mSurfaceGroupId; // Surface source type, this is only used by the deferred surface configuration objects. @@ -700,4 +592,6 @@ public final class OutputConfiguration implements Parcelable { private final int mConfiguredGenerationId; // Flag indicating if this config has deferred surface. private final boolean mIsDeferredConfig; + // Flag indicating if this config has shared surfaces + private boolean mIsShared; }