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.cts; 18 19 import android.media.MediaCodec; 20 import android.media.MediaCodecInfo; 21 import android.media.MediaExtractor; 22 import android.media.MediaFormat; 23 import android.util.Log; 24 import android.view.Surface; 25 26 import androidx.test.filters.LargeTest; 27 import androidx.test.filters.SmallTest; 28 29 import org.junit.Assume; 30 import org.junit.Ignore; 31 import org.junit.Test; 32 import org.junit.runner.RunWith; 33 import org.junit.runners.Parameterized; 34 35 import java.io.File; 36 import java.io.FileInputStream; 37 import java.io.IOException; 38 import java.nio.ByteBuffer; 39 import java.nio.ByteOrder; 40 import java.nio.channels.FileChannel; 41 import java.util.ArrayList; 42 import java.util.Arrays; 43 import java.util.Collection; 44 import java.util.List; 45 46 import static org.junit.Assert.assertEquals; 47 import static org.junit.Assert.assertTrue; 48 49 /** 50 * Validate decode functionality of listed decoder components 51 * 52 * The test aims to test all decoders advertised in MediaCodecList. Hence we are not using 53 * MediaCodecList#findDecoderForFormat to create codec. Further, it can so happen that the 54 * test clip chosen is not supported by component (codecCapabilities.isFormatSupported() 55 * fails), then it is better to replace the clip but not skip testing the component. The idea 56 * of these tests are not to cover CDD requirements but to test components and their plugins 57 */ 58 @RunWith(Parameterized.class) 59 public class CodecDecoderTest extends CodecDecoderTestBase { 60 private static final String LOG_TAG = CodecDecoderTest.class.getSimpleName(); 61 private static final float RMS_ERROR_TOLERANCE = 1.05f; // 5% 62 63 private final String mRefFile; 64 private final String mReconfigFile; 65 private final float mRmsError; 66 private final long mRefCRC; 67 CodecDecoderTest(String decoder, String mime, String testFile, String refFile, String reconfigFile, float rmsError, long refCRC)68 public CodecDecoderTest(String decoder, String mime, String testFile, String refFile, 69 String reconfigFile, float rmsError, long refCRC) { 70 super(decoder, mime, testFile); 71 mRefFile = refFile; 72 mReconfigFile = reconfigFile; 73 mRmsError = rmsError; 74 mRefCRC = refCRC; 75 } 76 setUpAudioReference(String file)77 static short[] setUpAudioReference(String file) throws IOException { 78 File refFile = new File(file); 79 short[] refData; 80 try (FileInputStream refStream = new FileInputStream(refFile)) { 81 FileChannel fileChannel = refStream.getChannel(); 82 int length = (int) refFile.length(); 83 ByteBuffer refBuffer = ByteBuffer.allocate(length); 84 refBuffer.order(ByteOrder.LITTLE_ENDIAN); 85 fileChannel.read(refBuffer); 86 refData = new short[length / 2]; 87 refBuffer.position(0); 88 for (int i = 0; i < length / 2; i++) { 89 refData[i] = refBuffer.getShort(); 90 } 91 } 92 return refData; 93 } 94 createSubFrames(ByteBuffer buffer, int sfCount)95 private ArrayList<MediaCodec.BufferInfo> createSubFrames(ByteBuffer buffer, int sfCount) { 96 int size = (int) mExtractor.getSampleSize(); 97 if (size < 0) return null; 98 mExtractor.readSampleData(buffer, 0); 99 long pts = mExtractor.getSampleTime(); 100 int flags = mExtractor.getSampleFlags(); 101 if (size < sfCount) sfCount = size; 102 ArrayList<MediaCodec.BufferInfo> list = new ArrayList<>(); 103 int offset = 0; 104 for (int i = 0; i < sfCount; i++) { 105 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 106 info.offset = offset; 107 info.presentationTimeUs = pts; 108 info.flags = 0; 109 if ((flags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) { 110 info.flags |= MediaCodec.BUFFER_FLAG_KEY_FRAME; 111 } 112 if ((flags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) { 113 info.flags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME; 114 } 115 if (i != sfCount - 1) { 116 info.size = size / sfCount; 117 info.flags |= MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME; 118 } else { 119 info.size = size - offset; 120 } 121 list.add(info); 122 offset += info.size; 123 } 124 return list; 125 } 126 127 @Parameterized.Parameters(name = "{index}({0}_{1})") input()128 public static Collection<Object[]> input() { 129 final boolean isEncoder = false; 130 final boolean needAudio = true; 131 final boolean needVideo = true; 132 // mime, testClip, referenceClip, reconfigureTestClip, refRmsError, refCRC32 133 final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{ 134 {MediaFormat.MIMETYPE_AUDIO_MPEG, "bbb_1ch_8kHz_lame_cbr.mp3", 135 "bbb_1ch_8kHz_s16le.raw", "bbb_2ch_44kHz_lame_vbr.mp3", 91.022f, -1L}, 136 {MediaFormat.MIMETYPE_AUDIO_MPEG, "bbb_2ch_44kHz_lame_cbr.mp3", 137 "bbb_2ch_44kHz_s16le.raw", "bbb_1ch_16kHz_lame_vbr.mp3", 103.60f, -1L}, 138 {MediaFormat.MIMETYPE_AUDIO_AMR_WB, "bbb_1ch_16kHz_16kbps_amrwb.3gp", 139 "bbb_1ch_16kHz_s16le.raw", "bbb_1ch_16kHz_23kbps_amrwb.3gp", 2393.598f, 140 -1L}, 141 {MediaFormat.MIMETYPE_AUDIO_AMR_NB, "bbb_1ch_8kHz_10kbps_amrnb.3gp", 142 "bbb_1ch_8kHz_s16le.raw", "bbb_1ch_8kHz_8kbps_amrnb.3gp", -1.0f, -1L}, 143 {MediaFormat.MIMETYPE_AUDIO_FLAC, "bbb_1ch_16kHz_flac.mka", 144 "bbb_1ch_16kHz_s16le.raw", "bbb_2ch_44kHz_flac.mka", 0.0f, -1L}, 145 {MediaFormat.MIMETYPE_AUDIO_FLAC, "bbb_2ch_44kHz_flac.mka", 146 "bbb_2ch_44kHz_s16le.raw", "bbb_1ch_16kHz_flac.mka", 0.0f, -1L}, 147 {MediaFormat.MIMETYPE_AUDIO_RAW, "bbb_1ch_16kHz.wav", "bbb_1ch_16kHz_s16le.raw", 148 "bbb_2ch_44kHz.wav", 0.0f, -1L}, 149 {MediaFormat.MIMETYPE_AUDIO_RAW, "bbb_2ch_44kHz.wav", "bbb_2ch_44kHz_s16le.raw", 150 "bbb_1ch_16kHz.wav", 0.0f, -1L}, 151 {MediaFormat.MIMETYPE_AUDIO_G711_ALAW, "bbb_1ch_8kHz_alaw.wav", 152 "bbb_1ch_8kHz_s16le.raw", "bbb_2ch_8kHz_alaw.wav", 23.08678f, -1L}, 153 {MediaFormat.MIMETYPE_AUDIO_G711_MLAW, "bbb_1ch_8kHz_mulaw.wav", 154 "bbb_1ch_8kHz_s16le.raw", "bbb_2ch_8kHz_mulaw.wav", 24.4131f, -1L}, 155 {MediaFormat.MIMETYPE_AUDIO_MSGSM, "bbb_1ch_8kHz_gsm.wav", 156 "bbb_1ch_8kHz_s16le.raw", "bbb_1ch_8kHz_gsm.wav", 946.02698f, -1L}, 157 {MediaFormat.MIMETYPE_AUDIO_VORBIS, "bbb_1ch_16kHz_vorbis.mka", 158 "bbb_1ch_8kHz_s16le.raw", "bbb_2ch_44kHz_vorbis.mka", -1.0f, -1L}, 159 {MediaFormat.MIMETYPE_AUDIO_OPUS, "bbb_2ch_48kHz_opus.mka", 160 "bbb_2ch_48kHz_s16le.raw", "bbb_1ch_48kHz_opus.mka", -1.0f, -1L}, 161 {MediaFormat.MIMETYPE_AUDIO_AAC, "bbb_1ch_16kHz_aac.mp4", 162 "bbb_1ch_16kHz_s16le.raw", "bbb_2ch_44kHz_aac.mp4", -1.0f, -1L}, 163 {MediaFormat.MIMETYPE_VIDEO_MPEG2, "bbb_340x280_768kbps_30fps_mpeg2.mp4", null, 164 "bbb_520x390_1mbps_30fps_mpeg2.mp4", -1.0f, -1L}, 165 {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_340x280_768kbps_30fps_avc.mp4", null, 166 "bbb_520x390_1mbps_30fps_avc.mp4", -1.0f, 1746312400L}, 167 {MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_520x390_1mbps_30fps_hevc.mp4", null, 168 "bbb_340x280_768kbps_30fps_hevc.mp4", -1.0f, 3061322606L}, 169 {MediaFormat.MIMETYPE_VIDEO_MPEG4, "bbb_128x96_64kbps_12fps_mpeg4.mp4", 170 null, "bbb_176x144_192kbps_15fps_mpeg4.mp4", -1.0f, -1L}, 171 {MediaFormat.MIMETYPE_VIDEO_H263, "bbb_176x144_128kbps_15fps_h263.3gp", 172 null, "bbb_176x144_192kbps_10fps_h263.3gp", -1.0f, -1L}, 173 {MediaFormat.MIMETYPE_VIDEO_VP8, "bbb_340x280_768kbps_30fps_vp8.webm", null, 174 "bbb_520x390_1mbps_30fps_vp8.webm", -1.0f, 2030620796L}, 175 {MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_340x280_768kbps_30fps_vp9.webm", null, 176 "bbb_520x390_1mbps_30fps_vp9.webm", -1.0f, 4122701060L}, 177 {MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_340x280_768kbps_30fps_av1.mp4", null, 178 "bbb_520x390_1mbps_30fps_av1.mp4", -1.0f, 400672933L}, 179 }); 180 return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, true); 181 } 182 nativeTestSimpleDecode(String decoder, Surface surface, String mime, String testFile, String refFile, float rmsError, long checksum)183 private native boolean nativeTestSimpleDecode(String decoder, Surface surface, String mime, 184 String testFile, String refFile, float rmsError, long checksum); 185 verify(OutputManager outBuff, String refFile, float rmsError, long refCRC)186 static void verify(OutputManager outBuff, String refFile, float rmsError, long refCRC) 187 throws IOException { 188 if (rmsError >= 0) { 189 short[] refData = setUpAudioReference(mInpPrefix + refFile); 190 float currError = outBuff.getRmsError(refData); 191 float errMargin = rmsError * RMS_ERROR_TOLERANCE; 192 assertTrue(String.format("%s rms error too high ref/exp/got %f/%f/%f", refFile, 193 rmsError, errMargin, currError), currError <= errMargin); 194 } else if (refCRC >= 0) { 195 assertEquals("checksum mismatch", refCRC, outBuff.getCheckSumImage()); 196 } 197 } 198 199 /** 200 * Tests decoder for combinations: 201 * 1. Codec Sync Mode, Signal Eos with Last frame 202 * 2. Codec Sync Mode, Signal Eos Separately 203 * 3. Codec Async Mode, Signal Eos with Last frame 204 * 4. Codec Async Mode, Signal Eos Separately 205 * In all these scenarios, Timestamp ordering is verified, For audio the Rms of output has to be 206 * within the allowed tolerance. The output has to be consistent (not flaky) in all runs. 207 */ 208 @LargeTest 209 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testSimpleDecode()210 public void testSimpleDecode() throws IOException, InterruptedException { 211 MediaFormat format = setUpSource(mTestFile); 212 boolean[] boolStates = {true, false}; 213 mSaveToMem = true; 214 OutputManager ref = new OutputManager(); 215 OutputManager test = new OutputManager(); 216 { 217 mCodec = MediaCodec.createByCodecName(mCodecName); 218 assertTrue("codec name act/got: " + mCodec.getName() + '/' + mCodecName, 219 mCodec.getName().equals(mCodecName)); 220 assertTrue("error! codec canonical name is null", 221 mCodec.getCanonicalName() != null && !mCodec.getCanonicalName().isEmpty()); 222 validateMetrics(mCodecName); 223 int loopCounter = 0; 224 for (boolean eosType : boolStates) { 225 for (boolean isAsync : boolStates) { 226 boolean validateFormat = true; 227 String log = String.format("codec: %s, file: %s, mode: %s, eos type: %s:: ", 228 mCodecName, mTestFile, (isAsync ? "async" : "sync"), 229 (eosType ? "eos with last frame" : "eos separate")); 230 mOutputBuff = loopCounter == 0 ? ref : test; 231 mOutputBuff.reset(); 232 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC); 233 configureCodec(format, isAsync, eosType, false); 234 MediaFormat defFormat = mCodec.getOutputFormat(); 235 if (isFormatSimilar(format, defFormat)) { 236 if (ENABLE_LOGS) { 237 Log.d("Input format is same as default for format for %s", mCodecName); 238 } 239 validateFormat = false; 240 } 241 mCodec.start(); 242 doWork(Integer.MAX_VALUE); 243 queueEOS(); 244 waitForAllOutputs(); 245 validateMetrics(mCodecName, format); 246 /* TODO(b/147348711) */ 247 if (false) mCodec.stop(); 248 else mCodec.reset(); 249 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); 250 assertTrue(log + "no input sent", 0 != mInputCount); 251 assertTrue(log + "output received", 0 != mOutputCount); 252 if (loopCounter != 0) { 253 assertTrue(log + "decoder output is flaky", ref.equals(test)); 254 } else { 255 if (mIsAudio) { 256 assertTrue(log + " pts is not strictly increasing", 257 ref.isPtsStrictlyIncreasing(mPrevOutputPts)); 258 } else { 259 assertTrue( 260 log + " input pts list and output pts list are not identical", 261 ref.isOutPtsListIdenticalToInpPtsList(false)); 262 } 263 } 264 if (validateFormat) { 265 assertTrue(log + "not received format change", 266 mIsCodecInAsyncMode ? mAsyncHandle.hasOutputFormatChanged() : 267 mSignalledOutFormatChanged); 268 assertTrue(log + "configured format and output format are not similar", 269 isFormatSimilar(format, 270 mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() : 271 mOutFormat)); 272 } 273 loopCounter++; 274 } 275 } 276 mCodec.release(); 277 if (mSaveToMem) verify(mOutputBuff, mRefFile, mRmsError, mRefCRC); 278 assertTrue(nativeTestSimpleDecode(mCodecName, null, mMime, mInpPrefix + mTestFile, 279 mInpPrefix + mRefFile, mRmsError, ref.getCheckSumBuffer())); 280 } 281 mExtractor.release(); 282 } 283 284 /** 285 * Tests flush when codec is in sync and async mode. In these scenarios, Timestamp 286 * ordering is verified. The output has to be consistent (not flaky) in all runs 287 */ 288 @Ignore("TODO(b/147576107)") 289 @LargeTest 290 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testFlush()291 public void testFlush() throws IOException, InterruptedException { 292 MediaFormat format = setUpSource(mTestFile); 293 mExtractor.release(); 294 mCsdBuffers.clear(); 295 for (int i = 0; ; i++) { 296 String csdKey = "csd-" + i; 297 if (format.containsKey(csdKey)) { 298 mCsdBuffers.add(format.getByteBuffer(csdKey)); 299 } else break; 300 } 301 final long pts = 500000; 302 final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC; 303 boolean[] boolStates = {true, false}; 304 OutputManager test = new OutputManager(); 305 { 306 decodeToMemory(mTestFile, mCodecName, pts, mode, Integer.MAX_VALUE); 307 OutputManager ref = mOutputBuff; 308 if (mIsAudio) { 309 assertTrue("reference output pts is not strictly increasing", 310 ref.isPtsStrictlyIncreasing(mPrevOutputPts)); 311 } else { 312 assertTrue("input pts list and output pts list are not identical", 313 ref.isOutPtsListIdenticalToInpPtsList(false)); 314 } 315 mOutputBuff = test; 316 setUpSource(mTestFile); 317 mCodec = MediaCodec.createByCodecName(mCodecName); 318 for (boolean isAsync : boolStates) { 319 String log = String.format("decoder: %s, input file: %s, mode: %s:: ", mCodecName, 320 mTestFile, (isAsync ? "async" : "sync")); 321 mExtractor.seekTo(0, mode); 322 configureCodec(format, isAsync, true, false); 323 MediaFormat defFormat = mCodec.getOutputFormat(); 324 boolean validateFormat = true; 325 if (isFormatSimilar(format, defFormat)) { 326 if (ENABLE_LOGS) { 327 Log.d("Input format is same as default for format for %s", mCodecName); 328 } 329 validateFormat = false; 330 } 331 mCodec.start(); 332 333 /* test flush in running state before queuing input */ 334 flushCodec(); 335 if (mIsCodecInAsyncMode) mCodec.start(); 336 queueCodecConfig(); /* flushed codec too soon after start, resubmit csd */ 337 338 doWork(1); 339 flushCodec(); 340 if (mIsCodecInAsyncMode) mCodec.start(); 341 queueCodecConfig(); /* flushed codec too soon after start, resubmit csd */ 342 343 mExtractor.seekTo(0, mode); 344 test.reset(); 345 doWork(23); 346 assertTrue(log + " pts is not strictly increasing", 347 test.isPtsStrictlyIncreasing(mPrevOutputPts)); 348 349 boolean checkMetrics = (mOutputCount != 0); 350 351 /* test flush in running state */ 352 flushCodec(); 353 if (checkMetrics) validateMetrics(mCodecName, format); 354 if (mIsCodecInAsyncMode) mCodec.start(); 355 mSaveToMem = true; 356 test.reset(); 357 mExtractor.seekTo(pts, mode); 358 doWork(Integer.MAX_VALUE); 359 queueEOS(); 360 waitForAllOutputs(); 361 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); 362 assertTrue(log + "no input sent", 0 != mInputCount); 363 assertTrue(log + "output received", 0 != mOutputCount); 364 assertTrue(log + "decoder output is flaky", ref.equals(test)); 365 366 /* test flush in eos state */ 367 flushCodec(); 368 if (mIsCodecInAsyncMode) mCodec.start(); 369 test.reset(); 370 mExtractor.seekTo(pts, mode); 371 doWork(Integer.MAX_VALUE); 372 queueEOS(); 373 waitForAllOutputs(); 374 /* TODO(b/147348711) */ 375 if (false) mCodec.stop(); 376 else mCodec.reset(); 377 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); 378 assertTrue(log + "no input sent", 0 != mInputCount); 379 assertTrue(log + "output received", 0 != mOutputCount); 380 assertTrue(log + "decoder output is flaky", ref.equals(test)); 381 if (validateFormat) { 382 assertTrue(log + "not received format change", 383 mIsCodecInAsyncMode ? mAsyncHandle.hasOutputFormatChanged() : 384 mSignalledOutFormatChanged); 385 assertTrue(log + "configured format and output format are not similar", 386 isFormatSimilar(format, 387 mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() : 388 mOutFormat)); 389 } 390 mSaveToMem = false; 391 } 392 mCodec.release(); 393 mExtractor.release(); 394 } 395 } 396 nativeTestFlush(String decoder, Surface surface, String mime, String testFile)397 private native boolean nativeTestFlush(String decoder, Surface surface, String mime, 398 String testFile); 399 400 @Ignore("TODO(b/147576107)") 401 @LargeTest 402 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testFlushNative()403 public void testFlushNative() { 404 { 405 assertTrue(nativeTestFlush(mCodecName, null, mMime, mInpPrefix + mTestFile)); 406 } 407 } 408 409 /** 410 * Tests reconfigure when codec is in sync and async mode. In these scenarios, Timestamp 411 * ordering is verified. The output has to be consistent (not flaky) in all runs 412 */ 413 @LargeTest 414 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testReconfigure()415 public void testReconfigure() throws IOException, InterruptedException { 416 Assume.assumeTrue("Test needs Android 11", IS_AT_LEAST_R); 417 418 MediaFormat format = setUpSource(mTestFile); 419 mExtractor.release(); 420 MediaFormat newFormat = setUpSource(mReconfigFile); 421 mExtractor.release(); 422 final long startTs = 0; 423 final long seekTs = 500000; 424 final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC; 425 boolean[] boolStates = {true, false}; 426 OutputManager test = new OutputManager(); 427 { 428 decodeToMemory(mTestFile, mCodecName, startTs, mode, Integer.MAX_VALUE); 429 OutputManager ref = mOutputBuff; 430 decodeToMemory(mReconfigFile, mCodecName, seekTs, mode, Integer.MAX_VALUE); 431 OutputManager configRef = mOutputBuff; 432 if (mIsAudio) { 433 assertTrue("reference output pts is not strictly increasing", 434 ref.isPtsStrictlyIncreasing(mPrevOutputPts)); 435 assertTrue("config reference output pts is not strictly increasing", 436 configRef.isPtsStrictlyIncreasing(mPrevOutputPts)); 437 } else { 438 assertTrue("input pts list and reference pts list are not identical", 439 ref.isOutPtsListIdenticalToInpPtsList(false)); 440 assertTrue("input pts list and reconfig ref output pts list are not identical", 441 ref.isOutPtsListIdenticalToInpPtsList(false)); 442 } 443 mOutputBuff = test; 444 mCodec = MediaCodec.createByCodecName(mCodecName); 445 for (boolean isAsync : boolStates) { 446 setUpSource(mTestFile); 447 String log = String.format("decoder: %s, input file: %s, mode: %s:: ", mCodecName, 448 mTestFile, (isAsync ? "async" : "sync")); 449 mExtractor.seekTo(startTs, MediaExtractor.SEEK_TO_CLOSEST_SYNC); 450 configureCodec(format, isAsync, true, false); 451 MediaFormat defFormat = mCodec.getOutputFormat(); 452 boolean validateFormat = true; 453 if (isFormatSimilar(format, defFormat)) { 454 if (ENABLE_LOGS) { 455 Log.d("Input format is same as default for format for %s", mCodecName); 456 } 457 validateFormat = false; 458 } 459 460 /* test reconfigure in stopped state */ 461 reConfigureCodec(format, !isAsync, false, false); 462 mCodec.start(); 463 464 /* test reconfigure in running state before queuing input */ 465 reConfigureCodec(format, !isAsync, false, false); 466 mCodec.start(); 467 doWork(23); 468 469 if (mOutputCount != 0) { 470 if (validateFormat) { 471 assertTrue(log + "not received format change", 472 mIsCodecInAsyncMode ? mAsyncHandle.hasOutputFormatChanged() : 473 mSignalledOutFormatChanged); 474 assertTrue(log + "configured format and output format are not similar", 475 isFormatSimilar(format, 476 mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() : 477 mOutFormat)); 478 } 479 validateMetrics(mCodecName, format); 480 } 481 482 /* test reconfigure codec in running state */ 483 reConfigureCodec(format, isAsync, true, false); 484 mCodec.start(); 485 mSaveToMem = true; 486 test.reset(); 487 mExtractor.seekTo(startTs, mode); 488 doWork(Integer.MAX_VALUE); 489 queueEOS(); 490 waitForAllOutputs(); 491 if (mSaveToMem) verify(mOutputBuff, mRefFile, mRmsError, mRefCRC); 492 /* TODO(b/147348711) */ 493 if (false) mCodec.stop(); 494 else mCodec.reset(); 495 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); 496 assertTrue(log + "no input sent", 0 != mInputCount); 497 assertTrue(log + "output received", 0 != mOutputCount); 498 assertTrue(log + "decoder output is flaky", ref.equals(test)); 499 if (validateFormat) { 500 assertTrue(log + "not received format change", 501 mIsCodecInAsyncMode ? mAsyncHandle.hasOutputFormatChanged() : 502 mSignalledOutFormatChanged); 503 assertTrue(log + "configured format and output format are not similar", 504 isFormatSimilar(format, 505 mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() : 506 mOutFormat)); 507 } 508 509 /* test reconfigure codec at eos state */ 510 reConfigureCodec(format, !isAsync, false, false); 511 mCodec.start(); 512 test.reset(); 513 mExtractor.seekTo(startTs, mode); 514 doWork(Integer.MAX_VALUE); 515 queueEOS(); 516 waitForAllOutputs(); 517 if (mSaveToMem) verify(mOutputBuff, mRefFile, mRmsError, mRefCRC); 518 /* TODO(b/147348711) */ 519 if (false) mCodec.stop(); 520 else mCodec.reset(); 521 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); 522 assertTrue(log + "no input sent", 0 != mInputCount); 523 assertTrue(log + "output received", 0 != mOutputCount); 524 assertTrue(log + "decoder output is flaky", ref.equals(test)); 525 if (validateFormat) { 526 assertTrue(log + "not received format change", 527 mIsCodecInAsyncMode ? mAsyncHandle.hasOutputFormatChanged() : 528 mSignalledOutFormatChanged); 529 assertTrue(log + "configured format and output format are not similar", 530 isFormatSimilar(format, 531 mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() : 532 mOutFormat)); 533 } 534 mExtractor.release(); 535 536 /* test reconfigure codec for new file */ 537 setUpSource(mReconfigFile); 538 log = String.format("decoder: %s, input file: %s, mode: %s:: ", mCodecName, 539 mReconfigFile, (isAsync ? "async" : "sync")); 540 reConfigureCodec(newFormat, isAsync, false, false); 541 if (isFormatSimilar(newFormat, defFormat)) { 542 if (ENABLE_LOGS) { 543 Log.d("Input format is same as default for format for %s", mCodecName); 544 } 545 validateFormat = false; 546 } 547 mCodec.start(); 548 test.reset(); 549 mExtractor.seekTo(seekTs, mode); 550 doWork(Integer.MAX_VALUE); 551 queueEOS(); 552 waitForAllOutputs(); 553 validateMetrics(mCodecName, newFormat); 554 /* TODO(b/147348711) */ 555 if (false) mCodec.stop(); 556 else mCodec.reset(); 557 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); 558 assertTrue(log + "no input sent", 0 != mInputCount); 559 assertTrue(log + "output received", 0 != mOutputCount); 560 assertTrue(log + "decoder output is flaky", configRef.equals(test)); 561 if (validateFormat) { 562 assertTrue(log + "not received format change", 563 mIsCodecInAsyncMode ? mAsyncHandle.hasOutputFormatChanged() : 564 mSignalledOutFormatChanged); 565 assertTrue(log + "configured format and output format are not similar", 566 isFormatSimilar(newFormat, 567 mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() : 568 mOutFormat)); 569 } 570 mSaveToMem = false; 571 mExtractor.release(); 572 } 573 mCodec.release(); 574 } 575 } 576 577 /** 578 * Tests decoder for only EOS frame 579 */ 580 @SmallTest 581 @Test(timeout = PER_TEST_TIMEOUT_SMALL_TEST_MS) testOnlyEos()582 public void testOnlyEos() throws IOException, InterruptedException { 583 MediaFormat format = setUpSource(mTestFile); 584 boolean[] boolStates = {true, false}; 585 OutputManager ref = new OutputManager(); 586 OutputManager test = new OutputManager(); 587 mSaveToMem = true; 588 { 589 mCodec = MediaCodec.createByCodecName(mCodecName); 590 int loopCounter = 0; 591 for (boolean isAsync : boolStates) { 592 String log = String.format("decoder: %s, input file: %s, mode: %s:: ", mCodecName, 593 mTestFile, (isAsync ? "async" : "sync")); 594 configureCodec(format, isAsync, false, false); 595 mOutputBuff = loopCounter == 0 ? ref : test; 596 mOutputBuff.reset(); 597 mCodec.start(); 598 queueEOS(); 599 waitForAllOutputs(); 600 mCodec.stop(); 601 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); 602 if (loopCounter != 0) { 603 assertTrue(log + "decoder output is flaky", ref.equals(test)); 604 } else { 605 if (mIsAudio) { 606 assertTrue(log + " pts is not strictly increasing", 607 ref.isPtsStrictlyIncreasing(mPrevOutputPts)); 608 } else { 609 assertTrue( 610 log + " input pts list and output pts list are not identical", 611 ref.isOutPtsListIdenticalToInpPtsList(false)); 612 } 613 } 614 loopCounter++; 615 } 616 mCodec.release(); 617 } 618 mExtractor.release(); 619 } 620 nativeTestOnlyEos(String decoder, String mime, String testFile)621 private native boolean nativeTestOnlyEos(String decoder, String mime, String testFile); 622 623 @SmallTest 624 @Test testOnlyEosNative()625 public void testOnlyEosNative() { 626 { 627 assertTrue(nativeTestOnlyEos(mCodecName, mMime, mInpPrefix + mTestFile)); 628 } 629 } 630 631 /** 632 * Test Decoder by Queuing CSD separately 633 */ 634 @LargeTest 635 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testSimpleDecodeQueueCSD()636 public void testSimpleDecodeQueueCSD() throws IOException, InterruptedException { 637 MediaFormat format = setUpSource(mTestFile); 638 if (!hasCSD(format)) { 639 mExtractor.release(); 640 return; 641 } 642 ArrayList<MediaFormat> formats = new ArrayList<>(); 643 formats.add(format); 644 formats.add(new MediaFormat(format)); 645 for (int i = 0; ; i++) { 646 String csdKey = "csd-" + i; 647 if (format.containsKey(csdKey)) { 648 mCsdBuffers.add(format.getByteBuffer(csdKey).duplicate()); 649 format.removeKey(csdKey); 650 } else break; 651 } 652 boolean[] boolStates = {true, false}; 653 mSaveToMem = true; 654 OutputManager ref = new OutputManager(); 655 OutputManager test = new OutputManager(); 656 { 657 mCodec = MediaCodec.createByCodecName(mCodecName); 658 int loopCounter = 0; 659 for (int i = 0; i < formats.size(); i++) { 660 MediaFormat fmt = formats.get(i); 661 for (boolean eosMode : boolStates) { 662 for (boolean isAsync : boolStates) { 663 boolean validateFormat = true; 664 String log = String.format("codec: %s, file: %s, mode: %s, eos type: %s:: ", 665 mCodecName, mTestFile, (isAsync ? "async" : "sync"), 666 (eosMode ? "eos with last frame" : "eos separate")); 667 mOutputBuff = loopCounter == 0 ? ref : test; 668 mOutputBuff.reset(); 669 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC); 670 configureCodec(fmt, isAsync, eosMode, false); 671 MediaFormat defFormat = mCodec.getOutputFormat(); 672 if (isFormatSimilar(defFormat, format)) { 673 if (ENABLE_LOGS) { 674 Log.d("Input format is same as default for format for %s", 675 mCodecName); 676 } 677 validateFormat = false; 678 } 679 mCodec.start(); 680 if (i == 0) queueCodecConfig(); 681 doWork(Integer.MAX_VALUE); 682 queueEOS(); 683 waitForAllOutputs(); 684 validateMetrics(mCodecName); 685 /* TODO(b/147348711) */ 686 if (false) mCodec.stop(); 687 else mCodec.reset(); 688 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); 689 assertTrue(log + "no input sent", 0 != mInputCount); 690 assertTrue(log + "output received", 0 != mOutputCount); 691 if (loopCounter != 0) { 692 assertTrue(log + "decoder output is flaky", ref.equals(test)); 693 } else { 694 if (mIsAudio) { 695 assertTrue(log + " pts is not strictly increasing", 696 ref.isPtsStrictlyIncreasing(mPrevOutputPts)); 697 } else { 698 assertTrue( 699 log + " input pts list and output pts list are not identical", 700 ref.isOutPtsListIdenticalToInpPtsList(false)); 701 } 702 } 703 if (validateFormat) { 704 assertTrue(log + "not received format change", 705 mIsCodecInAsyncMode ? mAsyncHandle.hasOutputFormatChanged() : 706 mSignalledOutFormatChanged); 707 assertTrue(log + "configured format and output format are not similar", 708 isFormatSimilar(format, 709 mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() : 710 mOutFormat)); 711 } 712 loopCounter++; 713 } 714 } 715 } 716 mCodec.release(); 717 } 718 mExtractor.release(); 719 } 720 nativeTestSimpleDecodeQueueCSD(String decoder, String mime, String testFile)721 private native boolean nativeTestSimpleDecodeQueueCSD(String decoder, String mime, 722 String testFile); 723 724 @LargeTest 725 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testSimpleDecodeQueueCSDNative()726 public void testSimpleDecodeQueueCSDNative() throws IOException { 727 MediaFormat format = setUpSource(mTestFile); 728 if (!hasCSD(format)) { 729 mExtractor.release(); 730 return; 731 } 732 { 733 assertTrue(nativeTestSimpleDecodeQueueCSD(mCodecName, mMime, mInpPrefix + mTestFile)); 734 } 735 mExtractor.release(); 736 } 737 738 /** 739 * Test decoder for partial frame 740 */ 741 @LargeTest 742 @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) testDecodePartialFrame()743 public void testDecodePartialFrame() throws IOException, InterruptedException { 744 Assume.assumeTrue(isFeatureSupported(mCodecName, mMime, 745 MediaCodecInfo.CodecCapabilities.FEATURE_PartialFrame)); 746 MediaFormat format = setUpSource(mTestFile); 747 boolean[] boolStates = {true, false}; 748 int frameLimit = 10; 749 ByteBuffer buffer = ByteBuffer.allocate(4 * 1024 * 1024); 750 OutputManager test = new OutputManager(); 751 { 752 decodeToMemory(mTestFile, mCodecName, 0, MediaExtractor.SEEK_TO_CLOSEST_SYNC, 753 frameLimit); 754 mCodec = MediaCodec.createByCodecName(mCodecName); 755 OutputManager ref = mOutputBuff; 756 if (mIsAudio) { 757 assertTrue("reference output pts is not strictly increasing", 758 ref.isPtsStrictlyIncreasing(mPrevOutputPts)); 759 } else { 760 assertTrue("input pts list and output pts list are not identical", 761 ref.isOutPtsListIdenticalToInpPtsList(false)); 762 } 763 mSaveToMem = true; 764 mOutputBuff = test; 765 for (boolean isAsync : boolStates) { 766 String log = String.format("decoder: %s, input file: %s, mode: %s:: ", mCodecName, 767 mTestFile, (isAsync ? "async" : "sync")); 768 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC); 769 test.reset(); 770 configureCodec(format, isAsync, true, false); 771 mCodec.start(); 772 doWork(frameLimit - 1); 773 ArrayList<MediaCodec.BufferInfo> list = createSubFrames(buffer, 4); 774 assertTrue("no sub frames in list received for " + mTestFile, 775 list != null && list.size() > 0); 776 doWork(buffer, list); 777 queueEOS(); 778 waitForAllOutputs(); 779 /* TODO(b/147348711) */ 780 if (false) mCodec.stop(); 781 else mCodec.reset(); 782 assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); 783 assertTrue(log + "no input sent", 0 != mInputCount); 784 assertTrue(log + "output received", 0 != mOutputCount); 785 assertTrue(log + "decoder output is not consistent with ref", ref.equals(test)); 786 } 787 mCodec.release(); 788 } 789 mExtractor.release(); 790 } 791 } 792