Camera2: Combine API for deferred surface and shared surface
- Unhide API to enable sharing surfaces via OutputConfiguration. - Combine the APIs for deferred surface and shared surfaces so that same functions can be used for both use cases. 1. setDeferredSurface becomes addSurface. 2. finishDeferredConfiguration becomes finalizeOutputConfiguration. - Better documentation on shared surface use cases. Test: Camera CTS, and preview/video/capture with GCA Bug: 33777818 Change-Id: I528b221492e07888a2da96658aaf8e7b9c4d1640
This commit is contained in:
@@ -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.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();
|
||||
@@ -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<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
|
||||
|
||||
@@ -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.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();
|
||||
@@ -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<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
|
||||
|
||||
@@ -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.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();
|
||||
@@ -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<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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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}.
|
||||
*
|
||||
* <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.
|
||||
@@ -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.
|
||||
*
|
||||
* <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.
|
||||
*
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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 <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.
|
||||
*/
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user