1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.hardware.camera2.params; 17 18 import android.annotation.NonNull; 19 import android.annotation.Nullable; 20 21 import android.graphics.ImageFormat; 22 import android.graphics.ImageFormat.Format; 23 import android.graphics.PixelFormat; 24 import android.hardware.camera2.params.MultiResolutionStreamInfo; 25 import android.hardware.camera2.params.StreamConfigurationMap; 26 import android.hardware.camera2.utils.HashCodeHelpers; 27 28 import android.util.Size; 29 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.Collection; 33 import java.util.Collections; 34 import java.util.Comparator; 35 import java.util.HashMap; 36 import java.util.Map; 37 import java.util.List; 38 import java.util.Set; 39 40 import static com.android.internal.util.Preconditions.*; 41 42 /** 43 * Immutable class to store the information of the multi-resolution streams supported by 44 * the camera device. 45 * 46 * <p>For a {@link 47 * android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA 48 * logical multi-camera} or an ultra high resolution sensor camera, the maximum resolution of images 49 * produced by the camera device may be variable. For example, for a logical multi-camera, depending 50 * on factors such as current zoom ratio, the camera device may be backed by different physical 51 * cameras. If the physical cameras are of different resolutions, the application may intend to 52 * consume the variable full resolution images from the physical cameras. For an ultra high 53 * resolution sensor camera, the same use case exists where depending on lighting conditions, the 54 * camera device may deem it better to run in default mode and maximum resolution mode. 55 * </p> 56 * 57 * <p>For the use cases described above, multi-resolution output streams can be used by 58 * {@link android.hardware.camera2.MultiResolutionImageReader} to allow the 59 * camera device to output variable size maximum-resolution images.</p> 60 * 61 * <p>Similarly, multi-resolution input streams can be used for reprocessing of variable size 62 * images. In order to reprocess input images of different sizes, the {@link InputConfiguration} 63 * used for creating reprocessable session can be initialized using the group of input stream 64 * configurations returned by {@link #getInputInfo}.</p> 65 */ 66 public final class MultiResolutionStreamConfigurationMap { 67 /** 68 * Create a new {@link MultiResolutionStreamConfigurationMap}. 69 * 70 * @param configurations a non-{@code null} array of multi-resolution stream 71 * configurations supported by this camera device 72 * @hide 73 */ MultiResolutionStreamConfigurationMap( @onNull Map<String, StreamConfiguration[]> configurations)74 public MultiResolutionStreamConfigurationMap( 75 @NonNull Map<String, StreamConfiguration[]> configurations) { 76 checkNotNull(configurations, "multi-resolution configurations must not be null"); 77 if (configurations.size() == 0) { 78 throw new IllegalArgumentException("multi-resolution configurations must not be empty"); 79 } 80 81 mConfigurations = configurations; 82 83 // For each multi-resolution stream configuration, track how many formats and sizes there 84 // are available to configure 85 for (Map.Entry<String, StreamConfiguration[]> entry : 86 mConfigurations.entrySet()) { 87 String cameraId = entry.getKey(); 88 StreamConfiguration[] configs = entry.getValue(); 89 90 for (int i = 0; i < configs.length; i++) { 91 StreamConfiguration config = configs[i]; 92 int format = config.getFormat(); 93 94 MultiResolutionStreamInfo multiResolutionStreamInfo = new MultiResolutionStreamInfo( 95 config.getWidth(), config.getHeight(), cameraId); 96 Map<Integer, List<MultiResolutionStreamInfo>> destMap; 97 if (config.isInput()) { 98 destMap = mMultiResolutionInputConfigs; 99 } else { 100 destMap = mMultiResolutionOutputConfigs; 101 } 102 103 if (!destMap.containsKey(format)) { 104 List<MultiResolutionStreamInfo> multiResolutionStreamInfoList = 105 new ArrayList<MultiResolutionStreamInfo>(); 106 destMap.put(format, multiResolutionStreamInfoList); 107 } 108 destMap.get(format).add(multiResolutionStreamInfo); 109 } 110 } 111 } 112 113 /** 114 * Size comparator that compares the number of pixels two MultiResolutionStreamInfo size covers. 115 * 116 * <p>If two the areas of two sizes are same, compare the widths.</p> 117 * 118 * @hide 119 */ 120 public static class SizeComparator implements Comparator<MultiResolutionStreamInfo> { 121 @Override compare(@onNull MultiResolutionStreamInfo lhs, @NonNull MultiResolutionStreamInfo rhs)122 public int compare(@NonNull MultiResolutionStreamInfo lhs, 123 @NonNull MultiResolutionStreamInfo rhs) { 124 return StreamConfigurationMap.compareSizes( 125 lhs.getWidth(), lhs.getHeight(), rhs.getWidth(), rhs.getHeight()); 126 } 127 } 128 129 /** 130 * Get the output formats in this multi-resolution stream configuration. 131 * 132 * <p>A logical multi-camera or an ultra high resolution sensor camera may support 133 * {@link android.hardware.camera2.MultiResolutionImageReader} to dynamically output maximum 134 * resolutions of different sizes (when switching between physical cameras, or between different 135 * modes of an ultra high resolution sensor camera). This function returns the formats 136 * supported for such case.</p> 137 * 138 * <p>All image formats returned by this function will be defined in either {@link ImageFormat} 139 * or in {@link PixelFormat} (and there is no possibility of collision).</p> 140 * 141 * @return an array of integer format, or empty array if multi-resolution output is not 142 * supported 143 * 144 * @see ImageFormat 145 * @see PixelFormat 146 * @see android.hardware.camera2.MultiResolutionImageReader 147 */ getOutputFormats()148 public @NonNull @Format int[] getOutputFormats() { 149 return getPublicImageFormats(/*output*/true); 150 } 151 152 /** 153 * Get the input formats in this multi-resolution stream configuration. 154 * 155 * <p>A logical multi-camera or ultra high resolution sensor camera may support reprocessing 156 * images of different resolutions when switching between physical cameras, or between 157 * different modes of the ultra high resolution sensor camera. This function returns the 158 * formats supported for such case.</p> 159 * 160 * <p>The supported output format for an input format can be queried by calling the camera 161 * device's {@link StreamConfigurationMap#getValidOutputFormatsForInput}.</p> 162 * 163 * <p>All image formats returned by this function will be defined in either {@link ImageFormat} 164 * or in {@link PixelFormat} (and there is no possibility of collision).</p> 165 * 166 * @return an array of integer format, or empty array if no multi-resolution reprocessing is 167 * supported 168 * 169 * @see ImageFormat 170 * @see PixelFormat 171 */ getInputFormats()172 public @NonNull @Format int[] getInputFormats() { 173 return getPublicImageFormats(/*output*/false); 174 } 175 176 // Get the list of publicly visible multi-resolution input/output stream formats getPublicImageFormats(boolean output)177 private int[] getPublicImageFormats(boolean output) { 178 Map<Integer, List<MultiResolutionStreamInfo>> multiResolutionConfigs = 179 output ? mMultiResolutionOutputConfigs : mMultiResolutionInputConfigs; 180 int formatCount = multiResolutionConfigs.size(); 181 182 int[] formats = new int[formatCount]; 183 int i = 0; 184 for (Integer format : multiResolutionConfigs.keySet()) { 185 formats[i++] = StreamConfigurationMap.imageFormatToPublic(format); 186 } 187 188 return formats; 189 } 190 191 /** 192 * Get a group of {@code MultiResolutionStreamInfo} with the requested output image 193 * {@code format} 194 * 195 * <p>The {@code format} should be a supported format (one of the formats returned by 196 * {@link #getOutputFormats}).</p> 197 * 198 * @param format an image format from {@link ImageFormat} or {@link PixelFormat} 199 * @return 200 * a group of supported {@link MultiResolutionStreamInfo}. If the {@code format} is not 201 * a supported multi-resolution output, an empty group is returned. 202 * 203 * @see ImageFormat 204 * @see PixelFormat 205 * @see #getOutputFormats 206 */ getOutputInfo(@ormat int format)207 public @NonNull Collection<MultiResolutionStreamInfo> getOutputInfo(@Format int format) { 208 return getInfo(format, /*false*/ true); 209 } 210 211 /** 212 * Get a group of {@code MultiResolutionStreamInfo} with the requested input image {@code format} 213 * 214 * <p>The {@code format} should be a supported format (one of the formats returned by 215 * {@link #getInputFormats}).</p> 216 * 217 * @param format an image format from {@link ImageFormat} or {@link PixelFormat} 218 * @return 219 * a group of supported {@link MultiResolutionStreamInfo}. If the {@code format} is not 220 * a supported multi-resolution input, an empty group is returned. 221 * 222 * @see ImageFormat 223 * @see PixelFormat 224 * @see #getInputFormats 225 */ getInputInfo(@ormat int format)226 public @NonNull Collection<MultiResolutionStreamInfo> getInputInfo(@Format int format) { 227 return getInfo(format, /*false*/ false); 228 } 229 230 // Get multi-resolution stream info for a particular format getInfo(int format, boolean output)231 private @NonNull Collection<MultiResolutionStreamInfo> getInfo(int format, boolean output) { 232 int internalFormat = StreamConfigurationMap.imageFormatToInternal(format); 233 Map<Integer, List<MultiResolutionStreamInfo>> multiResolutionConfigs = 234 output ? mMultiResolutionOutputConfigs : mMultiResolutionInputConfigs; 235 if (multiResolutionConfigs.containsKey(internalFormat)) { 236 return Collections.unmodifiableCollection(multiResolutionConfigs.get(internalFormat)); 237 } else { 238 return Collections.emptyList(); 239 } 240 } 241 appendConfigurationsString(StringBuilder sb, boolean output)242 private void appendConfigurationsString(StringBuilder sb, boolean output) { 243 sb.append(output ? "Outputs(" : "Inputs("); 244 int[] formats = getPublicImageFormats(output); 245 if (formats != null) { 246 for (int format : formats) { 247 Collection<MultiResolutionStreamInfo> streamInfoList = 248 getInfo(format, output); 249 sb.append("[" + StreamConfigurationMap.formatToString(format) + ":"); 250 for (MultiResolutionStreamInfo streamInfo : streamInfoList) { 251 sb.append(String.format("[w:%d, h:%d, id:%s], ", 252 streamInfo.getWidth(), streamInfo.getHeight(), 253 streamInfo.getPhysicalCameraId())); 254 } 255 // Remove the pending ", " 256 if (sb.charAt(sb.length() - 1) == ' ') { 257 sb.delete(sb.length() - 2, sb.length()); 258 } 259 sb.append("]"); 260 } 261 } 262 sb.append(")"); 263 } 264 265 /** 266 * Check if this {@link MultiResolutionStreamConfigurationMap} is equal to another 267 * {@link MultiResolutionStreamConfigurationMap}. 268 * 269 * @return {@code true} if the objects were equal, {@code false} otherwise 270 */ 271 @Override equals(final Object obj)272 public boolean equals(final Object obj) { 273 if (obj == null) { 274 return false; 275 } 276 if (this == obj) { 277 return true; 278 } 279 if (obj instanceof MultiResolutionStreamConfigurationMap) { 280 final MultiResolutionStreamConfigurationMap other = 281 (MultiResolutionStreamConfigurationMap) obj; 282 if (!mConfigurations.keySet().equals(other.mConfigurations.keySet())) { 283 return false; 284 } 285 286 for (String id : mConfigurations.keySet()) { 287 if (!Arrays.equals(mConfigurations.get(id), other.mConfigurations.get(id))) { 288 return false; 289 } 290 } 291 292 return true; 293 } 294 return false; 295 } 296 297 /** 298 * {@inheritDoc} 299 */ 300 @Override hashCode()301 public int hashCode() { 302 return HashCodeHelpers.hashCodeGeneric( 303 mConfigurations, mMultiResolutionOutputConfigs, mMultiResolutionInputConfigs); 304 } 305 306 /** 307 * Return this {@link MultiResolutionStreamConfigurationMap} as a string representation. 308 * 309 * <p>{@code "MultiResolutionStreamConfigurationMap(Outputs([format1: [w:%d, h:%d, id:%s], ... 310 * ... [w:%d, h:%d, id:%s]), [format2: [w:%d, h:%d, id:%s], ... [w:%d, h:%d, id:%s]], ...), 311 * Inputs([format1: [w:%d, h:%d, id:%s], ... [w:%d, h:%d, id:%s], ...).</p> 312 * 313 * @return string representation of {@link MultiResolutionStreamConfigurationMap} 314 */ 315 @Override toString()316 public String toString() { 317 StringBuilder sb = new StringBuilder("MultiResolutionStreamConfigurationMap("); 318 appendConfigurationsString(sb, /*output*/ true); 319 sb.append(","); 320 appendConfigurationsString(sb, /*output*/ false); 321 sb.append(")"); 322 323 return sb.toString(); 324 } 325 326 327 private final Map<String, StreamConfiguration[]> mConfigurations; 328 329 /** Format -> list of MultiResolutionStreamInfo used to create MultiResolutionImageReader */ 330 private final Map<Integer, List<MultiResolutionStreamInfo>> mMultiResolutionOutputConfigs 331 = new HashMap<Integer, List<MultiResolutionStreamInfo>>(); 332 /** Format -> list of MultiResolutionStreamInfo used for multi-resolution reprocessing */ 333 private final Map<Integer, List<MultiResolutionStreamInfo>> mMultiResolutionInputConfigs 334 = new HashMap<Integer, List<MultiResolutionStreamInfo>>(); 335 } 336