Merge "Camera: Add multi-resolution image reader and reprocessing support" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-03-05 20:14:08 +00:00
committed by Android (Google) Code Review
17 changed files with 1435 additions and 92 deletions

View File

@@ -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~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
@@ -2930,6 +2908,74 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
public static final Key<android.util.Size> SCALER_DEFAULT_SECURE_IMAGE_SIZE =
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 cameras multi-resolution stream
* configurations from all physical cameras. For an ultra high resolution sensor camera, this
* is used directly as the cameras 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>

View File

@@ -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

View File

@@ -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;
@@ -371,6 +373,47 @@ public final class CameraManager {
return ret;
}
/**
* 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) {

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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;
}
/**
@@ -80,6 +135,18 @@ public final class InputConfiguration {
return mFormat;
}
/**
* 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.
*
@@ -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);
}
}

View File

@@ -1296,19 +1296,6 @@ public final class MandatoryStreamCombination {
return kPreviewSizeBound;
}
/**
* 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.
*
@@ -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());
}
}

View File

@@ -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>>();
}

View File

@@ -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);
}
}

View File

@@ -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 {
@@ -205,6 +218,33 @@ public final class OutputConfiguration implements Parcelable {
this(surfaceGroupId, surface, ROTATION_0);
}
/**
* Set the multi-resolution output flag.
*
* <p>Specify that this OutputConfiguration is part of a multi-resolution output stream group
* used by {@link android.hardware.camera2.MultiResolutionImageReader}.</p>
*
* <p>This function must only be called for an OutputConfiguration with a non-negative
* group ID. And all OutputConfigurations of a MultiResolutionImageReader will have the same
* group ID and have this flag set.</p>
*
* @throws IllegalStateException If surface sharing is enabled via {@link #enableSurfaceSharing}
* call, or no non-negative group ID has been set.
* @hide
*/
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.
*
@@ -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;
}

View File

@@ -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);
}

View File

@@ -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";