1 /* 2 * Copyright 2023 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 17 package androidx.camera.video.internal.utils; 18 19 import static android.media.EncoderProfiles.VideoProfile.HDR_DOLBY_VISION; 20 import static android.media.EncoderProfiles.VideoProfile.HDR_HDR10; 21 import static android.media.EncoderProfiles.VideoProfile.HDR_HDR10PLUS; 22 import static android.media.EncoderProfiles.VideoProfile.HDR_HLG; 23 import static android.media.EncoderProfiles.VideoProfile.HDR_NONE; 24 import static android.media.MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10; 25 import static android.media.MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10HDR10; 26 import static android.media.MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10HDR10Plus; 27 import static android.media.MediaCodecInfo.CodecProfileLevel.AV1ProfileMain8; 28 import static android.media.MediaCodecInfo.CodecProfileLevel.DolbyVisionProfileDvavSe; 29 import static android.media.MediaCodecInfo.CodecProfileLevel.DolbyVisionProfileDvheSt; 30 import static android.media.MediaCodecInfo.CodecProfileLevel.HEVCProfileMain; 31 import static android.media.MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10; 32 import static android.media.MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10; 33 import static android.media.MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10Plus; 34 import static android.media.MediaCodecInfo.CodecProfileLevel.VP9Profile0; 35 import static android.media.MediaCodecInfo.CodecProfileLevel.VP9Profile2; 36 import static android.media.MediaCodecInfo.CodecProfileLevel.VP9Profile2HDR; 37 import static android.media.MediaCodecInfo.CodecProfileLevel.VP9Profile2HDR10Plus; 38 39 import static androidx.camera.core.DynamicRange.BIT_DEPTH_10_BIT; 40 import static androidx.camera.core.DynamicRange.BIT_DEPTH_8_BIT; 41 import static androidx.camera.core.DynamicRange.BIT_DEPTH_UNSPECIFIED; 42 import static androidx.camera.core.DynamicRange.DOLBY_VISION_10_BIT; 43 import static androidx.camera.core.DynamicRange.DOLBY_VISION_8_BIT; 44 import static androidx.camera.core.DynamicRange.ENCODING_DOLBY_VISION; 45 import static androidx.camera.core.DynamicRange.ENCODING_HDR10; 46 import static androidx.camera.core.DynamicRange.ENCODING_HDR10_PLUS; 47 import static androidx.camera.core.DynamicRange.ENCODING_HDR_UNSPECIFIED; 48 import static androidx.camera.core.DynamicRange.ENCODING_HLG; 49 import static androidx.camera.core.DynamicRange.ENCODING_SDR; 50 import static androidx.camera.core.DynamicRange.ENCODING_UNSPECIFIED; 51 import static androidx.camera.core.DynamicRange.HDR10_10_BIT; 52 import static androidx.camera.core.DynamicRange.HDR10_PLUS_10_BIT; 53 import static androidx.camera.core.DynamicRange.HLG_10_BIT; 54 import static androidx.camera.core.DynamicRange.SDR; 55 import static androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy.BIT_DEPTH_10; 56 import static androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy.BIT_DEPTH_8; 57 import static androidx.core.util.Preconditions.checkArgument; 58 59 import static java.util.Arrays.asList; 60 import static java.util.Collections.singletonList; 61 import static java.util.Objects.requireNonNull; 62 63 import android.media.MediaFormat; 64 65 import androidx.camera.core.DynamicRange; 66 import androidx.camera.core.impl.EncoderProfilesProxy; 67 68 import org.jspecify.annotations.NonNull; 69 70 import java.util.Collections; 71 import java.util.HashMap; 72 import java.util.HashSet; 73 import java.util.Map; 74 import java.util.Set; 75 76 /** 77 * Utility class for dynamic range related operations. 78 */ 79 public class DynamicRangeUtil { 80 public static final Map<Integer, Set<Integer>> DR_TO_VP_BIT_DEPTH_MAP = new HashMap<>(); 81 public static final Map<Integer, Set<Integer>> DR_TO_VP_FORMAT_MAP = new HashMap<>(); 82 public static final Map<Integer, Integer> VP_TO_DR_BIT_DEPTH = new HashMap<>(); 83 public static final Map<Integer, Integer> VP_TO_DR_FORMAT_MAP = new HashMap<>(); 84 private static final Map<String, Map<DynamicRange, Integer>> MIME_TO_DEFAULT_PROFILE_LEVEL_MAP = 85 new HashMap<>(); 86 DynamicRangeUtil()87 private DynamicRangeUtil() { 88 } 89 90 static { 91 // DynamicRange bit depth to VideoProfile bit depth. DR_TO_VP_BIT_DEPTH_MAP.put(BIT_DEPTH_8_BIT, new HashSet<>(singletonList(BIT_DEPTH_8)))92 DR_TO_VP_BIT_DEPTH_MAP.put(BIT_DEPTH_8_BIT, new HashSet<>(singletonList(BIT_DEPTH_8))); DR_TO_VP_BIT_DEPTH_MAP.put(BIT_DEPTH_10_BIT, new HashSet<>(singletonList(BIT_DEPTH_10)))93 DR_TO_VP_BIT_DEPTH_MAP.put(BIT_DEPTH_10_BIT, new HashSet<>(singletonList(BIT_DEPTH_10))); DR_TO_VP_BIT_DEPTH_MAP.put(BIT_DEPTH_UNSPECIFIED, new HashSet<>(asList(BIT_DEPTH_8, BIT_DEPTH_10)))94 DR_TO_VP_BIT_DEPTH_MAP.put(BIT_DEPTH_UNSPECIFIED, 95 new HashSet<>(asList(BIT_DEPTH_8, BIT_DEPTH_10))); 96 97 // DynamicRange encoding to VideoProfile HDR format. DR_TO_VP_FORMAT_MAP.put(ENCODING_UNSPECIFIED, new HashSet<>(asList(HDR_NONE, HDR_HLG, HDR_HDR10, HDR_HDR10PLUS, HDR_DOLBY_VISION)))98 DR_TO_VP_FORMAT_MAP.put(ENCODING_UNSPECIFIED, new HashSet<>(asList(HDR_NONE, HDR_HLG, 99 HDR_HDR10, HDR_HDR10PLUS, HDR_DOLBY_VISION))); DR_TO_VP_FORMAT_MAP.put(ENCODING_SDR, new HashSet<>(singletonList(HDR_NONE)))100 DR_TO_VP_FORMAT_MAP.put(ENCODING_SDR, new HashSet<>(singletonList(HDR_NONE))); DR_TO_VP_FORMAT_MAP.put(ENCODING_HDR_UNSPECIFIED, new HashSet<>(asList(HDR_HLG, HDR_HDR10, HDR_HDR10PLUS, HDR_DOLBY_VISION)))101 DR_TO_VP_FORMAT_MAP.put(ENCODING_HDR_UNSPECIFIED, 102 new HashSet<>(asList(HDR_HLG, HDR_HDR10, HDR_HDR10PLUS, HDR_DOLBY_VISION))); DR_TO_VP_FORMAT_MAP.put(ENCODING_HLG, new HashSet<>(singletonList(HDR_HLG)))103 DR_TO_VP_FORMAT_MAP.put(ENCODING_HLG, new HashSet<>(singletonList(HDR_HLG))); DR_TO_VP_FORMAT_MAP.put(ENCODING_HDR10, new HashSet<>(singletonList(HDR_HDR10)))104 DR_TO_VP_FORMAT_MAP.put(ENCODING_HDR10, new HashSet<>(singletonList(HDR_HDR10))); DR_TO_VP_FORMAT_MAP.put(ENCODING_HDR10_PLUS, new HashSet<>(singletonList(HDR_HDR10PLUS)))105 DR_TO_VP_FORMAT_MAP.put(ENCODING_HDR10_PLUS, new HashSet<>(singletonList(HDR_HDR10PLUS))); DR_TO_VP_FORMAT_MAP.put(ENCODING_DOLBY_VISION, new HashSet<>(singletonList(HDR_DOLBY_VISION)))106 DR_TO_VP_FORMAT_MAP.put(ENCODING_DOLBY_VISION, 107 new HashSet<>(singletonList(HDR_DOLBY_VISION))); 108 109 // VideoProfile bit depth to DynamicRange bit depth. VP_TO_DR_BIT_DEPTH.put(BIT_DEPTH_8, BIT_DEPTH_8_BIT)110 VP_TO_DR_BIT_DEPTH.put(BIT_DEPTH_8, BIT_DEPTH_8_BIT); VP_TO_DR_BIT_DEPTH.put(BIT_DEPTH_10, BIT_DEPTH_10_BIT)111 VP_TO_DR_BIT_DEPTH.put(BIT_DEPTH_10, BIT_DEPTH_10_BIT); 112 113 // VideoProfile HDR format to DynamicRange encoding. VP_TO_DR_FORMAT_MAP.put(HDR_NONE, ENCODING_SDR)114 VP_TO_DR_FORMAT_MAP.put(HDR_NONE, ENCODING_SDR); VP_TO_DR_FORMAT_MAP.put(HDR_HLG, ENCODING_HLG)115 VP_TO_DR_FORMAT_MAP.put(HDR_HLG, ENCODING_HLG); VP_TO_DR_FORMAT_MAP.put(HDR_HDR10, ENCODING_HDR10)116 VP_TO_DR_FORMAT_MAP.put(HDR_HDR10, ENCODING_HDR10); VP_TO_DR_FORMAT_MAP.put(HDR_HDR10PLUS, ENCODING_HDR10_PLUS)117 VP_TO_DR_FORMAT_MAP.put(HDR_HDR10PLUS, ENCODING_HDR10_PLUS); VP_TO_DR_FORMAT_MAP.put(HDR_DOLBY_VISION, ENCODING_DOLBY_VISION)118 VP_TO_DR_FORMAT_MAP.put(HDR_DOLBY_VISION, ENCODING_DOLBY_VISION); 119 120 //--------------------------------------------------------------------------------------// 121 // Default CodecProfileLevel mappings from // 122 // frameworks/av/media/codec2/sfplugin/utils/Codec2Mapper.cpp // 123 //--------------------------------------------------------------------------------------// 124 // DynamicRange encodings to HEVC profiles 125 Map<DynamicRange, Integer> hevcMap = new HashMap<>(); hevcMap.put(SDR, HEVCProfileMain)126 hevcMap.put(SDR, HEVCProfileMain); hevcMap.put(HLG_10_BIT, HEVCProfileMain10)127 hevcMap.put(HLG_10_BIT, HEVCProfileMain10); hevcMap.put(HDR10_10_BIT, HEVCProfileMain10HDR10)128 hevcMap.put(HDR10_10_BIT, HEVCProfileMain10HDR10); hevcMap.put(HDR10_PLUS_10_BIT, HEVCProfileMain10HDR10Plus)129 hevcMap.put(HDR10_PLUS_10_BIT, HEVCProfileMain10HDR10Plus); 130 131 // DynamicRange encodings to AV1 profiles for YUV 4:2:0 chroma subsampling 132 Map<DynamicRange, Integer> av1420Map = new HashMap<>(); av1420Map.put(SDR, AV1ProfileMain8)133 av1420Map.put(SDR, AV1ProfileMain8); av1420Map.put(HLG_10_BIT, AV1ProfileMain10)134 av1420Map.put(HLG_10_BIT, AV1ProfileMain10); av1420Map.put(HDR10_10_BIT, AV1ProfileMain10HDR10)135 av1420Map.put(HDR10_10_BIT, AV1ProfileMain10HDR10); av1420Map.put(HDR10_PLUS_10_BIT, AV1ProfileMain10HDR10Plus)136 av1420Map.put(HDR10_PLUS_10_BIT, AV1ProfileMain10HDR10Plus); 137 138 // DynamicRange encodings to VP9 profile for YUV 4:2:0 chroma subsampling 139 Map<DynamicRange, Integer> vp9420Map = new HashMap<>(); vp9420Map.put(SDR, VP9Profile0)140 vp9420Map.put(SDR, VP9Profile0); vp9420Map.put(HLG_10_BIT, VP9Profile2)141 vp9420Map.put(HLG_10_BIT, VP9Profile2); vp9420Map.put(HDR10_10_BIT, VP9Profile2HDR)142 vp9420Map.put(HDR10_10_BIT, VP9Profile2HDR); vp9420Map.put(HDR10_PLUS_10_BIT, VP9Profile2HDR10Plus)143 vp9420Map.put(HDR10_PLUS_10_BIT, VP9Profile2HDR10Plus); 144 145 // Dolby vision encodings to dolby vision profiles 146 Map<DynamicRange, Integer> dvMap = new HashMap<>(); 147 // Taken from the (now deprecated) Dolby Vision Profile Specification 1.3.3 148 // DV Profile 8 (10-bit HEVC) dvMap.put(DOLBY_VISION_10_BIT, DolbyVisionProfileDvheSt)149 dvMap.put(DOLBY_VISION_10_BIT, DolbyVisionProfileDvheSt); 150 // DV Profile 9 (8-bit AVC) dvMap.put(DOLBY_VISION_8_BIT, DolbyVisionProfileDvavSe)151 dvMap.put(DOLBY_VISION_8_BIT, DolbyVisionProfileDvavSe); 152 153 // Combine all mime type maps MIME_TO_DEFAULT_PROFILE_LEVEL_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, hevcMap)154 MIME_TO_DEFAULT_PROFILE_LEVEL_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, hevcMap); MIME_TO_DEFAULT_PROFILE_LEVEL_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, av1420Map)155 MIME_TO_DEFAULT_PROFILE_LEVEL_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, av1420Map); MIME_TO_DEFAULT_PROFILE_LEVEL_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, vp9420Map)156 MIME_TO_DEFAULT_PROFILE_LEVEL_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, vp9420Map); MIME_TO_DEFAULT_PROFILE_LEVEL_MAP.put(MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION, dvMap)157 MIME_TO_DEFAULT_PROFILE_LEVEL_MAP.put(MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION, dvMap); 158 //--------------------------------------------------------------------------------------// 159 } 160 161 /** 162 * Returns a set of possible HDR formats for the given {@link DynamicRange}. 163 * 164 * <p>The returned HDR formats are those defined in 165 * {@link android.media.EncoderProfiles.VideoProfile} prefixed with {@code HDR_}, such as 166 * {@link android.media.EncoderProfiles.VideoProfile#HDR_HLG}. 167 * 168 * <p>Returns an empty set if no HDR formats are supported for the provided dynamic range. 169 */ dynamicRangeToVideoProfileHdrFormats( @onNull DynamicRange dynamicRange)170 public static @NonNull Set<Integer> dynamicRangeToVideoProfileHdrFormats( 171 @NonNull DynamicRange dynamicRange) { 172 Set<Integer> hdrFormats = DR_TO_VP_FORMAT_MAP.get(dynamicRange.getEncoding()); 173 if (hdrFormats == null) { 174 hdrFormats = Collections.emptySet(); 175 } 176 return hdrFormats; 177 } 178 179 /** 180 * Returns a set of possible bit depths for the given {@link DynamicRange}. 181 * 182 * <p>The returned bit depths are the defined in 183 * {@link androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy} prefixed with {@code 184 * BIT_DEPTH_}, such as 185 * {@link androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy#BIT_DEPTH_10}. 186 * 187 * <p>Returns an empty set if no bit depths are supported for the provided dynamic range. 188 */ dynamicRangeToVideoProfileBitDepth( @onNull DynamicRange dynamicRange)189 public static @NonNull Set<Integer> dynamicRangeToVideoProfileBitDepth( 190 @NonNull DynamicRange dynamicRange) { 191 Set<Integer> bitDepths = DR_TO_VP_BIT_DEPTH_MAP.get(dynamicRange.getBitDepth()); 192 if (bitDepths == null) { 193 bitDepths = Collections.emptySet(); 194 } 195 return bitDepths; 196 } 197 198 /** 199 * Returns a codec profile level for a given mime type and dynamic range. 200 * 201 * <p>If the mime type or dynamic range is not supported, returns 202 * {@link EncoderProfilesProxy#CODEC_PROFILE_NONE}. 203 * 204 * <p>Only fully-specified dynamic ranges are supported. All other dynamic ranges will return 205 * {@link EncoderProfilesProxy#CODEC_PROFILE_NONE}. 206 */ dynamicRangeToCodecProfileLevelForMime(@onNull String mimeType, @NonNull DynamicRange dynamicRange)207 public static int dynamicRangeToCodecProfileLevelForMime(@NonNull String mimeType, 208 @NonNull DynamicRange dynamicRange) { 209 Map<DynamicRange, Integer> hdrToProfile = MIME_TO_DEFAULT_PROFILE_LEVEL_MAP.get(mimeType); 210 if (hdrToProfile != null) { 211 Integer profile = hdrToProfile.get(dynamicRange); 212 if (profile != null) { 213 return profile; 214 } 215 } 216 217 return EncoderProfilesProxy.CODEC_PROFILE_NONE; 218 } 219 220 /** 221 * Returns the encoding of {@link DynamicRange} for the given HDR format of 222 * {@link androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy}. 223 * 224 * @throws IllegalArgumentException if the input HDR format is not defined in VideoProfileProxy. 225 */ videoProfileHdrFormatsToDynamicRangeEncoding(int hdrFormat)226 public static int videoProfileHdrFormatsToDynamicRangeEncoding(int hdrFormat) { 227 checkArgument(VP_TO_DR_FORMAT_MAP.containsKey(hdrFormat)); 228 return requireNonNull(VP_TO_DR_FORMAT_MAP.get(hdrFormat)); 229 } 230 231 /** 232 * Returns the bit depth of {@link DynamicRange} for the given bit depth of 233 * {@link androidx.camera.core.impl.EncoderProfilesProxy.VideoProfileProxy}. 234 * 235 * @throws IllegalArgumentException if the input bit depth is not defined in VideoProfileProxy. 236 */ videoProfileBitDepthToDynamicRangeBitDepth(int vpBitDepth)237 public static int videoProfileBitDepthToDynamicRangeBitDepth(int vpBitDepth) { 238 checkArgument(VP_TO_DR_BIT_DEPTH.containsKey(vpBitDepth)); 239 return requireNonNull(VP_TO_DR_BIT_DEPTH.get(vpBitDepth)); 240 } 241 242 /** 243 * Checks if the HDR settings match between a {@link EncoderProfilesProxy.VideoProfileProxy} 244 * and a {@link DynamicRange}. 245 * 246 * <p>HDR settings includes bit depth and encoding. 247 */ isHdrSettingsMatched( EncoderProfilesProxy.@onNull VideoProfileProxy videoProfile, @NonNull DynamicRange dynamicRange)248 public static boolean isHdrSettingsMatched( 249 EncoderProfilesProxy.@NonNull VideoProfileProxy videoProfile, 250 @NonNull DynamicRange dynamicRange) { 251 return isBitDepthMatched(videoProfile.getBitDepth(), dynamicRange) 252 && isHdrEncodingMatched(videoProfile.getHdrFormat(), dynamicRange); 253 } 254 isBitDepthMatched(int bitDepth, @NonNull DynamicRange dynamicRange)255 private static boolean isBitDepthMatched(int bitDepth, @NonNull DynamicRange dynamicRange) { 256 Set<Integer> matchedBitDepths = DR_TO_VP_BIT_DEPTH_MAP.get(dynamicRange.getBitDepth()); 257 return matchedBitDepths != null && matchedBitDepths.contains(bitDepth); 258 } 259 isHdrEncodingMatched(int hdrFormat, @NonNull DynamicRange dynamicRange)260 private static boolean isHdrEncodingMatched(int hdrFormat, @NonNull DynamicRange dynamicRange) { 261 Set<Integer> matchedHdrEncodings = DR_TO_VP_FORMAT_MAP.get(dynamicRange.getEncoding()); 262 return matchedHdrEncodings != null && matchedHdrEncodings.contains(hdrFormat); 263 } 264 } 265