summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Shuzhen Wang <shuzhenwang@google.com> 2020-11-16 13:05:47 -0800
committer Shuzhen Wang <shuzhenwang@google.com> 2021-03-02 14:27:16 -0800
commit80fc5cad540e14d27e2eed1ac8e96f3eb0fa96e1 (patch)
treef3c71a7d506eb29cee09ec93f632bd400e9a0452
parent04501d566e0f4e976f3993f6dbbd997d0e014a51 (diff)
Camera: Add multi-resolution image reader and reprocessing support
- Add MultiResolutionImageReader class for variable size image output. - Add camera characteristics for support multi-resolution output and input streams. - Update reprocessing interface doc for variable size support. Test: atest MultiResolutionImageReaderTest, MultiResolutionReprocessCaptureTest Bug: 156254356 Change-Id: I913abe38020c224c40903f42b8d45d40b65a5612
-rw-r--r--core/api/current.txt31
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java92
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java57
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java52
-rw-r--r--core/java/android/hardware/camera2/MultiResolutionImageReader.java309
-rw-r--r--core/java/android/hardware/camera2/TotalCaptureResult.java42
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java42
-rw-r--r--core/java/android/hardware/camera2/impl/CameraMetadataNative.java76
-rw-r--r--core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java5
-rw-r--r--core/java/android/hardware/camera2/params/InputConfiguration.java82
-rw-r--r--core/java/android/hardware/camera2/params/MandatoryStreamCombination.java17
-rw-r--r--core/java/android/hardware/camera2/params/MultiResolutionStreamConfigurationMap.java335
-rw-r--r--core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java113
-rw-r--r--core/java/android/hardware/camera2/params/OutputConfiguration.java126
-rw-r--r--core/java/android/hardware/camera2/params/SessionConfiguration.java6
-rw-r--r--core/java/android/hardware/camera2/params/StreamConfigurationMap.java22
-rw-r--r--media/java/android/media/ImageReader.java120
17 files changed, 1435 insertions, 92 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 8a543de40b18..a96f2ba2fa6e 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -17745,6 +17745,7 @@ package android.hardware.camera2 {
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SCALER_DEFAULT_SECURE_IMAGE_SIZE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap> SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SENSOR_AVAILABLE_TEST_PATTERN_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.BlackLevelPattern> SENSOR_BLACK_LEVEL_PATTERN;
@@ -18349,9 +18350,20 @@ package android.hardware.camera2 {
field public static final int MAX_THUMBNAIL_DIMENSION = 256; // 0x100
}
+ public class MultiResolutionImageReader implements java.lang.AutoCloseable {
+ method public void close();
+ method protected void finalize();
+ method public void flush();
+ method @NonNull public android.hardware.camera2.params.MultiResolutionStreamInfo getStreamInfoForImageReader(@NonNull android.media.ImageReader);
+ method @NonNull public android.view.Surface getSurface();
+ method @NonNull public static android.hardware.camera2.MultiResolutionImageReader newInstance(@NonNull java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo>, int, @IntRange(from=1) int);
+ method public void setOnImageAvailableListener(@Nullable android.media.ImageReader.OnImageAvailableListener, @Nullable java.util.concurrent.Executor);
+ }
+
public final class TotalCaptureResult extends android.hardware.camera2.CaptureResult {
method @NonNull public java.util.List<android.hardware.camera2.CaptureResult> getPartialResults();
- method public java.util.Map<java.lang.String,android.hardware.camera2.CaptureResult> getPhysicalCameraResults();
+ method @Deprecated public java.util.Map<java.lang.String,android.hardware.camera2.CaptureResult> getPhysicalCameraResults();
+ method @NonNull public java.util.Map<java.lang.String,android.hardware.camera2.TotalCaptureResult> getPhysicalCameraTotalResults();
}
}
@@ -18400,9 +18412,11 @@ package android.hardware.camera2.params {
public final class InputConfiguration {
ctor public InputConfiguration(int, int, int);
+ ctor public InputConfiguration(@NonNull java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo>, int);
method public int getFormat();
method public int getHeight();
method public int getWidth();
+ method public boolean isMultiResolution();
}
public final class LensShadingMap {
@@ -18445,6 +18459,20 @@ package android.hardware.camera2.params {
field public static final int METERING_WEIGHT_MIN = 0; // 0x0
}
+ public final class MultiResolutionStreamConfigurationMap {
+ method @NonNull public int[] getInputFormats();
+ method @NonNull public java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo> getInputInfo(int);
+ method @NonNull public int[] getOutputFormats();
+ method @NonNull public java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo> getOutputInfo(int);
+ }
+
+ public class MultiResolutionStreamInfo {
+ ctor public MultiResolutionStreamInfo(int, int, @NonNull String);
+ method public int getHeight();
+ method @NonNull public String getPhysicalCameraId();
+ method public int getWidth();
+ }
+
public final class OisSample {
ctor public OisSample(long, float, float);
method public long getTimestamp();
@@ -18457,6 +18485,7 @@ package android.hardware.camera2.params {
ctor public OutputConfiguration(int, @NonNull android.view.Surface);
ctor public OutputConfiguration(@NonNull android.util.Size, @NonNull Class<T>);
method public void addSurface(@NonNull android.view.Surface);
+ method @NonNull public static java.util.Collection<android.hardware.camera2.params.OutputConfiguration> createInstancesForMultiResolutionOutput(@NonNull android.hardware.camera2.MultiResolutionImageReader);
method public int describeContents();
method public void enableSurfaceSharing();
method public int getMaxSharedSurfaceCount();
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 16ab900dee06..07ebbaff67ea 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -24,13 +24,11 @@ import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
import android.hardware.camera2.params.SessionConfiguration;
-import android.hardware.camera2.utils.ArrayUtils;
import android.hardware.camera2.utils.TypeReference;
import android.os.Build;
import android.util.Rational;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -641,27 +639,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
*/
@NonNull
public Set<String> getPhysicalCameraIds() {
- int[] availableCapabilities = get(REQUEST_AVAILABLE_CAPABILITIES);
- if (availableCapabilities == null) {
- throw new AssertionError("android.request.availableCapabilities must be non-null "
- + "in the characteristics");
- }
-
- if (!ArrayUtils.contains(availableCapabilities,
- REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) {
- return Collections.emptySet();
- }
- byte[] physicalCamIds = get(LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
-
- String physicalCamIdString = null;
- try {
- physicalCamIdString = new String(physicalCamIds, "UTF-8");
- } catch (java.io.UnsupportedEncodingException e) {
- throw new AssertionError("android.logicalCam.physicalIds must be UTF-8 string");
- }
- String[] physicalCameraIdArray = physicalCamIdString.split("\0");
-
- return Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(physicalCameraIdArray)));
+ return mProperties.getPhysicalCameraIds();
}
/*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
@@ -2931,6 +2909,74 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<android.util.Size>("android.scaler.defaultSecureImageSize", android.util.Size.class);
/**
+ * <p>The available multi-resolution stream configurations that this
+ * physical camera device supports
+ * (i.e. format, width, height, output/input stream).</p>
+ * <p>This list contains a subset of the parent logical camera's multi-resolution stream
+ * configurations which belong to this physical camera, and it will advertise and will only
+ * advertise the maximum supported resolutions for a particular format.</p>
+ * <p>If this camera device isn't a physical camera device constituting a logical camera,
+ * but a standalone ULTRA_HIGH_RESOLUTION_SENSOR camera, this field represents the
+ * multi-resolution input/output stream configurations of default mode and max resolution
+ * modes. The sizes will be the maximum resolution of a particular format for default mode
+ * and max resolution mode.</p>
+ * <p>This field will only be advertised if the device is a physical camera of a
+ * logical multi-camera device or an ultra high resolution sensor camera. For a logical
+ * multi-camera, the camera API will derive the logical camera’s multi-resolution stream
+ * configurations from all physical cameras. For an ultra high resolution sensor camera, this
+ * is used directly as the camera’s multi-resolution stream configurations.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.StreamConfiguration[]> SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS =
+ new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.scaler.physicalCameraMultiResolutionStreamConfigurations", android.hardware.camera2.params.StreamConfiguration[].class);
+
+ /**
+ * <p>The multi-resolution stream configurations supported by this logical camera
+ * or ultra high resolution sensor camera device.</p>
+ * <p>Multi-resolution streams can be used by a LOGICAL_MULTI_CAMERA or an
+ * ULTRA_HIGH_RESOLUTION_SENSOR camera where the images sent or received can vary in
+ * resolution per frame. This is useful in cases where the camera device's effective full
+ * resolution changes depending on factors such as the current zoom level, lighting
+ * condition, focus distance, or pixel mode.</p>
+ * <ul>
+ * <li>For a logical multi-camera implementing optical zoom, at different zoom level, a
+ * different physical camera may be active, resulting in different full-resolution image
+ * sizes.</li>
+ * <li>For an ultra high resolution camera, depending on whether the camera operates in default
+ * mode, or maximum resolution mode, the output full-size images may be of either binned
+ * resolution or maximum resolution.</li>
+ * </ul>
+ * <p>To use multi-resolution output streams, the supported formats can be queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputFormats }.
+ * A {@link android.hardware.camera2.MultiResolutionImageReader } can then be created for a
+ * supported format with the MultiResolutionStreamInfo group queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputInfo }.</p>
+ * <p>If a camera device supports multi-resolution output streams for a particular format, for
+ * each of its mandatory stream combinations, the camera device will support using a
+ * MultiResolutionImageReader for the MAXIMUM stream of supported formats. Refer to
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession } for additional details.</p>
+ * <p>To use multi-resolution input streams, the supported formats can be queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getInputFormats }.
+ * A reprocessable CameraCaptureSession can then be created using an {@link android.hardware.camera2.params.InputConfiguration InputConfiguration} constructed with
+ * the input MultiResolutionStreamInfo group, queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getInputInfo }.</p>
+ * <p>If a camera device supports multi-resolution {@code YUV} input and multi-resolution
+ * {@code YUV} output, or multi-resolution {@code PRIVATE} input and multi-resolution
+ * {@code PRIVATE} output, {@code JPEG} and {@code YUV} are guaranteed to be supported
+ * multi-resolution output stream formats. Refer to
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession } for
+ * details about the additional mandatory stream combinations in this case.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ @NonNull
+ @SyntheticKey
+ public static final Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap> SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP =
+ new Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap>("android.scaler.multiResolutionStreamConfigurationMap", android.hardware.camera2.params.MultiResolutionStreamConfigurationMap.class);
+
+ /**
* <p>The area of the image sensor which corresponds to active pixels after any geometric
* distortion correction has been applied.</p>
* <p>This is the rectangle representing the size of the active region of the sensor (i.e.
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index ac6ba0a4ac58..af48b71f9962 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -770,6 +770,8 @@ public abstract class CameraDevice implements AutoCloseable {
* streams with {@code Y8} in all guaranteed stream combinations for the device's hardware level
* and capabilities.</p>
*
+ * <p>Clients can access the above mandatory stream combination tables via
+ * {@link android.hardware.camera2.params.MandatoryStreamCombination}.</p>
*
* <p>Devices capable of outputting HEIC formats ({@link StreamConfigurationMap#getOutputFormats}
* contains {@link android.graphics.ImageFormat#HEIC}) will support substituting {@code JPEG}
@@ -777,8 +779,33 @@ public abstract class CameraDevice implements AutoCloseable {
* level and capabilities. Calling createCaptureSession with both JPEG and HEIC outputs is not
* supported.</p>
*
- * <p>Clients can access the above mandatory stream combination tables via
- * {@link android.hardware.camera2.params.MandatoryStreamCombination}.</p>
+ * <p>Devices capable of multi-resolution output for a particular format (
+ * {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputInfo}
+ * returns a non-empty list) support using {@link MultiResolutionImageReader} for MAXIMUM
+ * resolution streams of that format for all mandatory stream combinations. For example,
+ * if a LIMITED camera device supports multi-resolution output streams for both {@code JPEG} and
+ * {@code PRIVATE}, in addition to the stream configurations
+ * in the LIMITED and Legacy table above, the camera device supports the following guaranteed
+ * stream combinations ({@code MULTI_RES} in the Max size column refers to a {@link
+ * MultiResolutionImageReader} created based on the variable max resolutions supported):
+ *
+ * <table>
+ * <tr><th colspan="7">LEGACY-level additional guaranteed combinations with MultiResolutionoutputs</th></tr>
+ * <tr> <th colspan="2" id="rb">Target 1</th> <th colspan="2" id="rb">Target 2</th> <th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr>
+ * <tr> <th>Type</th><th id="rb">Max size</th> <th>Type</th><th id="rb">Max size</th> <th>Type</th><th id="rb">Max size</th></tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td colspan="2" id="rb"></td> <td colspan="2" id="rb"></td> <td>Simple preview, GPU video processing, or no-preview video recording.</td> </tr>
+ * <tr> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td colspan="2" id="rb"></td> <td colspan="2" id="rb"></td> <td>No-viewfinder still image capture.</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td colspan="2" id="rb"></td> <td>Standard still imaging.</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>Still capture plus in-app processing.</td> </tr>
+ * </table><br>
+ * <table>
+ * <tr><th colspan="7">LIMITED-level additional guaranteed configurations with MultiResolutionoutputs</th></tr>
+ * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr>
+ * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
+ * <tr> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>Two-input in-app processing with still capture.</td> </tr>
+ * </table><br>
+ * The same logic applies to other hardware levels and capabilities.
+ * </p>
*
* <p>Since the capabilities of camera devices vary greatly, a given camera device may support
* target combinations with sizes outside of these guarantees, but this can only be tested for
@@ -939,6 +966,32 @@ public abstract class CameraDevice implements AutoCloseable {
* </table><br>
* </p>
*
+ * <p>If a camera device supports multi-resolution {@code YUV} input and multi-resolution
+ * {@code YUV} output or supports multi-resolution {@code PRIVATE} input and multi-resolution
+ * {@code PRIVATE} output, the additional mandatory stream combinations for LIMITED and FULL devices are listed
+ * below ({@code MULTI_RES} in the Max size column refers to a
+ * {@link MultiResolutionImageReader} for output, and a multi-resolution
+ * {@link InputConfiguration} for input):
+ * <table>
+ * <tr><th colspan="11">LIMITED-level additional guaranteed configurations for creating a reprocessable capture session with multi-resolution input and multi-resolution outputs<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr>
+ * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr>
+ * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
+ * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td></td><td id="rb"></td> <td>No-viewfinder still image reprocessing.</td> </tr>
+ * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td>ZSL(Zero-Shutter-Lag) still imaging.</td> </tr>
+ * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td>ZSL still and in-app processing imaging.</td> </tr>
+ * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>ZSL in-app processing with still capture.</td> </tr>
+ * </table><br>
+ * <table>
+ * <tr><th colspan="11">FULL-level additional guaranteed configurations for creating a reprocessable capture session with multi-resolution input and multi-resolution outputs<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr>
+ * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr>
+ * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td>Maximum-resolution ZSL in-app processing with regular preview.</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td>Maximum-resolution two-input ZSL in-app processing.</td> </tr>
+ * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>ZSL still capture and in-app processing.</td> </tr>
+ * </table><br>
+ * No additional mandatory stream combinations for RAW capability and LEVEL-3 hardware level.
+ * </p>
+ *
* <h3>Constrained high-speed recording</h3>
*
* <p>The application can use a
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 3e0e3f62574f..a3c6f2f1eafd 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -31,6 +31,7 @@ import android.hardware.camera2.impl.CameraDeviceImpl;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
+import android.hardware.camera2.params.StreamConfiguration;
import android.hardware.camera2.utils.CameraIdAndSessionConfiguration;
import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
import android.hardware.display.DisplayManager;
@@ -51,6 +52,7 @@ import android.view.Display;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -372,6 +374,47 @@ public final class CameraManager {
}
/**
+ * Get all physical cameras' multi-resolution stream configuration map
+ *
+ * <p>For a logical multi-camera, query the map between physical camera id and
+ * the physical camera's multi-resolution stream configuration. This map is in turn
+ * combined to form the logical camera's multi-resolution stream configuration map.</p>
+ */
+ private Map<String, StreamConfiguration[]> getPhysicalCameraMultiResolutionConfigs(
+ CameraMetadataNative info, ICameraService cameraService)
+ throws CameraAccessException {
+ HashMap<String, StreamConfiguration[]> multiResolutionStreamConfigurations =
+ new HashMap<String, StreamConfiguration[]>();
+
+ // Query the characteristics of all physical sub-cameras, and combine the multi-resolution
+ // stream configurations. Note that framework derived formats such as HEIC and DEPTH_JPEG
+ // aren't supported as multi-resolution input or output formats.
+ Set<String> physicalCameraIds = info.getPhysicalCameraIds();
+ try {
+ for (String physicalCameraId : physicalCameraIds) {
+ CameraMetadataNative physicalCameraInfo =
+ cameraService.getCameraCharacteristics(physicalCameraId);
+ StreamConfiguration[] configs = physicalCameraInfo.get(
+ CameraCharacteristics.
+ SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS);
+ if (configs != null) {
+ multiResolutionStreamConfigurations.put(physicalCameraId, configs);
+ }
+ }
+
+ // TODO: If this is an ultra high resolution sensor camera, combine the multi-resolution
+ // stream combination from "info" as well.
+ } catch (RemoteException e) {
+ ServiceSpecificException sse = new ServiceSpecificException(
+ ICameraService.ERROR_DISCONNECTED,
+ "Camera service is currently unavailable");
+ throwAsPublicException(sse);
+ }
+
+ return multiResolutionStreamConfigurations;
+ }
+
+ /**
* <p>Query the capabilities of a camera device. These capabilities are
* immutable for a given camera.</p>
*
@@ -418,12 +461,19 @@ public final class CameraManager {
} catch (NumberFormatException e) {
Log.v(TAG, "Failed to parse camera Id " + cameraId + " to integer");
}
+
boolean hasConcurrentStreams =
CameraManagerGlobal.get().cameraIdHasConcurrentStreamsLocked(cameraId);
info.setHasMandatoryConcurrentStreams(hasConcurrentStreams);
info.setDisplaySize(displaySize);
- characteristics = new CameraCharacteristics(info);
+ Map<String, StreamConfiguration[]> multiResolutionSizeMap =
+ getPhysicalCameraMultiResolutionConfigs(info, cameraService);
+ if (multiResolutionSizeMap.size() > 0) {
+ info.setMultiResolutionStreamConfigurationMap(multiResolutionSizeMap);
+ }
+
+ characteristics = new CameraCharacteristics(info);
} catch (ServiceSpecificException e) {
throwAsPublicException(e);
} catch (RemoteException e) {
diff --git a/core/java/android/hardware/camera2/MultiResolutionImageReader.java b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
new file mode 100644
index 000000000000..c592f19bc45c
--- /dev/null
+++ b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2021 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;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.graphics.ImageFormat;
+import android.graphics.ImageFormat.Format;
+import android.hardware.HardwareBuffer;
+import android.hardware.HardwareBuffer.Usage;
+import android.media.Image;
+import android.media.ImageReader;
+import android.hardware.camera2.params.MultiResolutionStreamInfo;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.Surface;
+
+
+import java.nio.NioUtils;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * <p>The MultiResolutionImageReader class wraps a group of {@link ImageReader ImageReaders} with
+ * the same format and different sizes, source camera Id, or camera sensor modes.</p>
+ *
+ * <p>The main use case of this class is for a
+ * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA logical
+ * multi-camera} or an ultra high resolution sensor camera to output variable-size images. For a
+ * logical multi-camera which implements optical zoom, different physical cameras may have different
+ * maximum resolutions. As a result, when the camera device switches between physical cameras
+ * depending on zoom ratio, the maximum resolution for a particular format may change. For an
+ * ultra high resolution sensor camera, the camera device may deem it better or worse to run in
+ * maximum resolution mode / default mode depending on lighting conditions. So the application may
+ * choose to let the camera device decide on its behalf.</p>
+ *
+ * <p>MultiResolutionImageReader should be used for a camera device only if the camera device
+ * supports multi-resolution output stream by advertising the specified output format in {@link
+ * CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP}.</p>
+ *
+ * <p>To acquire images from the MultiResolutionImageReader, the application must use the
+ * {@link ImageReader} object passed by
+ * {@link ImageReader.OnImageAvailableListener#onImageAvailable} callback to call
+ * {@link ImageReader#acquireNextImage} or {@link ImageReader#acquireLatestImage}. The application
+ * must not use the {@link ImageReader} passed by an {@link
+ * ImageReader.OnImageAvailableListener#onImageAvailable} callback to acquire future images
+ * because future images may originate from a different {@link ImageReader} contained within the
+ * {@code MultiResolutionImageReader}.</p>
+ *
+ *
+ * @see ImageReader
+ * @see android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP
+ */
+public class MultiResolutionImageReader implements AutoCloseable {
+
+ private static final String TAG = "MultiResolutionImageReader";
+
+ /**
+ * <p>
+ * Create a new multi-resolution reader based on a group of camera stream properties returned
+ * by a camera device.
+ * </p>
+ * <p>
+ * The valid size and formats depend on the camera characteristics.
+ * {@code MultiResolutionImageReader} for an image format is supported by the camera device if
+ * the format is in the supported multi-resolution output stream formats returned by
+ * {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputFormats}.
+ * If the image format is supported, the {@code MultiResolutionImageReader} object can be
+ * created with the {@code streams} objects returned by
+ * {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputInfo}.
+ * </p>
+ * <p>
+ * The {@code maxImages} parameter determines the maximum number of
+ * {@link Image} objects that can be be acquired from each of the {@code ImageReader}
+ * within the {@code MultiResolutionImageReader}. However, requesting more buffers will
+ * use up more memory, so it is important to use only the minimum number necessary. The
+ * application is strongly recommended to acquire no more than {@code maxImages} images
+ * from all of the internal ImageReader objects combined. By keeping track of the number of
+ * acquired images for the MultiResolutionImageReader, the application doesn't need to do the
+ * bookkeeping for each internal ImageReader returned from {@link
+ * ImageReader.OnImageAvailableListener#onImageAvailable onImageAvailable} callback.
+ * </p>
+ * <p>
+ * Unlike the normal ImageReader, the MultiResolutionImageReader has a more complex
+ * configuration sequence. Instead of passing the same surface to OutputConfiguration and
+ * CaptureRequest, the
+ * {@link android.hardware.camera2.params.OutputConfiguration#createInstancesForMultiResolutionOutput}
+ * call needs to be used to create the OutputConfigurations for session creation, and then
+ * {@link #getSurface} is used to get {@link CaptureRequest.Builder#addTarget the target for
+ * CaptureRequest}.
+ * </p>
+ * @param streams The group of multi-resolution stream info, which is used to create
+ * a multi-resolution reader containing a number of ImageReader objects. Each
+ * ImageReader object represents a multi-resolution stream in the group.
+ * @param format The format of the Image that this multi-resolution reader will produce.
+ * This must be one of the {@link android.graphics.ImageFormat} or
+ * {@link android.graphics.PixelFormat} constants. Note that not all formats are
+ * supported, like ImageFormat.NV21. The supported multi-resolution
+ * reader format can be queried by {@link
+ * android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputFormats}.
+ * @param maxImages The maximum number of images the user will want to
+ * access simultaneously. This should be as small as possible to
+ * limit memory use. Once maxImages images are obtained by the
+ * user from any given internal ImageReader, one of them has to be released before
+ * a new Image will become available for access through the ImageReader's
+ * {@link ImageReader#acquireLatestImage()} or
+ * {@link ImageReader#acquireNextImage()}. Must be greater than 0.
+ * @see Image
+ * @see
+ * android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP
+ * @see
+ * android.hardware.camera2.params.MultiResolutionStreamConfigurationMap
+ */
+ public static @NonNull MultiResolutionImageReader newInstance(
+ @NonNull Collection<MultiResolutionStreamInfo> streams,
+ @Format int format,
+ @IntRange(from = 1) int maxImages) {
+ return new MultiResolutionImageReader(streams, format, maxImages);
+ }
+
+ /**
+ * @hide
+ */
+ protected MultiResolutionImageReader(Collection<MultiResolutionStreamInfo> streams,
+ int format, int maxImages) {
+ mFormat = format;
+ mMaxImages = maxImages;
+
+ if (streams == null || streams.size() <= 1) {
+ throw new IllegalArgumentException(
+ "The streams info collection must contain at least 2 entries");
+ }
+ if (mMaxImages < 1) {
+ throw new IllegalArgumentException(
+ "Maximum outstanding image count must be at least 1");
+ }
+
+ if (format == ImageFormat.NV21) {
+ throw new IllegalArgumentException(
+ "NV21 format is not supported");
+ }
+
+ int numImageReaders = streams.size();
+ mReaders = new ImageReader[numImageReaders];
+ mStreamInfo = new MultiResolutionStreamInfo[numImageReaders];
+ int index = 0;
+ for (MultiResolutionStreamInfo streamInfo : streams) {
+ mReaders[index] = ImageReader.newInstance(streamInfo.getWidth(),
+ streamInfo.getHeight(), format, maxImages);
+ mStreamInfo[index] = streamInfo;
+ index++;
+ }
+ }
+
+ /**
+ * Set onImageAvailableListener callback.
+ *
+ * <p>This function sets the onImageAvailableListener for all the internal
+ * {@link ImageReader} objects.</p>
+ *
+ * <p>For a multi-resolution ImageReader, the timestamps of images acquired in
+ * onImageAvailable callback from different internal ImageReaders may become
+ * out-of-order due to the asynchronous callbacks between the different resolution
+ * image queues.</p>
+ *
+ * @param listener
+ * The listener that will be run.
+ * @param executor
+ * The executor which will be used when invoking the callback.
+ */
+ @SuppressLint({"ExecutorRegistration", "SamShouldBeLast"})
+ public void setOnImageAvailableListener(
+ @Nullable ImageReader.OnImageAvailableListener listener,
+ @Nullable @CallbackExecutor Executor executor) {
+ for (int i = 0; i < mReaders.length; i++) {
+ mReaders[i].setOnImageAvailableListenerWithExecutor(listener, executor);
+ }
+ }
+
+ @Override
+ public void close() {
+ flush();
+
+ for (int i = 0; i < mReaders.length; i++) {
+ mReaders[i].close();
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ close();
+ }
+
+ /**
+ * Flush pending images from all internal ImageReaders
+ *
+ * <p>Acquire and close pending images from all internal ImageReaders. This has the same
+ * effect as calling acquireLatestImage() on all internal ImageReaders, and closing all
+ * latest images.</p>
+ */
+ public void flush() {
+ flushOther(null);
+ }
+
+ /**
+ * Flush pending images from other internal ImageReaders
+ *
+ * <p>Acquire and close pending images from all internal ImageReaders except for the
+ * one specified.</p>
+ *
+ * @param reader The ImageReader object that won't be flushed.
+ *
+ * @hide
+ */
+ public void flushOther(ImageReader reader) {
+ for (int i = 0; i < mReaders.length; i++) {
+ if (reader != null && reader == mReaders[i]) {
+ continue;
+ }
+
+ while (true) {
+ Image image = mReaders[i].acquireNextImageNoThrowISE();
+ if (image == null) {
+ break;
+ } else {
+ image.close();
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the internal ImageReader objects
+ *
+ * @hide
+ */
+ public @NonNull ImageReader[] getReaders() {
+ return mReaders;
+ }
+
+ /**
+ * Get the surface that is used as a target for {@link CaptureRequest}
+ *
+ * <p>The application must use the surface returned by this function as a target for
+ * {@link CaptureRequest}. The camera device makes the decision on which internal
+ * {@code ImageReader} will receive the output image.</p>
+ *
+ * <p>Please note that holding on to the Surface objects returned by this method is not enough
+ * to keep their parent MultiResolutionImageReaders from being reclaimed. In that sense, a
+ * Surface acts like a {@link java.lang.ref.WeakReference weak reference} to the
+ * MultiResolutionImageReader that provides it.</p>
+ *
+ * @return a {@link Surface} to use as the target for a capture request.
+ */
+ public @NonNull Surface getSurface() {
+ //TODO: Pick the surface from the reader for default mode stream.
+ return mReaders[0].getSurface();
+ }
+
+ /**
+ * Get the MultiResolutionStreamInfo describing the ImageReader an image originates from
+ *
+ *<p>An image from a {@code MultiResolutionImageReader} is produced from one of the underlying
+ *{@code ImageReader}s. This function returns the {@link MultiResolutionStreamInfo} to describe
+ *the property for that {@code ImageReader}, such as width, height, and physical camera Id.</p>
+ *
+ * @param reader An internal ImageReader within {@code MultiResolutionImageReader}.
+ *
+ * @return The stream info describing the internal {@code ImageReader}.
+ */
+ public @NonNull MultiResolutionStreamInfo getStreamInfoForImageReader(
+ @NonNull ImageReader reader) {
+ for (int i = 0; i < mReaders.length; i++) {
+ if (reader == mReaders[i]) {
+ return mStreamInfo[i];
+ }
+ }
+
+ throw new IllegalArgumentException("ImageReader doesn't belong to this multi-resolution "
+ + "imagereader");
+ }
+
+ // mReaders and mStreamInfo has the same length, and their entries are 1:1 mapped.
+ private final ImageReader[] mReaders;
+ private final MultiResolutionStreamInfo[] mStreamInfo;
+
+ private final int mFormat;
+ private final int mMaxImages;
+}
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index da65f71ce02c..df8eeccbe800 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -61,8 +61,8 @@ public final class TotalCaptureResult extends CaptureResult {
private final List<CaptureResult> mPartialResults;
private final int mSessionId;
- // The map between physical camera id and capture result
- private final HashMap<String, CaptureResult> mPhysicalCaptureResults;
+ // The map between physical camera ids and their total capture result
+ private final HashMap<String, TotalCaptureResult> mPhysicalCaptureResults;
/**
* Takes ownership of the passed-in camera metadata and the partial results
@@ -83,10 +83,11 @@ public final class TotalCaptureResult extends CaptureResult {
mSessionId = sessionId;
- mPhysicalCaptureResults = new HashMap<String, CaptureResult>();
+ mPhysicalCaptureResults = new HashMap<String, TotalCaptureResult>();
for (PhysicalCaptureResultInfo onePhysicalResult : physicalResults) {
- CaptureResult physicalResult = new CaptureResult(onePhysicalResult.getCameraId(),
- onePhysicalResult.getCameraMetadata(), parent, extras);
+ TotalCaptureResult physicalResult = new TotalCaptureResult(
+ onePhysicalResult.getCameraId(), onePhysicalResult.getCameraMetadata(),
+ parent, extras, /*partials*/null, sessionId, new PhysicalCaptureResultInfo[0]);
mPhysicalCaptureResults.put(onePhysicalResult.getCameraId(),
physicalResult);
}
@@ -103,7 +104,7 @@ public final class TotalCaptureResult extends CaptureResult {
mPartialResults = new ArrayList<>();
mSessionId = CameraCaptureSession.SESSION_ID_NONE;
- mPhysicalCaptureResults = new HashMap<String, CaptureResult>();
+ mPhysicalCaptureResults = new HashMap<String, TotalCaptureResult>();
}
/**
@@ -146,8 +147,37 @@ public final class TotalCaptureResult extends CaptureResult {
* cameras. Otherwise, an empty map is returned.</p>
* @return unmodifiable map between physical camera ids and their capture result metadata
+ *
+ * @deprecated
+ * <p>Please use {@link #getPhysicalCameraTotalResults() instead to get the
+ * physical cameras' {@code TotalCaptureResult}.</p>
*/
public Map<String, CaptureResult> getPhysicalCameraResults() {
return Collections.unmodifiableMap(mPhysicalCaptureResults);
}
+
+ /**
+ * Get the map between physical camera ids and their total capture result metadata
+ *
+ * <p>This function can be called for logical multi-camera devices, which are devices that have
+ * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability.</p>
+ *
+ * <p>If one or more streams from the underlying physical cameras were requested by the
+ * corresponding capture request, this function returns the total result metadata for those
+ * physical cameras. Otherwise, an empty map is returned.</p>
+ *
+ * <p>This function replaces the deprecated {@link #getPhysicalCameraResults}, and its return
+ * value is a map of TotalCaptureResult rather than CaptureResult. </p>
+ *
+ * <p>To reprocess an image from a physical camera stream, typically returned from a
+ * {@link MultiResolutionImageReader}, the application must look up this map to get the {@link
+ * TotalCaptureResult} from the physical camera and pass it to {@link
+ * CameraDevice#createReprocessCaptureRequest}.</p>
+ *
+ * @return unmodifiable map between physical camera ids and their total capture result metadata
+ */
+ @NonNull
+ public Map<String, TotalCaptureResult> getPhysicalCameraTotalResults() {
+ return Collections.unmodifiableMap(mPhysicalCaptureResults);
+ }
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index ce3c81a2bfd6..4defd23231b8 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -36,6 +36,8 @@ import android.hardware.camera2.ICameraOfflineSession;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.InputConfiguration;
+import android.hardware.camera2.params.MultiResolutionStreamConfigurationMap;
+import android.hardware.camera2.params.MultiResolutionStreamInfo;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
@@ -468,7 +470,8 @@ public class CameraDeviceImpl extends CameraDevice
}
if (inputConfig != null) {
int streamId = mRemoteDevice.createInputStream(inputConfig.getWidth(),
- inputConfig.getHeight(), inputConfig.getFormat());
+ inputConfig.getHeight(), inputConfig.getFormat(),
+ inputConfig.isMultiResolution());
mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(
streamId, inputConfig);
}
@@ -1355,7 +1358,42 @@ public class CameraDeviceImpl extends CameraDevice
}
private void checkInputConfiguration(InputConfiguration inputConfig) {
- if (inputConfig != null) {
+ if (inputConfig == null) {
+ return;
+ }
+
+ if (inputConfig.isMultiResolution()) {
+ MultiResolutionStreamConfigurationMap configMap = mCharacteristics.get(
+ CameraCharacteristics.SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP);
+
+ int[] inputFormats = configMap.getInputFormats();
+ boolean validFormat = false;
+ for (int format : inputFormats) {
+ if (format == inputConfig.getFormat()) {
+ validFormat = true;
+ }
+ }
+
+ if (validFormat == false) {
+ throw new IllegalArgumentException("multi-resolution input format " +
+ inputConfig.getFormat() + " is not valid");
+ }
+
+ boolean validSize = false;
+ Collection<MultiResolutionStreamInfo> inputStreamInfo =
+ configMap.getInputInfo(inputConfig.getFormat());
+ for (MultiResolutionStreamInfo info : inputStreamInfo) {
+ if (inputConfig.getWidth() == info.getWidth() &&
+ inputConfig.getHeight() == info.getHeight()) {
+ validSize = true;
+ }
+ }
+
+ if (validSize == false) {
+ throw new IllegalArgumentException("Multi-resolution input size " +
+ inputConfig.getWidth() + "x" + inputConfig.getHeight() + " is not valid");
+ }
+ } else {
StreamConfigurationMap configMap = mCharacteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index e9bae0b621ce..0cdf744ecd68 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -16,6 +16,7 @@
package android.hardware.camera2.impl;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.ImageFormat;
import android.graphics.Point;
@@ -53,6 +54,7 @@ import android.hardware.camera2.params.Face;
import android.hardware.camera2.params.HighSpeedVideoConfiguration;
import android.hardware.camera2.params.LensShadingMap;
import android.hardware.camera2.params.MandatoryStreamCombination;
+import android.hardware.camera2.params.MultiResolutionStreamConfigurationMap;
import android.hardware.camera2.params.OisSample;
import android.hardware.camera2.params.RecommendedStreamConfiguration;
import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
@@ -61,6 +63,7 @@ import android.hardware.camera2.params.StreamConfiguration;
import android.hardware.camera2.params.StreamConfigurationDuration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.params.TonemapCurve;
+import android.hardware.camera2.utils.ArrayUtils;
import android.hardware.camera2.utils.TypeReference;
import android.location.Location;
import android.location.LocationManager;
@@ -79,9 +82,14 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* Implementation of camera metadata marshal/unmarshal across Binder to
@@ -747,6 +755,15 @@ public class CameraMetadataNative implements Parcelable {
return (T) metadata.getExtendedSceneModeCapabilities();
}
});
+ sGetCommandMap.put(
+ CameraCharacteristics.SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP.getNativeKey(),
+ new GetCommand() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+ return (T) metadata.getMultiResolutionStreamConfigurationMap();
+ }
+ });
}
private int[] getAvailableFormats() {
@@ -1688,6 +1705,7 @@ public class CameraMetadataNative implements Parcelable {
private boolean mHasMandatoryConcurrentStreams = false;
private Size mDisplaySize = new Size(0, 0);
private long mBufferSize = 0;
+ private MultiResolutionStreamConfigurationMap mMultiResolutionStreamConfigurationMap = null;
/**
* Set the current camera Id.
@@ -1723,6 +1741,30 @@ public class CameraMetadataNative implements Parcelable {
mDisplaySize = displaySize;
}
+ /**
+ * Set the multi-resolution stream configuration map.
+ *
+ * @param multiResolutionMap The multi-resolution stream configuration map.
+ *
+ * @hide
+ */
+ public void setMultiResolutionStreamConfigurationMap(
+ @NonNull Map<String, StreamConfiguration[]> multiResolutionMap) {
+ mMultiResolutionStreamConfigurationMap =
+ new MultiResolutionStreamConfigurationMap(multiResolutionMap);
+ }
+
+ /**
+ * Get the multi-resolution stream configuration map.
+ *
+ * @return The multi-resolution stream configuration map.
+ *
+ * @hide
+ */
+ public MultiResolutionStreamConfigurationMap getMultiResolutionStreamConfigurationMap() {
+ return mMultiResolutionStreamConfigurationMap;
+ }
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private long mMetadataPtr; // native std::shared_ptr<CameraMetadata>*
@@ -1777,6 +1819,7 @@ public class CameraMetadataNative implements Parcelable {
mCameraId = other.mCameraId;
mHasMandatoryConcurrentStreams = other.mHasMandatoryConcurrentStreams;
mDisplaySize = other.mDisplaySize;
+ mMultiResolutionStreamConfigurationMap = other.mMultiResolutionStreamConfigurationMap;
updateNativeAllocation();
other.updateNativeAllocation();
}
@@ -1980,6 +2023,39 @@ public class CameraMetadataNative implements Parcelable {
return true;
}
+ /**
+ * Return the set of physical camera ids that this logical {@link CameraDevice} is made
+ * up of.
+ *
+ * If the camera device isn't a logical camera, return an empty set.
+ *
+ * @hide
+ */
+ public Set<String> getPhysicalCameraIds() {
+ int[] availableCapabilities = get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+ if (availableCapabilities == null) {
+ throw new AssertionError("android.request.availableCapabilities must be non-null "
+ + "in the characteristics");
+ }
+
+ if (!ArrayUtils.contains(availableCapabilities,
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) {
+ return Collections.emptySet();
+ }
+ byte[] physicalCamIds = get(CameraCharacteristics.LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
+
+ String physicalCamIdString = null;
+ try {
+ physicalCamIdString = new String(physicalCamIds, "UTF-8");
+ } catch (java.io.UnsupportedEncodingException e) {
+ throw new AssertionError("android.logicalCam.physicalIds must be UTF-8 string");
+ }
+ String[] physicalCameraIdArray = physicalCamIdString.split("\0");
+
+ return Collections.unmodifiableSet(
+ new HashSet<String>(Arrays.asList(physicalCameraIdArray)));
+ }
+
static {
registerAllMarshalers();
}
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index ba4395f70214..b6b1968bfcdd 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -140,9 +140,10 @@ public class ICameraDeviceUserWrapper {
}
}
- public int createInputStream(int width, int height, int format) throws CameraAccessException {
+ public int createInputStream(int width, int height, int format, boolean isMultiResolution)
+ throws CameraAccessException {
try {
- return mRemoteDevice.createInputStream(width, height, format);
+ return mRemoteDevice.createInputStream(width, height, format, isMultiResolution);
} catch (Throwable t) {
CameraManager.throwAsPublicException(t);
throw new UnsupportedOperationException("Unexpected exception", t);
diff --git a/core/java/android/hardware/camera2/params/InputConfiguration.java b/core/java/android/hardware/camera2/params/InputConfiguration.java
index 0a50f974aca8..d63683feed9b 100644
--- a/core/java/android/hardware/camera2/params/InputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/InputConfiguration.java
@@ -16,9 +16,17 @@
package android.hardware.camera2.params;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.ImageFormat.Format;
+import android.hardware.camera2.params.MultiResolutionStreamInfo;
import android.hardware.camera2.utils.HashCodeHelpers;
+import java.util.Collection;
+import java.util.List;
+
+import static com.android.internal.util.Preconditions.*;
+
/**
* Immutable class to store an input configuration that is used to create a reprocessable capture
* session.
@@ -31,11 +39,12 @@ public final class InputConfiguration {
private final int mWidth;
private final int mHeight;
private final int mFormat;
+ private final boolean mIsMultiResolution;
/**
* Create an input configration with the width, height, and user-defined format.
*
- * <p>Images of an user-defined format are accessible by applications. Use
+ * <p>Images of a user-defined format are accessible by applications. Use
* {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}
* to query supported input formats</p>
*
@@ -51,6 +60,52 @@ public final class InputConfiguration {
mWidth = width;
mHeight = height;
mFormat = format;
+ mIsMultiResolution = false;
+ }
+
+ /**
+ * Create an input configration with the format and a list of multi-resolution input stream
+ * info.
+ *
+ * <p>Use {@link
+ * android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP}
+ * to query supported multi-resolution input formats.</p>
+ *
+ * <p>To do reprocessing with variable resolution input, the application calls
+ * {@link android.media.ImageWriter#queueInputImage ImageWriter.queueInputImage}
+ * using an image from an {@link android.media.ImageReader ImageReader} or {@link
+ * android.hardware.camera2.MultiResolutionImageReader MultiResolutionImageReader}. See
+ * {@link android.hardware.camera2.CameraDevice#createReprocessCaptureRequest} for more
+ * details on camera reprocessing.
+ * </p>
+ *
+ * @param multiResolutionInputs A group of multi-resolution input info for the specified format.
+ * @param format Format of the input buffers. One of ImageFormat or PixelFormat constants.
+ *
+ * @see android.graphics.ImageFormat
+ * @see android.graphics.PixelFormat
+ * @see
+ * android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP
+ */
+ public InputConfiguration(@NonNull Collection<MultiResolutionStreamInfo> multiResolutionInputs,
+ @Format int format) {
+ checkCollectionNotEmpty(multiResolutionInputs, "Input multi-resolution stream info");
+ //TODO: Pick the default mode stream info for ultra-high resolution sensor camera
+ MultiResolutionStreamInfo info = multiResolutionInputs.iterator().next();
+ mWidth = info.getWidth();
+ mHeight = info.getHeight();
+ mFormat = format;
+ mIsMultiResolution = true;
+ }
+
+ /**
+ * @hide
+ */
+ public InputConfiguration(int width, int height, int format, boolean isMultiResolution) {
+ mWidth = width;
+ mHeight = height;
+ mFormat = format;
+ mIsMultiResolution = isMultiResolution;
}
/**
@@ -81,6 +136,18 @@ public final class InputConfiguration {
}
/**
+ * Whether this input configuration is of multi-resolution.
+ *
+ * <p>An multi-resolution InputConfiguration means that the reprocessing session created from it
+ * allows input images of different sizes.</p>
+ *
+ * @return this input configuration is multi-resolution or not.
+ */
+ public boolean isMultiResolution() {
+ return mIsMultiResolution;
+ }
+
+ /**
* Check if this InputConfiguration is equal to another InputConfiguration.
*
* <p>Two input configurations are equal if and only if they have the same widths, heights, and
@@ -100,7 +167,8 @@ public final class InputConfiguration {
if (otherInputConfig.getWidth() == mWidth &&
otherInputConfig.getHeight() == mHeight &&
- otherInputConfig.getFormat() == mFormat) {
+ otherInputConfig.getFormat() == mFormat &&
+ otherInputConfig.isMultiResolution() == mIsMultiResolution) {
return true;
}
return false;
@@ -111,19 +179,21 @@ public final class InputConfiguration {
*/
@Override
public int hashCode() {
- return HashCodeHelpers.hashCode(mWidth, mHeight, mFormat);
+ return HashCodeHelpers.hashCode(mWidth, mHeight, mFormat, mIsMultiResolution ? 1 : 0);
}
/**
* Return this {@link InputConfiguration} as a string representation.
*
- * <p> {@code "InputConfiguration(w:%d, h:%d, format:%d)"}, where {@code %d} represents
- * the width, height, and format, respectively.</p>
+ * <p> {@code "InputConfiguration(w:%d, h:%d, format:%d, isMultiResolution:%d)"},
+ * where {@code %d} represents the width, height, format, and multi-resolution flag
+ * respectively.</p>
*
* @return string representation of {@link InputConfiguration}
*/
@Override
public String toString() {
- return String.format("InputConfiguration(w:%d, h:%d, format:%d)", mWidth, mHeight, mFormat);
+ return String.format("InputConfiguration(w:%d, h:%d, format:%d, isMultiResolution %b)",
+ mWidth, mHeight, mFormat, mIsMultiResolution);
}
}
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 776d155e5b3e..8a0172ee8018 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -1297,19 +1297,6 @@ public final class MandatoryStreamCombination {
}
/**
- * Size comparison method used by size comparators.
- */
- private static int compareSizes(int widthA, int heightA, int widthB, int heightB) {
- long left = widthA * (long) heightA;
- long right = widthB * (long) heightB;
- if (left == right) {
- left = widthA;
- right = widthB;
- }
- return (left < right) ? -1 : (left > right ? 1 : 0);
- }
-
- /**
* Size comparator that compares the number of pixels it covers.
*
* <p>If two the areas of two sizes are same, compare the widths.</p>
@@ -1317,8 +1304,8 @@ public final class MandatoryStreamCombination {
public static class SizeComparator implements Comparator<Size> {
@Override
public int compare(@NonNull Size lhs, @NonNull Size rhs) {
- return compareSizes(lhs.getWidth(), lhs.getHeight(), rhs.getWidth(),
- rhs.getHeight());
+ return StreamConfigurationMap.compareSizes(lhs.getWidth(), lhs.getHeight(),
+ rhs.getWidth(), rhs.getHeight());
}
}
diff --git a/core/java/android/hardware/camera2/params/MultiResolutionStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/MultiResolutionStreamConfigurationMap.java
new file mode 100644
index 000000000000..1b368fb8c010
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/MultiResolutionStreamConfigurationMap.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2021 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 android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import android.graphics.ImageFormat;
+import android.graphics.ImageFormat.Format;
+import android.graphics.PixelFormat;
+import android.hardware.camera2.params.MultiResolutionStreamInfo;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.hardware.camera2.utils.HashCodeHelpers;
+
+import android.util.Size;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.List;
+import java.util.Set;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Immutable class to store the information of the multi-resolution streams supported by
+ * the camera device.
+ *
+ * <p>For a {@link
+ * android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
+ * logical multi-camera} or an ultra high resolution sensor camera, the maximum resolution of images
+ * produced by the camera device may be variable. For example, for a logical multi-camera, depending
+ * on factors such as current zoom ratio, the camera device may be backed by different physical
+ * cameras. If the physical cameras are of different resolutions, the application may intend to
+ * consume the variable full resolution images from the physical cameras. For an ultra high
+ * resolution sensor camera, the same use case exists where depending on lighting conditions, the
+ * camera device may deem it better to run in default mode and maximum resolution mode.
+ * </p>
+ *
+ * <p>For the use cases described above, multi-resolution output streams can be used by
+ * {@link android.hardware.camera2.MultiResolutionImageReader} to allow the
+ * camera device to output variable size maximum-resolution images.</p>
+ *
+ * <p>Similarly, multi-resolution input streams can be used for reprocessing of variable size
+ * images. In order to reprocess input images of different sizes, the {@link InputConfiguration}
+ * used for creating reprocessable session can be initialized using the group of input stream
+ * configurations returned by {@link #getInputInfo}.</p>
+ */
+public final class MultiResolutionStreamConfigurationMap {
+ /**
+ * Create a new {@link MultiResolutionStreamConfigurationMap}.
+ *
+ * @param configurations a non-{@code null} array of multi-resolution stream
+ * configurations supported by this camera device
+ * @hide
+ */
+ public MultiResolutionStreamConfigurationMap(
+ @NonNull Map<String, StreamConfiguration[]> configurations) {
+ checkNotNull(configurations, "multi-resolution configurations must not be null");
+ if (configurations.size() == 0) {
+ throw new IllegalArgumentException("multi-resolution configurations must not be empty");
+ }
+
+ mConfigurations = configurations;
+
+ // For each multi-resolution stream configuration, track how many formats and sizes there
+ // are available to configure
+ for (Map.Entry<String, StreamConfiguration[]> entry :
+ mConfigurations.entrySet()) {
+ String cameraId = entry.getKey();
+ StreamConfiguration[] configs = entry.getValue();
+
+ for (int i = 0; i < configs.length; i++) {
+ StreamConfiguration config = configs[i];
+ int format = config.getFormat();
+
+ MultiResolutionStreamInfo multiResolutionStreamInfo = new MultiResolutionStreamInfo(
+ config.getWidth(), config.getHeight(), cameraId);
+ Map<Integer, List<MultiResolutionStreamInfo>> destMap;
+ if (config.isInput()) {
+ destMap = mMultiResolutionInputConfigs;
+ } else {
+ destMap = mMultiResolutionOutputConfigs;
+ }
+
+ if (!destMap.containsKey(format)) {
+ List<MultiResolutionStreamInfo> multiResolutionStreamInfoList =
+ new ArrayList<MultiResolutionStreamInfo>();
+ destMap.put(format, multiResolutionStreamInfoList);
+ }
+ destMap.get(format).add(multiResolutionStreamInfo);
+ }
+ }
+ }
+
+ /**
+ * Size comparator that compares the number of pixels two MultiResolutionStreamInfo size covers.
+ *
+ * <p>If two the areas of two sizes are same, compare the widths.</p>
+ *
+ * @hide
+ */
+ public static class SizeComparator implements Comparator<MultiResolutionStreamInfo> {
+ @Override
+ public int compare(@NonNull MultiResolutionStreamInfo lhs,
+ @NonNull MultiResolutionStreamInfo rhs) {
+ return StreamConfigurationMap.compareSizes(
+ lhs.getWidth(), lhs.getHeight(), rhs.getWidth(), rhs.getHeight());
+ }
+ }
+
+ /**
+ * Get the output formats in this multi-resolution stream configuration.
+ *
+ * <p>A logical multi-camera or an ultra high resolution sensor camera may support
+ * {@link android.hardware.camera2.MultiResolutionImageReader} to dynamically output maximum
+ * resolutions of different sizes (when switching between physical cameras, or between different
+ * modes of an ultra high resolution sensor camera). This function returns the formats
+ * supported for such case.</p>
+ *
+ * <p>All image formats returned by this function will be defined in either {@link ImageFormat}
+ * or in {@link PixelFormat} (and there is no possibility of collision).</p>
+ *
+ * @return an array of integer format, or empty array if multi-resolution output is not
+ * supported
+ *
+ * @see ImageFormat
+ * @see PixelFormat
+ * @see android.hardware.camera2.MultiResolutionImageReader
+ */
+ public @NonNull @Format int[] getOutputFormats() {
+ return getPublicImageFormats(/*output*/true);
+ }
+
+ /**
+ * Get the input formats in this multi-resolution stream configuration.
+ *
+ * <p>A logical multi-camera or ultra high resolution sensor camera may support reprocessing
+ * images of different resolutions when switching between physical cameras, or between
+ * different modes of the ultra high resolution sensor camera. This function returns the
+ * formats supported for such case.</p>
+ *
+ * <p>The supported output format for an input format can be queried by calling the camera
+ * device's {@link StreamConfigurationMap#getValidOutputFormatsForInput}.</p>
+ *
+ * <p>All image formats returned by this function will be defined in either {@link ImageFormat}
+ * or in {@link PixelFormat} (and there is no possibility of collision).</p>
+ *
+ * @return an array of integer format, or empty array if no multi-resolution reprocessing is
+ * supported
+ *
+ * @see ImageFormat
+ * @see PixelFormat
+ */
+ public @NonNull @Format int[] getInputFormats() {
+ return getPublicImageFormats(/*output*/false);
+ }
+
+ // Get the list of publicly visible multi-resolution input/output stream formats
+ private int[] getPublicImageFormats(boolean output) {
+ Map<Integer, List<MultiResolutionStreamInfo>> multiResolutionConfigs =
+ output ? mMultiResolutionOutputConfigs : mMultiResolutionInputConfigs;
+ int formatCount = multiResolutionConfigs.size();
+
+ int[] formats = new int[formatCount];
+ int i = 0;
+ for (Integer format : multiResolutionConfigs.keySet()) {
+ formats[i++] = StreamConfigurationMap.imageFormatToPublic(format);
+ }
+
+ return formats;
+ }
+
+ /**
+ * Get a group of {@code MultiResolutionStreamInfo} with the requested output image
+ * {@code format}
+ *
+ * <p>The {@code format} should be a supported format (one of the formats returned by
+ * {@link #getOutputFormats}).</p>
+ *
+ * @param format an image format from {@link ImageFormat} or {@link PixelFormat}
+ * @return
+ * a group of supported {@link MultiResolutionStreamInfo}. If the {@code format} is not
+ * a supported multi-resolution output, an empty group is returned.
+ *
+ * @see ImageFormat
+ * @see PixelFormat
+ * @see #getOutputFormats
+ */
+ public @NonNull Collection<MultiResolutionStreamInfo> getOutputInfo(@Format int format) {
+ return getInfo(format, /*false*/ true);
+ }
+
+ /**
+ * Get a group of {@code MultiResolutionStreamInfo} with the requested input image {@code format}
+ *
+ * <p>The {@code format} should be a supported format (one of the formats returned by
+ * {@link #getInputFormats}).</p>
+ *
+ * @param format an image format from {@link ImageFormat} or {@link PixelFormat}
+ * @return
+ * a group of supported {@link MultiResolutionStreamInfo}. If the {@code format} is not
+ * a supported multi-resolution input, an empty group is returned.
+ *
+ * @see ImageFormat
+ * @see PixelFormat
+ * @see #getInputFormats
+ */
+ public @NonNull Collection<MultiResolutionStreamInfo> getInputInfo(@Format int format) {
+ return getInfo(format, /*false*/ false);
+ }
+
+ // Get multi-resolution stream info for a particular format
+ private @NonNull Collection<MultiResolutionStreamInfo> getInfo(int format, boolean output) {
+ int internalFormat = StreamConfigurationMap.imageFormatToInternal(format);
+ Map<Integer, List<MultiResolutionStreamInfo>> multiResolutionConfigs =
+ output ? mMultiResolutionOutputConfigs : mMultiResolutionInputConfigs;
+ if (multiResolutionConfigs.containsKey(internalFormat)) {
+ return Collections.unmodifiableCollection(multiResolutionConfigs.get(internalFormat));
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ private void appendConfigurationsString(StringBuilder sb, boolean output) {
+ sb.append(output ? "Outputs(" : "Inputs(");
+ int[] formats = getPublicImageFormats(output);
+ if (formats != null) {
+ for (int format : formats) {
+ Collection<MultiResolutionStreamInfo> streamInfoList =
+ getInfo(format, output);
+ sb.append("[" + StreamConfigurationMap.formatToString(format) + ":");
+ for (MultiResolutionStreamInfo streamInfo : streamInfoList) {
+ sb.append(String.format("[w:%d, h:%d, id:%s], ",
+ streamInfo.getWidth(), streamInfo.getHeight(),
+ streamInfo.getPhysicalCameraId()));
+ }
+ // Remove the pending ", "
+ if (sb.charAt(sb.length() - 1) == ' ') {
+ sb.delete(sb.length() - 2, sb.length());
+ }
+ sb.append("]");
+ }
+ }
+ sb.append(")");
+ }
+
+ /**
+ * Check if this {@link MultiResolutionStreamConfigurationMap} is equal to another
+ * {@link MultiResolutionStreamConfigurationMap}.
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof MultiResolutionStreamConfigurationMap) {
+ final MultiResolutionStreamConfigurationMap other =
+ (MultiResolutionStreamConfigurationMap) obj;
+ if (!mConfigurations.keySet().equals(other.mConfigurations.keySet())) {
+ return false;
+ }
+
+ for (String id : mConfigurations.keySet()) {
+ if (!Arrays.equals(mConfigurations.get(id), other.mConfigurations.get(id))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return HashCodeHelpers.hashCodeGeneric(
+ mConfigurations, mMultiResolutionOutputConfigs, mMultiResolutionInputConfigs);
+ }
+
+ /**
+ * Return this {@link MultiResolutionStreamConfigurationMap} as a string representation.
+ *
+ * <p>{@code "MultiResolutionStreamConfigurationMap(Outputs([format1: [w:%d, h:%d, id:%s], ...
+ * ... [w:%d, h:%d, id:%s]), [format2: [w:%d, h:%d, id:%s], ... [w:%d, h:%d, id:%s]], ...),
+ * Inputs([format1: [w:%d, h:%d, id:%s], ... [w:%d, h:%d, id:%s], ...).</p>
+ *
+ * @return string representation of {@link MultiResolutionStreamConfigurationMap}
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("MultiResolutionStreamConfigurationMap(");
+ appendConfigurationsString(sb, /*output*/ true);
+ sb.append(",");
+ appendConfigurationsString(sb, /*output*/ false);
+ sb.append(")");
+
+ return sb.toString();
+ }
+
+
+ private final Map<String, StreamConfiguration[]> mConfigurations;
+
+ /** Format -> list of MultiResolutionStreamInfo used to create MultiResolutionImageReader */
+ private final Map<Integer, List<MultiResolutionStreamInfo>> mMultiResolutionOutputConfigs
+ = new HashMap<Integer, List<MultiResolutionStreamInfo>>();
+ /** Format -> list of MultiResolutionStreamInfo used for multi-resolution reprocessing */
+ private final Map<Integer, List<MultiResolutionStreamInfo>> mMultiResolutionInputConfigs
+ = new HashMap<Integer, List<MultiResolutionStreamInfo>>();
+}
diff --git a/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java b/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java
new file mode 100644
index 000000000000..aa1d1d4aaa18
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 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 android.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * A utility class describing the properties of one stream of fixed-size image buffers
+ * backing a multi-resolution image stream.
+ *
+ * <p>A group of {@link MultiResolutionStreamInfo} are used to describe the properties of a
+ * multi-resolution image stream for a particular format. The
+ * {@link android.hardware.camera2.MultiResolutionImageReader} class represents a
+ * multi-resolution output stream, and is constructed using a group of
+ * {@link MultiResolutionStreamInfo}. A group of {@link MultiResolutionStreamInfo} can also be used
+ * to create a multi-resolution reprocessable camera capture session. See
+ * {@link android.hardware.camera2.params.InputConfiguration} for details.</p>
+ *
+ * @see InputConfiguration
+ * @see android.hardware.camera2.MultiResolutionImageReader
+ */
+public class MultiResolutionStreamInfo {
+ private int mStreamWidth;
+ private int mStreamHeight;
+ private String mPhysicalCameraId;
+
+ /**
+ * Create a new {@link MultiResolutionStreamInfo}.
+ *
+ * <p>This class creates a {@link MultiResolutionStreamInfo} using image width, image height,
+ * and the physical camera Id images originate from.</p>
+ *
+ * <p>Normally applications do not need to create these directly. Use {@link
+ * MultiResolutionStreamConfigurationMap#getOutputInfo} or {@link
+ * MultiResolutionStreamConfigurationMap#getInputInfo} to obtain them for a particular format
+ * instead.</p>
+ */
+ public MultiResolutionStreamInfo(int streamWidth, int streamHeight,
+ @NonNull String physicalCameraId) {
+ mStreamWidth = streamWidth;
+ mStreamHeight = streamHeight;
+ mPhysicalCameraId = physicalCameraId;
+ }
+
+ /**
+ * The width of this particular image buffer stream in pixels.
+ */
+ public int getWidth() {
+ return mStreamWidth;
+ }
+
+ /**
+ * The height of this particular image buffer stream in pixels.
+ */
+ public int getHeight() {
+ return mStreamHeight;
+ }
+
+ /**
+ * The physical camera Id of this particular image buffer stream.
+ */
+ public @NonNull String getPhysicalCameraId() {
+ return mPhysicalCameraId;
+ }
+
+ /**
+ * Check if this {@link MultiResolutionStreamInfo} is equal to another
+ * {@link MultiResolutionStreamInfo}.
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof MultiResolutionStreamInfo) {
+ final MultiResolutionStreamInfo other = (MultiResolutionStreamInfo) obj;
+ return mStreamWidth == other.mStreamWidth &&
+ mStreamHeight == other.mStreamHeight &&
+ mPhysicalCameraId.equals(other.mPhysicalCameraId);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mStreamWidth, mStreamHeight, mPhysicalCameraId);
+ }
+}
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index a20a1bf194ea..e31bd601fc03 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -24,9 +24,13 @@ import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.MultiResolutionImageReader;
+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;
@@ -34,6 +38,7 @@ import android.util.Size;
import android.view.Surface;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -81,6 +86,13 @@ import java.util.Objects;
* {@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
@@ -88,6 +100,7 @@ import java.util.Objects;
* 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 {
@@ -206,6 +219,33 @@ public final class OutputConfiguration implements Parcelable {
}
/**
+ * 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
+ */
+ 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;
+ }
+
+ /**
* Create a new {@link OutputConfiguration} instance.
*
* <p>This constructor takes an argument for desired camera rotation</p>
@@ -265,6 +305,45 @@ public final class OutputConfiguration implements Parcelable {
mIsDeferredConfig = false;
mIsShared = false;
mPhysicalCameraId = null;
+ mIsMultiResolution = false;
+ }
+
+ /**
+ * 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 = MULTI_RESOLUTION_GROUP_ID_COUNTER;
+ MULTI_RESOLUTION_GROUP_ID_COUNTER++;
+ // Skip in case the group id counter overflows to -1, the invalid value.
+ if (MULTI_RESOLUTION_GROUP_ID_COUNTER == -1) {
+ MULTI_RESOLUTION_GROUP_ID_COUNTER++;
+ }
+
+ 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);
+ // TODO: Set sensor pixel mode for ultra high resolution sensor camera.
+ }
+
+ return configs;
}
/**
@@ -319,6 +398,7 @@ public final class OutputConfiguration implements Parcelable {
mIsDeferredConfig = true;
mIsShared = false;
mPhysicalCameraId = null;
+ mIsMultiResolution = false;
}
/**
@@ -355,8 +435,18 @@ public final class OutputConfiguration implements Parcelable {
* <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 OuptutConfigurations 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;
}
@@ -368,8 +458,7 @@ public final class OutputConfiguration implements Parcelable {
* 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
- * android.hardware.camera2.CameraCharacteristics#getPhysicalCameraIds}.
- * </p>
+ * CameraCharacteristics#getPhysicalCameraIds}.</p>
*
* <p>Passing in a null physicalCameraId means that the OutputConfiguration is for a logical
* stream.</p>
@@ -380,8 +469,16 @@ public final class OutputConfiguration implements Parcelable {
* after {@link CameraDevice#createCaptureSessionByOutputConfigurations} or {@link
* CameraDevice#createReprocessableCaptureSessionByConfigurations} has no effect.</p>
*
- * <p>The surface belonging to a physical camera OutputConfiguration must not be used as input
- * or output of a reprocessing request. </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;
@@ -527,6 +624,7 @@ public final class OutputConfiguration implements Parcelable {
this.mIsDeferredConfig = other.mIsDeferredConfig;
this.mIsShared = other.mIsShared;
this.mPhysicalCameraId = other.mPhysicalCameraId;
+ this.mIsMultiResolution = other.mIsMultiResolution;
}
/**
@@ -543,6 +641,7 @@ public final class OutputConfiguration implements Parcelable {
ArrayList<Surface> surfaces = new ArrayList<Surface>();
source.readTypedList(surfaces, Surface.CREATOR);
String physicalCameraId = source.readString();
+ boolean isMultiResolutionOutput = source.readInt() == 1;
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
@@ -566,6 +665,7 @@ public final class OutputConfiguration implements Parcelable {
mConfiguredGenerationId = 0;
}
mPhysicalCameraId = physicalCameraId;
+ mIsMultiResolution = isMultiResolutionOutput;
}
/**
@@ -665,6 +765,7 @@ public final class OutputConfiguration implements Parcelable {
dest.writeInt(mIsShared ? 1 : 0);
dest.writeTypedList(mSurfaces);
dest.writeString(mPhysicalCameraId);
+ dest.writeInt(mIsMultiResolution ? 1 : 0);
}
/**
@@ -694,7 +795,8 @@ public final class OutputConfiguration implements Parcelable {
mConfiguredFormat != other.mConfiguredFormat ||
mConfiguredDataspace != other.mConfiguredDataspace ||
mConfiguredGenerationId != other.mConfiguredGenerationId ||
- !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId))
+ !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId) ||
+ mIsMultiResolution != other.mIsMultiResolution)
return false;
int minLen = Math.min(mSurfaces.size(), other.mSurfaces.size());
@@ -720,17 +822,24 @@ public final class OutputConfiguration implements Parcelable {
return HashCodeHelpers.hashCode(
mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace,
mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0,
- mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode());
+ mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
+ mIsMultiResolution ? 1 : 0);
}
return HashCodeHelpers.hashCode(
mRotation, mSurfaces.hashCode(), mConfiguredGenerationId,
mConfiguredSize.hashCode(), mConfiguredFormat,
mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0,
- mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode());
+ mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
+ mIsMultiResolution ? 1 : 0);
}
private static final String TAG = "OutputConfiguration";
+
+ // A surfaceGroupId counter used for MultiResolutionImageReader. Its value is
+ // incremented everytime {@link createInstancesForMultiResolutionOutput} is called.
+ private static int MULTI_RESOLUTION_GROUP_ID_COUNTER = 0;
+
private ArrayList<Surface> mSurfaces;
private final int mRotation;
private final int mSurfaceGroupId;
@@ -749,4 +858,7 @@ public final class OutputConfiguration implements Parcelable {
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;
}
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index 8fc919f142a2..ea6b92d4f33e 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -130,11 +130,13 @@ public final class SessionConfiguration implements Parcelable {
int inputWidth = source.readInt();
int inputHeight = source.readInt();
int inputFormat = source.readInt();
+ boolean isInputMultiResolution = source.readBoolean();
ArrayList<OutputConfiguration> outConfigs = new ArrayList<OutputConfiguration>();
source.readTypedList(outConfigs, OutputConfiguration.CREATOR);
if ((inputWidth > 0) && (inputHeight > 0) && (inputFormat != -1)) {
- mInputConfig = new InputConfiguration(inputWidth, inputHeight, inputFormat);
+ mInputConfig = new InputConfiguration(inputWidth, inputHeight,
+ inputFormat, isInputMultiResolution);
}
mSessionType = sessionType;
mOutputConfigurations = outConfigs;
@@ -169,10 +171,12 @@ public final class SessionConfiguration implements Parcelable {
dest.writeInt(mInputConfig.getWidth());
dest.writeInt(mInputConfig.getHeight());
dest.writeInt(mInputConfig.getFormat());
+ dest.writeBoolean(mInputConfig.isMultiResolution());
} else {
dest.writeInt(/*inputWidth*/ 0);
dest.writeInt(/*inputHeight*/ 0);
dest.writeInt(/*inputFormat*/ -1);
+ dest.writeBoolean(/*isMultiResolution*/ false);
}
dest.writeTypedList(mOutputConfigurations);
}
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index 10a814acd70b..a25ae6041d77 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -1575,7 +1575,7 @@ public final class StreamConfigurationMap {
return sizes;
}
- /** Get the list of publically visible output formats; does not include IMPL_DEFINED */
+ /** Get the list of publicly visible output formats */
private int[] getPublicFormats(boolean output) {
int[] formats = new int[getPublicFormatCount(output)];
@@ -1746,6 +1746,21 @@ public final class StreamConfigurationMap {
return sb.toString();
}
+ /**
+ * Size comparison method used by size comparators.
+ *
+ * @hide
+ */
+ public static int compareSizes(int widthA, int heightA, int widthB, int heightB) {
+ long left = widthA * (long) heightA;
+ long right = widthB * (long) heightB;
+ if (left == right) {
+ left = widthA;
+ right = widthB;
+ }
+ return (left < right) ? -1 : (left > right ? 1 : 0);
+ }
+
private void appendOutputsString(StringBuilder sb) {
sb.append("Outputs(");
int[] formats = getOutputFormats();
@@ -1843,7 +1858,10 @@ public final class StreamConfigurationMap {
sb.append(")");
}
- private String formatToString(int format) {
+ /**
+ * @hide
+ */
+ public static String formatToString(int format) {
switch (format) {
case ImageFormat.YV12:
return "YV12";
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index f5b204a4a908..5656dffe1d4a 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -16,6 +16,7 @@
package android.media;
+import android.annotation.CallbackExecutor;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.graphics.GraphicBuffer;
@@ -24,6 +25,7 @@ import android.graphics.ImageFormat.Format;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.hardware.HardwareBuffer.Usage;
+import android.hardware.camera2.MultiResolutionImageReader;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -36,7 +38,9 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.NioUtils;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -134,7 +138,8 @@ public class ImageReader implements AutoCloseable {
// If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not
// work, and is inscrutable anyway
return new ImageReader(width, height, format, maxImages,
- format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN);
+ format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN,
+ /*parent*/ null);
}
/**
@@ -250,18 +255,37 @@ public class ImageReader implements AutoCloseable {
// throw new IllegalArgumentException("The given format=" + Integer.toHexString(format)
// + " & usage=" + Long.toHexString(usage) + " is not supported");
// }
- return new ImageReader(width, height, format, maxImages, usage);
+ return new ImageReader(width, height, format, maxImages, usage, /*parent*/ null);
}
+ /**
+ * @hide
+ */
+ public static @NonNull ImageReader newInstance(
+ @IntRange(from = 1) int width,
+ @IntRange(from = 1) int height,
+ @Format int format,
+ @IntRange(from = 1) int maxImages,
+ @NonNull MultiResolutionImageReader parent) {
+ // If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not
+ // work, and is inscrutable anyway
+ return new ImageReader(width, height, format, maxImages,
+ format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN,
+ parent);
+ }
+
+
/**
* @hide
*/
- protected ImageReader(int width, int height, int format, int maxImages, long usage) {
+ protected ImageReader(int width, int height, int format, int maxImages, long usage,
+ MultiResolutionImageReader parent) {
mWidth = width;
mHeight = height;
mFormat = format;
mUsage = usage;
mMaxImages = maxImages;
+ mParent = parent;
if (width < 1 || height < 1) {
throw new IllegalArgumentException(
@@ -433,6 +457,9 @@ public class ImageReader implements AutoCloseable {
if (image != null) {
image.close();
}
+ if (mParent != null) {
+ mParent.flushOther(this);
+ }
}
}
@@ -584,12 +611,38 @@ public class ImageReader implements AutoCloseable {
}
if (mListenerHandler == null || mListenerHandler.getLooper() != looper) {
mListenerHandler = new ListenerHandler(looper);
+ mListenerExecutor = new HandlerExecutor(mListenerHandler);
}
- mListener = listener;
} else {
- mListener = null;
mListenerHandler = null;
+ mListenerExecutor = null;
}
+ mListener = listener;
+ }
+ }
+
+ /**
+ * Register a listener to be invoked when a new image becomes available
+ * from the ImageReader.
+ *
+ * @param listener
+ * The listener that will be run.
+ * @param executor
+ * The executor which will be used to invoke the listener.
+ * @throws IllegalArgumentException
+ * If no handler specified and the calling thread has no looper.
+ *
+ * @hide
+ */
+ public void setOnImageAvailableListenerWithExecutor(@NonNull OnImageAvailableListener listener,
+ @NonNull Executor executor) {
+ if (executor == null) {
+ throw new IllegalArgumentException("executor must not be null");
+ }
+
+ synchronized (mListenerLock) {
+ mListenerExecutor = executor;
+ mListener = listener;
}
}
@@ -763,12 +816,27 @@ public class ImageReader implements AutoCloseable {
return;
}
- final Handler handler;
+ final Executor executor;
+ final OnImageAvailableListener listener;
synchronized (ir.mListenerLock) {
- handler = ir.mListenerHandler;
+ executor = ir.mListenerExecutor;
+ listener = ir.mListener;
}
- if (handler != null) {
- handler.sendEmptyMessage(0);
+ final boolean isReaderValid;
+ synchronized (ir.mCloseLock) {
+ isReaderValid = ir.mIsReaderValid;
+ }
+
+ // It's dangerous to fire onImageAvailable() callback when the ImageReader
+ // is being closed, as application could acquire next image in the
+ // onImageAvailable() callback.
+ if (executor != null && listener != null && isReaderValid) {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ listener.onImageAvailable(ir);
+ }
+ });
}
}
@@ -785,11 +853,16 @@ public class ImageReader implements AutoCloseable {
private final Object mCloseLock = new Object();
private boolean mIsReaderValid = false;
private OnImageAvailableListener mListener;
+ private Executor mListenerExecutor;
private ListenerHandler mListenerHandler;
// Keep track of the successfully acquired Images. This need to be thread safe as the images
// could be closed by different threads (e.g., application thread and GC thread).
private List<Image> mAcquiredImages = new CopyOnWriteArrayList<>();
+ // Applicable if this isn't a standalone ImageReader, but belongs to a
+ // MultiResolutionImageReader.
+ private final MultiResolutionImageReader mParent;
+
/**
* This field is used by native code, do not access or modify.
*/
@@ -802,23 +875,22 @@ public class ImageReader implements AutoCloseable {
public ListenerHandler(Looper looper) {
super(looper, null, true /*async*/);
}
+ }
- @Override
- public void handleMessage(Message msg) {
- OnImageAvailableListener listener;
- synchronized (mListenerLock) {
- listener = mListener;
- }
+ /**
+ * An adapter {@link Executor} that posts all executed tasks onto the
+ * given {@link Handler}.
+ **/
+ private final class HandlerExecutor implements Executor {
+ private final Handler mHandler;
+
+ public HandlerExecutor(@NonNull Handler handler) {
+ mHandler = Objects.requireNonNull(handler);
+ }
- // It's dangerous to fire onImageAvailable() callback when the ImageReader is being
- // closed, as application could acquire next image in the onImageAvailable() callback.
- boolean isReaderValid = false;
- synchronized (mCloseLock) {
- isReaderValid = mIsReaderValid;
- }
- if (listener != null && isReaderValid) {
- listener.onImageAvailable(ImageReader.this);
- }
+ @Override
+ public void execute(Runnable command) {
+ mHandler.post(command);
}
}