1 /* 2 * Copyright (C) 2019 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 android.mediav2.common.cts; 18 19 import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_HdrEditing; 20 import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_HlgEditing; 21 import static android.media.MediaCodecInfo.CodecProfileLevel.*; 22 23 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertFalse; 25 import static org.junit.Assert.assertNotNull; 26 import static org.junit.Assert.assertTrue; 27 import static org.junit.Assert.fail; 28 29 import android.content.Context; 30 import android.hardware.camera2.CameraAccessException; 31 import android.hardware.camera2.CameraCharacteristics; 32 import android.hardware.camera2.CameraManager; 33 import android.hardware.camera2.CameraMetadata; 34 import android.hardware.camera2.params.DynamicRangeProfiles; 35 import android.hardware.display.DisplayManager; 36 import android.media.MediaCodec; 37 import android.media.MediaCodecInfo; 38 import android.media.MediaCodecInfo.CodecCapabilities; 39 import android.media.MediaCodecInfo.CodecProfileLevel; 40 import android.media.MediaCodecList; 41 import android.media.MediaFormat; 42 import android.media.cts.TestUtils; 43 import android.os.Build; 44 import android.os.Bundle; 45 import android.os.PersistableBundle; 46 import android.os.SystemProperties; 47 import android.platform.test.flag.junit.CheckFlagsRule; 48 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 49 import android.util.Log; 50 import android.util.Pair; 51 import android.view.Display; 52 import android.view.Surface; 53 54 import androidx.test.platform.app.InstrumentationRegistry; 55 56 import com.android.compatibility.common.util.ApiLevelUtil; 57 import com.android.compatibility.common.util.MediaUtils; 58 59 import org.junit.After; 60 import org.junit.Assume; 61 import org.junit.Before; 62 import org.junit.Rule; 63 import org.junit.rules.TestName; 64 65 import java.io.IOException; 66 import java.nio.ByteBuffer; 67 import java.util.ArrayList; 68 import java.util.Arrays; 69 import java.util.Comparator; 70 import java.util.HashMap; 71 import java.util.HashSet; 72 import java.util.List; 73 import java.util.Locale; 74 import java.util.Map; 75 import java.util.Set; 76 import java.util.function.Function; 77 import java.util.regex.Matcher; 78 import java.util.regex.Pattern; 79 import java.util.stream.IntStream; 80 import java.util.stream.Stream; 81 82 /** 83 * This class comprises of routines that are generic to media codec component trying and testing. 84 * <p> 85 * A media codec component essentially processes input data to generate output data. The 86 * component uses a set of input and output buffers. At a simplistic level, the client requests 87 * (or receive) an empty input buffer, fills it up with data and sends it to the codec for 88 * processing. The codec uses up the data and transforms it into one of its empty output 89 * buffers. Finally, the client asks (or receive) a filled output buffer, consume its contents and 90 * release it back to the codec. 91 * <p> 92 * The type of data that a component receives and sends is dependent on the component. For 93 * instance video encoders expect raw yuv/rgb data and send compressed data. A video decoder 94 * receives compressed access-unit and sends reconstructed yuv. But the processes surrounding 95 * this enqueue and dequeue remain common to all components. Operations like configure, requesting 96 * the component for an empty input buffer, feeding the component a filled input buffer, 97 * requesting the component for an output buffer, releasing the processed output buffer, Sending 98 * state transition commands, waiting on component to send all outputs, closing the component and 99 * releasing the resources, ... remain more or less identical to all components. The routines 100 * that manage these generic processes are maintained by this class. Not only the methods that 101 * are responsible for component trying but also methods that test its functionality are covered 102 * here. A video component is expected to give same number of outputs as inputs. The output 103 * buffer timestamps of an audio component or a decoder component is expected to be strictly 104 * increasing. The routines that enforce these generic rules of all components at all times are 105 * part of this class. Besides these, mediaType specific constants, helper utilities to test 106 * specific components or mediaTypes are covered here. 107 * <p> 108 * enqueueInput and dequeueOutput are routines responsible for filling the input buffer and 109 * handing the received output buffer respectively. These shall be component specific, hence they 110 * are abstract methods. Any client intending to use this class shall implement these methods 111 * basing on the component under test. 112 * <p> 113 * In simple terms, the CodecTestBase is a wrapper class comprising generic routines for 114 * component trying and testing. 115 */ 116 public abstract class CodecTestBase { 117 public static final boolean IS_Q = ApiLevelUtil.getApiLevel() == Build.VERSION_CODES.Q; 118 public static final boolean IS_AT_LEAST_R = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R); 119 public static final boolean IS_AT_LEAST_T = 120 ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU); 121 public static final boolean IS_AT_LEAST_U = 122 ApiLevelUtil.isAtLeast(Build.VERSION_CODES.UPSIDE_DOWN_CAKE); 123 public static final boolean IS_BEFORE_U = !IS_AT_LEAST_U; 124 //TODO(b/248315681) Remove codenameEquals() check once devices return correct version for V 125 public static final boolean IS_AT_LEAST_V = 126 ApiLevelUtil.isAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 127 || ApiLevelUtil.codenameEquals("VanillaIceCream"); 128 public static final boolean FIRST_SDK_IS_AT_LEAST_T = 129 ApiLevelUtil.isFirstApiAtLeast(Build.VERSION_CODES.TIRAMISU); 130 public static final boolean FIRST_SDK_IS_AT_LEAST_V = 131 ApiLevelUtil.isFirstApiAtLeast(Build.VERSION_CODES.VANILLA_ICE_CREAM); 132 public static final boolean VNDK_IS_AT_LEAST_T = 133 SystemProperties.getInt("ro.vndk.version", Build.VERSION_CODES.CUR_DEVELOPMENT) 134 >= Build.VERSION_CODES.TIRAMISU; 135 public static final boolean VNDK_IS_BEFORE_U = 136 SystemProperties.getInt("ro.vndk.version", Build.VERSION_CODES.CUR_DEVELOPMENT) 137 < Build.VERSION_CODES.UPSIDE_DOWN_CAKE; 138 public static final boolean VNDK_IS_AT_MOST_U = 139 SystemProperties.getInt("ro.vndk.version", Build.VERSION_CODES.CUR_DEVELOPMENT) 140 <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE; 141 public static final boolean BOARD_SDK_IS_AT_LEAST_T = 142 SystemProperties.getInt("ro.board.api_level", Build.VERSION_CODES.CUR_DEVELOPMENT) 143 >= Build.VERSION_CODES.TIRAMISU; 144 public static final boolean BOARD_SDK_IS_BEFORE_U = 145 SystemProperties.getInt("ro.board.api_level", Build.VERSION_CODES.CUR_DEVELOPMENT) 146 < Build.VERSION_CODES.UPSIDE_DOWN_CAKE; 147 public static final int ANDROID_VENDOR_API_202404 = 202404; 148 // ro.vendor.api_level is guaranteed to be set on devices running in Android T and above, 149 // so using a default of 0 when not defined is safe to detect devices launching with 202404. 150 // These tests run on older versions where ro.vendor.api_level is not defined. So this 151 // needs to use default of 0 to ensure that this does't get treated as >= 202404 on those 152 // builds. 153 public static final boolean BOARD_FIRST_SDK_IS_AT_LEAST_202404 = 154 SystemProperties.getInt("ro.vendor.api_level", 0) >= ANDROID_VENDOR_API_202404; 155 public static final boolean IS_HDR_EDITING_SUPPORTED; 156 public static final boolean IS_HLG_EDITING_SUPPORTED; 157 public static final boolean IS_HDR_CAPTURE_SUPPORTED; 158 private static final String LOG_TAG = CodecTestBase.class.getSimpleName(); 159 160 public static final ArrayList<String> HDR_INFO_IN_BITSTREAM_CODECS = new ArrayList<>(); 161 public static final String HDR_STATIC_INFO = 162 "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42 40 a0 0f 32 00 10 27 df 0d"; 163 public static final String HDR_STATIC_INCORRECT_INFO = 164 "00 d0 84 80 3e c2 33 c4 86 10 27 d0 07 13 3d 42 40 a0 0f 32 00 10 27 df 0d"; 165 public static final String CODEC_PREFIX_KEY = "codec-prefix"; 166 public static final String CODEC_FILTER_KEY = "codec-filter"; 167 public static final String MEDIA_TYPE_PREFIX_KEY = "media-type-prefix"; 168 public static final String MEDIA_TYPE_SEL_KEY = "media-type-sel"; 169 public static final Map<String, String> CODEC_SEL_KEY_MEDIA_TYPE_MAP = new HashMap<>(); 170 public static final Map<String, String> DEFAULT_ENCODERS = new HashMap<>(); 171 public static final Map<String, String> DEFAULT_DECODERS = new HashMap<>(); 172 public static final HashMap<String, int[]> PROFILE_MAP = new HashMap<>(); 173 public static final HashMap<String, int[]> PROFILE_SDR_MAP = new HashMap<>(); 174 public static final HashMap<String, int[]> PROFILE_HLG_MAP = new HashMap<>(); 175 public static final HashMap<String, int[]> PROFILE_HDR10_MAP = new HashMap<>(); 176 public static final HashMap<String, int[]> PROFILE_HDR10_PLUS_MAP = new HashMap<>(); 177 public static final HashMap<String, int[]> PROFILE_HDR_MAP = new HashMap<>(); 178 public static final boolean ENABLE_LOGS = false; 179 public static final int PER_TEST_TIMEOUT_LARGE_TEST_MS = 300000; 180 public static final int PER_TEST_TIMEOUT_SMALL_TEST_MS = 60000; 181 public static final int UNSPECIFIED = 0; 182 // Maintain Timeouts in sync with their counterpart in NativeMediaCommon.h 183 // block at most 5ms while looking for io buffers 184 public static final long Q_DEQ_TIMEOUT_US = 5000; 185 // max poll counter before test aborts and returns error 186 public static final int RETRY_LIMIT = 100; 187 public static final String INVALID_CODEC = "unknown.codec_"; 188 static final int[] MPEG2_PROFILES = new int[]{MPEG2ProfileSimple, MPEG2ProfileMain, 189 MPEG2Profile422, MPEG2ProfileSNR, MPEG2ProfileSpatial, MPEG2ProfileHigh}; 190 static final int[] MPEG4_PROFILES = new int[]{MPEG4ProfileSimple, MPEG4ProfileSimpleScalable, 191 MPEG4ProfileCore, MPEG4ProfileMain, MPEG4ProfileNbit, MPEG4ProfileScalableTexture, 192 MPEG4ProfileSimpleFace, MPEG4ProfileSimpleFBA, MPEG4ProfileBasicAnimated, 193 MPEG4ProfileHybrid, MPEG4ProfileAdvancedRealTime, MPEG4ProfileCoreScalable, 194 MPEG4ProfileAdvancedCoding, MPEG4ProfileAdvancedCore, MPEG4ProfileAdvancedScalable, 195 MPEG4ProfileAdvancedSimple}; 196 static final int[] H263_PROFILES = new int[]{H263ProfileBaseline, H263ProfileH320Coding, 197 H263ProfileBackwardCompatible, H263ProfileISWV2, H263ProfileISWV3, 198 H263ProfileHighCompression, H263ProfileInternet, H263ProfileInterlace, 199 H263ProfileHighLatency}; 200 static final int[] VP8_PROFILES = new int[]{VP8ProfileMain}; 201 static final int[] AVC_SDR_PROFILES = new int[]{AVCProfileBaseline, AVCProfileMain, 202 AVCProfileExtended, AVCProfileHigh, AVCProfileConstrainedBaseline, 203 AVCProfileConstrainedHigh}; 204 static final int[] AVC_HLG_PROFILES = new int[]{AVCProfileHigh10}; 205 static final int[] AVC_HDR_PROFILES = AVC_HLG_PROFILES; 206 static final int[] AVC_PROFILES = combine(AVC_SDR_PROFILES, AVC_HDR_PROFILES); 207 static final int[] VP9_SDR_PROFILES = new int[]{VP9Profile0}; 208 static final int[] VP9_HLG_PROFILES = new int[]{VP9Profile2}; 209 static final int[] VP9_HDR10_PROFILES = new int[]{VP9Profile2HDR}; 210 static final int[] VP9_HDR10_PLUS_PROFILES = new int[]{VP9Profile2HDR10Plus}; 211 static final int[] VP9_HDR_PROFILES = 212 combine(VP9_HLG_PROFILES, combine(VP9_HDR10_PROFILES, VP9_HDR10_PLUS_PROFILES)); 213 static final int[] VP9_PROFILES = combine(VP9_SDR_PROFILES, VP9_HDR_PROFILES); 214 static final int[] HEVC_SDR_PROFILES = new int[]{HEVCProfileMain}; 215 static final int[] HEVC_HLG_PROFILES = new int[]{HEVCProfileMain10}; 216 static final int[] HEVC_HDR10_PROFILES = new int[]{HEVCProfileMain10HDR10}; 217 static final int[] HEVC_HDR10_PLUS_PROFILES = new int[]{HEVCProfileMain10HDR10Plus}; 218 static final int[] HEVC_HDR_PROFILES = 219 combine(HEVC_HLG_PROFILES, combine(HEVC_HDR10_PROFILES, HEVC_HDR10_PLUS_PROFILES)); 220 static final int[] HEVC_PROFILES = combine(HEVC_SDR_PROFILES, HEVC_HDR_PROFILES); 221 static final int[] AV1_SDR_PROFILES = new int[]{AV1ProfileMain8}; 222 static final int[] AV1_HLG_PROFILES = new int[]{AV1ProfileMain10}; 223 static final int[] AV1_HDR10_PROFILES = new int[]{AV1ProfileMain10HDR10}; 224 static final int[] AV1_HDR10_PLUS_PROFILES = new int[]{AV1ProfileMain10HDR10Plus}; 225 static final int[] AV1_HDR_PROFILES = 226 combine(AV1_HLG_PROFILES, combine(AV1_HDR10_PROFILES, AV1_HDR10_PLUS_PROFILES)); 227 static final int[] AV1_PROFILES = combine(AV1_SDR_PROFILES, AV1_HDR_PROFILES); 228 static final int[] AAC_PROFILES = new int[]{AACObjectMain, AACObjectLC, AACObjectSSR, 229 AACObjectLTP, AACObjectHE, AACObjectScalable, AACObjectERLC, AACObjectERScalable, 230 AACObjectLD, AACObjectELD, AACObjectXHE}; 231 public static final Context CONTEXT = 232 InstrumentationRegistry.getInstrumentation().getTargetContext(); 233 234 public static final int MAX_DISPLAY_HEIGHT_CURRENT = 235 Arrays.stream(CONTEXT.getSystemService(DisplayManager.class).getDisplays()) 236 .map(Display::getSupportedModes) 237 .flatMap(Stream::of) 238 .max(Comparator.comparing(Display.Mode::getPhysicalHeight)) 239 .orElseThrow(() -> new RuntimeException("Failed to determine max height")) 240 .getPhysicalHeight(); 241 public static final int MAX_DISPLAY_WIDTH_CURRENT = 242 Arrays.stream(CONTEXT.getSystemService(DisplayManager.class).getDisplays()) 243 .map(Display::getSupportedModes) 244 .flatMap(Stream::of) 245 .max(Comparator.comparing(Display.Mode::getPhysicalHeight)) 246 .orElseThrow(() -> new RuntimeException("Failed to determine max height")) 247 .getPhysicalWidth(); 248 public static final int MAX_DISPLAY_WIDTH_LAND = 249 Math.max(MAX_DISPLAY_WIDTH_CURRENT, MAX_DISPLAY_HEIGHT_CURRENT); 250 public static final int MAX_DISPLAY_HEIGHT_LAND = 251 Math.min(MAX_DISPLAY_WIDTH_CURRENT, MAX_DISPLAY_HEIGHT_CURRENT); 252 253 public static final String HDR10_INFO_SCENE_A = 254 "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" 255 + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" 256 + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" 257 + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"; 258 public static final String HDR10_INFO_SCENE_B = 259 "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" 260 + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" 261 + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" 262 + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"; 263 public static final String HDR10_INFO_SCENE_C = 264 "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" 265 + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" 266 + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" 267 + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"; 268 public static final String HDR10_INFO_SCENE_D = 269 "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" 270 + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" 271 + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" 272 + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"; 273 274 public static final String HDR10_INCORRECT_INFO_SCENE_A = 275 "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" 276 + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" 277 + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" 278 + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"; 279 public static final String HDR10_INCORRECT_INFO_SCENE_B = 280 "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" 281 + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" 282 + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" 283 + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 01"; 284 public static final String HDR10_INCORRECT_INFO_SCENE_C = 285 "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" 286 + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" 287 + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" 288 + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 02"; 289 public static final String HDR10_INCORRECT_INFO_SCENE_D = 290 "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" 291 + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" 292 + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" 293 + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 03"; 294 295 public static String mediaTypeSelKeys; 296 public static String codecPrefix; 297 public static String mediaTypePrefix; 298 public static Pattern codecFilter; 299 300 public enum SupportClass { 301 CODEC_ALL, // All codecs must support 302 CODEC_ANY, // At least one codec must support 303 CODEC_DEFAULT, // Default codec must support 304 CODEC_HW, // If the component is hardware, then it must support 305 CODEC_SHOULD, // Codec support is optional, but recommended 306 CODEC_HW_RECOMMENDED, // Codec support is optional, but strongly recommended if component 307 // is hardware accelerated 308 CODEC_OPTIONAL; // Codec support is optional 309 toString(SupportClass supportRequirements)310 public static String toString(SupportClass supportRequirements) { 311 switch (supportRequirements) { 312 case CODEC_ALL: 313 return "CODEC_ALL"; 314 case CODEC_ANY: 315 return "CODEC_ANY"; 316 case CODEC_DEFAULT: 317 return "CODEC_DEFAULT"; 318 case CODEC_HW: 319 return "CODEC_HW"; 320 case CODEC_SHOULD: 321 return "CODEC_SHOULD"; 322 case CODEC_HW_RECOMMENDED: 323 return "CODEC_HW_RECOMMENDED"; 324 case CODEC_OPTIONAL: 325 return "CODEC_OPTIONAL"; 326 default: 327 return "Unknown support class"; 328 } 329 } 330 } 331 332 public enum ComponentClass { 333 ALL, 334 SOFTWARE, 335 HARDWARE; 336 toString(ComponentClass selectSwitch)337 public static String toString(ComponentClass selectSwitch) { 338 switch (selectSwitch) { 339 case ALL: 340 return "all"; 341 case SOFTWARE: 342 return "software only"; 343 case HARDWARE: 344 return "hardware accelerated"; 345 default: 346 return "Unknown select switch"; 347 } 348 } 349 } 350 CodecTestBase(String encoder, String mediaType, String allTestParams)351 public CodecTestBase(String encoder, String mediaType, String allTestParams) { 352 mCodecName = encoder; 353 mMediaType = mediaType; 354 mAllTestParams = allTestParams; 355 mAsyncHandle = new CodecAsyncHandler(); 356 mIsAudio = mMediaType.startsWith("audio/"); 357 mIsVideo = mMediaType.startsWith("video/"); 358 } 359 360 protected final String mCodecName; 361 protected final String mMediaType; 362 protected final String mAllTestParams; // logging 363 364 protected final boolean mIsAudio; 365 protected final boolean mIsVideo; 366 protected CodecAsyncHandler mAsyncHandle; 367 protected boolean mIsCodecInAsyncMode; 368 protected boolean mSawInputEOS; 369 protected boolean mSawOutputEOS; 370 protected boolean mSignalEOSWithLastFrame; 371 protected int mInputCount; 372 protected int mOutputCount; 373 protected long mPrevOutputPts; 374 protected boolean mSignalledOutFormatChanged; 375 protected MediaFormat mOutFormat; 376 377 protected StringBuilder mTestConfig = new StringBuilder(); 378 protected StringBuilder mTestEnv = new StringBuilder(); 379 380 protected boolean mSaveToMem; 381 protected OutputManager mOutputBuff; 382 383 protected MediaCodec mCodec; 384 protected Surface mSurface; 385 // NOTE: mSurface is a place holder of current Surface used by the CodecTestBase. 386 // This doesn't own the handle. The ownership with instances that manage 387 // SurfaceView or TextureView, ... They hold the responsibility of calling release(). 388 protected CodecTestActivity mActivity; 389 protected ImageSurface mImageSurface; 390 391 public static final MediaCodecList MEDIA_CODEC_LIST_ALL; 392 public static final MediaCodecList MEDIA_CODEC_LIST_REGULAR; 393 static { 394 MEDIA_CODEC_LIST_ALL = new MediaCodecList(MediaCodecList.ALL_CODECS); 395 MEDIA_CODEC_LIST_REGULAR = new MediaCodecList(MediaCodecList.REGULAR_CODECS); 396 IS_HDR_CAPTURE_SUPPORTED = isHDRCaptureSupported(); 397 IS_HDR_EDITING_SUPPORTED = isEncoderFeatureSupported(FEATURE_HdrEditing); 398 IS_HLG_EDITING_SUPPORTED = (IS_AT_LEAST_V && android.media.codec.Flags.hlgEditing()) 399 ? isEncoderFeatureSupported(FEATURE_HlgEditing) : false; 400 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("vp8", MediaFormat.MIMETYPE_VIDEO_VP8); 401 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("vp9", MediaFormat.MIMETYPE_VIDEO_VP9); 402 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("av1", MediaFormat.MIMETYPE_VIDEO_AV1); 403 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("avc", MediaFormat.MIMETYPE_VIDEO_AVC); 404 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("hevc", MediaFormat.MIMETYPE_VIDEO_HEVC); 405 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("mpeg4", MediaFormat.MIMETYPE_VIDEO_MPEG4); 406 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("h263", MediaFormat.MIMETYPE_VIDEO_H263); 407 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("mpeg2", MediaFormat.MIMETYPE_VIDEO_MPEG2); 408 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("vraw", MediaFormat.MIMETYPE_VIDEO_RAW); 409 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("amrnb", MediaFormat.MIMETYPE_AUDIO_AMR_NB); 410 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("amrwb", MediaFormat.MIMETYPE_AUDIO_AMR_WB); 411 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("mp3", MediaFormat.MIMETYPE_AUDIO_MPEG); 412 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("aac", MediaFormat.MIMETYPE_AUDIO_AAC); 413 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("vorbis", MediaFormat.MIMETYPE_AUDIO_VORBIS); 414 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("opus", MediaFormat.MIMETYPE_AUDIO_OPUS); 415 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("g711alaw", MediaFormat.MIMETYPE_AUDIO_G711_ALAW); 416 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("g711mlaw", MediaFormat.MIMETYPE_AUDIO_G711_MLAW); 417 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("araw", MediaFormat.MIMETYPE_AUDIO_RAW); 418 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("flac", MediaFormat.MIMETYPE_AUDIO_FLAC); 419 CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("gsm", MediaFormat.MIMETYPE_AUDIO_MSGSM); 420 421 android.os.Bundle args = InstrumentationRegistry.getArguments(); 422 mediaTypeSelKeys = args.getString(MEDIA_TYPE_SEL_KEY); 423 codecPrefix = args.getString(CODEC_PREFIX_KEY); 424 mediaTypePrefix = args.getString(MEDIA_TYPE_PREFIX_KEY); 425 String codecFilterStr = args.getString(CODEC_FILTER_KEY); 426 if (codecFilterStr != null) { 427 codecFilter = Pattern.compile(codecFilterStr); 428 } 429 PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_SDR_PROFILES)430 PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_SDR_PROFILES); PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_SDR_PROFILES)431 PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_SDR_PROFILES); PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_H263, H263_PROFILES)432 PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_H263, H263_PROFILES); PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_MPEG2, MPEG2_PROFILES)433 PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_MPEG2, MPEG2_PROFILES); PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_MPEG4, MPEG4_PROFILES)434 PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_MPEG4, MPEG4_PROFILES); PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP8, VP8_PROFILES)435 PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP8, VP8_PROFILES); PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_SDR_PROFILES)436 PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_SDR_PROFILES); PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_SDR_PROFILES)437 PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_SDR_PROFILES); PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_AUDIO_AAC, AAC_PROFILES)438 PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_AUDIO_AAC, AAC_PROFILES); 439 PROFILE_HLG_MAP.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_HLG_PROFILES)440 PROFILE_HLG_MAP.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_HLG_PROFILES); PROFILE_HLG_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HLG_PROFILES)441 PROFILE_HLG_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HLG_PROFILES); PROFILE_HLG_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HLG_PROFILES)442 PROFILE_HLG_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HLG_PROFILES); PROFILE_HLG_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HLG_PROFILES)443 PROFILE_HLG_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HLG_PROFILES); 444 PROFILE_HDR10_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HDR10_PROFILES)445 PROFILE_HDR10_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HDR10_PROFILES); PROFILE_HDR10_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR10_PROFILES)446 PROFILE_HDR10_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR10_PROFILES); PROFILE_HDR10_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HDR10_PROFILES)447 PROFILE_HDR10_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HDR10_PROFILES); 448 PROFILE_HDR10_PLUS_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HDR10_PLUS_PROFILES)449 PROFILE_HDR10_PLUS_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HDR10_PLUS_PROFILES); PROFILE_HDR10_PLUS_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR10_PLUS_PROFILES)450 PROFILE_HDR10_PLUS_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR10_PLUS_PROFILES); PROFILE_HDR10_PLUS_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HDR10_PLUS_PROFILES)451 PROFILE_HDR10_PLUS_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HDR10_PLUS_PROFILES); 452 PROFILE_HDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_HDR_PROFILES)453 PROFILE_HDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_HDR_PROFILES); PROFILE_HDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HDR_PROFILES)454 PROFILE_HDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HDR_PROFILES); PROFILE_HDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR_PROFILES)455 PROFILE_HDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR_PROFILES); PROFILE_HDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HDR_PROFILES)456 PROFILE_HDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HDR_PROFILES); 457 PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_PROFILES)458 PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_PROFILES); PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_PROFILES)459 PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_PROFILES); PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_H263, H263_PROFILES)460 PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_H263, H263_PROFILES); PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_MPEG2, MPEG2_PROFILES)461 PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_MPEG2, MPEG2_PROFILES); PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_MPEG4, MPEG4_PROFILES)462 PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_MPEG4, MPEG4_PROFILES); PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP8, VP8_PROFILES)463 PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP8, VP8_PROFILES); PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_PROFILES)464 PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_PROFILES); PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_PROFILES)465 PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_PROFILES); PROFILE_MAP.put(MediaFormat.MIMETYPE_AUDIO_AAC, AAC_PROFILES)466 PROFILE_MAP.put(MediaFormat.MIMETYPE_AUDIO_AAC, AAC_PROFILES); 467 468 HDR_INFO_IN_BITSTREAM_CODECS.add(MediaFormat.MIMETYPE_VIDEO_AV1); 469 HDR_INFO_IN_BITSTREAM_CODECS.add(MediaFormat.MIMETYPE_VIDEO_AVC); 470 HDR_INFO_IN_BITSTREAM_CODECS.add(MediaFormat.MIMETYPE_VIDEO_HEVC); 471 } 472 combine(int[] first, int[] second)473 static int[] combine(int[] first, int[] second) { 474 int[] result = Arrays.copyOf(first, first.length + second.length); 475 System.arraycopy(second, 0, result, first.length, second.length); 476 return result; 477 } 478 isMediaTypeLossless(String mediaType)479 public static boolean isMediaTypeLossless(String mediaType) { 480 if (mediaType.equals(MediaFormat.MIMETYPE_AUDIO_FLAC)) return true; 481 if (mediaType.equals(MediaFormat.MIMETYPE_AUDIO_RAW)) return true; 482 return false; 483 } 484 485 // some media types decode a pre-roll amount before playback. This would mean that decoding 486 // after seeking may not return the exact same values as would be obtained by decoding the 487 // stream straight through isMediaTypeOutputUnAffectedBySeek(String mediaType)488 public static boolean isMediaTypeOutputUnAffectedBySeek(String mediaType) { 489 if (mediaType.equals(MediaFormat.MIMETYPE_AUDIO_FLAC)) return true; 490 if (mediaType.equals(MediaFormat.MIMETYPE_AUDIO_RAW)) return true; 491 if (mediaType.startsWith("video/")) return true; 492 return false; 493 } 494 hasDecoder(String mediaType)495 public static boolean hasDecoder(String mediaType) { 496 return CodecTestBase.selectCodecs(mediaType, null, null, false).size() != 0; 497 } 498 hasEncoder(String mediaType)499 public static boolean hasEncoder(String mediaType) { 500 return CodecTestBase.selectCodecs(mediaType, null, null, true).size() != 0; 501 } 502 checkFormatSupport(String codecName, String mediaType, boolean isEncoder, ArrayList<MediaFormat> formats, String[] features, SupportClass supportRequirements)503 public static void checkFormatSupport(String codecName, String mediaType, boolean isEncoder, 504 ArrayList<MediaFormat> formats, String[] features, SupportClass supportRequirements) 505 throws IOException { 506 boolean hasSupport = true; 507 if (formats != null) { 508 hasSupport &= areFormatsSupported(codecName, mediaType, formats); 509 } 510 if (features != null) { 511 for (String feature : features) { 512 hasSupport &= isFeatureSupported(codecName, mediaType, feature); 513 } 514 } 515 if (!hasSupport) { 516 switch (supportRequirements) { 517 case CODEC_ALL: 518 fail("format(s) not supported by codec: " + codecName + " for mediaType : " 519 + mediaType + " formats: " + formats); 520 break; 521 case CODEC_ANY: 522 if (selectCodecs(mediaType, formats, features, isEncoder).isEmpty()) { 523 fail("format(s) not supported by any component for mediaType : " + mediaType 524 + " formats: " + formats); 525 } 526 break; 527 case CODEC_DEFAULT: 528 if (isDefaultCodec(codecName, mediaType, isEncoder)) { 529 fail("format(s) not supported by default codec : " + codecName 530 + "for mediaType : " + mediaType + " formats: " + formats); 531 } 532 break; 533 case CODEC_HW: 534 if (isHardwareAcceleratedCodec(codecName)) { 535 fail("format(s) not supported by codec: " + codecName + " for mediaType : " 536 + mediaType + " formats: " + formats); 537 } 538 break; 539 case CODEC_SHOULD: 540 Assume.assumeTrue(String.format("format(s) not supported by codec: %s for" 541 + " mediaType : %s. It is recommended to support it", 542 codecName, mediaType), false); 543 break; 544 case CODEC_HW_RECOMMENDED: 545 Assume.assumeTrue(String.format( 546 "format(s) not supported by codec: %s for mediaType : %s. It is %s " 547 + "recommended to support it", codecName, mediaType, 548 isHardwareAcceleratedCodec(codecName) ? "strongly" : ""), false); 549 break; 550 case CODEC_OPTIONAL: 551 default: 552 // the later assumeTrue() ensures we skip the test for unsupported codecs 553 break; 554 } 555 Assume.assumeTrue("format(s) not supported by codec: " + codecName + " for mediaType : " 556 + mediaType, false); 557 } 558 } 559 isFeatureSupported(String name, String mediaType, String feature)560 public static boolean isFeatureSupported(String name, String mediaType, String feature) { 561 for (MediaCodecInfo codecInfo : MEDIA_CODEC_LIST_ALL.getCodecInfos()) { 562 if (name.equals(codecInfo.getName())) { 563 return codecInfo.getCapabilitiesForType(mediaType).isFeatureSupported(feature); 564 } 565 } 566 return false; 567 } 568 isHDRCaptureSupported()569 public static boolean isHDRCaptureSupported() { 570 // If the device supports HDR, hlg support should always return true 571 if (!MediaUtils.hasCamera()) return false; 572 CameraManager cm = CONTEXT.getSystemService(CameraManager.class); 573 try { 574 String[] cameraIds = cm.getCameraIdList(); 575 for (String id : cameraIds) { 576 CameraCharacteristics ch = cm.getCameraCharacteristics(id); 577 int[] caps = ch.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); 578 if (IntStream.of(caps).anyMatch(x -> x 579 == CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT)) { 580 Set<Long> profiles = 581 ch.get(CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES) 582 .getSupportedProfiles(); 583 if (profiles.contains(DynamicRangeProfiles.HLG10)) return true; 584 } 585 } 586 } catch (CameraAccessException e) { 587 Log.e(LOG_TAG, "encountered " + e.getMessage() 588 + " marking hdr capture to be available to catch your attention"); 589 return true; 590 } 591 return false; 592 } 593 594 /** 595 * Check if any encoder on the device supports the given feature 596 */ isEncoderFeatureSupported(String feature)597 public static boolean isEncoderFeatureSupported(String feature) { 598 for (MediaCodecInfo codecInfo : MEDIA_CODEC_LIST_REGULAR.getCodecInfos()) { 599 if (!codecInfo.isEncoder()) { 600 continue; 601 } 602 for (String mediaType : codecInfo.getSupportedTypes()) { 603 CodecCapabilities caps = codecInfo.getCapabilitiesForType(mediaType); 604 if (caps != null && caps.isFeatureSupported(feature)) { 605 return true; 606 } 607 } 608 } 609 return false; 610 } 611 doesAnyFormatHaveHDRProfile(String mediaType, ArrayList<MediaFormat> formats)612 public static boolean doesAnyFormatHaveHDRProfile(String mediaType, 613 ArrayList<MediaFormat> formats) { 614 int[] profileArray = PROFILE_HDR_MAP.get(mediaType); 615 if (profileArray != null) { 616 for (MediaFormat format : formats) { 617 assertEquals(mediaType, format.getString(MediaFormat.KEY_MIME)); 618 int profile = format.getInteger(MediaFormat.KEY_PROFILE, -1); 619 if (IntStream.of(profileArray).anyMatch(x -> x == profile)) return true; 620 } 621 } 622 return false; 623 } 624 doesCodecSupportHDRProfile(String codecName, String mediaType)625 public static boolean doesCodecSupportHDRProfile(String codecName, String mediaType) { 626 int[] hdrProfiles = PROFILE_HDR_MAP.get(mediaType); 627 if (hdrProfiles == null) { 628 return false; 629 } 630 for (MediaCodecInfo codecInfo : MEDIA_CODEC_LIST_REGULAR.getCodecInfos()) { 631 if (!codecName.equals(codecInfo.getName())) { 632 continue; 633 } 634 CodecCapabilities caps = codecInfo.getCapabilitiesForType(mediaType); 635 if (caps == null) { 636 return false; 637 } 638 for (CodecProfileLevel pl : caps.profileLevels) { 639 if (IntStream.of(hdrProfiles).anyMatch(x -> x == pl.profile)) { 640 return true; 641 } 642 } 643 } 644 return false; 645 } 646 canDisplaySupportHDRContent()647 public static boolean canDisplaySupportHDRContent() { 648 DisplayManager displayManager = CONTEXT.getSystemService(DisplayManager.class); 649 return displayManager.getDisplay(Display.DEFAULT_DISPLAY).getHdrCapabilities() 650 .getSupportedHdrTypes().length > 0; 651 } 652 isFormatSupported(String name, String mediaType, MediaFormat format)653 public static boolean isFormatSupported(String name, String mediaType, MediaFormat format) { 654 for (MediaCodecInfo codecInfo : MEDIA_CODEC_LIST_ALL.getCodecInfos()) { 655 if (name.equals(codecInfo.getName())) { 656 MediaCodecInfo.CodecCapabilities cap = codecInfo.getCapabilitiesForType(mediaType); 657 boolean isSupported = true; 658 if (format != null) { 659 isSupported = cap.isFormatSupported(format); 660 } 661 if (isSupported) return true; 662 } 663 } 664 return false; 665 } 666 areFormatsSupported(String name, String mediaType, List<MediaFormat> formats)667 public static boolean areFormatsSupported(String name, String mediaType, 668 List<MediaFormat> formats) throws IOException { 669 for (MediaCodecInfo codecInfo : MEDIA_CODEC_LIST_ALL.getCodecInfos()) { 670 if (name.equals(codecInfo.getName())) { 671 MediaCodecInfo.CodecCapabilities cap = codecInfo.getCapabilitiesForType(mediaType); 672 boolean isSupported = true; 673 if (formats != null) { 674 for (int i = 0; i < formats.size() && isSupported; i++) { 675 isSupported = cap.isFormatSupported(formats.get(i)); 676 } 677 } 678 if (isSupported) return true; 679 } 680 } 681 return false; 682 } 683 hasSupportForColorFormat(String name, String mediaType, int colorFormat)684 public static boolean hasSupportForColorFormat(String name, String mediaType, int colorFormat) { 685 for (MediaCodecInfo codecInfo : MEDIA_CODEC_LIST_ALL.getCodecInfos()) { 686 if (name.equals(codecInfo.getName())) { 687 MediaCodecInfo.CodecCapabilities cap = codecInfo.getCapabilitiesForType(mediaType); 688 for (int c : cap.colorFormats) { 689 if (c == colorFormat) { 690 return true; 691 } 692 } 693 } 694 } 695 return false; 696 } 697 isDefaultCodec(String codecName, String mediaType, boolean isEncoder)698 public static boolean isDefaultCodec(String codecName, String mediaType, boolean isEncoder) 699 throws IOException { 700 Map<String, String> mDefaultCodecs = isEncoder ? DEFAULT_ENCODERS : DEFAULT_DECODERS; 701 if (mDefaultCodecs.containsKey(mediaType)) { 702 return mDefaultCodecs.get(mediaType).equalsIgnoreCase(codecName); 703 } 704 MediaCodec codec = isEncoder ? MediaCodec.createEncoderByType(mediaType) 705 : MediaCodec.createDecoderByType(mediaType); 706 boolean isDefault = codec.getName().equalsIgnoreCase(codecName); 707 mDefaultCodecs.put(mediaType, codec.getName()); 708 codec.release(); 709 return isDefault; 710 } 711 isVendorCodec(String codecName)712 public static boolean isVendorCodec(String codecName) { 713 for (MediaCodecInfo codecInfo : MEDIA_CODEC_LIST_ALL.getCodecInfos()) { 714 if (codecName.equals(codecInfo.getName())) { 715 return codecInfo.isVendor(); 716 } 717 } 718 return false; 719 } 720 isSoftwareCodec(String codecName)721 public static boolean isSoftwareCodec(String codecName) { 722 for (MediaCodecInfo codecInfo : MEDIA_CODEC_LIST_ALL.getCodecInfos()) { 723 if (codecName.equals(codecInfo.getName())) { 724 return codecInfo.isSoftwareOnly(); 725 } 726 } 727 return false; 728 } 729 isHardwareAcceleratedCodec(String codecName)730 public static boolean isHardwareAcceleratedCodec(String codecName) { 731 for (MediaCodecInfo codecInfo : MEDIA_CODEC_LIST_ALL.getCodecInfos()) { 732 if (codecName.equals(codecInfo.getName())) { 733 return codecInfo.isHardwareAccelerated(); 734 } 735 } 736 return false; 737 } 738 739 /** 740 * Stop the current codec session and transfer component to uninitialized state. 741 * <p> 742 * Some legacy OMX components do not properly clear the internal state at stop(). 743 * Workaround the issue with resetting the component. 744 */ endCodecSession(MediaCodec codec)745 public static void endCodecSession(MediaCodec codec) { 746 if (codec.getName().toUpperCase(Locale.getDefault()).startsWith("OMX")) { 747 codec.reset(); 748 } else { 749 codec.stop(); 750 } 751 } 752 paramToString(Object[] param)753 public static String paramToString(Object[] param) { 754 StringBuilder paramStr = new StringBuilder("[ "); 755 for (int j = 0; j < param.length - 1; j++) { 756 Object o = param[j]; 757 if (o == null) { 758 paramStr.append("null, "); 759 } else if (o instanceof String[]) { 760 int length = Math.min(((String[]) o).length, 3); 761 paramStr.append("{"); 762 for (int i = 0; i < length; i++) { 763 paramStr.append(((String[]) o)[i]).append(", "); 764 } 765 paramStr.delete(paramStr.length() - 2, paramStr.length()) 766 .append(length == ((String[]) o).length ? "}, " : ", ... }, "); 767 } else if (o instanceof int[]) { 768 paramStr.append("{"); 769 for (int i = 0; i < ((int[]) o).length; i++) { 770 paramStr.append(((int[]) o)[i]).append(", "); 771 } 772 paramStr.delete(paramStr.length() - 2, paramStr.length()).append("}, "); 773 } else if (o instanceof Map) { 774 int length = 0; 775 paramStr.append("{ "); 776 Map map = (Map) o; 777 for (Object key : map.keySet()) { 778 paramStr.append(key).append(" = ").append(map.get(key)).append(", "); 779 length++; 780 if (length > 1) break; 781 } 782 paramStr.delete(paramStr.length() - 2, paramStr.length()) 783 .append(length == map.size() ? "}, " : ", ... }, "); 784 } else if (o instanceof EncoderConfigParams[]) { 785 int length = Math.min(((EncoderConfigParams[]) o).length, 3); 786 paramStr.append("{"); 787 for (int i = 0; i < ((EncoderConfigParams[]) o).length; i++) { 788 paramStr.append(((EncoderConfigParams[]) o)[i]).append(", "); 789 } 790 paramStr.delete(paramStr.length() - 2, paramStr.length()) 791 .append(length == ((EncoderConfigParams[]) o).length ? "}, " : ", ... }, "); 792 } else paramStr.append(o).append(", "); 793 } 794 paramStr.delete(paramStr.length() - 2, paramStr.length()).append(" ]"); 795 return paramStr.toString(); 796 } 797 compileRequiredMediaTypeList(boolean isEncoder, boolean needAudio, boolean needVideo)798 public static ArrayList<String> compileRequiredMediaTypeList(boolean isEncoder, 799 boolean needAudio, boolean needVideo) { 800 Set<String> list = new HashSet<>(); 801 if (!isEncoder) { 802 if (MediaUtils.hasAudioOutput() && needAudio) { 803 // sec 5.1.2 804 list.add(MediaFormat.MIMETYPE_AUDIO_AAC); 805 list.add(MediaFormat.MIMETYPE_AUDIO_FLAC); 806 list.add(MediaFormat.MIMETYPE_AUDIO_MPEG); 807 list.add(MediaFormat.MIMETYPE_AUDIO_VORBIS); 808 list.add(MediaFormat.MIMETYPE_AUDIO_RAW); 809 list.add(MediaFormat.MIMETYPE_AUDIO_OPUS); 810 } 811 if (MediaUtils.isHandheld() || MediaUtils.isTablet() || MediaUtils.isTv() 812 || MediaUtils.isAutomotive()) { 813 // sec 2.2.2, 2.3.2, 2.5.2 814 if (needAudio) { 815 list.add(MediaFormat.MIMETYPE_AUDIO_AAC); 816 } 817 if (needVideo) { 818 list.add(MediaFormat.MIMETYPE_VIDEO_AVC); 819 list.add(MediaFormat.MIMETYPE_VIDEO_MPEG4); 820 list.add(MediaFormat.MIMETYPE_VIDEO_VP8); 821 list.add(MediaFormat.MIMETYPE_VIDEO_VP9); 822 } 823 } 824 if (MediaUtils.isHandheld() || MediaUtils.isTablet()) { 825 // sec 2.2.2 826 if (needAudio) { 827 list.add(MediaFormat.MIMETYPE_AUDIO_AMR_NB); 828 list.add(MediaFormat.MIMETYPE_AUDIO_AMR_WB); 829 } 830 if (needVideo) { 831 list.add(MediaFormat.MIMETYPE_VIDEO_HEVC); 832 if (IS_AT_LEAST_U) { 833 list.add(MediaFormat.MIMETYPE_VIDEO_AV1); 834 } 835 } 836 } 837 if (MediaUtils.isTv() && needVideo) { 838 // sec 2.3.2 839 list.add(MediaFormat.MIMETYPE_VIDEO_HEVC); 840 list.add(MediaFormat.MIMETYPE_VIDEO_MPEG2); 841 if (IS_AT_LEAST_U) { 842 list.add(MediaFormat.MIMETYPE_VIDEO_AV1); 843 } 844 } 845 } else { 846 if (MediaUtils.hasMicrophone() && needAudio) { 847 // sec 5.1.1 848 // TODO(b/154423550) 849 // list.add(MediaFormat.MIMETYPE_AUDIO_RAW); 850 list.add(MediaFormat.MIMETYPE_AUDIO_FLAC); 851 list.add(MediaFormat.MIMETYPE_AUDIO_OPUS); 852 } 853 if (MediaUtils.isHandheld() || MediaUtils.isTablet() || MediaUtils.isTv() 854 || MediaUtils.isAutomotive()) { 855 // sec 2.2.2, 2.3.2, 2.5.2 856 if (needAudio) { 857 list.add(MediaFormat.MIMETYPE_AUDIO_AAC); 858 } 859 if (needVideo) { 860 if ((MediaUtils.isHandheld() || MediaUtils.isTablet() || MediaUtils.isTv()) 861 && IS_AT_LEAST_U) { 862 list.add(MediaFormat.MIMETYPE_VIDEO_AV1); 863 } 864 list.add(MediaFormat.MIMETYPE_VIDEO_AVC); 865 list.add(MediaFormat.MIMETYPE_VIDEO_VP8); 866 } 867 } 868 if ((MediaUtils.isHandheld() || MediaUtils.isTablet()) && needAudio) { 869 // sec 2.2.2 870 list.add(MediaFormat.MIMETYPE_AUDIO_AMR_NB); 871 list.add(MediaFormat.MIMETYPE_AUDIO_AMR_WB); 872 } 873 } 874 return new ArrayList<>(list); 875 } 876 compileCompleteTestMediaTypesList(boolean isEncoder, boolean needAudio, boolean needVideo)877 private static ArrayList<String> compileCompleteTestMediaTypesList(boolean isEncoder, 878 boolean needAudio, boolean needVideo) { 879 ArrayList<String> mediaTypes = new ArrayList<>(); 880 if (mediaTypeSelKeys == null) { 881 ArrayList<String> cddRequiredMediaTypesList = 882 compileRequiredMediaTypeList(isEncoder, needAudio, needVideo); 883 MediaCodecInfo[] codecInfos = MEDIA_CODEC_LIST_REGULAR.getCodecInfos(); 884 for (MediaCodecInfo codecInfo : codecInfos) { 885 if (codecInfo.isEncoder() != isEncoder) continue; 886 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && codecInfo.isAlias()) continue; 887 String[] types = codecInfo.getSupportedTypes(); 888 for (String type : types) { 889 if (mediaTypePrefix != null && !type.startsWith(mediaTypePrefix)) { 890 continue; 891 } 892 if (!needAudio && type.startsWith("audio/")) continue; 893 if (!needVideo && type.startsWith("video/")) continue; 894 if (!mediaTypes.contains(type)) { 895 mediaTypes.add(type); 896 } 897 } 898 } 899 if (mediaTypePrefix != null) { 900 return mediaTypes; 901 } 902 // feature_video_output is not exposed to package manager. Testing for video output 903 // ports, such as VGA, HDMI, DisplayPort, or a wireless port for display is also not 904 // direct. 905 /* sec 5.2: device implementations include an embedded screen display with the 906 diagonal length of at least 2.5 inches or include a video output port or declare the 907 support of a camera */ 908 if (isEncoder && needVideo 909 && (MediaUtils.hasCamera() || MediaUtils.getScreenSizeInInches() >= 2.5) 910 && !mediaTypes.contains(MediaFormat.MIMETYPE_VIDEO_AVC) 911 && !mediaTypes.contains(MediaFormat.MIMETYPE_VIDEO_VP8)) { 912 // Add required cdd mediaTypes here so that respective codec tests fail. 913 mediaTypes.add(MediaFormat.MIMETYPE_VIDEO_AVC); 914 mediaTypes.add(MediaFormat.MIMETYPE_VIDEO_VP8); 915 Log.e(LOG_TAG, "device must support at least one of VP8 or AVC video encoders"); 916 } 917 for (String mediaType : cddRequiredMediaTypesList) { 918 if (!mediaTypes.contains(mediaType)) { 919 // Add required cdd mediaTypes here so that respective codec tests fail. 920 mediaTypes.add(mediaType); 921 Log.e(LOG_TAG, "no codec found for mediaType " + mediaType 922 + " as required by cdd"); 923 } 924 } 925 } else { 926 for (Map.Entry<String, String> entry : CODEC_SEL_KEY_MEDIA_TYPE_MAP.entrySet()) { 927 String key = entry.getKey(); 928 String value = entry.getValue(); 929 if (mediaTypeSelKeys.contains(key) && !mediaTypes.contains(value)) { 930 mediaTypes.add(value); 931 } 932 } 933 } 934 return mediaTypes; 935 } 936 prepareParamList(List<Object[]> exhaustiveArgsList, boolean isEncoder, boolean needAudio, boolean needVideo, boolean mustTestAllCodecs)937 public static List<Object[]> prepareParamList(List<Object[]> exhaustiveArgsList, 938 boolean isEncoder, boolean needAudio, boolean needVideo, boolean mustTestAllCodecs) { 939 return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, 940 mustTestAllCodecs, ComponentClass.ALL, null /* features */); 941 } 942 prepareParamList(List<Object[]> exhaustiveArgsList, boolean isEncoder, boolean needAudio, boolean needVideo, boolean mustTestAllCodecs, ComponentClass selectSwitch)943 public static List<Object[]> prepareParamList(List<Object[]> exhaustiveArgsList, 944 boolean isEncoder, boolean needAudio, boolean needVideo, boolean mustTestAllCodecs, 945 ComponentClass selectSwitch) { 946 return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, 947 mustTestAllCodecs, selectSwitch, null); 948 } 949 prepareParamList(List<Object[]> exhaustiveArgsList, boolean isEncoder, boolean needAudio, boolean needVideo, boolean mustTestAllCodecs, ComponentClass selectSwitch, String[] features)950 public static List<Object[]> prepareParamList(List<Object[]> exhaustiveArgsList, 951 boolean isEncoder, boolean needAudio, boolean needVideo, boolean mustTestAllCodecs, 952 ComponentClass selectSwitch, String[] features) { 953 ArrayList<String> mediaTypes = compileCompleteTestMediaTypesList(isEncoder, 954 needAudio, needVideo); 955 ArrayList<String> cddRequiredMediaTypesList = 956 compileRequiredMediaTypeList(isEncoder, needAudio, needVideo); 957 final List<Object[]> argsList = new ArrayList<>(); 958 int argLength = exhaustiveArgsList.get(0).length; 959 for (String mediaType : mediaTypes) { 960 ArrayList<String> totalListOfCodecs = 961 selectCodecs(mediaType, null /* formats */, features, isEncoder, selectSwitch); 962 ArrayList<String> listOfCodecs = new ArrayList<>(); 963 964 // when mustTestAllCodecs, verify that we start with a non-empty list of 965 // codecs to test. If there are no codecs, the system is broken. We will 966 // usually trim that list (e.g. as part of CTS/MCTS partitioning) to get the 967 // set of codecs that we will actually run in this mode. 968 // 969 if (mustTestAllCodecs && totalListOfCodecs.size() == 0) { 970 listOfCodecs.add(INVALID_CODEC + mediaType); 971 } 972 973 for (String codec : totalListOfCodecs) { 974 // general mode exclusions 975 if (!TestUtils.isTestableCodecInCurrentMode(codec)) { 976 Log.v(LOG_TAG, "codec " + codec + " skipped in mode " 977 + TestUtils.currentTestModeName()); 978 continue; 979 } 980 // and then prefix checking exclusions 981 if (codecPrefix != null && !codec.startsWith(codecPrefix)) { 982 Log.v(LOG_TAG, "codec " + codec + " skipped, doesn't match prefix " 983 + codecPrefix); 984 continue; 985 } 986 if (codecFilter != null && !codecFilter.matcher(codec).matches()) { 987 Log.v(LOG_TAG, "codec " + codec + " skipped, doesn't match pattern"); 988 continue; 989 } 990 listOfCodecs.add(codec); 991 } 992 993 // we excluded eligible codecs all the way down to nothing. 994 // So we don't need to mess with adding to argsList. 995 // we trimmed the candidates down to an empty list; go look at the 996 // next mediatype 997 if (listOfCodecs.size() == 0) { 998 continue; 999 } 1000 1001 boolean miss = true; 1002 for (Object[] arg : exhaustiveArgsList) { 1003 if (mediaType.equals(arg[0])) { 1004 for (String codec : listOfCodecs) { 1005 Object[] argUpdate = new Object[argLength + 2]; 1006 argUpdate[0] = codec; 1007 System.arraycopy(arg, 0, argUpdate, 1, argLength); 1008 argUpdate[argLength + 1] = paramToString(argUpdate); 1009 argsList.add(argUpdate); 1010 } 1011 miss = false; 1012 } 1013 } 1014 1015 // when we're testing exhaustively... see if we are looking at a media type 1016 // that has no test vectors. 1017 // This means 1018 // -- we're looking at an optional or new media type. 1019 // -- our test suite is broken, we *should* have an asset for this type. 1020 // 1021 if (miss && mustTestAllCodecs) { 1022 if (!cddRequiredMediaTypesList.contains(mediaType)) { 1023 Log.w(LOG_TAG, "no test vectors available for optional mediaType type " 1024 + mediaType); 1025 continue; 1026 } 1027 1028 // create a fake codec name that indicates the 1029 // brokenness of this particular test case 1030 // (since it's painful to fail within the parameterization calculation) 1031 // 1032 Object[] argUpdate = new Object[argLength + 2]; 1033 argUpdate[0] = "No.Test.Assets.Exist.For." + mediaType; 1034 argUpdate[1] = mediaType; 1035 // skipping the initial 'mediatype' slot in the args list 1036 System.arraycopy(exhaustiveArgsList.get(0), 1, argUpdate, 2, argLength - 1); 1037 argUpdate[argLength + 1] = paramToString(argUpdate); 1038 argsList.add(argUpdate); 1039 } 1040 } 1041 return argsList; 1042 } 1043 selectCodecs(String mediaType, ArrayList<MediaFormat> formats, String[] features, boolean isEncoder)1044 public static ArrayList<String> selectCodecs(String mediaType, ArrayList<MediaFormat> formats, 1045 String[] features, boolean isEncoder) { 1046 return selectCodecs(mediaType, formats, features, isEncoder, ComponentClass.ALL); 1047 } 1048 selectCodecs(String mediaType, ArrayList<MediaFormat> formats, String[] features, boolean isEncoder, ComponentClass selectSwitch)1049 public static ArrayList<String> selectCodecs(String mediaType, ArrayList<MediaFormat> formats, 1050 String[] features, boolean isEncoder, ComponentClass selectSwitch) { 1051 MediaCodecInfo[] codecInfos = MEDIA_CODEC_LIST_REGULAR.getCodecInfos(); 1052 ArrayList<String> listOfCodecs = new ArrayList<>(); 1053 for (MediaCodecInfo codecInfo : codecInfos) { 1054 if (codecInfo.isEncoder() != isEncoder) continue; 1055 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && codecInfo.isAlias()) continue; 1056 if (selectSwitch == ComponentClass.HARDWARE && !codecInfo.isHardwareAccelerated()) { 1057 continue; 1058 } else if (selectSwitch == ComponentClass.SOFTWARE && !codecInfo.isSoftwareOnly()) { 1059 continue; 1060 } 1061 String[] types = codecInfo.getSupportedTypes(); 1062 for (String type : types) { 1063 if (type.equalsIgnoreCase(mediaType)) { 1064 boolean isOk = true; 1065 MediaCodecInfo.CodecCapabilities codecCapabilities = 1066 codecInfo.getCapabilitiesForType(type); 1067 if (formats != null) { 1068 for (MediaFormat format : formats) { 1069 if (!codecCapabilities.isFormatSupported(format)) { 1070 isOk = false; 1071 break; 1072 } 1073 } 1074 } 1075 if (features != null) { 1076 for (String feature : features) { 1077 if (!codecCapabilities.isFeatureSupported(feature)) { 1078 isOk = false; 1079 break; 1080 } 1081 } 1082 } 1083 if (isOk) listOfCodecs.add(codecInfo.getName()); 1084 } 1085 } 1086 } 1087 return listOfCodecs; 1088 } 1089 getWidth(MediaFormat format)1090 public static int getWidth(MediaFormat format) { 1091 int width = format.getInteger(MediaFormat.KEY_WIDTH, -1); 1092 if (format.containsKey("crop-left") && format.containsKey("crop-right")) { 1093 width = format.getInteger("crop-right") + 1 - format.getInteger("crop-left"); 1094 } 1095 return width; 1096 } 1097 getHeight(MediaFormat format)1098 public static int getHeight(MediaFormat format) { 1099 int height = format.getInteger(MediaFormat.KEY_HEIGHT, -1); 1100 if (format.containsKey("crop-top") && format.containsKey("crop-bottom")) { 1101 height = format.getInteger("crop-bottom") + 1 - format.getInteger("crop-top"); 1102 } 1103 return height; 1104 } 1105 loadByteArrayFromString(final String str)1106 public static byte[] loadByteArrayFromString(final String str) { 1107 if (str == null) { 1108 return null; 1109 } 1110 Pattern pattern = Pattern.compile("[0-9a-fA-F]{2}"); 1111 Matcher matcher = pattern.matcher(str); 1112 // allocate a large enough byte array first 1113 byte[] tempArray = new byte[str.length() / 2]; 1114 int i = 0; 1115 while (matcher.find()) { 1116 tempArray[i++] = (byte) Integer.parseInt(matcher.group(), 16); 1117 } 1118 return Arrays.copyOfRange(tempArray, 0, i); 1119 } 1120 byteArrayToHexString(byte[] bytes)1121 public static String byteArrayToHexString(byte[] bytes) { 1122 final char[] hexArray = 1123 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 1124 char[] hexChars = new char[bytes.length * 3]; 1125 int v; 1126 for (int j = 0; j < bytes.length; j++) { 1127 v = bytes[j] & 0xFF; 1128 hexChars[j * 3] = hexArray[v >>> 4]; 1129 hexChars[j * 3 + 1] = hexArray[v & 0x0F]; 1130 hexChars[j * 3 + 2] = ' '; 1131 } 1132 return new String(hexChars); 1133 } 1134 enqueueInput(int bufferIndex)1135 protected abstract void enqueueInput(int bufferIndex) throws IOException; 1136 dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info)1137 protected abstract void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info); 1138 1139 @Rule 1140 public final TestName mTestName = new TestName(); 1141 1142 @Rule 1143 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 1144 1145 @Before setUpCodecTestBase()1146 public void setUpCodecTestBase() { 1147 mTestConfig.setLength(0); 1148 mTestConfig.append("\n################## Test Details ####################\n"); 1149 mTestConfig.append("Test Name :- ").append(mTestName.getMethodName()).append("\n"); 1150 mTestConfig.append("Test Parameters :- ").append(mAllTestParams).append("\n"); 1151 if (mCodecName != null && mCodecName.startsWith(INVALID_CODEC)) { 1152 fail("no valid component available for current test \n" + mTestConfig); 1153 } 1154 } 1155 1156 @After tearDownCodecTestBase()1157 public void tearDownCodecTestBase() { 1158 mSurface = null; 1159 if (mActivity != null) { 1160 mActivity.finish(); 1161 mActivity = null; 1162 } 1163 if (mImageSurface != null) { 1164 mImageSurface.release(); 1165 mImageSurface = null; 1166 } 1167 if (mCodec != null) { 1168 mCodec.release(); 1169 mCodec = null; 1170 } 1171 } 1172 1173 // reusable portions of configureCodec(...) are handled here configureCodecCommon(MediaFormat format, boolean isAsync, boolean signalEOSWithLastFrame, boolean isEncoder, int flags)1174 protected void configureCodecCommon(MediaFormat format, boolean isAsync, 1175 boolean signalEOSWithLastFrame, boolean isEncoder, int flags) { 1176 resetContext(isAsync, signalEOSWithLastFrame); 1177 mAsyncHandle.setCallBack(mCodec, isAsync); 1178 1179 mTestEnv.setLength(0); 1180 mTestEnv.append("################### Test Environment #####################\n"); 1181 mTestEnv.append(String.format("Component under test :- %s \n", mCodecName)); 1182 mTestEnv.append("Format under test :- ").append(format).append("\n"); 1183 mTestEnv.append(String.format("Component operating in :- %s mode \n", 1184 (isAsync ? "asynchronous" : "synchronous"))); 1185 mTestEnv.append(String.format("Component received input eos :- %s \n", 1186 (signalEOSWithLastFrame ? "with full buffer" : "with empty buffer"))); 1187 mTestEnv.append(String.format("Component is :- %s \n", 1188 (isEncoder ? "encoder" : "decoder"))); 1189 mTestEnv.append("Component configure flags :- ").append(flags).append("\n"); 1190 } 1191 configureCodec(MediaFormat format, boolean isAsync, boolean cryptoCallAndSignalEosWithLastFrame, boolean isEncoder)1192 protected void configureCodec(MediaFormat format, boolean isAsync, 1193 boolean cryptoCallAndSignalEosWithLastFrame, boolean isEncoder) { 1194 configureCodec(format, isAsync, cryptoCallAndSignalEosWithLastFrame, 1195 isEncoder, 0 /* flags */); 1196 } 1197 configureCodec(MediaFormat format, boolean isAsync, boolean cryptoCallAndSignalEosWithLastFrame, boolean isEncoder, int flags)1198 protected void configureCodec(MediaFormat format, boolean isAsync, 1199 boolean cryptoCallAndSignalEosWithLastFrame, boolean isEncoder, int flags) { 1200 if (IS_AT_LEAST_R && ((flags & MediaCodec.CONFIGURE_FLAG_USE_BLOCK_MODEL) != 0)) { 1201 if (!isAsync) { 1202 throw new RuntimeException("Block model feature testing requires mode of operation" 1203 + " to be asynchronous"); 1204 } 1205 } 1206 1207 configureCodecCommon(format, isAsync, cryptoCallAndSignalEosWithLastFrame, isEncoder, 1208 flags); 1209 1210 // signalEOS flag has nothing to do with configure. We are using this flag to try all 1211 // available configure apis 1212 if (cryptoCallAndSignalEosWithLastFrame) { 1213 mCodec.configure(format, mSurface, null /* crypto */, 1214 (isEncoder ? MediaCodec.CONFIGURE_FLAG_ENCODE : 0) | flags); 1215 } else { 1216 mCodec.configure(format, mSurface, 1217 (isEncoder ? MediaCodec.CONFIGURE_FLAG_ENCODE : 0) | flags, 1218 null /* descrambler */); 1219 } 1220 if (ENABLE_LOGS) { 1221 Log.v(LOG_TAG, "codec configured"); 1222 } 1223 } 1224 configureCodecInDetachedMode(MediaFormat format, boolean isAsync, boolean cryptoCallAndSignalEosWithLastFrame)1225 protected void configureCodecInDetachedMode(MediaFormat format, boolean isAsync, 1226 boolean cryptoCallAndSignalEosWithLastFrame) { 1227 configureCodecCommon(format, isAsync, cryptoCallAndSignalEosWithLastFrame, 1228 false /* isEncoder */, MediaCodec.CONFIGURE_FLAG_DETACHED_SURFACE); 1229 1230 // signalEOS flag has nothing to do with configure. We are using this flag to try all 1231 // available configure apis 1232 if (cryptoCallAndSignalEosWithLastFrame) { 1233 mCodec.configure(format, null /* surface */, null /* crypto */, 1234 MediaCodec.CONFIGURE_FLAG_DETACHED_SURFACE); 1235 } else { 1236 mCodec.configure(format, null /* surface */, 1237 MediaCodec.CONFIGURE_FLAG_DETACHED_SURFACE, null /* descrambler */); 1238 } 1239 if (ENABLE_LOGS) { 1240 Log.v(LOG_TAG, "codec configured"); 1241 } 1242 } 1243 getOutputManager()1244 public OutputManager getOutputManager() { 1245 return mOutputBuff; 1246 } 1247 getOutputFormat()1248 public MediaFormat getOutputFormat() { 1249 return mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() : mOutFormat; 1250 } 1251 getOutputCount()1252 public int getOutputCount() { 1253 return mOutputCount; 1254 } 1255 flushCodec()1256 protected void flushCodec() { 1257 mCodec.flush(); 1258 // TODO(b/147576107): is it ok to clearQueues right away or wait for some signal 1259 mAsyncHandle.clearQueues(); 1260 mSawInputEOS = false; 1261 mSawOutputEOS = false; 1262 mInputCount = 0; 1263 mOutputCount = 0; 1264 mPrevOutputPts = Long.MIN_VALUE; 1265 if (ENABLE_LOGS) { 1266 Log.v(LOG_TAG, "codec flushed"); 1267 } 1268 } 1269 reConfigureCodec(MediaFormat format, boolean isAsync, boolean signalEOSWithLastFrame, boolean isEncoder)1270 protected void reConfigureCodec(MediaFormat format, boolean isAsync, 1271 boolean signalEOSWithLastFrame, boolean isEncoder) { 1272 /* TODO(b/147348711) */ 1273 if (false) mCodec.stop(); 1274 else mCodec.reset(); 1275 configureCodec(format, isAsync, signalEOSWithLastFrame, isEncoder); 1276 } 1277 resetContext(boolean isAsync, boolean signalEOSWithLastFrame)1278 protected void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) { 1279 mAsyncHandle.resetContext(); 1280 mIsCodecInAsyncMode = isAsync; 1281 mSawInputEOS = false; 1282 mSawOutputEOS = false; 1283 mSignalEOSWithLastFrame = signalEOSWithLastFrame; 1284 mInputCount = 0; 1285 mOutputCount = 0; 1286 mPrevOutputPts = Long.MIN_VALUE; 1287 mSignalledOutFormatChanged = false; 1288 } 1289 enqueueEOS(int bufferIndex)1290 protected void enqueueEOS(int bufferIndex) { 1291 if (!mSawInputEOS) { 1292 mCodec.queueInputBuffer(bufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); 1293 mSawInputEOS = true; 1294 if (ENABLE_LOGS) { 1295 Log.v(LOG_TAG, "Queued End of Stream"); 1296 } 1297 } 1298 } 1299 doWork(int frameLimit)1300 protected void doWork(int frameLimit) throws InterruptedException, IOException { 1301 int frameCount = 0; 1302 if (mIsCodecInAsyncMode) { 1303 // dequeue output after inputEOS is expected to be done in waitForAllOutputs() 1304 while (!mAsyncHandle.hasSeenError() && !mSawInputEOS && frameCount < frameLimit) { 1305 Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork(); 1306 if (element != null) { 1307 int bufferID = element.first; 1308 MediaCodec.BufferInfo info = element.second; 1309 if (info != null) { 1310 // <id, info> corresponds to output callback. Handle it accordingly 1311 dequeueOutput(bufferID, info); 1312 } else { 1313 // <id, null> corresponds to input callback. Handle it accordingly 1314 enqueueInput(bufferID); 1315 frameCount++; 1316 } 1317 } 1318 } 1319 } else { 1320 MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo(); 1321 // dequeue output after inputEOS is expected to be done in waitForAllOutputs() 1322 while (!mSawInputEOS && frameCount < frameLimit) { 1323 int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US); 1324 if (outputBufferId >= 0) { 1325 dequeueOutput(outputBufferId, outInfo); 1326 } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 1327 mOutFormat = mCodec.getOutputFormat(); 1328 mSignalledOutFormatChanged = true; 1329 } 1330 int inputBufferId = mCodec.dequeueInputBuffer(Q_DEQ_TIMEOUT_US); 1331 if (inputBufferId != -1) { 1332 enqueueInput(inputBufferId); 1333 frameCount++; 1334 } 1335 } 1336 } 1337 } 1338 queueEOS()1339 protected void queueEOS() throws InterruptedException { 1340 if (mIsCodecInAsyncMode) { 1341 while (!mAsyncHandle.hasSeenError() && !mSawInputEOS) { 1342 Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork(); 1343 if (element != null) { 1344 int bufferID = element.first; 1345 MediaCodec.BufferInfo info = element.second; 1346 if (info != null) { 1347 dequeueOutput(bufferID, info); 1348 } else { 1349 enqueueEOS(element.first); 1350 } 1351 } 1352 } 1353 } else { 1354 MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo(); 1355 while (!mSawInputEOS) { 1356 int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US); 1357 if (outputBufferId >= 0) { 1358 dequeueOutput(outputBufferId, outInfo); 1359 } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 1360 mOutFormat = mCodec.getOutputFormat(); 1361 mSignalledOutFormatChanged = true; 1362 } 1363 int inputBufferId = mCodec.dequeueInputBuffer(Q_DEQ_TIMEOUT_US); 1364 if (inputBufferId != -1) { 1365 enqueueEOS(inputBufferId); 1366 } 1367 } 1368 } 1369 } 1370 waitForAllOutputs()1371 protected void waitForAllOutputs() throws InterruptedException { 1372 if (mIsCodecInAsyncMode) { 1373 while (!mAsyncHandle.hasSeenError() && !mSawOutputEOS) { 1374 Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getOutput(); 1375 if (element != null) { 1376 dequeueOutput(element.first, element.second); 1377 } 1378 } 1379 } else { 1380 MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo(); 1381 while (!mSawOutputEOS) { 1382 int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US); 1383 if (outputBufferId >= 0) { 1384 dequeueOutput(outputBufferId, outInfo); 1385 } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 1386 mOutFormat = mCodec.getOutputFormat(); 1387 mSignalledOutFormatChanged = true; 1388 } 1389 } 1390 } 1391 validateTestState(); 1392 } 1393 validateTestState()1394 void validateTestState() { 1395 assertFalse("Encountered error in async mode. \n" + mTestConfig + mTestEnv 1396 + mAsyncHandle.getErrMsg(), mAsyncHandle.hasSeenError()); 1397 if (mInputCount > 0) { 1398 assertTrue(String.format("fed %d input frames, received no output frames \n", 1399 mInputCount) + mTestConfig + mTestEnv, mOutputCount > 0); 1400 } 1401 /*if (mInputCount == 0 && mInputCount != mOutputCount) { 1402 String msg = String.format("The number of output frames received is not same as number " 1403 + "of input frames queued. Output count is %d, Input count is %d \n", 1404 mOutputCount, mInputCount); 1405 // check the pts lists to see what frames are dropped, the below call is needed to 1406 // get useful error messages 1407 mOutputBuff.isOutPtsListIdenticalToInpPtsList(true); 1408 fail(msg + mTestConfig + mTestEnv + mOutputBuff.getErrMsg()); 1409 }*/ 1410 } 1411 insertHdrDynamicInfo(byte[] info)1412 protected void insertHdrDynamicInfo(byte[] info) { 1413 final Bundle params = new Bundle(); 1414 params.putByteArray(MediaFormat.KEY_HDR10_PLUS_INFO, info); 1415 mCodec.setParameters(params); 1416 } 1417 isFormatSimilar(MediaFormat inpFormat, MediaFormat outFormat)1418 public boolean isFormatSimilar(MediaFormat inpFormat, MediaFormat outFormat) { 1419 if (inpFormat == null || outFormat == null) return false; 1420 String inpMediaType = inpFormat.getString(MediaFormat.KEY_MIME); 1421 String outMediaType = outFormat.getString(MediaFormat.KEY_MIME); 1422 // not comparing input and output mediaTypes because for a codec, mediaType is raw on one 1423 // side and encoded type on the other 1424 if (outMediaType.startsWith("audio/")) { 1425 return (inpFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT, -1) 1426 == outFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT, -2)) 1427 && (inpFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE, -1) 1428 == outFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE, -2)) 1429 && inpMediaType.startsWith("audio/"); 1430 } else if (outMediaType.startsWith("video/")) { 1431 return getWidth(inpFormat) == getWidth(outFormat) 1432 && getHeight(inpFormat) == getHeight(outFormat) 1433 && inpMediaType.startsWith("video/"); 1434 } 1435 return true; 1436 } 1437 validateMetrics(String codec)1438 protected PersistableBundle validateMetrics(String codec) { 1439 PersistableBundle metrics = mCodec.getMetrics(); 1440 assertNotNull("error! MediaCodec.getMetrics() returns null \n" + mTestConfig + mTestEnv, 1441 metrics); 1442 assertEquals("error! metrics#MetricsConstants.CODEC is not as expected \n" + mTestConfig 1443 + mTestEnv, metrics.getString(MediaCodec.MetricsConstants.CODEC), codec); 1444 assertEquals("error! metrics#MetricsConstants.MODE is not as expected \n" + mTestConfig 1445 + mTestEnv, mIsAudio ? MediaCodec.MetricsConstants.MODE_AUDIO : 1446 MediaCodec.MetricsConstants.MODE_VIDEO, 1447 metrics.getString(MediaCodec.MetricsConstants.MODE)); 1448 return metrics; 1449 } 1450 validateMetrics(String codec, MediaFormat format)1451 protected PersistableBundle validateMetrics(String codec, MediaFormat format) { 1452 PersistableBundle metrics = validateMetrics(codec); 1453 if (mIsVideo) { 1454 assertEquals("error! metrics#MetricsConstants.WIDTH is not as expected\n" + mTestConfig 1455 + mTestEnv, metrics.getInt(MediaCodec.MetricsConstants.WIDTH), 1456 getWidth(format)); 1457 assertEquals("error! metrics#MetricsConstants.HEIGHT is not as expected\n" + mTestConfig 1458 + mTestEnv, metrics.getInt(MediaCodec.MetricsConstants.HEIGHT), 1459 getHeight(format)); 1460 } 1461 assertEquals("error! metrics#MetricsConstants.SECURE is not as expected\n" + mTestConfig 1462 + mTestEnv, 0, metrics.getInt(MediaCodec.MetricsConstants.SECURE)); 1463 return metrics; 1464 } 1465 validateColorAspects(MediaFormat fmt, int range, int standard, int transfer)1466 public void validateColorAspects(MediaFormat fmt, int range, int standard, int transfer) { 1467 int colorRange = fmt.getInteger(MediaFormat.KEY_COLOR_RANGE, UNSPECIFIED); 1468 int colorStandard = fmt.getInteger(MediaFormat.KEY_COLOR_STANDARD, UNSPECIFIED); 1469 int colorTransfer = fmt.getInteger(MediaFormat.KEY_COLOR_TRANSFER, UNSPECIFIED); 1470 if (range > UNSPECIFIED) { 1471 assertEquals("error! color range mismatch \n" + mTestConfig + mTestEnv, range, 1472 colorRange); 1473 } 1474 if (standard > UNSPECIFIED) { 1475 assertEquals("error! color standard mismatch \n" + mTestConfig + mTestEnv, standard, 1476 colorStandard); 1477 } 1478 if (transfer > UNSPECIFIED) { 1479 assertEquals("error! color transfer mismatch \n" + mTestConfig + mTestEnv, transfer, 1480 colorTransfer); 1481 } 1482 } 1483 validateHDRInfo(String hdrInfoKey, ByteBuffer hdrInfoRef, ByteBuffer hdrInfoTest, Long framePts)1484 protected void validateHDRInfo(String hdrInfoKey, ByteBuffer hdrInfoRef, ByteBuffer hdrInfoTest, 1485 Long framePts) { 1486 if (!hdrInfoRef.equals(hdrInfoTest)) { 1487 StringBuilder msg = new StringBuilder( 1488 "################### Error Details #####################\n"); 1489 byte[] ref = new byte[hdrInfoRef.capacity()]; 1490 hdrInfoRef.get(ref); 1491 hdrInfoRef.rewind(); 1492 byte[] test = new byte[hdrInfoTest.capacity()]; 1493 hdrInfoTest.get(test); 1494 hdrInfoTest.rewind(); 1495 msg.append("ref info :- \n"); 1496 for (byte b : ref) { 1497 msg.append(String.format("%2x ", b)); 1498 } 1499 msg.append("\ntest info :- \n"); 1500 for (byte b : test) { 1501 msg.append(String.format("%2x ", b)); 1502 } 1503 fail("Frame pts " + framePts + ": error! mismatch seen between ref and test info of " 1504 + hdrInfoKey + "\n" + mTestConfig + mTestEnv + msg); 1505 } 1506 } 1507 validateHDRInfo(MediaFormat fmt, String hdrInfoKey, ByteBuffer hdrInfoRef, Long framePts)1508 protected void validateHDRInfo(MediaFormat fmt, String hdrInfoKey, ByteBuffer hdrInfoRef, 1509 Long framePts) { 1510 ByteBuffer hdrInfo = fmt.getByteBuffer(hdrInfoKey, null); 1511 assertNotNull("error! no " + hdrInfoKey + " present in format : " + fmt + "\n " 1512 + mTestConfig + mTestEnv, hdrInfo); 1513 validateHDRInfo(hdrInfoKey, hdrInfoRef, hdrInfo, framePts); 1514 } 1515 setUpSurface(CodecTestActivity activity)1516 protected void setUpSurface(CodecTestActivity activity) throws InterruptedException { 1517 activity.waitTillSurfaceIsCreated(); 1518 mSurface = activity.getSurface(); 1519 assertNotNull("Surface created is null \n" + mTestConfig + mTestEnv, mSurface); 1520 assertTrue("Surface created is invalid \n" + mTestConfig + mTestEnv, mSurface.isValid()); 1521 } 1522 setUpSurface(int width, int height, int format, int maxImages, int surfaceId, Function<ImageSurface.ImageAndAttributes, Boolean> predicate)1523 protected void setUpSurface(int width, int height, int format, int maxImages, 1524 int surfaceId, Function<ImageSurface.ImageAndAttributes, Boolean> predicate) { 1525 mImageSurface.createSurface(width, height, format, maxImages, surfaceId, predicate); 1526 mSurface = mImageSurface.getSurface(); 1527 assertNotNull("Surface created is null \n" + mTestConfig + mTestEnv, mSurface); 1528 assertTrue("Surface created is invalid \n" + mTestConfig + mTestEnv, mSurface.isValid()); 1529 } 1530 } 1531