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