diff options
| author | 2017-01-25 01:54:19 +0000 | |
|---|---|---|
| committer | 2017-01-25 01:54:24 +0000 | |
| commit | 58ac1310db09e89734e9fb90a5c9025c90277e4a (patch) | |
| tree | 17d741c329f0829c668c46be692ec192515175a2 | |
| parent | 1909ec1cd524bbe8528485d5ec1bcd2a79e763e6 (diff) | |
| parent | 4bd7abe72a647ceb2175a4fe66aa640815c116f8 (diff) | |
Merge "Camera2: Combine API for deferred surface and shared surface"
10 files changed, 196 insertions, 287 deletions
diff --git a/api/current.txt b/api/current.txt index fa343d47783b..16cfff2d84b8 100644 --- a/api/current.txt +++ b/api/current.txt @@ -14458,7 +14458,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.CaptureRequest>, 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<android.hardware.camera2.params.OutputConfiguration>) throws android.hardware.camera2.CameraAccessException; + method public abstract void finalizeOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>) 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(); @@ -15099,10 +15099,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<T>); + 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<android.view.Surface> getSurfaces(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> 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 fe8af623e23b..066f9ce6366e 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -15024,7 +15024,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.CaptureRequest>, 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<android.hardware.camera2.params.OutputConfiguration>) throws android.hardware.camera2.CameraAccessException; + method public abstract void finalizeOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>) 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(); @@ -15667,11 +15667,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<T>); + 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<android.view.Surface> getSurfaces(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR; field public static final int ROTATION_0 = 0; // 0x0 diff --git a/api/test-current.txt b/api/test-current.txt index 028c67e38d6c..f0c628ceadac 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -14490,7 +14490,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.CaptureRequest>, 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<android.hardware.camera2.params.OutputConfiguration>) throws android.hardware.camera2.CameraAccessException; + method public abstract void finalizeOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>) 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(); @@ -15131,10 +15131,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<T>); + 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<android.view.Surface> getSurfaces(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> 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 62b7f32c43c0..bcebb7dc594d 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; /** - * <p>Finish the deferred output configurations where the output Surface was not configured - * before.</p> + * <p>Finalize the output configurations that now have their deferred and/or extra Surfaces + * included.</p> * * <p>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.</p> * - * <p>The output Surfaces included by this list of deferred + * <p>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.</p> + * + * <p>The output Surfaces included by this list of * {@link OutputConfiguration OutputConfigurations} can be used as {@link CaptureRequest} * targets as soon as this call returns.</p> * * <p>This method is not supported by * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}-level devices.</p> * - * @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<OutputConfiguration> deferredOutputConfigs) throws CameraAccessException; + public abstract void finalizeOutputConfigurations( + List<OutputConfiguration> outputConfigs) throws CameraAccessException; /** * <p>Submit a request for an image to be captured by the camera device.</p> diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index 4befb29ae075..891df6362d29 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<OutputConfiguration> deferredOutputConfigs) throws CameraAccessException { - mDeviceImpl.finishDeferredConfig(deferredOutputConfigs); + public void finalizeOutputConfigurations( + List<OutputConfiguration> 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 01e58f450a4d..15dbf2620608 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<OutputConfiguration> deferredOutputConfigs) + public void finalizeOutputConfigurations(List<OutputConfiguration> 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 d2aeaea2b37d..2364ebe0f891 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<OutputConfiguration> deferredConfigs) + public void finalizeOutputConfigs(List<OutputConfiguration> 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 d77f60bf0953..d9f666e54330 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 2a9bf6bace95..d8ec4df504bb 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 4654fc2b2524..2d5c4ce1f16d 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.*; @@ -116,6 +117,15 @@ public final class OutputConfiguration implements Parcelable { private final int SURFACE_TYPE_SURFACE_TEXTURE = 1; /** + * Maximum number of surfaces supported by one {@link OutputConfiguration}. + * + * <p>The combined number of surfaces added by the constructor and + * {@link OutputConfiguration#addSurface} should not exceed this value.</p> + * + */ + private static final int MAX_SURFACES_COUNT = 2; + + /** * Create a new {@link OutputConfiguration} instance with a {@link Surface}, * with a surface group ID. * @@ -151,50 +161,6 @@ public final class OutputConfiguration implements Parcelable { } /** - * Create a new {@link OutputConfiguration} instance with two surfaces sharing the same stream, - * with a surface group ID. - * - * <p>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. </p> - * - * <p>Two surfaces are compatible in below 2 cases:</p> - * - * <ol> - * <li> Surfaces with the same size, format, dataSpace, and Surface source class. In this case, - * {@link CameraDevice#createCaptureSessionByOutputConfigurations} is guaranteed to succeed. - * - * <li> 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}. - * </ol> - * - * @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. * * <p>This constructor takes an argument for desired camera rotation</p> @@ -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. - * - * <p>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.</p> - * - * <p>surface2 should be compatible with surface. See {@link #OutputConfiguration(int, Surface, - * Surface} for details of compatibility between surfaces.</p> - * - * <p>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. - * </p> - * - * @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<Surface>(); + 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. * <p> * 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. * </p> * <p> - * 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)}). * </p> @@ -329,41 +246,68 @@ public final class OutputConfiguration implements Parcelable { * {@link android.graphics.SurfaceTexture SurfaceTexture.class} are supported. */ public <T> OutputConfiguration(@NonNull Size surfaceSize, @NonNull Class<T> 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<Surface>(); + 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 * - * <p>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.</p> + * <p>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. </p> * - * <p> The deferred surface and secondary surface should be compatible. See - * {@link #OutputConfiguration(int, Surface, Surface)} for details of compatible surfaces. + * <p>Two surfaces are compatible in the below cases:</p> * - * @hide + * <li> Surfaces with the same size, format, dataSpace, and Surface source class. In this case, + * {@link CameraDevice#createCaptureSessionByOutputConfigurations} is guaranteed to succeed. + * + * <li> 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}. + * </ol> + * + * <p>To enable surface sharing, this function must be called before {@link + * CameraDevice#createCaptureSessionByOutputConfigurations}. Calling this function after {@link + * CameraDevice#createCaptureSessionByOutputConfigurations} has no effect.</p> + * + * <p>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.</p> */ - public <T> OutputConfiguration(@NonNull Size surfaceSize, @NonNull Class<T> 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. * - * <p>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.</p> + * <p>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}.</p> * * @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. * - * <p> - * 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. + * <p> 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}. * </p> * - * @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. + * <p> 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)}).</p> + * + * <p> 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.</p> + * + * <p> If the OutputConfiguration already contains a Surface, {@link #enableSurfaceSharing} must + * be called before calling this function to add a new Surface.</p> + * + * @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); } /** @@ -432,49 +396,6 @@ public final class OutputConfiguration implements Parcelable { } /** - * Private constructor to initialize Configuration based on surface size and class - */ - private <T> OutputConfiguration(@NonNull Size surfaceSize, @NonNull Class<T> 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. */ private OutputConfiguration(@NonNull Parcel source) { @@ -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<Surface> surfaces = new ArrayList<Surface>(); + 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<Surface> 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<Surface> 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; } |