1 /* 2 * Copyright (C) 2024 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.cts; 18 19 import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_MultipleFrames; 20 import static android.mediav2.common.cts.CodecTestBase.SupportClass.CODEC_OPTIONAL; 21 import static android.mediav2.cts.CodecDecoderMultiAccessUnitTest.RECONFIG_FILE_MEDIA_TYPE_MAP; 22 import static android.mediav2.cts.CodecDecoderMultiAccessUnitTest.exhaustiveArgsList; 23 24 import static org.junit.Assert.fail; 25 26 import android.media.MediaCodec; 27 import android.media.MediaExtractor; 28 import android.media.MediaFormat; 29 import android.mediav2.common.cts.CodecDecoderBlockModelMultiAccessUnitTestBase; 30 import android.mediav2.common.cts.CodecDecoderBlockModelTestBase; 31 import android.mediav2.common.cts.CodecDecoderTestBase; 32 import android.mediav2.common.cts.OutputManager; 33 import android.os.Build; 34 import android.platform.test.annotations.AppModeFull; 35 import android.platform.test.annotations.RequiresFlagsEnabled; 36 37 import androidx.test.filters.LargeTest; 38 import androidx.test.filters.SdkSuppress; 39 40 import com.android.compatibility.common.util.ApiTest; 41 import com.android.media.codec.flags.Flags; 42 43 import org.junit.Before; 44 import org.junit.Ignore; 45 import org.junit.Test; 46 import org.junit.runner.RunWith; 47 import org.junit.runners.Parameterized; 48 49 import java.io.IOException; 50 import java.util.ArrayList; 51 import java.util.Collection; 52 53 /** 54 * Tests audio decoders support for feature MultipleFrames in block model mode. 55 * <p> 56 * MultipleFrames feature is optional and is not required to support by all components. If a 57 * component supports this feature, then multiple access units are grouped together (demarcated 58 * with access unit offsets and timestamps) are sent as input to the component. The components 59 * processes the input sent and returns output in a large enough buffer (demarcated with access 60 * unit offsets and timestamps). The number of access units that can be grouped is dependent on 61 * format keys, KEY_MAX_INPUT_SIZE, KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE. 62 * <p> 63 * The test runs the component in MultipleFrames block model mode and normal mode and expects same 64 * output for a given input. 65 **/ 66 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = "VanillaIceCream") 67 @AppModeFull(reason = "Instant apps cannot access the SD card") 68 @RequiresFlagsEnabled(Flags.FLAG_LARGE_AUDIO_FRAME) 69 @RunWith(Parameterized.class) 70 public class CodecDecoderBlockModelMultiAccessUnitTest 71 extends CodecDecoderBlockModelMultiAccessUnitTestBase { 72 private static final String LOG_TAG = 73 CodecDecoderBlockModelMultiAccessUnitTest.class.getSimpleName(); 74 private static final String MEDIA_DIR = WorkDir.getMediaDirString(); 75 private static final int[][] OUT_SIZE_IN_MS = { 76 {1000, 250}, // max out size, threshold batch out size 77 {1000, 100}, 78 {500, 20}, 79 {100, 100}, 80 {40, 100} 81 }; 82 83 private final String mReconfigFile; 84 85 @Parameterized.Parameters(name = "{index}_{0}_{1}") input()86 public static Collection<Object[]> input() { 87 return prepareParamList(exhaustiveArgsList, false, true, false, true, ComponentClass.ALL, 88 new String[]{FEATURE_MultipleFrames}); 89 } 90 CodecDecoderBlockModelMultiAccessUnitTest(String decoder, String mediaType, String testFile, String allTestParams)91 public CodecDecoderBlockModelMultiAccessUnitTest(String decoder, String mediaType, 92 String testFile, String allTestParams) { 93 super(decoder, mediaType, MEDIA_DIR + testFile, allTestParams); 94 mReconfigFile = MEDIA_DIR + RECONFIG_FILE_MEDIA_TYPE_MAP.get(mediaType); 95 } 96 97 @Before setUp()98 public void setUp() throws IOException { 99 MediaFormat format = setUpSource(mTestFile); 100 mExtractor.release(); 101 ArrayList<MediaFormat> formatList = new ArrayList<>(); 102 formatList.add(format); 103 checkFormatSupport(mCodecName, mMediaType, false, formatList, null, CODEC_OPTIONAL); 104 } 105 106 /** 107 * Verifies if the component under test can decode the test file correctly in multiple frame 108 * block model mode. The decoding happens in asynchronous mode with eos flag signalled with 109 * last compressed frame. The test verifies if the component / framework output is consistent 110 * with single access unit normal mode and single access unit block model mode. 111 * <p> 112 * Check description of class {@link CodecDecoderBlockModelMultiAccessUnitTest} 113 */ 114 @ApiTest(apis = {"android.media.MediaFormat#KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE", 115 "android.media.MediaFormat#KEY_BUFFER_BATCH_THRESHOLD_OUTPUT_SIZE", 116 "android.media.MediaCodec.Callback#onOutputBuffersAvailable", 117 "android.media.MediaCodec#CONFIGURE_FLAG_USE_BLOCK_MODEL"}) 118 @LargeTest 119 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testSimpleDecode()120 public void testSimpleDecode() throws IOException, InterruptedException { 121 CodecDecoderTestBase cdtb = new CodecDecoderTestBase(mCodecName, mMediaType, null, 122 mAllTestParams); 123 cdtb.decodeToMemory(mTestFile, mCodecName, 0, MediaExtractor.SEEK_TO_CLOSEST_SYNC, 124 Integer.MAX_VALUE); 125 OutputManager ref = cdtb.getOutputManager(); 126 127 CodecDecoderBlockModelTestBase cdbmtb = new CodecDecoderBlockModelTestBase( 128 mCodecName, mMediaType, null, mAllTestParams); 129 OutputManager test = new OutputManager(ref.getSharedErrorLogs()); 130 cdbmtb.decodeToMemory(mTestFile, mCodecName, test, 0, 131 MediaExtractor.SEEK_TO_CLOSEST_SYNC, Integer.MAX_VALUE); 132 if (!ref.equals(test)) { 133 fail("Output in block model mode is not same as output in normal mode. \n" 134 + mTestConfig + mTestEnv + test.getErrMsg()); 135 } 136 137 boolean[] boolStates = {true, false}; 138 OutputManager testA = new OutputManager(ref.getSharedErrorLogs()); 139 OutputManager testB = new OutputManager(ref.getSharedErrorLogs()); 140 mSaveToMem = true; 141 MediaFormat format = setUpSource(mTestFile); 142 int maxSampleSize = getMaxSampleSizeForMediaType(mTestFile, mMediaType); 143 mCodec = MediaCodec.createByCodecName(mCodecName); 144 for (int[] outSizeInMs : OUT_SIZE_IN_MS) { 145 configureKeysForLargeAudioBlockModelFrameMode(format, maxSampleSize, outSizeInMs[0], 146 outSizeInMs[1]); 147 for (boolean signalEosWithLastFrame : boolStates) { 148 mOutputBuff = signalEosWithLastFrame ? testA : testB; 149 mOutputBuff.reset(); 150 configureCodec(format, true, signalEosWithLastFrame, false); 151 mMaxInputLimitMs = outSizeInMs[0]; 152 mCodec.start(); 153 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC); 154 doWork(Integer.MAX_VALUE); 155 queueEOS(); 156 waitForAllOutputs(); 157 mCodec.reset(); 158 if (!ref.equalsByteOutput(mOutputBuff)) { 159 fail("Output of decoder component when fed with multiple access units in single" 160 + " enqueue call differs from output received when each access unit is" 161 + "fed separately. \n" + mTestConfig + mTestEnv 162 + mOutputBuff.getErrMsg()); 163 } 164 } 165 if (!testA.equals(testB)) { 166 fail("Output of decoder component is not consistent across runs. \n" + mTestConfig 167 + mTestEnv + testB.getErrMsg()); 168 } 169 } 170 mCodec.release(); 171 mExtractor.release(); 172 } 173 174 /** 175 * Verifies component and framework behaviour to flush API when the codec is operating in 176 * multiple frame block model mode. The test verifies if the component / framework output 177 * is consistent with single access unit normal mode. 178 * <p> 179 * While the component is decoding the test clip, mediacodec flush() is called. The flush API 180 * is called at various points :- 181 * <ul> 182 * <li>In running state, after queueing n frames.</li> 183 * <li>In eos state.</li> 184 * </ul> 185 * <p> 186 * In all situations (pre-flush or post-flush), the test expects the output timestamps to be 187 * strictly increasing. The flush call makes the output received non-deterministic even for a 188 * given input. Hence, besides timestamp checks, no additional validation is done for outputs 189 * received before flush. Post flush, the decode begins from a sync frame. So the test 190 * expects consistent output and this needs to be identical to the reference 191 * (single access unit mode) 192 * <p> 193 */ 194 @ApiTest(apis = {"android.media.MediaFormat#KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE", 195 "android.media.MediaFormat#KEY_BUFFER_BATCH_THRESHOLD_OUTPUT_SIZE", 196 "android.media.MediaCodec.Callback#onOutputBuffersAvailable", 197 "android.media.MediaCodec#flush"}) 198 @LargeTest 199 @Ignore("TODO(b/147576107)") 200 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testFlush()201 public void testFlush() throws IOException, InterruptedException { 202 MediaFormat format = setUpSource(mTestFile); 203 final long pts = 250000; 204 mExtractor.release(); 205 OutputManager ref = null, test; 206 if (isMediaTypeOutputUnAffectedBySeek(mMediaType)) { 207 CodecDecoderTestBase cdtb = new CodecDecoderTestBase(mCodecName, mMediaType, null, 208 mAllTestParams); 209 cdtb.decodeToMemory(mTestFile, mCodecName, pts, MediaExtractor.SEEK_TO_CLOSEST_SYNC, 210 Integer.MAX_VALUE); 211 ref = cdtb.getOutputManager(); 212 test = new OutputManager(ref.getSharedErrorLogs()); 213 } else { 214 test = new OutputManager(); 215 } 216 217 mOutputBuff = test; 218 setUpSource(mTestFile); 219 int maxSampleSize = getMaxSampleSizeForMediaType(mTestFile, mMediaType); 220 configureKeysForLargeAudioBlockModelFrameMode(format, maxSampleSize, OUT_SIZE_IN_MS[0][0], 221 OUT_SIZE_IN_MS[0][1]); 222 mMaxInputLimitMs = OUT_SIZE_IN_MS[0][0]; 223 mCodec = MediaCodec.createByCodecName(mCodecName); 224 test.reset(); 225 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC); 226 configureCodec(format, true, true, false); 227 228 mCodec.start(); 229 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC); 230 test.reset(); 231 doWork(23); 232 if (!test.isPtsStrictlyIncreasing(mPrevOutputPts)) { 233 fail("Output timestamps are not strictly increasing \n" + mTestConfig + mTestEnv 234 + test.getErrMsg()); 235 } 236 237 /* test flush in running state */ 238 flushCodec(); 239 mCodec.start(); 240 mSaveToMem = true; 241 test.reset(); 242 mExtractor.seekTo(pts, MediaExtractor.SEEK_TO_CLOSEST_SYNC); 243 doWork(Integer.MAX_VALUE); 244 queueEOS(); 245 waitForAllOutputs(); 246 if (ref != null && !ref.equalsByteOutput(test)) { 247 fail("Decoder output is not consistent across runs \n" + mTestConfig + mTestEnv 248 + test.getErrMsg()); 249 } 250 251 /* test flush in eos state */ 252 flushCodec(); 253 mCodec.start(); 254 test.reset(); 255 mExtractor.seekTo(pts, MediaExtractor.SEEK_TO_CLOSEST_SYNC); 256 doWork(Integer.MAX_VALUE); 257 queueEOS(); 258 waitForAllOutputs(); 259 mCodec.stop(); 260 if (ref != null && !ref.equalsByteOutput(test)) { 261 fail("Decoder output is not consistent across runs \n" + mTestConfig + mTestEnv 262 + test.getErrMsg()); 263 } 264 265 mSaveToMem = false; 266 mCodec.release(); 267 mExtractor.release(); 268 } 269 270 /** 271 * Verifies component and framework behaviour for format change in multiple frame block model 272 * mode. The format change is not seamless (AdaptivePlayback) but done via reconfigure. 273 * <p> 274 * The reconfiguring of media codec component happens at various points :- 275 * <ul> 276 * <li>After initial configuration (stopped state).</li> 277 * <li>In running state, before queueing any input.</li> 278 * <li>In running state, after queuing n frames.</li> 279 * <li>In eos state.</li> 280 * </ul> 281 * In eos state, 282 * <ul> 283 * <li>reconfigure with same clip.</li> 284 * <li>reconfigure with different clip (different resolution).</li> 285 * </ul> 286 * <p> 287 * In all situations (pre-reconfigure or post-reconfigure), the test expects the output 288 * timestamps to be strictly increasing. The reconfigure call makes the output received 289 * non-deterministic even for a given input. Hence, besides timestamp checks, no additional 290 * validation is done for outputs received before reconfigure. Post reconfigure, the decode 291 * begins from a sync frame. So the test expects consistent output and this needs to be 292 * identical to the reference (single access unit mode). 293 * <p> 294 */ 295 @ApiTest(apis = {"android.media.MediaFormat#KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE", 296 "android.media.MediaFormat#KEY_BUFFER_BATCH_THRESHOLD_OUTPUT_SIZE", 297 "android.media.MediaCodec.Callback#onOutputBuffersAvailable", 298 "android.media.MediaCodec#configure"}) 299 @LargeTest 300 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testReconfigure()301 public void testReconfigure() throws IOException, InterruptedException { 302 MediaFormat format = setUpSource(mTestFile); 303 mExtractor.release(); 304 MediaFormat newFormat = setUpSource(mReconfigFile); 305 mExtractor.release(); 306 ArrayList<MediaFormat> formatList = new ArrayList<>(); 307 formatList.add(newFormat); 308 checkFormatSupport(mCodecName, mMediaType, false, formatList, null, CODEC_OPTIONAL); 309 310 CodecDecoderTestBase cdtbA = new CodecDecoderTestBase(mCodecName, mMediaType, null, 311 mAllTestParams); 312 cdtbA.decodeToMemory(mTestFile, mCodecName, 0, MediaExtractor.SEEK_TO_CLOSEST_SYNC, 313 Integer.MAX_VALUE); 314 OutputManager ref = cdtbA.getOutputManager(); 315 OutputManager test = new OutputManager(ref.getSharedErrorLogs()); 316 317 CodecDecoderTestBase cdtbB = new CodecDecoderTestBase(mCodecName, mMediaType, null, 318 mAllTestParams); 319 cdtbB.decodeToMemory(mReconfigFile, mCodecName, 0, MediaExtractor.SEEK_TO_CLOSEST_SYNC, 320 Integer.MAX_VALUE); 321 OutputManager configRef = cdtbB.getOutputManager(); 322 OutputManager configTest = new OutputManager(configRef.getSharedErrorLogs()); 323 324 int maxSampleSize = getMaxSampleSizeForMediaType(mTestFile, mMediaType); 325 configureKeysForLargeAudioBlockModelFrameMode(format, maxSampleSize, OUT_SIZE_IN_MS[0][0], 326 OUT_SIZE_IN_MS[0][1]); 327 mMaxInputLimitMs = OUT_SIZE_IN_MS[0][0]; 328 mCodec = MediaCodec.createByCodecName(mCodecName); 329 mOutputBuff = test; 330 setUpSource(mTestFile); 331 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC); 332 configureCodec(format, true, true, false); 333 334 /* test reconfigure in stopped state */ 335 reConfigureCodec(format, true, false, false); 336 mCodec.start(); 337 338 /* test reconfigure in running state before queuing input */ 339 reConfigureCodec(format, true, false, false); 340 mCodec.start(); 341 doWork(23); 342 343 /* test reconfigure codec in running state */ 344 reConfigureCodec(format, true, true, false); 345 mCodec.start(); 346 mSaveToMem = true; 347 test.reset(); 348 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC); 349 doWork(Integer.MAX_VALUE); 350 queueEOS(); 351 waitForAllOutputs(); 352 mCodec.stop(); 353 if (!ref.equalsByteOutput(test)) { 354 fail("Decoder output is not consistent across runs \n" + mTestConfig + mTestEnv 355 + test.getErrMsg()); 356 } 357 358 /* test reconfigure codec at eos state */ 359 reConfigureCodec(format, true, false, false); 360 mCodec.start(); 361 test.reset(); 362 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC); 363 doWork(Integer.MAX_VALUE); 364 queueEOS(); 365 waitForAllOutputs(); 366 mCodec.stop(); 367 if (!ref.equalsByteOutput(test)) { 368 fail("Decoder output is not consistent across runs \n" + mTestConfig + mTestEnv 369 + test.getErrMsg()); 370 } 371 mExtractor.release(); 372 373 /* test reconfigure codec for new file */ 374 maxSampleSize = getMaxSampleSizeForMediaType(mReconfigFile, mMediaType); 375 configureKeysForLargeAudioBlockModelFrameMode(newFormat, maxSampleSize, 376 OUT_SIZE_IN_MS[0][0], OUT_SIZE_IN_MS[0][1]); 377 mOutputBuff = configTest; 378 setUpSource(mReconfigFile); 379 reConfigureCodec(newFormat, true, false, false); 380 mCodec.start(); 381 configTest.reset(); 382 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC); 383 doWork(Integer.MAX_VALUE); 384 queueEOS(); 385 waitForAllOutputs(); 386 mCodec.stop(); 387 if (!configRef.equalsByteOutput(configTest)) { 388 fail("Decoder output is not consistent across runs \n" + mTestConfig + mTestEnv 389 + configTest.getErrMsg()); 390 } 391 mSaveToMem = false; 392 mExtractor.release(); 393 mCodec.release(); 394 } 395 } 396