blob: 1b368fb8c01032a0ea4048124269d5f45b1dba46 [file] [log] [blame]
/*
* 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>>();
}