script-astra/Android/Sdk/sources/android-35/android/hardware/camera2/params/OutputConfiguration.java
localadmin 4380f00a78 init
2025-01-20 18:15:20 +03:00

1850 lines
87 KiB
Java

/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.camera2.params;
import static com.android.internal.util.Preconditions.*;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.graphics.ColorSpace;
import android.graphics.ImageFormat;
import android.graphics.ImageFormat.Format;
import android.hardware.HardwareBuffer;
import android.hardware.HardwareBuffer.Usage;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.MultiResolutionImageReader;
import android.hardware.camera2.params.DynamicRangeProfiles;
import android.hardware.camera2.params.MultiResolutionStreamInfo;
import android.hardware.camera2.utils.HashCodeHelpers;
import android.hardware.camera2.utils.SurfaceUtils;
import android.media.ImageReader;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import com.android.internal.camera.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
/**
* A class for describing camera output, which contains a {@link Surface} and its specific
* configuration for creating capture session.
*
* <p>There are several ways to instantiate, modify and use OutputConfigurations. The most common
* and recommended usage patterns are summarized in the following list:</p>
*<ul>
* <li>Passing a {@link Surface} to the constructor and using the OutputConfiguration instance as
* argument to {@link CameraDevice#createCaptureSessionByOutputConfigurations}. This is the most
* frequent usage and clients should consider it first before other more complicated alternatives.
* </li>
*
* <li>Passing only a surface source class as an argument to the constructor. This is usually
* followed by a call to create a capture session
* (see {@link CameraDevice#createCaptureSessionByOutputConfigurations} and a {@link Surface} add
* call {@link #addSurface} with a valid {@link Surface}. The sequence completes with
* {@link CameraCaptureSession#finalizeOutputConfigurations}. This is the deferred usage case which
* aims to enhance performance by allowing the resource-intensive capture session create call to
* execute in parallel with any {@link Surface} initialization, such as waiting for a
* {@link android.view.SurfaceView} to be ready as part of the UI initialization.</li>
*
* <li>The third and most complex usage pattern involves surface sharing. Once instantiated an
* OutputConfiguration can be enabled for surface sharing via {@link #enableSurfaceSharing}. This
* must be done before creating a new capture session and enables calls to
* {@link CameraCaptureSession#updateOutputConfiguration}. An OutputConfiguration with enabled
* surface sharing can be modified via {@link #addSurface} or {@link #removeSurface}. The updates
* to this OutputConfiguration will only come into effect after
* {@link CameraCaptureSession#updateOutputConfiguration} returns without throwing exceptions.
* Such updates can be done as long as the session is active. Clients should always consider the
* additional requirements and limitations placed on the output surfaces (for more details see
* {@link #enableSurfaceSharing}, {@link #addSurface}, {@link #removeSurface},
* {@link CameraCaptureSession#updateOutputConfiguration}). A trade-off exists between additional
* complexity and flexibility. If exercised correctly surface sharing can switch between different
* output surfaces without interrupting any ongoing repeating capture requests. This saves time and
* can significantly improve the user experience.</li>
*
* <li>Surface sharing can be used in combination with deferred surfaces. The rules from both cases
* are combined and clients must call {@link #enableSurfaceSharing} before creating a capture
* session. Attach and/or remove output surfaces via {@link #addSurface}/{@link #removeSurface} and
* finalize the configuration using {@link CameraCaptureSession#finalizeOutputConfigurations}.
* {@link CameraCaptureSession#updateOutputConfiguration} can be called after the configuration
* finalize method returns without exceptions.</li>
*
* <li>If the camera device supports multi-resolution output streams, {@link
* CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP} will contain the
* formats and their corresponding stream info. The application can use an OutputConfiguration
* created with the multi-resolution stream info queried from {@link
* MultiResolutionStreamConfigurationMap#getOutputInfo} and
* {@link android.hardware.camera2.MultiResolutionImageReader} to capture variable size images.
*
* </ul>
*
* <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats except
* {@link ImageFormat#JPEG} and {@link ImageFormat#RAW_PRIVATE} can be used for sharing, subject to
* device support. On prior API levels, only {@link ImageFormat#PRIVATE} format may be used.</p>
*
* @see CameraDevice#createCaptureSessionByOutputConfigurations
* @see CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP
*
*/
public final class OutputConfiguration implements Parcelable {
/**
* Rotation constant: 0 degree rotation (no rotation)
*
* @hide
*/
@SystemApi
public static final int ROTATION_0 = 0;
/**
* Rotation constant: 90 degree counterclockwise rotation.
*
* @hide
*/
@SystemApi
public static final int ROTATION_90 = 1;
/**
* Rotation constant: 180 degree counterclockwise rotation.
*
* @hide
*/
@SystemApi
public static final int ROTATION_180 = 2;
/**
* Rotation constant: 270 degree counterclockwise rotation.
*
* @hide
*/
@SystemApi
public static final int ROTATION_270 = 3;
/**
* Invalid surface group ID.
*
*<p>An {@link OutputConfiguration} with this value indicates that the included surface
*doesn't belong to any surface group.</p>
*/
public static final int SURFACE_GROUP_ID_NONE = -1;
/**
* Default timestamp base.
*
* <p>The camera device decides the timestamp based on the properties of the
* output surface.</p>
*
* <li> For a SurfaceView output surface, the timestamp base is {@link
* #TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED}. The timestamp is overridden with choreographer
* pulses from the display subsystem for smoother display of camera frames when the camera
* device runs in fixed frame rate. The timestamp is roughly in the same time base as
* {@link android.os.SystemClock#uptimeMillis}.</li>
* <li> For an output surface of MediaRecorder, MediaCodec, or ImageReader with {@link
* android.hardware.HardwareBuffer#USAGE_VIDEO_ENCODE} usage flag, the timestamp base is
* {@link #TIMESTAMP_BASE_MONOTONIC}, which is roughly the same time base as
* {@link android.os.SystemClock#uptimeMillis}.</li>
* <li> For all other cases, the timestamp base is {@link #TIMESTAMP_BASE_SENSOR}, the same
* as what's specified by {@link CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE}.
* <ul><li> For a SurfaceTexture output surface, the camera system re-spaces the delivery
* of output frames based on image readout intervals, reducing viewfinder jitter. The timestamps
* of images remain to be {@link #TIMESTAMP_BASE_SENSOR}.</li></ul></li>
*
* <p>Note that the reduction of frame jitter for SurfaceView and SurfaceTexture comes with
* slight increase in photon-to-photon latency, which is the time from when photons hit the
* scene to when the corresponding pixels show up on the screen. If the photon-to-photon latency
* is more important than the smoothness of viewfinder, {@link #TIMESTAMP_BASE_SENSOR} should be
* used instead.</p>
*
* @see #TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED
* @see #TIMESTAMP_BASE_MONOTONIC
* @see #TIMESTAMP_BASE_SENSOR
*/
public static final int TIMESTAMP_BASE_DEFAULT = 0;
/**
* Timestamp base of {@link CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE}.
*
* <p>The timestamps of the output images are in the time base as specified by {@link
* CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE}. The application can look up the
* corresponding result metadata by matching the timestamp with a {@link
* CameraCaptureSession.CaptureCallback#onCaptureStarted}, or with a {@link
* CameraCaptureSession.CaptureCallback#onReadoutStarted} if readout timestamp is used.</p>
*/
public static final int TIMESTAMP_BASE_SENSOR = 1;
/**
* Timestamp base roughly the same as {@link android.os.SystemClock#uptimeMillis}.
*
* <p>The timestamps of the output images are monotonically increasing, and are roughly in the
* same time base as {@link android.os.SystemClock#uptimeMillis}. The timestamps with this
* time base can be directly used for audio-video sync in video recording.</p>
*
* <p>If the camera device's {@link CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE} is
* REALTIME, timestamps with this time base cannot directly match the timestamps in
* {@link CameraCaptureSession.CaptureCallback#onCaptureStarted}, {@link
* CameraCaptureSession.CaptureCallback#onReadoutStarted}, or the sensor timestamps in
* {@link android.hardware.camera2.CaptureResult}.</p>
*/
public static final int TIMESTAMP_BASE_MONOTONIC = 2;
/**
* Timestamp base roughly the same as {@link android.os.SystemClock#elapsedRealtime}.
*
* <p>The timestamps of the output images are roughly in the
* same time base as {@link android.os.SystemClock#elapsedRealtime}. The timestamps with this
* time base cannot be directly used for audio-video sync in video recording.</p>
*
* <p>If the camera device's {@link CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE} is
* UNKNOWN, timestamps with this time base cannot directly match the timestamps in
* {@link CameraCaptureSession.CaptureCallback#onCaptureStarted}, {@link
* CameraCaptureSession.CaptureCallback#onReadoutStarted}, or the sensor timestamps in
* {@link android.hardware.camera2.CaptureResult}.</p>
*
* <p>If using a REALTIME timestamp base on a device that supports only
* TIMESTAMP_SOURCE_UNKNOWN, the accuracy of timestamps is only what is guaranteed in the
* documentation for UNKNOWN. In particular, they have no guarantees about being accurate
* enough to use in fusing image data with the output of inertial sensors, for features such as
* image stabilization or augmented reality.</p>
*/
public static final int TIMESTAMP_BASE_REALTIME = 3;
/**
* Timestamp is synchronized to choreographer.
*
* <p>The timestamp of the output images are overridden with choreographer pulses from the
* display subsystem for smoother display of camera frames. An output target of SurfaceView
* uses this time base by default. Note that the timestamp override is done for fixed camera
* frame rate only.</p>
*
* <p>This timestamp base isn't applicable to SurfaceTexture targets. SurfaceTexture's
* {@link android.graphics.SurfaceTexture#updateTexImage updateTexImage} function always
* uses the latest image from the camera stream. In the case of a TextureView, the image is
* displayed right away.</p>
*
* <p>Timestamps with this time base cannot directly match the timestamps in
* {@link CameraCaptureSession.CaptureCallback#onCaptureStarted}, {@link
* CameraCaptureSession.CaptureCallback#onReadoutStarted}, or the sensor timestamps in
* {@link android.hardware.camera2.CaptureResult}. This timestamp base shouldn't be used if the
* timestamp needs to be used for audio-video synchronization.</p>
*/
public static final int TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED = 4;
/**
* Timestamp is the start of readout in the same time domain as TIMESTAMP_BASE_SENSOR.
*
* <p>NOTE: do not use! Use setReadoutTimestampEnabled instead.</p>
*
* @hide
*/
public static final int TIMESTAMP_BASE_READOUT_SENSOR = 5;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"TIMESTAMP_BASE_"}, value =
{TIMESTAMP_BASE_DEFAULT,
TIMESTAMP_BASE_SENSOR,
TIMESTAMP_BASE_MONOTONIC,
TIMESTAMP_BASE_REALTIME,
TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED,
TIMESTAMP_BASE_READOUT_SENSOR})
public @interface TimestampBase {};
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"SENSOR_PIXEL_MODE_"}, value =
{CameraMetadata.SENSOR_PIXEL_MODE_DEFAULT,
CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION})
public @interface SensorPixelMode {};
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"STREAM_USE_CASE_"}, value =
{CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT,
CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW,
CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE,
CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD,
CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL,
CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL,
CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_CROPPED_RAW})
public @interface StreamUseCase {};
/**
* Automatic mirroring based on camera facing
*
* <p>This is the default mirroring mode for the camera device. With this mode,
* the camera output is mirrored horizontally for front-facing cameras. There is
* no mirroring for rear-facing and external cameras.</p>
*/
public static final int MIRROR_MODE_AUTO = 0;
/**
* No mirror transform is applied
*
* <p>No mirroring is applied to the camera output regardless of the camera facing.</p>
*/
public static final int MIRROR_MODE_NONE = 1;
/**
* Camera output is mirrored horizontally
*
* <p>The camera output is mirrored horizontally, the same behavior as in AUTO mode for
* front facing camera.</p>
*/
public static final int MIRROR_MODE_H = 2;
/**
* Camera output is mirrored vertically
*/
public static final int MIRROR_MODE_V = 3;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"MIRROR_MODE_"}, value =
{MIRROR_MODE_AUTO,
MIRROR_MODE_NONE,
MIRROR_MODE_H,
MIRROR_MODE_V})
public @interface MirrorMode {};
/**
* Create a new {@link OutputConfiguration} instance with a {@link Surface}.
*
* @param surface
* A Surface for camera to output to.
*
* <p>This constructor creates a default configuration, with a surface group ID of
* {@value #SURFACE_GROUP_ID_NONE}.</p>
*
*/
public OutputConfiguration(@NonNull Surface surface) {
this(SURFACE_GROUP_ID_NONE, surface, ROTATION_0);
}
/**
* Unknown surface source type.
*/
private final int SURFACE_TYPE_UNKNOWN = -1;
/**
* The surface is obtained from {@link android.view.SurfaceView}.
*/
private final int SURFACE_TYPE_SURFACE_VIEW = 0;
/**
* The surface is obtained from {@link android.graphics.SurfaceTexture}.
*/
private final int SURFACE_TYPE_SURFACE_TEXTURE = 1;
/**
* The surface is obtained from {@link android.media.MediaRecorder}.
*/
private static final int SURFACE_TYPE_MEDIA_RECORDER = 2;
/**
* The surface is obtained from {@link android.media.MediaCodec}.
*/
private static final int SURFACE_TYPE_MEDIA_CODEC = 3;
/**
* The surface is obtained from {@link android.media.ImageReader}.
*/
private static final int SURFACE_TYPE_IMAGE_READER = 4;
/**
* 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 = 4;
/**
* Create a new {@link OutputConfiguration} instance with a {@link Surface},
* with a surface group ID.
*
* <p>
* A surface group ID is used to identify which surface group this output surface belongs to. A
* surface group is a group of output surfaces that are not intended to receive camera output
* buffer streams simultaneously. The {@link CameraDevice} may be able to share the buffers used
* by all the surfaces from the same surface group, therefore may reduce the overall memory
* footprint. The application should only set the same set ID for the streams that are not
* simultaneously streaming. A negative ID indicates that this surface doesn't belong to any
* surface group. The default value is {@value #SURFACE_GROUP_ID_NONE}.</p>
*
* <p>For example, a video chat application that has an adaptive output resolution feature would
* need two (or more) output resolutions, to switch resolutions without any output glitches.
* However, at any given time, only one output is active to minimize outgoing network bandwidth
* and encoding overhead. To save memory, the application should set the video outputs to have
* the same non-negative group ID, so that the camera device can share the same memory region
* for the alternating outputs.</p>
*
* <p>It is not an error to include output streams with the same group ID in the same capture
* request, but the resulting memory consumption may be higher than if the two streams were
* not in the same surface group to begin with, especially if the outputs have substantially
* different dimensions.</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.
*
*/
public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface) {
this(surfaceGroupId, surface, ROTATION_0);
}
/**
* Set the multi-resolution output flag.
*
* <p>Specify that this OutputConfiguration is part of a multi-resolution output stream group
* used by {@link android.hardware.camera2.MultiResolutionImageReader}.</p>
*
* <p>This function must only be called for an OutputConfiguration with a non-negative
* group ID. And all OutputConfigurations of a MultiResolutionImageReader will have the same
* group ID and have this flag set.</p>
*
* @throws IllegalStateException If surface sharing is enabled via {@link #enableSurfaceSharing}
* call, or no non-negative group ID has been set.
* @hide
*/
public void setMultiResolutionOutput() {
if (mIsShared) {
throw new IllegalStateException("Multi-resolution output flag must not be set for " +
"configuration with surface sharing");
}
if (mSurfaceGroupId == SURFACE_GROUP_ID_NONE) {
throw new IllegalStateException("Multi-resolution output flag should only be set for " +
"surface with non-negative group ID");
}
mIsMultiResolution = true;
}
/**
* Set a specific device supported dynamic range profile.
*
* <p>Clients can choose from any profile advertised as supported in
* CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES
* queried using {@link DynamicRangeProfiles#getSupportedProfiles()}.
* If this is not explicitly set, then the default profile will be
* {@link DynamicRangeProfiles#STANDARD}.</p>
*
* <p>Do note that invalid combinations between the registered output
* surface pixel format and the configured dynamic range profile will
* cause capture session initialization failure. Invalid combinations
* include any 10-bit dynamic range profile advertised in
* {@link DynamicRangeProfiles#getSupportedProfiles()} combined with
* an output Surface pixel format different from {@link ImageFormat#PRIVATE}
* (the default for Surfaces initialized by {@link android.view.SurfaceView},
* {@link android.view.TextureView}, {@link android.media.MediaRecorder},
* {@link android.media.MediaCodec} etc.)
* or {@link ImageFormat#YCBCR_P010}.</p>
*/
public void setDynamicRangeProfile(@DynamicRangeProfiles.Profile long profile) {
mDynamicRangeProfile = profile;
}
/**
* Return current dynamic range profile.
*
* @return the currently set dynamic range profile
*/
public @DynamicRangeProfiles.Profile long getDynamicRangeProfile() {
return mDynamicRangeProfile;
}
/**
* Set a specific device-supported color space.
*
* <p>Clients can choose from any profile advertised as supported in
* {@link CameraCharacteristics#REQUEST_AVAILABLE_COLOR_SPACE_PROFILES}
* queried using {@link ColorSpaceProfiles#getSupportedColorSpaces}.
* When set, the colorSpace will override the default color spaces of the output targets,
* or the color space implied by the dataSpace passed into an {@link ImageReader}'s
* constructor.</p>
*
* @hide
*/
@TestApi
public void setColorSpace(@NonNull ColorSpace.Named colorSpace) {
mColorSpace = colorSpace.ordinal();
}
/**
* Clear the color space, such that the default color space will be used.
*
* @hide
*/
@TestApi
public void clearColorSpace() {
mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
}
/**
* Return the current color space.
*
* @return the currently set color space
* @hide
*/
@TestApi
@SuppressLint("MethodNameUnits")
public @Nullable ColorSpace getColorSpace() {
if (mColorSpace != ColorSpaceProfiles.UNSPECIFIED) {
return ColorSpace.get(ColorSpace.Named.values()[mColorSpace]);
} else {
return null;
}
}
/**
* Create a new {@link OutputConfiguration} instance.
*
* <p>This constructor takes an argument for desired camera rotation</p>
*
* @param surface
* A Surface for camera to output to.
* @param rotation
* The desired rotation to be applied on camera output. Value must be one of
* ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degrees,
* application should make sure corresponding surface size has width and height
* transposed relative to the width and height without rotation. For example,
* if application needs camera to capture 1280x720 picture and rotate it by 90 degree,
* application should set rotation to {@code ROTATION_90} and make sure the
* corresponding Surface size is 720x1280. Note that {@link CameraDevice} might
* throw {@code IllegalArgumentException} if device cannot perform such rotation.
* @hide
*/
@SystemApi
public OutputConfiguration(@NonNull Surface surface, int rotation) {
this(SURFACE_GROUP_ID_NONE, surface, rotation);
}
/**
* Create a new {@link OutputConfiguration} instance, with rotation and a group ID.
*
* <p>This constructor takes an argument for desired camera rotation and for the surface group
* ID. See {@link #OutputConfiguration(int, Surface)} for details of the group ID.</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.
* @hide
*/
@SystemApi
public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface, int rotation) {
checkNotNull(surface, "Surface must not be null");
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
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;
mIsShared = false;
mPhysicalCameraId = null;
mIsMultiResolution = false;
mSensorPixelModesUsed = new ArrayList<Integer>();
mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
mTimestampBase = TIMESTAMP_BASE_DEFAULT;
mMirrorMode = MIRROR_MODE_AUTO;
mReadoutTimestampEnabled = false;
mIsReadoutSensorTimestampBase = false;
mUsage = 0;
}
/**
* Create a list of {@link OutputConfiguration} instances for the outputs used by a
* {@link android.hardware.camera2.MultiResolutionImageReader}.
*
* <p>This constructor takes an argument for a
* {@link android.hardware.camera2.MultiResolutionImageReader}.</p>
*
* @param multiResolutionImageReader
* The multi-resolution image reader object.
*/
public static @NonNull Collection<OutputConfiguration> createInstancesForMultiResolutionOutput(
@NonNull MultiResolutionImageReader multiResolutionImageReader) {
checkNotNull(multiResolutionImageReader, "Multi-resolution image reader must not be null");
int groupId = getAndIncreaseMultiResolutionGroupId();
ImageReader[] imageReaders = multiResolutionImageReader.getReaders();
ArrayList<OutputConfiguration> configs = new ArrayList<OutputConfiguration>();
for (int i = 0; i < imageReaders.length; i++) {
MultiResolutionStreamInfo streamInfo =
multiResolutionImageReader.getStreamInfoForImageReader(imageReaders[i]);
OutputConfiguration config = new OutputConfiguration(
groupId, imageReaders[i].getSurface());
config.setPhysicalCameraId(streamInfo.getPhysicalCameraId());
config.setMultiResolutionOutput();
configs.add(config);
// No need to call addSensorPixelModeUsed for ultra high resolution sensor camera,
// because regular and max resolution output configurations are used for DEFAULT mode
// and MAX_RESOLUTION mode respectively by default.
}
return configs;
}
/**
* Create a list of {@link OutputConfiguration} instances for a
* {@link android.hardware.camera2.params.MultiResolutionImageReader}.
*
* <p>This method can be used to create query OutputConfigurations for a
* MultiResolutionImageReader that can be included in a SessionConfiguration passed into
* {@link CameraDeviceSetup#isSessionConfigurationSupported} before opening and setting up
* a camera device in full, at which point {@link #setSurfacesForMultiResolutionOutput}
* can be used to link to the actual MultiResolutionImageReader.</p>
*
* <p>This constructor takes same arguments used to create a {@link
* MultiResolutionImageReader}: a collection of {@link MultiResolutionStreamInfo}
* objects and the format.</p>
*
* @param streams The group of multi-resolution stream info objects, which are used to create a
* multi-resolution image reader containing a number of ImageReaders.
* @param format The format of the MultiResolutionImageReader. This must be one of the {@link
* android.graphics.ImageFormat} or {@link android.graphics.PixelFormat} constants
* supported by the camera device. Note that not all formats are supported, like
* {@link ImageFormat.NV21}. The supported multi-resolution reader format can be
* queried by {@link MultiResolutionStreamConfigurationMap#getOutputFormats}.
*
* @return The list of {@link OutputConfiguration} objects for a MultiResolutionImageReader.
*
* @throws IllegaArgumentException If the {@code streams} is null or doesn't contain
* at least 2 items, or if {@code format} isn't a valid camera
* format.
*
* @see MultiResolutionImageReader
* @see MultiResolutionStreamInfo
*/
@FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
public static @NonNull List<OutputConfiguration> createInstancesForMultiResolutionOutput(
@NonNull Collection<MultiResolutionStreamInfo> streams,
@Format int format) {
if (streams == null || streams.size() <= 1) {
throw new IllegalArgumentException(
"The streams list must contain at least 2 entries");
}
if (format == ImageFormat.NV21) {
throw new IllegalArgumentException(
"NV21 format is not supported");
}
int groupId = getAndIncreaseMultiResolutionGroupId();
ArrayList<OutputConfiguration> configs = new ArrayList<OutputConfiguration>();
for (MultiResolutionStreamInfo stream : streams) {
Size surfaceSize = new Size(stream.getWidth(), stream.getHeight());
OutputConfiguration config = new OutputConfiguration(
groupId, format, surfaceSize);
config.setPhysicalCameraId(stream.getPhysicalCameraId());
config.setMultiResolutionOutput();
configs.add(config);
// No need to call addSensorPixelModeUsed for ultra high resolution sensor camera,
// because regular and max resolution output configurations are used for DEFAULT mode
// and MAX_RESOLUTION mode respectively by default.
}
return configs;
}
/**
* Set the OutputConfiguration surfaces corresponding to the {@link MultiResolutionImageReader}.
*
* <p>This function should be used together with {@link
* #createInstancesForMultiResolutionOutput}. The application calls {@link
* #createInstancesForMultiResolutionOutput} first to create a list of
* OutputConfiguration objects without the actual MultiResolutionImageReader.
* Once the MultiResolutionImageReader is created later during full camera setup, the
* application then calls this function to assign the surfaces to the OutputConfiguration
* instances.</p>
*
* @param outputConfigurations The OutputConfiguration objects created by {@link
* #createInstancesFromMultiResolutionOutput}
* @param multiResolutionImageReader The MultiResolutionImageReader object created from the same
* MultiResolutionStreamInfo parameters as
* {@code outputConfigurations}.
* @throws IllegalArgumentException If {@code outputConfigurations} or {@code
* multiResolutionImageReader} is {@code null}, the {@code
* outputConfigurations} and {@code multiResolutionImageReader}
* sizes don't match, or if the
* {@code multiResolutionImageReader}'s surfaces don't match
* with the {@code outputConfigurations}.
* @throws IllegalStateException If {@code outputConfigurations} already contains valid output
* surfaces.
*/
@FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
public static void setSurfacesForMultiResolutionOutput(
@NonNull Collection<OutputConfiguration> outputConfigurations,
@NonNull MultiResolutionImageReader multiResolutionImageReader) {
checkNotNull(outputConfigurations, "outputConfigurations must not be null");
checkNotNull(multiResolutionImageReader, "multiResolutionImageReader must not be null");
if (outputConfigurations.size() != multiResolutionImageReader.getReaders().length) {
throw new IllegalArgumentException(
"outputConfigurations and multiResolutionImageReader sizes must match");
}
for (OutputConfiguration config : outputConfigurations) {
String physicalCameraId = config.getPhysicalCameraId();
if (physicalCameraId == null) {
physicalCameraId = "";
}
Surface surface = multiResolutionImageReader.getSurface(config.getConfiguredSize(),
physicalCameraId);
config.addSurface(surface);
}
}
/**
* Create a new {@link OutputConfiguration} instance, with desired Surface size and Surface
* 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 an output configuration
* with a deferred Surface. The application can use this output configuration to create a
* session.
* </p>
*
* <p>Starting from {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V},
* the deferred Surface can be obtained: (1) from {@link android.view.SurfaceView}
* by calling {@link android.view.SurfaceHolder#getSurface}, (2) from
* {@link android.graphics.SurfaceTexture} via
* {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}, (3) from {@link
* android.media.MediaRecorder} via {@link android.media.MediaRecorder.getSurface} or {@link
* android.media.MediaCodec#createPersistentInputSurface}, or (4) from {@link
* android.media.MediaCodce} via {@link android.media.MediaCodec#createInputSurface} or {@link
* android.media.MediaCodec#createPersistentInputSource}.</p>
*
* <ul>
* <li>Surfaces for {@link android.view.SurfaceView} and {@link android.graphics.SurfaceTexture}
* can be deferred until after {@link CameraDevice#createCaptureSession}. In that case, the
* output Surface must be set via {@link #addSurface}, and the Surface configuration must be
* finalized via {@link CameraCaptureSession#finalizeOutputConfiguration} before submitting
* a request with the Surface target.</li>
* <li>For all other target types, the output Surface must be set by {@link #addSurface},
* and {@link CameraCaptureSession#finalizeOutputConfiguration} is not needed because the
* OutputConfiguration used to create the session will contain the actual Surface.</li>
* </ul>
*
* <p>Before {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V}, only {@link
* android.view.SurfaceView} and {@link android.graphics.SurfaceTexture} are supported. Both
* kind of outputs can be deferred until after {@link
* CameraDevice#createCaptureSessionByOutputConfiguration}.</p>
*
* <p>An OutputConfiguration object created by this constructor can be used for {@link
* CameraDeviceSetup.isSessionConfigurationSupported} and {@link
* CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
*
* @param surfaceSize Size for the deferred surface.
* @param klass a non-{@code null} {@link Class} object reference that indicates the source of
* this surface. Only {@link android.view.SurfaceHolder SurfaceHolder.class},
* {@link android.graphics.SurfaceTexture SurfaceTexture.class}, {@link
* android.media.MediaRecorder MediaRecorder.class}, and
* {@link android.media.MediaCodec MediaCodec.class} are supported.
* Before {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V}, only
* {@link android.view.SurfaceHolder SurfaceHolder.class} and {@link
* android.graphics.SurfaceTexture SurfaceTexture.class} are supported.
* @throws IllegalArgumentException if the Surface source class is not supported, or Surface
* size is zero.
*/
public <T> OutputConfiguration(@NonNull Size surfaceSize, @NonNull Class<T> klass) {
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;
mIsDeferredConfig = true;
} else if (klass == android.graphics.SurfaceTexture.class) {
mSurfaceType = SURFACE_TYPE_SURFACE_TEXTURE;
mIsDeferredConfig = true;
} else if (klass == android.media.MediaRecorder.class) {
mSurfaceType = SURFACE_TYPE_MEDIA_RECORDER;
mIsDeferredConfig = false;
} else if (klass == android.media.MediaCodec.class) {
mSurfaceType = SURFACE_TYPE_MEDIA_CODEC;
mIsDeferredConfig = false;
} else {
mSurfaceType = SURFACE_TYPE_UNKNOWN;
throw new IllegalArgumentException("Unknown surface source class type");
}
if (surfaceSize.getWidth() == 0 || surfaceSize.getHeight() == 0) {
throw new IllegalArgumentException("Surface size needs to be non-zero");
}
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;
mIsShared = false;
mPhysicalCameraId = null;
mIsMultiResolution = false;
mSensorPixelModesUsed = new ArrayList<Integer>();
mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
mReadoutTimestampEnabled = false;
mIsReadoutSensorTimestampBase = false;
mUsage = 0;
}
/**
* Create a new {@link OutputConfiguration} instance for an {@link ImageReader} for a given
* format and size.
*
* <p>This constructor creates an OutputConfiguration for an ImageReader without providing
* the actual output Surface. The actual output Surface must be set via {@link #addSurface}
* before creating the capture session.</p>
*
* <p>An OutputConfiguration object created by this constructor can be used for {@link
* CameraDeviceSetup.isSessionConfigurationSupported} and {@link
* CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
*
* @param format The format of the ImageReader output. This must be one of the
* {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
* constants. Note that not all formats are supported by the camera device.
* @param surfaceSize Size for the ImageReader surface.
* @throws IllegalArgumentException if the Surface size is null or zero.
*/
@FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
public OutputConfiguration(@Format int format, @NonNull Size surfaceSize) {
this(format, surfaceSize,
format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN);
}
/**
* Create a new {@link OutputConfiguration} instance for an {@link ImageReader} for a given
* surfaceGroupId, format, and size.
*
* <p>This constructor creates an OutputConfiguration for an ImageReader without providing
* the actual output Surface. The actual output Surface must be set via {@link #addSurface}
* before creating the capture session.</p>
*
* <p>An OutputConfiguration object created by this constructor can be used for {@link
* CameraDeviceSetup.isSessionConfigurationSupported} and {@link
* CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
*
* @param surfaceGroupId A group ID for this output, used for sharing memory between multiple
* outputs.
* @param format The format of the ImageReader output. This must be one of the
* {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
* constants. Note that not all formats are supported by the camera device.
* @param surfaceSize Size for the ImageReader surface.
* @throws IllegalArgumentException if the Surface size is null or zero.
*/
@FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
public OutputConfiguration(int surfaceGroupId, @Format int format, @NonNull Size surfaceSize) {
this(surfaceGroupId, format, surfaceSize,
format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN);
}
/**
* Create a new {@link OutputConfiguration} instance for an {@link ImageReader} for a given
* format, size, and usage flags.
*
* <p>This constructor creates an OutputConfiguration for an ImageReader without providing
* the actual output Surface. The actual output Surface must be set via {@link #addSurface}
* before creating the capture session.</p>
*
* <p>An OutputConfiguration object created by this constructor can be used for {@link
* CameraDeviceSetup.isSessionConfigurationSupported} and {@link
* CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
*
* @param format The format of the ImageReader output. This must be one of the
* {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
* constants. Note that not all formats are supported by the camera device.
* @param surfaceSize Size for the ImageReader surface.
* @param usage The usage flags of the ImageReader output surface.
* @throws IllegalArgumentException if the Surface size is null or zero.
*/
@FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
public OutputConfiguration(@Format int format, @NonNull Size surfaceSize, @Usage long usage) {
this(SURFACE_GROUP_ID_NONE, format, surfaceSize, usage);
}
/**
* Create a new {@link OutputConfiguration} instance for an {@link ImageReader} for a given
* surface group id, format, size, and usage flags.
*
* <p>This constructor creates an OutputConfiguration for an ImageReader without providing
* the actual output Surface. The actual output Surface must be set via {@link #addSurface}
* before creating the capture session.</p>
*
* <p>An OutputConfiguration object created by this constructor can be used for {@link
* CameraDeviceSetup.isSessionConfigurationSupported} and {@link
* CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
*
* @param surfaceGroupId A group ID for this output, used for sharing memory between multiple
* outputs.
* @param format The format of the ImageReader output. This must be one of the
* {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
* constants. Note that not all formats are supported by the camera device.
* @param surfaceSize Size for the ImageReader surface.
* @param usage The usage flags of the ImageReader output surface.
* @throws IllegalArgumentException if the Surface size is null or zero.
*/
@FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
public OutputConfiguration(int surfaceGroupId, @Format int format,
@NonNull Size surfaceSize, @Usage long usage) {
checkNotNull(surfaceSize, "surfaceSize must not be null");
if (surfaceSize.getWidth() == 0 || surfaceSize.getHeight() == 0) {
throw new IllegalArgumentException("Surface size needs to be non-zero");
}
mSurfaceType = SURFACE_TYPE_IMAGE_READER;
mSurfaceGroupId = surfaceGroupId;
mSurfaces = new ArrayList<Surface>();
mRotation = ROTATION_0;
mConfiguredSize = surfaceSize;
mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(format);
mConfiguredDataspace = StreamConfigurationMap.imageFormatToDataspace(format);
mConfiguredGenerationId = 0;
mIsDeferredConfig = false;
mIsShared = false;
mPhysicalCameraId = null;
mIsMultiResolution = false;
mSensorPixelModesUsed = new ArrayList<Integer>();
mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
mReadoutTimestampEnabled = false;
mIsReadoutSensorTimestampBase = false;
mUsage = usage;
}
/**
* Enable multiple surfaces sharing the same OutputConfiguration
*
* <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. Due to buffer sharing
* clients should be careful when adding surface outputs that modify their input data. If such
* case exists, camera clients should have an additional mechanism to synchronize read and write
* access between individual consumers.</p>
*
* <p>Two surfaces are compatible in the below cases:</p>
*
* <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 fails with {@link
* CameraCaptureSession.StateCallback#onConfigureFailed}.
* </ol>
*
* <p>To enable surface sharing, this function must be called before {@link
* CameraDevice#createCaptureSessionByOutputConfigurations} or {@link
* CameraDevice#createReprocessableCaptureSessionByConfigurations}. Calling this function after
* {@link CameraDevice#createCaptureSessionByOutputConfigurations} has no effect.</p>
*
* <p>Up to {@link #getMaxSharedSurfaceCount} 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>
*
* <p>This function must not be called from OutputConfigurations created by {@link
* #createInstancesForMultiResolutionOutput}.</p>
*
* @throws IllegalStateException If this OutputConfiguration is created via {@link
* #createInstancesForMultiResolutionOutput} to back a MultiResolutionImageReader.
*/
public void enableSurfaceSharing() {
if (mIsMultiResolution) {
throw new IllegalStateException("Cannot enable surface sharing on "
+ "multi-resolution output configurations");
}
mIsShared = true;
}
/**
* Set the id of the physical camera for this OutputConfiguration
*
* <p>In the case one logical camera is made up of multiple physical cameras, it could be
* desirable for the camera application to request streams from individual physical cameras.
* This call achieves it by mapping the OutputConfiguration to the physical camera id.</p>
*
* <p>The valid physical camera ids can be queried by {@link
* CameraCharacteristics#getPhysicalCameraIds}.</p>
*
* <p>Passing in a null physicalCameraId means that the OutputConfiguration is for a logical
* stream.</p>
*
* <p>This function must be called before {@link
* CameraDevice#createCaptureSessionByOutputConfigurations} or {@link
* CameraDevice#createReprocessableCaptureSessionByConfigurations}. Calling this function
* after {@link CameraDevice#createCaptureSessionByOutputConfigurations} or {@link
* CameraDevice#createReprocessableCaptureSessionByConfigurations} has no effect.</p>
*
* <p>As of {@link android.os.Build.VERSION_CODES#S Android 12}, an image buffer from a
* physical camera stream can be used for reprocessing to logical camera streams and streams
* from the same physical camera if the camera device supports multi-resolution input and output
* streams. See {@link CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP}
* for details. The behaviors of reprocessing from a non-physical camera stream to a physical
* camera stream, and from a physical camera stream to a physical camera stream of different
* physical camera, are device-specific and not guaranteed to be supported.</p>
*
* <p>On prior API levels, the surface belonging to a physical camera OutputConfiguration must
* not be used as input or output of a reprocessing request. </p>
*/
public void setPhysicalCameraId(@Nullable String physicalCameraId) {
mPhysicalCameraId = physicalCameraId;
}
/**
* Add a sensor pixel mode that this OutputConfiguration will be used in.
*
* <p> In the case that this output stream configuration (format, width, height) is
* available through {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}
* configurations and
* {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION},
* configurations, the camera sub-system will assume that this {@link OutputConfiguration} will
* be used only with {@link android.hardware.camera2.CaptureRequest}s which has
* {@link android.hardware.camera2.CaptureRequest#SENSOR_PIXEL_MODE} set to
* {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT}.
* In such cases, if clients intend to use the
* {@link OutputConfiguration}(s) in a {@link android.hardware.camera2.CaptureRequest} with
* other sensor pixel modes, they must specify which
* {@link android.hardware.camera2.CaptureRequest#SENSOR_PIXEL_MODE}(s) they will use this
* {@link OutputConfiguration} with, by calling this method.
*
* In case this output stream configuration (format, width, height) is only in
* {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION},
* configurations, this output target must only be used with
* {@link android.hardware.camera2.CaptureRequest}s which has
* {@link android.hardware.camera2.CaptureRequest#SENSOR_PIXEL_MODE} set to
* {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION} and that
* is what the camera sub-system will assume. If clients add
* {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT} in this
* case, session configuration will fail, if this {@link OutputConfiguration} is included.
*
* In case this output stream configuration (format, width, height) is only in
* {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP},
* configurations, this output target must only be used with
* {@link android.hardware.camera2.CaptureRequest}s which has
* {@link android.hardware.camera2.CaptureRequest#SENSOR_PIXEL_MODE} set to
* {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT} and that is what
* the camera sub-system will assume. If clients add
* {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION} in this
* case, session configuration will fail, if this {@link OutputConfiguration} is included.
*
* @param sensorPixelModeUsed The sensor pixel mode this OutputConfiguration will be used with
* </p>
*
*/
public void addSensorPixelModeUsed(@SensorPixelMode int sensorPixelModeUsed) {
// Verify that the values are in range.
if (sensorPixelModeUsed != CameraMetadata.SENSOR_PIXEL_MODE_DEFAULT &&
sensorPixelModeUsed != CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) {
throw new IllegalArgumentException("Not a valid sensor pixel mode " +
sensorPixelModeUsed);
}
if (mSensorPixelModesUsed.contains(sensorPixelModeUsed)) {
// Already added, ignore;
return;
}
mSensorPixelModesUsed.add(sensorPixelModeUsed);
}
/**
* Remove a sensor pixel mode, previously added through addSensorPixelModeUsed, from this
* OutputConfiguration.
*
* <p> Sensor pixel modes added via calls to {@link #addSensorPixelModeUsed} can also be removed
* from the OutputConfiguration.</p>
*
* @param sensorPixelModeUsed The sensor pixel mode to be removed.
*
* @throws IllegalArgumentException If the sensor pixel mode wasn't previously added
* through {@link #addSensorPixelModeUsed}.
*/
public void removeSensorPixelModeUsed(@SensorPixelMode int sensorPixelModeUsed) {
if (!mSensorPixelModesUsed.remove(Integer.valueOf(sensorPixelModeUsed))) {
throw new IllegalArgumentException("sensorPixelMode " + sensorPixelModeUsed +
"is not part of this output configuration");
}
}
/**
* Check if this configuration is for a physical camera.
*
* <p>This returns true if the output configuration was for a physical camera making up a
* logical multi camera via {@link OutputConfiguration#setPhysicalCameraId}.</p>
* @hide
*/
public boolean isForPhysicalCamera() {
return (mPhysicalCameraId != null);
}
/**
* Check if this configuration has deferred configuration.
*
* <p>This will return true if the output configuration was constructed with {@link
* android.view.SurfaceView} or {@link android.graphics.SurfaceTexture} 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
*/
public boolean isDeferredConfiguration() {
return mIsDeferredConfig;
}
/**
* Add a surface to this OutputConfiguration.
*
* <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}. It is possible to call this method
* after the output configurations have been finalized only in cases of enabled surface sharing
* see {@link #enableSurfaceSharing}. The modified output configuration must be updated with
* {@link CameraCaptureSession#updateOutputConfiguration}. If this function is called before
* session creation, {@link CameraCaptureSession#finalizeOutputConfigurations} doesn't need to
* be called.</p>
*
* <p> If the OutputConfiguration was constructed by {@link
* OutputConfiguration#OutputConfiguration(Size, Class)}, the added surface must be obtained:
* <ul>
* <li>from {@link android.view.SurfaceView} by calling
* {@link android.view.SurfaceHolder#getSurface}</li>
* <li>from {@link android.graphics.SurfaceTexture} by calling
* {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}</li>
* <li>from {@link android.media.MediaRecorder} by calling
* {@link android.media.MediaRecorder#getSurface} or {@link
* android.media.MediaCodec#createPersistentInputSurface}</li>
* <li>from {@link android.media.MediaCodce} by calling
* {@link android.media.MediaCodec#createInputSurface} or {@link
* android.media.MediaCodec#createPersistentInputSource}</li>
* </ul>
*
* <p> If the OutputConfiguration was constructed by {@link #OutputConfiguration(int, Size)}
* or its variants, the added surface must be obtained from {@link android.media.ImageReader}
* by calling {@link android.media.ImageReader#getSurface}.</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
* 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 addSurface(@NonNull Surface surface) {
checkNotNull(surface, "Surface must not be null");
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, "Added surface size " + surfaceSize +
" is different than pre-configured size " + mConfiguredSize +
", the pre-configured size will be used.");
}
if (mConfiguredFormat != SurfaceUtils.getSurfaceFormat(surface)) {
throw new IllegalArgumentException("The format of added surface format doesn't match");
}
// If the surface format is PRIVATE, do not enforce dataSpace because camera device may
// override it.
if (mConfiguredFormat != ImageFormat.PRIVATE &&
mConfiguredDataspace != SurfaceUtils.getSurfaceDataspace(surface)) {
throw new IllegalArgumentException("The dataspace of added surface doesn't match");
}
mSurfaces.add(surface);
}
/**
* Remove a surface from this OutputConfiguration.
*
* <p> Surfaces added via calls to {@link #addSurface} can also be removed from the
* OutputConfiguration. The only notable exception is the surface associated with
* the OutputConfiguration (see {@link #getSurface}) which was passed as part of the
* constructor or was added first in the case of
* {@link OutputConfiguration#OutputConfiguration(Size, Class)}, {@link
* OutputConfiguration#OutputConfiguration(int, Size)}, {@link
* OutputConfiguration#OutputConfiguration(int, Size, long)}, {@link
* OutputConfiguration#OutputConfiguration(int, int, Size)}, {@link
* OutputConfiguration#OutputConfiguration(int, int, Size, long)}.</p>
*
* @param surface The surface to be removed.
*
* @throws IllegalArgumentException If the surface is associated with this OutputConfiguration
* (see {@link #getSurface}) or the surface didn't get added
* with {@link #addSurface}.
*/
public void removeSurface(@NonNull Surface surface) {
checkNotNull(surface, "Surface must not be null");
if (getSurface() == surface) {
throw new IllegalArgumentException(
"Cannot remove surface associated with this output configuration");
}
if (!mSurfaces.remove(surface)) {
throw new IllegalArgumentException("Surface is not part of this output configuration");
}
}
/**
* Set stream use case for this OutputConfiguration
*
* <p>Stream use case is used to describe the purpose of the stream, whether it's for live
* preview, still image capture, video recording, or their combinations. This flag is useful
* for scenarios where the immediate consumer target isn't sufficient to indicate the stream's
* usage.</p>
*
* <p>The main difference between stream use case and capture intent is that the former
* enables the camera device to optimize camera hardware and software pipelines based on user
* scenarios for each stream, whereas the latter is mainly a hint to camera to decide
* optimal 3A strategy that's applicable to the whole session. The camera device carries out
* configurations such as selecting tuning parameters, choosing camera sensor mode, and
* constructing image processing pipeline based on the streams's use cases. Capture intents are
* then used to fine tune 3A behaviors such as adjusting AE/AF convergence speed, and capture
* intents may change during the lifetime of a session. For example, for a session with a
* PREVIEW_VIDEO_STILL use case stream and a STILL_CAPTURE use case stream, the capture intents
* may be PREVIEW with fast 3A convergence speed and flash metering with automatic control for
* live preview, STILL_CAPTURE with best 3A parameters for still photo capture, or VIDEO_RECORD
* with slower 3A convergence speed for better video playback experience.</p>
*
* <p>The supported stream use cases supported by a camera device can be queried by
* {@link android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES}.</p>
*
* <p>The mandatory stream combinations involving stream use cases can be found at {@link
* android.hardware.camera2.CameraDevice#createCaptureSession}, as well as queried via
* {@link android.hardware.camera2.params.MandatoryStreamCombination}. The application is
* strongly recommended to select one of the guaranteed stream combinations where all streams'
* use cases are set to non-DEFAULT values. If the application chooses a stream combination
* not in the mandatory list, the camera device may ignore some use case flags due to
* hardware constraints or implementation details.</p>
*
* <p>This function must be called before {@link CameraDevice#createCaptureSession} or {@link
* CameraDevice#createCaptureSessionByOutputConfigurations}. Calling this function after
* {@link CameraDevice#createCaptureSession} or
* {@link CameraDevice#createCaptureSessionByOutputConfigurations} has no effect to the camera
* session.</p>
*
* @param streamUseCase The stream use case to be set.
*
* @throws IllegalArgumentException If the streamUseCase isn't within the range of valid
* values.
*/
public void setStreamUseCase(@StreamUseCase long streamUseCase) {
// Verify that the value is in range
long maxUseCaseValue = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_CROPPED_RAW;
if (streamUseCase > maxUseCaseValue &&
streamUseCase < CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VENDOR_START) {
throw new IllegalArgumentException("Not a valid stream use case value " +
streamUseCase);
}
mStreamUseCase = streamUseCase;
}
/**
* Get the current stream use case
*
* <p>If no {@link #setStreamUseCase} is called first, this function returns
* {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT DEFAULT}.</p>
*
* @return the currently set stream use case
*/
public long getStreamUseCase() {
return mStreamUseCase;
}
/**
* Set timestamp base for this output target
*
* <p>Timestamp base describes the time domain of images from this
* camera output and its relationship with {@link
* CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE}.</p>
*
* <p>If this function is not called, the timestamp base for this output
* is {@link #TIMESTAMP_BASE_DEFAULT}, with which the camera device adjusts
* timestamps based on the output target.</p>
*
* <p>See {@link #TIMESTAMP_BASE_DEFAULT}, {@link #TIMESTAMP_BASE_SENSOR},
* and {@link #TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED} for details of each timestamp base.</p>
*
* @param timestampBase The timestamp base to be set.
*
* @throws IllegalArgumentException If the timestamp base isn't within the range of valid
* values.
*/
public void setTimestampBase(@TimestampBase int timestampBase) {
// Verify that the value is in range
if (timestampBase < TIMESTAMP_BASE_DEFAULT ||
timestampBase > TIMESTAMP_BASE_READOUT_SENSOR) {
throw new IllegalArgumentException("Not a valid timestamp base value " +
timestampBase);
}
if (timestampBase == TIMESTAMP_BASE_READOUT_SENSOR) {
mTimestampBase = TIMESTAMP_BASE_SENSOR;
mReadoutTimestampEnabled = true;
mIsReadoutSensorTimestampBase = true;
} else {
mTimestampBase = timestampBase;
mIsReadoutSensorTimestampBase = false;
}
}
/**
* Get the current timestamp base
*
* <p>If no {@link #setTimestampBase} is called first, this function returns
* {@link #TIMESTAMP_BASE_DEFAULT}.</p>
*
* @return The currently set timestamp base
*/
public @TimestampBase int getTimestampBase() {
if (mIsReadoutSensorTimestampBase) {
return TIMESTAMP_BASE_READOUT_SENSOR;
} else {
return mTimestampBase;
}
}
/**
* Set the mirroring mode for this output target
*
* <p>If this function is not called, the mirroring mode for this output is
* {@link #MIRROR_MODE_AUTO}, with which the camera API will mirror the output images
* horizontally for front facing camera.</p>
*
* <p>For efficiency, the mirror effect is applied as a transform flag, so it is only effective
* in some outputs. It works automatically for SurfaceView and TextureView outputs. For manual
* use of SurfaceTexture, it is reflected in the value of
* {@link android.graphics.SurfaceTexture#getTransformMatrix}. For other end points, such as
* ImageReader, MediaRecorder, or MediaCodec, the mirror mode has no effect. If mirroring is
* needed for such outputs, the application needs to mirror the image buffers itself before
* passing them onward.</p>
*/
public void setMirrorMode(@MirrorMode int mirrorMode) {
// Verify that the value is in range
if (mirrorMode < MIRROR_MODE_AUTO ||
mirrorMode > MIRROR_MODE_V) {
throw new IllegalArgumentException("Not a valid mirror mode " + mirrorMode);
}
mMirrorMode = mirrorMode;
}
/**
* Get the current mirroring mode
*
* <p>If no {@link #setMirrorMode} is called first, this function returns
* {@link #MIRROR_MODE_AUTO}.</p>
*
* @return The currently set mirroring mode
*/
public @MirrorMode int getMirrorMode() {
return mMirrorMode;
}
/**
* Use the camera sensor's readout time for the image timestamp.
*
* <p>The start of the camera sensor readout after exposure. For a rolling shutter camera
* sensor, the timestamp is typically equal to {@code (the start of exposure time) +
* (exposure time) + (certain fixed offset)}. The fixed offset can vary per session, depending
* on the underlying sensor configuration. The benefit of using readout time is that when
* camera runs in a fixed frame rate, the timestamp intervals between frames are constant.</p>
*
* <p>Readout timestamp is supported only if {@link
* CameraCharacteristics#SENSOR_READOUT_TIMESTAMP} is
* {@link CameraMetadata#SENSOR_READOUT_TIMESTAMP_HARDWARE}.</p>
*
* <p>As long as readout timestamp is supported, if the timestamp base is
* {@link #TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED}, or if the timestamp base is DEFAULT for a
* SurfaceView output, the image timestamps for the output are always readout time regardless
* of whether this function is called.</p>
*
* @param on The output image timestamp is the start of exposure time if false, and
* the start of readout time if true.
*/
public void setReadoutTimestampEnabled(boolean on) {
mReadoutTimestampEnabled = on;
}
/** Whether readout timestamp is used for this OutputConfiguration.
*
* @see #setReadoutTimestampEnabled
*/
public boolean isReadoutTimestampEnabled() {
return mReadoutTimestampEnabled;
}
/**
* Create a new {@link OutputConfiguration} instance with another {@link OutputConfiguration}
* instance.
*
* @param other Another {@link OutputConfiguration} instance to be copied.
*
* @hide
*/
public OutputConfiguration(@NonNull OutputConfiguration other) {
if (other == null) {
throw new IllegalArgumentException("OutputConfiguration shouldn't be null");
}
this.mSurfaces = other.mSurfaces;
this.mRotation = other.mRotation;
this.mSurfaceGroupId = other.mSurfaceGroupId;
this.mSurfaceType = other.mSurfaceType;
this.mConfiguredDataspace = other.mConfiguredDataspace;
this.mConfiguredFormat = other.mConfiguredFormat;
this.mConfiguredSize = other.mConfiguredSize;
this.mConfiguredGenerationId = other.mConfiguredGenerationId;
this.mIsDeferredConfig = other.mIsDeferredConfig;
this.mIsShared = other.mIsShared;
this.mPhysicalCameraId = other.mPhysicalCameraId;
this.mIsMultiResolution = other.mIsMultiResolution;
this.mSensorPixelModesUsed = other.mSensorPixelModesUsed;
this.mDynamicRangeProfile = other.mDynamicRangeProfile;
this.mColorSpace = other.mColorSpace;
this.mStreamUseCase = other.mStreamUseCase;
this.mTimestampBase = other.mTimestampBase;
this.mMirrorMode = other.mMirrorMode;
this.mReadoutTimestampEnabled = other.mReadoutTimestampEnabled;
this.mUsage = other.mUsage;
}
/**
* Create an OutputConfiguration from Parcel.
*/
private OutputConfiguration(@NonNull Parcel source) {
int rotation = source.readInt();
int surfaceSetId = source.readInt();
int surfaceType = source.readInt();
int width = source.readInt();
int height = source.readInt();
boolean isDeferred = source.readInt() == 1;
boolean isShared = source.readInt() == 1;
ArrayList<Surface> surfaces = new ArrayList<Surface>();
source.readTypedList(surfaces, Surface.CREATOR);
String physicalCameraId = source.readString();
boolean isMultiResolutionOutput = source.readInt() == 1;
int[] sensorPixelModesUsed = source.createIntArray();
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
long dynamicRangeProfile = source.readLong();
DynamicRangeProfiles.checkProfileValue(dynamicRangeProfile);
int colorSpace = source.readInt();
long streamUseCase = source.readLong();
int timestampBase = source.readInt();
int mirrorMode = source.readInt();
boolean readoutTimestampEnabled = source.readInt() == 1;
int format = source.readInt();
int dataSpace = source.readInt();
long usage = source.readLong();
mSurfaceGroupId = surfaceSetId;
mRotation = rotation;
mSurfaces = surfaces;
mConfiguredSize = new Size(width, height);
mIsDeferredConfig = isDeferred;
mIsShared = isShared;
mSurfaces = surfaces;
mUsage = 0;
if (mSurfaces.size() > 0) {
mSurfaceType = SURFACE_TYPE_UNKNOWN;
mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurfaces.get(0));
mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurfaces.get(0));
mConfiguredGenerationId = mSurfaces.get(0).getGenerationId();
} else {
mSurfaceType = surfaceType;
if (mSurfaceType != SURFACE_TYPE_IMAGE_READER) {
mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(
ImageFormat.PRIVATE);
mConfiguredDataspace =
StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
} else {
mConfiguredFormat = format;
mConfiguredDataspace = dataSpace;
mUsage = usage;
}
mConfiguredGenerationId = 0;
}
mPhysicalCameraId = physicalCameraId;
mIsMultiResolution = isMultiResolutionOutput;
mSensorPixelModesUsed = convertIntArrayToIntegerList(sensorPixelModesUsed);
mDynamicRangeProfile = dynamicRangeProfile;
mColorSpace = colorSpace;
mStreamUseCase = streamUseCase;
mTimestampBase = timestampBase;
mMirrorMode = mirrorMode;
mReadoutTimestampEnabled = readoutTimestampEnabled;
}
/**
* Get the maximum supported shared {@link Surface} count.
*
* @return the maximum number of surfaces that can be added per each OutputConfiguration.
*
* @see #enableSurfaceSharing
*/
public int getMaxSharedSurfaceCount() {
return MAX_SURFACES_COUNT;
}
/**
* Get 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 or {@link OutputConfiguration#addSurface}.
*/
public @Nullable Surface getSurface() {
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} as specified in
* the constructor and {@link OutputConfiguration#addSurface}. The list should not be modified.
*/
@NonNull
public List<Surface> getSurfaces() {
return Collections.unmodifiableList(mSurfaces);
}
/**
* Get the rotation associated with this {@link OutputConfiguration}.
*
* @return the rotation associated with this {@link OutputConfiguration}.
* Value will be one of ROTATION_[0, 90, 180, 270]
*
* @hide
*/
@SystemApi
public int getRotation() {
return mRotation;
}
/**
* Get the surface group ID associated with this {@link OutputConfiguration}.
*
* @return the surface group ID associated with this {@link OutputConfiguration}.
* The default value is {@value #SURFACE_GROUP_ID_NONE}.
*/
public int getSurfaceGroupId() {
return mSurfaceGroupId;
}
/**
* Get the configured size associated with this {@link OutputConfiguration}.
*
* @return The configured size associated with this {@link OutputConfiguration}.
*
* @hide
*/
public Size getConfiguredSize() {
return mConfiguredSize;
}
/**
* Get the physical camera ID associated with this {@link OutputConfiguration}.
*
* <p>If this OutputConfiguration isn't targeting a physical camera of a logical
* multi-camera, this function returns {@code null}.</p>
*
* @return The physical camera Id associated with this {@link OutputConfiguration}.
*
* @hide
*/
public @Nullable String getPhysicalCameraId() {
return mPhysicalCameraId;
}
public static final @android.annotation.NonNull Parcelable.Creator<OutputConfiguration> CREATOR =
new Parcelable.Creator<OutputConfiguration>() {
@Override
public OutputConfiguration createFromParcel(Parcel source) {
return new OutputConfiguration(source);
}
@Override
public OutputConfiguration[] newArray(int size) {
return new OutputConfiguration[size];
}
};
@Override
public int describeContents() {
return 0;
}
private static int[] convertIntegerToIntList(List<Integer> integerList) {
int[] integerArray = new int[integerList.size()];
for (int i = 0; i < integerList.size(); i++) {
integerArray[i] = integerList.get(i);
}
return integerArray;
}
private static ArrayList<Integer> convertIntArrayToIntegerList(int[] intArray) {
ArrayList<Integer> integerList = new ArrayList<Integer>();
if (intArray == null) {
return integerList;
}
for (int i = 0; i < intArray.length; i++) {
integerList.add(intArray[i]);
}
return integerList;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
if (dest == null) {
throw new IllegalArgumentException("dest must not be null");
}
dest.writeInt(mRotation);
dest.writeInt(mSurfaceGroupId);
dest.writeInt(mSurfaceType);
dest.writeInt(mConfiguredSize.getWidth());
dest.writeInt(mConfiguredSize.getHeight());
dest.writeInt(mIsDeferredConfig ? 1 : 0);
dest.writeInt(mIsShared ? 1 : 0);
dest.writeTypedList(mSurfaces);
dest.writeString(mPhysicalCameraId);
dest.writeInt(mIsMultiResolution ? 1 : 0);
// writeList doesn't seem to work well with Integer list.
dest.writeIntArray(convertIntegerToIntList(mSensorPixelModesUsed));
dest.writeLong(mDynamicRangeProfile);
dest.writeInt(mColorSpace);
dest.writeLong(mStreamUseCase);
dest.writeInt(mTimestampBase);
dest.writeInt(mMirrorMode);
dest.writeInt(mReadoutTimestampEnabled ? 1 : 0);
dest.writeInt(mConfiguredFormat);
dest.writeInt(mConfiguredDataspace);
dest.writeLong(mUsage);
}
/**
* Check if this {@link OutputConfiguration} is equal to another {@link OutputConfiguration}.
*
* <p>Two output configurations are only equal if and only if the underlying surfaces, surface
* properties (width, height, format, dataspace) when the output configurations are created,
* and all other configuration parameters are equal. </p>
*
* @return {@code true} if the objects were equal, {@code false} otherwise
*/
@Override
public boolean equals(@Nullable Object obj) {
if (obj == null) {
return false;
} else if (this == obj) {
return true;
} else if (obj instanceof OutputConfiguration) {
final OutputConfiguration other = (OutputConfiguration) obj;
if (mRotation != other.mRotation
|| !mConfiguredSize.equals(other.mConfiguredSize)
|| mConfiguredFormat != other.mConfiguredFormat
|| mSurfaceGroupId != other.mSurfaceGroupId
|| mSurfaceType != other.mSurfaceType
|| mIsDeferredConfig != other.mIsDeferredConfig
|| mIsShared != other.mIsShared
|| mConfiguredDataspace != other.mConfiguredDataspace
|| mConfiguredGenerationId != other.mConfiguredGenerationId
|| !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId)
|| mIsMultiResolution != other.mIsMultiResolution
|| mStreamUseCase != other.mStreamUseCase
|| mTimestampBase != other.mTimestampBase
|| mMirrorMode != other.mMirrorMode
|| mReadoutTimestampEnabled != other.mReadoutTimestampEnabled
|| mUsage != other.mUsage) {
return false;
}
if (mSensorPixelModesUsed.size() != other.mSensorPixelModesUsed.size()) {
return false;
}
for (int j = 0; j < mSensorPixelModesUsed.size(); j++) {
if (!Objects.equals(
mSensorPixelModesUsed.get(j), other.mSensorPixelModesUsed.get(j))) {
return false;
}
}
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;
}
if (!mIsDeferredConfig && mSurfaces.size() != other.mSurfaces.size()) return false;
if (mDynamicRangeProfile != other.mDynamicRangeProfile) {
return false;
}
if (mColorSpace != other.mColorSpace) {
return false;
}
return true;
}
return false;
}
/**
* Get and increase the next MultiResolution group id.
*
* If the ID reaches -1, skip it.
*/
private static int getAndIncreaseMultiResolutionGroupId() {
return sNextMultiResolutionGroupId.getAndUpdate(i ->
i + 1 == SURFACE_GROUP_ID_NONE ? i + 2 : i + 1);
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
// Need ensure that the hashcode remains unchanged after adding a deferred surface.
// Otherwise the deferred output configuration will be lost in the camera stream map
// after the deferred surface is set.
if (mIsDeferredConfig) {
return HashCodeHelpers.hashCode(
mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace,
mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0,
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
mDynamicRangeProfile, mColorSpace, mStreamUseCase,
mTimestampBase, mMirrorMode, mReadoutTimestampEnabled ? 1 : 0,
Long.hashCode(mUsage));
}
return HashCodeHelpers.hashCode(
mRotation, mSurfaces.hashCode(), mConfiguredGenerationId,
mConfiguredSize.hashCode(), mConfiguredFormat,
mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0,
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
mDynamicRangeProfile, mColorSpace, mStreamUseCase, mTimestampBase,
mMirrorMode, mReadoutTimestampEnabled ? 1 : 0, Long.hashCode(mUsage));
}
private static final String TAG = "OutputConfiguration";
// A surfaceGroupId counter used for MultiResolutionImageReader. Its value is
// incremented every time {@link createInstancesForMultiResolutionOutput} is called.
private static AtomicInteger sNextMultiResolutionGroupId = new AtomicInteger(0);
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.
private final int mSurfaceType;
// The size, format, and dataspace of the surface when OutputConfiguration is created.
private final Size mConfiguredSize;
private final int mConfiguredFormat;
private final int mConfiguredDataspace;
// Surface generation ID to distinguish changes to Surface native internals
private final int mConfiguredGenerationId;
// Flag indicating if this config has deferred surface.
private final boolean mIsDeferredConfig;
// Flag indicating if this config has shared surfaces
private boolean mIsShared;
// The physical camera id that this output configuration is for.
private String mPhysicalCameraId;
// Flag indicating if this config is for a multi-resolution output with a
// MultiResolutionImageReader
private boolean mIsMultiResolution;
// The sensor pixel modes that this OutputConfiguration will use
private ArrayList<Integer> mSensorPixelModesUsed;
// Dynamic range profile
private long mDynamicRangeProfile;
// Color space
private int mColorSpace;
// Stream use case
private long mStreamUseCase;
// Timestamp base
private int mTimestampBase;
// Mirroring mode
private int mMirrorMode;
// readout timestamp
private boolean mReadoutTimestampEnabled;
// Whether the timestamp base is set to READOUT_SENSOR
private boolean mIsReadoutSensorTimestampBase;
// The usage flags. Only set for instances created for ImageReader without specifying surface.
private long mUsage;
}