1 /* 2 * Copyright (C) 2012 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.media.cts; 18 19 import android.media.cts.R; 20 21 import android.content.Context; 22 import android.content.pm.PackageManager; 23 import android.content.res.AssetFileDescriptor; 24 import android.content.res.Resources; 25 import android.graphics.ImageFormat; 26 import android.media.cts.CodecUtils; 27 import android.media.Image; 28 import android.media.AudioManager; 29 import android.media.MediaCodec; 30 import android.media.MediaCodec.BufferInfo; 31 import android.media.MediaCodecList; 32 import android.media.MediaCodecInfo; 33 import android.media.MediaCodecInfo.CodecCapabilities; 34 import android.media.MediaExtractor; 35 import android.media.MediaFormat; 36 import android.platform.test.annotations.AppModeFull; 37 import android.util.Log; 38 import android.view.Surface; 39 import android.net.Uri; 40 41 import com.android.compatibility.common.util.DeviceReportLog; 42 import com.android.compatibility.common.util.DynamicConfigDeviceSide; 43 import com.android.compatibility.common.util.MediaUtils; 44 import com.android.compatibility.common.util.ResultType; 45 import com.android.compatibility.common.util.ResultUnit; 46 47 import java.io.BufferedInputStream; 48 import java.io.IOException; 49 import java.io.InputStream; 50 import java.nio.ByteBuffer; 51 import java.util.ArrayList; 52 import java.util.Arrays; 53 import java.util.List; 54 import java.util.zip.CRC32; 55 import java.util.concurrent.CountDownLatch; 56 import java.util.concurrent.TimeUnit; 57 import java.util.regex.Matcher; 58 import java.util.regex.Pattern; 59 60 import static android.media.MediaCodecInfo.CodecProfileLevel.*; 61 62 @MediaHeavyPresubmitTest 63 @AppModeFull(reason = "There should be no instant apps specific behavior related to decoders") 64 public class DecoderTest extends MediaPlayerTestBase { 65 private static final String TAG = "DecoderTest"; 66 67 private static final int RESET_MODE_NONE = 0; 68 private static final int RESET_MODE_RECONFIGURE = 1; 69 private static final int RESET_MODE_FLUSH = 2; 70 private static final int RESET_MODE_EOS_FLUSH = 3; 71 72 private static final String[] CSD_KEYS = new String[] { "csd-0", "csd-1" }; 73 74 private static final int CONFIG_MODE_NONE = 0; 75 private static final int CONFIG_MODE_QUEUE = 1; 76 77 private Resources mResources; 78 short[] mMasterBuffer; 79 80 private MediaCodecTunneledPlayer mMediaCodecPlayer; 81 private static final int SLEEP_TIME_MS = 1000; 82 private static final long PLAY_TIME_MS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES); 83 84 private static final String AUDIO_URL_KEY = "decoder_test_audio_url"; 85 private static final String VIDEO_URL_KEY = "decoder_test_video_url"; 86 private static final String MODULE_NAME = "CtsMediaTestCases"; 87 private DynamicConfigDeviceSide dynamicConfig; 88 89 @Override 90 protected void setUp() throws Exception { 91 super.setUp(); 92 mResources = mContext.getResources(); 93 94 // read master file into memory 95 AssetFileDescriptor masterFd = mResources.openRawResourceFd(R.raw.sinesweepraw); 96 long masterLength = masterFd.getLength(); 97 mMasterBuffer = new short[(int) (masterLength / 2)]; 98 InputStream is = masterFd.createInputStream(); 99 BufferedInputStream bis = new BufferedInputStream(is); 100 for (int i = 0; i < mMasterBuffer.length; i++) { 101 int lo = bis.read(); 102 int hi = bis.read(); 103 if (hi >= 128) { 104 hi -= 256; 105 } 106 int sample = hi * 256 + lo; 107 mMasterBuffer[i] = (short) sample; 108 } 109 bis.close(); 110 masterFd.close(); 111 112 dynamicConfig = new DynamicConfigDeviceSide(MODULE_NAME); 113 } 114 115 @Override 116 protected void tearDown() throws Exception { 117 // ensure MediaCodecPlayer resources are released even if an exception is thrown. 118 if (mMediaCodecPlayer != null) { 119 mMediaCodecPlayer.reset(); 120 mMediaCodecPlayer = null; 121 } 122 } 123 124 // TODO: add similar tests for other audio and video formats 125 public void testBug11696552() throws Exception { 126 MediaCodec mMediaCodec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_AUDIO_AAC); 127 MediaFormat mFormat = MediaFormat.createAudioFormat( 128 MediaFormat.MIMETYPE_AUDIO_AAC, 48000 /* frequency */, 2 /* channels */); 129 mFormat.setByteBuffer("csd-0", ByteBuffer.wrap( new byte [] {0x13, 0x10} )); 130 mFormat.setInteger(MediaFormat.KEY_IS_ADTS, 1); 131 mMediaCodec.configure(mFormat, null, null, 0); 132 mMediaCodec.start(); 133 int index = mMediaCodec.dequeueInputBuffer(250000); 134 mMediaCodec.queueInputBuffer(index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); 135 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 136 mMediaCodec.dequeueOutputBuffer(info, 250000); 137 } 138 139 // The allowed errors in the following tests are the actual maximum measured 140 // errors with the standard decoders, plus 10%. 141 // This should allow for some variation in decoders, while still detecting 142 // phase and delay errors, channel swap, etc. 143 public void testDecodeMp3Lame() throws Exception { 144 decode(R.raw.sinesweepmp3lame, 804.f); 145 testTimeStampOrdering(R.raw.sinesweepmp3lame); 146 } 147 public void testDecodeMp3Smpb() throws Exception { 148 decode(R.raw.sinesweepmp3smpb, 413.f); 149 testTimeStampOrdering(R.raw.sinesweepmp3smpb); 150 } 151 public void testDecodeM4a() throws Exception { 152 decode(R.raw.sinesweepm4a, 124.f); 153 testTimeStampOrdering(R.raw.sinesweepm4a); 154 } 155 public void testDecodeOgg() throws Exception { 156 decode(R.raw.sinesweepogg, 168.f); 157 testTimeStampOrdering(R.raw.sinesweepogg); 158 } 159 public void testDecodeOggMkv() throws Exception { 160 decode(R.raw.sinesweepoggmkv, 168.f); 161 testTimeStampOrdering(R.raw.sinesweepoggmkv); 162 } 163 public void testDecodeOggMp4() throws Exception { 164 decode(R.raw.sinesweepoggmp4, 168.f); 165 testTimeStampOrdering(R.raw.sinesweepoggmp4); 166 } 167 public void testDecodeWav() throws Exception { 168 decode(R.raw.sinesweepwav, 0.0f); 169 testTimeStampOrdering(R.raw.sinesweepwav); 170 } 171 public void testDecodeFlacMkv() throws Exception { 172 decode(R.raw.sinesweepflacmkv, 0.0f); 173 testTimeStampOrdering(R.raw.sinesweepflacmkv); 174 } 175 public void testDecodeFlac() throws Exception { 176 decode(R.raw.sinesweepflac, 0.0f); 177 testTimeStampOrdering(R.raw.sinesweepflac); 178 } 179 public void testDecodeFlacMp4() throws Exception { 180 decode(R.raw.sinesweepflacmp4, 0.0f); 181 testTimeStampOrdering(R.raw.sinesweepflacmp4); 182 } 183 184 public void testDecodeMonoMp3() throws Exception { 185 monoTest(R.raw.monotestmp3, 44100); 186 testTimeStampOrdering(R.raw.monotestmp3); 187 } 188 189 public void testDecodeMonoM4a() throws Exception { 190 monoTest(R.raw.monotestm4a, 44100); 191 testTimeStampOrdering(R.raw.monotestm4a); 192 } 193 194 public void testDecodeMonoOgg() throws Exception { 195 monoTest(R.raw.monotestogg, 44100); 196 testTimeStampOrdering(R.raw.monotestogg); 197 } 198 public void testDecodeMonoOggMkv() throws Exception { 199 monoTest(R.raw.monotestoggmkv, 44100); 200 testTimeStampOrdering(R.raw.monotestoggmkv); 201 } 202 public void testDecodeMonoOggMp4() throws Exception { 203 monoTest(R.raw.monotestoggmp4, 44100); 204 testTimeStampOrdering(R.raw.monotestoggmp4); 205 } 206 207 public void testDecodeMonoGsm() throws Exception { 208 if (MediaUtils.hasCodecsForResource(mContext, R.raw.monotestgsm)) { 209 monoTest(R.raw.monotestgsm, 8000); 210 testTimeStampOrdering(R.raw.monotestgsm); 211 } else { 212 MediaUtils.skipTest("not mandatory"); 213 } 214 } 215 216 public void testDecodeAacTs() throws Exception { 217 testTimeStampOrdering(R.raw.sinesweeptsaac); 218 } 219 220 public void testDecodeVorbis() throws Exception { 221 testTimeStampOrdering(R.raw.sinesweepvorbis); 222 } 223 public void testDecodeVorbisMp4() throws Exception { 224 testTimeStampOrdering(R.raw.sinesweepvorbismp4); 225 } 226 227 public void testDecodeOpus() throws Exception { 228 testTimeStampOrdering(R.raw.sinesweepopus); 229 } 230 public void testDecodeOpusMp4() throws Exception { 231 testTimeStampOrdering(R.raw.sinesweepopusmp4); 232 } 233 234 public void testDecode51M4a() throws Exception { 235 decodeToMemory(R.raw.sinesweep51m4a, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 236 } 237 238 private void testTimeStampOrdering(int res) throws Exception { 239 List<Long> timestamps = new ArrayList<Long>(); 240 decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, timestamps); 241 Long lastTime = Long.MIN_VALUE; 242 for (int i = 0; i < timestamps.size(); i++) { 243 Long thisTime = timestamps.get(i); 244 assertTrue("timetravel occurred: " + lastTime + " > " + thisTime, thisTime >= lastTime); 245 lastTime = thisTime; 246 } 247 } 248 249 public void testTrackSelection() throws Exception { 250 testTrackSelection(R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz); 251 testTrackSelection( 252 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented); 253 testTrackSelection( 254 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash); 255 } 256 257 public void testTrackSelectionMkv() throws Exception { 258 Log.d(TAG, "testTrackSelectionMkv!!!!!! "); 259 testTrackSelection(R.raw.mkv_avc_adpcm_ima); 260 Log.d(TAG, "mkv_avc_adpcm_ima finished!!!!!! "); 261 testTrackSelection(R.raw.mkv_avc_adpcm_ms); 262 Log.d(TAG, "mkv_avc_adpcm_ms finished!!!!!! "); 263 testTrackSelection(R.raw.mkv_avc_wma); 264 Log.d(TAG, "mkv_avc_wma finished!!!!!! "); 265 testTrackSelection(R.raw.mkv_avc_mp2); 266 Log.d(TAG, "mkv_avc_mp2 finished!!!!!! "); 267 } 268 269 public void testBFrames() throws Exception { 270 int testsRun = 271 testBFrames(R.raw.video_h264_main_b_frames) + 272 testBFrames(R.raw.video_h264_main_b_frames_frag); 273 if (testsRun == 0) { 274 MediaUtils.skipTest("no codec found"); 275 } 276 } 277 278 public int testBFrames(int res) throws Exception { 279 AssetFileDescriptor fd = mResources.openRawResourceFd(res); 280 MediaExtractor ex = new MediaExtractor(); 281 ex.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength()); 282 MediaFormat format = ex.getTrackFormat(0); 283 String mime = format.getString(MediaFormat.KEY_MIME); 284 assertTrue("not a video track. Wrong test file?", mime.startsWith("video/")); 285 if (!MediaUtils.canDecode(format)) { 286 ex.release(); 287 fd.close(); 288 return 0; // skip 289 } 290 MediaCodec dec = MediaCodec.createDecoderByType(mime); 291 Surface s = getActivity().getSurfaceHolder().getSurface(); 292 dec.configure(format, s, null, 0); 293 dec.start(); 294 ByteBuffer[] buf = dec.getInputBuffers(); 295 ex.selectTrack(0); 296 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 297 long lastPresentationTimeUsFromExtractor = -1; 298 long lastPresentationTimeUsFromDecoder = -1; 299 boolean inputoutoforder = false; 300 while(true) { 301 int flags = ex.getSampleFlags(); 302 long time = ex.getSampleTime(); 303 if (time >= 0 && time < lastPresentationTimeUsFromExtractor) { 304 inputoutoforder = true; 305 } 306 lastPresentationTimeUsFromExtractor = time; 307 int bufidx = dec.dequeueInputBuffer(5000); 308 if (bufidx >= 0) { 309 int n = ex.readSampleData(buf[bufidx], 0); 310 if (n < 0) { 311 flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM; 312 time = 0; 313 n = 0; 314 } 315 dec.queueInputBuffer(bufidx, 0, n, time, flags); 316 ex.advance(); 317 } 318 int status = dec.dequeueOutputBuffer(info, 5000); 319 if (status >= 0) { 320 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 321 break; 322 } 323 assertTrue("out of order timestamp from decoder", 324 info.presentationTimeUs > lastPresentationTimeUsFromDecoder); 325 dec.releaseOutputBuffer(status, true); 326 lastPresentationTimeUsFromDecoder = info.presentationTimeUs; 327 } 328 } 329 assertTrue("extractor timestamps were ordered, wrong test file?", inputoutoforder); 330 dec.release(); 331 ex.release(); 332 fd.close(); 333 return 1; 334 } 335 336 /** 337 * Test ColorAspects of all the AVC decoders. Decoders should handle 338 * the colors aspects presented in both the mp4 atom 'colr' and VUI 339 * in the bitstream correctly. The following table lists the color 340 * aspects contained in the color box and VUI for the test stream. 341 * P = primaries, T = transfer, M = coeffs, R = range. '-' means 342 * empty value. 343 * | colr | VUI 344 * ------------------------------------------------------------------- 345 * File Name | P T M R | P T M R 346 * ------------------------------------------------------------------- 347 * color_176x144_bt709_lr_sdr_h264 | 1 1 1 0 | - - - - 348 * color_176x144_bt601_625_fr_sdr_h264 | 1 6 6 0 | 5 2 2 1 349 * color_176x144_bt601_525_lr_sdr_h264 | 6 5 4 0 | 2 6 6 0 350 * color_176x144_srgb_lr_sdr_h264 | 2 0 2 1 | 1 13 1 0 351 */ 352 public void testH264ColorAspects() throws Exception { 353 testColorAspects( 354 R.raw.color_176x144_bt709_lr_sdr_h264, 1 /* testId */, 355 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT709, 356 MediaFormat.COLOR_TRANSFER_SDR_VIDEO); 357 testColorAspects( 358 R.raw.color_176x144_bt601_625_fr_sdr_h264, 2 /* testId */, 359 MediaFormat.COLOR_RANGE_FULL, MediaFormat.COLOR_STANDARD_BT601_PAL, 360 MediaFormat.COLOR_TRANSFER_SDR_VIDEO); 361 testColorAspects( 362 R.raw.color_176x144_bt601_525_lr_sdr_h264, 3 /* testId */, 363 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT601_NTSC, 364 MediaFormat.COLOR_TRANSFER_SDR_VIDEO); 365 testColorAspects( 366 R.raw.color_176x144_srgb_lr_sdr_h264, 4 /* testId */, 367 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT709, 368 2 /* MediaFormat.COLOR_TRANSFER_SRGB */); 369 } 370 371 /** 372 * Test ColorAspects of all the HEVC decoders. Decoders should handle 373 * the colors aspects presented in both the mp4 atom 'colr' and VUI 374 * in the bitstream correctly. The following table lists the color 375 * aspects contained in the color box and VUI for the test stream. 376 * P = primaries, T = transfer, M = coeffs, R = range. '-' means 377 * empty value. 378 * | colr | VUI 379 * ------------------------------------------------------------------- 380 * File Name | P T M R | P T M R 381 * ------------------------------------------------------------------- 382 * color_176x144_bt709_lr_sdr_h265 | 1 1 1 0 | - - - - 383 * color_176x144_bt601_625_fr_sdr_h265 | 1 6 6 0 | 5 2 2 1 384 * color_176x144_bt601_525_lr_sdr_h265 | 6 5 4 0 | 2 6 6 0 385 * color_176x144_srgb_lr_sdr_h265 | 2 0 2 1 | 1 13 1 0 386 */ 387 public void testH265ColorAspects() throws Exception { 388 testColorAspects( 389 R.raw.color_176x144_bt709_lr_sdr_h265, 1 /* testId */, 390 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT709, 391 MediaFormat.COLOR_TRANSFER_SDR_VIDEO); 392 testColorAspects( 393 R.raw.color_176x144_bt601_625_fr_sdr_h265, 2 /* testId */, 394 MediaFormat.COLOR_RANGE_FULL, MediaFormat.COLOR_STANDARD_BT601_PAL, 395 MediaFormat.COLOR_TRANSFER_SDR_VIDEO); 396 testColorAspects( 397 R.raw.color_176x144_bt601_525_lr_sdr_h265, 3 /* testId */, 398 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT601_NTSC, 399 MediaFormat.COLOR_TRANSFER_SDR_VIDEO); 400 testColorAspects( 401 R.raw.color_176x144_srgb_lr_sdr_h265, 4 /* testId */, 402 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT709, 403 2 /* MediaFormat.COLOR_TRANSFER_SRGB */); 404 // Test the main10 streams with surface as the decoder might 405 // support opaque buffers only. 406 testColorAspects( 407 R.raw.color_176x144_bt2020_lr_smpte2084_h265, 5 /* testId */, 408 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT2020, 409 MediaFormat.COLOR_TRANSFER_ST2084, 410 getActivity().getSurfaceHolder().getSurface()); 411 testColorAspects( 412 R.raw.color_176x144_bt2020_lr_hlg_h265, 6 /* testId */, 413 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT2020, 414 MediaFormat.COLOR_TRANSFER_HLG, 415 getActivity().getSurfaceHolder().getSurface()); 416 } 417 418 /** 419 * Test ColorAspects of all the MPEG2 decoders if avaiable. Decoders should 420 * handle the colors aspects presented in both the mp4 atom 'colr' and Sequence 421 * in the bitstream correctly. The following table lists the color aspects 422 * contained in the color box and SeqInfo for the test stream. 423 * P = primaries, T = transfer, M = coeffs, R = range. '-' means 424 * empty value. 425 * | colr | SeqInfo 426 * ------------------------------------------------------------------- 427 * File Name | P T M R | P T M R 428 * ------------------------------------------------------------------- 429 * color_176x144_bt709_lr_sdr_mpeg2 | 1 1 1 0 | - - - - 430 * color_176x144_bt601_625_lr_sdr_mpeg2 | 1 6 6 0 | 5 2 2 0 431 * color_176x144_bt601_525_lr_sdr_mpeg2 | 6 5 4 0 | 2 6 6 0 432 * color_176x144_srgb_lr_sdr_mpeg2 | 2 0 2 0 | 1 13 1 0 433 */ 434 public void testMPEG2ColorAspectsTV() throws Exception { 435 testColorAspects( 436 R.raw.color_176x144_bt709_lr_sdr_mpeg2, 1 /* testId */, 437 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT709, 438 MediaFormat.COLOR_TRANSFER_SDR_VIDEO); 439 testColorAspects( 440 R.raw.color_176x144_bt601_625_lr_sdr_mpeg2, 2 /* testId */, 441 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT601_PAL, 442 MediaFormat.COLOR_TRANSFER_SDR_VIDEO); 443 testColorAspects( 444 R.raw.color_176x144_bt601_525_lr_sdr_mpeg2, 3 /* testId */, 445 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT601_NTSC, 446 MediaFormat.COLOR_TRANSFER_SDR_VIDEO); 447 testColorAspects( 448 R.raw.color_176x144_srgb_lr_sdr_mpeg2, 4 /* testId */, 449 MediaFormat.COLOR_RANGE_LIMITED, MediaFormat.COLOR_STANDARD_BT709, 450 2 /* MediaFormat.COLOR_TRANSFER_SRGB */); 451 } 452 453 private void testColorAspects( 454 int res, int testId, int expectRange, int expectStandard, int expectTransfer) 455 throws Exception { 456 testColorAspects( 457 res, testId, expectRange, expectStandard, expectTransfer, null /*surface*/); 458 } 459 460 private void testColorAspects( 461 int res, int testId, int expectRange, int expectStandard, int expectTransfer, 462 Surface surface) throws Exception { 463 MediaFormat format = MediaUtils.getTrackFormatForResource(mContext, res, "video"); 464 MediaFormat mimeFormat = new MediaFormat(); 465 mimeFormat.setString(MediaFormat.KEY_MIME, format.getString(MediaFormat.KEY_MIME)); 466 467 for (String decoderName: MediaUtils.getDecoderNames(mimeFormat)) { 468 if (!MediaUtils.supports(decoderName, format)) { 469 MediaUtils.skipTest(decoderName + " cannot play resource " + res); 470 } else { 471 testColorAspects(decoderName, res, testId, 472 expectRange, expectStandard, expectTransfer, surface); 473 } 474 } 475 } 476 477 private void testColorAspects( 478 String decoderName, int res, int testId, int expectRange, 479 int expectStandard, int expectTransfer, Surface surface) throws Exception { 480 AssetFileDescriptor fd = mResources.openRawResourceFd(res); 481 MediaExtractor ex = new MediaExtractor(); 482 ex.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength()); 483 MediaFormat format = ex.getTrackFormat(0); 484 MediaCodec dec = MediaCodec.createByCodecName(decoderName); 485 dec.configure(format, surface, null, 0); 486 dec.start(); 487 ByteBuffer[] buf = dec.getInputBuffers(); 488 ex.selectTrack(0); 489 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 490 boolean sawInputEOS = false; 491 boolean getOutputFormat = false; 492 boolean rangeMatch = false; 493 boolean colorMatch = false; 494 boolean transferMatch = false; 495 int colorRange = 0; 496 int colorStandard = 0; 497 int colorTransfer = 0; 498 499 while (true) { 500 if (!sawInputEOS) { 501 int flags = ex.getSampleFlags(); 502 long time = ex.getSampleTime(); 503 int bufidx = dec.dequeueInputBuffer(200 * 1000); 504 if (bufidx >= 0) { 505 int n = ex.readSampleData(buf[bufidx], 0); 506 if (n < 0) { 507 flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM; 508 sawInputEOS = true; 509 n = 0; 510 } 511 dec.queueInputBuffer(bufidx, 0, n, time, flags); 512 ex.advance(); 513 } else { 514 assertEquals( 515 "codec.dequeueInputBuffer() unrecognized return value: " + bufidx, 516 MediaCodec.INFO_TRY_AGAIN_LATER, bufidx); 517 } 518 } 519 520 int status = dec.dequeueOutputBuffer(info, sawInputEOS ? 3000 * 1000 : 100 * 1000); 521 if (status == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 522 MediaFormat fmt = dec.getOutputFormat(); 523 colorRange = fmt.containsKey("color-range") ? fmt.getInteger("color-range") : 0; 524 colorStandard = fmt.containsKey("color-standard") ? fmt.getInteger("color-standard") : 0; 525 colorTransfer = fmt.containsKey("color-transfer") ? fmt.getInteger("color-transfer") : 0; 526 rangeMatch = colorRange == expectRange; 527 colorMatch = colorStandard == expectStandard; 528 transferMatch = colorTransfer == expectTransfer; 529 getOutputFormat = true; 530 // Test only needs to check the color format in the first format changed event. 531 break; 532 } else if (status >= 0) { 533 // Test should get at least one format changed event before getting first frame. 534 assertTrue(getOutputFormat == true); 535 break; 536 } else { 537 assertFalse( 538 "codec.dequeueOutputBuffer() timeout after seeing input EOS", 539 status == MediaCodec.INFO_TRY_AGAIN_LATER && sawInputEOS); 540 } 541 } 542 543 String reportName = decoderName + "_colorAspectsTest Test " + testId + 544 " (Get R: " + colorRange + " S: " + colorStandard + " T: " + colorTransfer + ")" + 545 " (Expect R: " + expectRange + " S: " + expectStandard + " T: " + expectTransfer + ")"; 546 Log.d(TAG, reportName); 547 548 DeviceReportLog log = new DeviceReportLog("CtsMediaTestCases", "color_aspects_test"); 549 log.addValue("decoder_name", decoderName, ResultType.NEUTRAL, ResultUnit.NONE); 550 log.addValue("test_id", testId, ResultType.NEUTRAL, ResultUnit.NONE); 551 log.addValues( 552 "rst_actual", new int[] { colorRange, colorStandard, colorTransfer }, 553 ResultType.NEUTRAL, ResultUnit.NONE); 554 log.addValues( 555 "rst_expected", new int[] { expectRange, expectStandard, expectTransfer }, 556 ResultType.NEUTRAL, ResultUnit.NONE); 557 558 if (rangeMatch && colorMatch && transferMatch) { 559 log.setSummary("result", 1, ResultType.HIGHER_BETTER, ResultUnit.COUNT); 560 } else { 561 log.setSummary("result", 0, ResultType.HIGHER_BETTER, ResultUnit.COUNT); 562 } 563 log.submit(getInstrumentation()); 564 565 assertTrue(rangeMatch && colorMatch && transferMatch); 566 567 dec.release(); 568 ex.release(); 569 fd.close(); 570 } 571 572 private void testTrackSelection(int resid) throws Exception { 573 AssetFileDescriptor fd1 = null; 574 MediaExtractor ex1 = new MediaExtractor(); 575 try { 576 fd1 = mResources.openRawResourceFd(resid); 577 ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength()); 578 579 ByteBuffer buf1 = ByteBuffer.allocate(1024*1024); 580 ArrayList<Integer> vid = new ArrayList<Integer>(); 581 ArrayList<Integer> aud = new ArrayList<Integer>(); 582 583 // scan the file once and build lists of audio and video samples 584 ex1.selectTrack(0); 585 ex1.selectTrack(1); 586 while(true) { 587 int n1 = ex1.readSampleData(buf1, 0); 588 if (n1 < 0) { 589 break; 590 } 591 int idx = ex1.getSampleTrackIndex(); 592 if (idx == 0) { 593 vid.add(n1); 594 } else if (idx == 1) { 595 aud.add(n1); 596 } else { 597 fail("unexpected track index: " + idx); 598 } 599 ex1.advance(); 600 } 601 602 // read the video track once, then rewind and do it again, and 603 // verify we get the right samples 604 ex1.release(); 605 ex1 = new MediaExtractor(); 606 ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength()); 607 ex1.selectTrack(0); 608 for (int i = 0; i < 2; i++) { 609 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 610 int idx = 0; 611 while(true) { 612 int n1 = ex1.readSampleData(buf1, 0); 613 if (n1 < 0) { 614 assertEquals(vid.size(), idx); 615 break; 616 } 617 assertEquals(vid.get(idx++).intValue(), n1); 618 ex1.advance(); 619 } 620 } 621 622 // read the audio track once, then rewind and do it again, and 623 // verify we get the right samples 624 ex1.release(); 625 ex1 = new MediaExtractor(); 626 ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength()); 627 ex1.selectTrack(1); 628 for (int i = 0; i < 2; i++) { 629 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 630 int idx = 0; 631 while(true) { 632 int n1 = ex1.readSampleData(buf1, 0); 633 if (n1 < 0) { 634 assertEquals(aud.size(), idx); 635 break; 636 } 637 assertEquals(aud.get(idx++).intValue(), n1); 638 ex1.advance(); 639 } 640 } 641 642 // read the video track first, then rewind and get the audio track instead, and 643 // verify we get the right samples 644 ex1.release(); 645 ex1 = new MediaExtractor(); 646 ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength()); 647 for (int i = 0; i < 2; i++) { 648 ex1.selectTrack(i); 649 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 650 int idx = 0; 651 while(true) { 652 int n1 = ex1.readSampleData(buf1, 0); 653 if (i == 0) { 654 if (n1 < 0) { 655 assertEquals(vid.size(), idx); 656 break; 657 } 658 assertEquals(vid.get(idx++).intValue(), n1); 659 } else if (i == 1) { 660 if (n1 < 0) { 661 assertEquals(aud.size(), idx); 662 break; 663 } 664 assertEquals(aud.get(idx++).intValue(), n1); 665 } else { 666 fail("unexpected track index: " + idx); 667 } 668 ex1.advance(); 669 } 670 ex1.unselectTrack(i); 671 } 672 673 // read the video track first, then rewind, enable the audio track in addition 674 // to the video track, and verify we get the right samples 675 ex1.release(); 676 ex1 = new MediaExtractor(); 677 ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength()); 678 for (int i = 0; i < 2; i++) { 679 ex1.selectTrack(i); 680 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 681 int vididx = 0; 682 int audidx = 0; 683 while(true) { 684 int n1 = ex1.readSampleData(buf1, 0); 685 if (n1 < 0) { 686 // we should have read all audio and all video samples at this point 687 assertEquals(vid.size(), vididx); 688 if (i == 1) { 689 assertEquals(aud.size(), audidx); 690 } 691 break; 692 } 693 int trackidx = ex1.getSampleTrackIndex(); 694 if (trackidx == 0) { 695 assertEquals(vid.get(vididx++).intValue(), n1); 696 } else if (trackidx == 1) { 697 assertEquals(aud.get(audidx++).intValue(), n1); 698 } else { 699 fail("unexpected track index: " + trackidx); 700 } 701 ex1.advance(); 702 } 703 } 704 705 // read both tracks from the start, then rewind and verify we get the right 706 // samples both times 707 ex1.release(); 708 ex1 = new MediaExtractor(); 709 ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength()); 710 for (int i = 0; i < 2; i++) { 711 ex1.selectTrack(0); 712 ex1.selectTrack(1); 713 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 714 int vididx = 0; 715 int audidx = 0; 716 while(true) { 717 int n1 = ex1.readSampleData(buf1, 0); 718 if (n1 < 0) { 719 // we should have read all audio and all video samples at this point 720 assertEquals(vid.size(), vididx); 721 assertEquals(aud.size(), audidx); 722 break; 723 } 724 int trackidx = ex1.getSampleTrackIndex(); 725 if (trackidx == 0) { 726 assertEquals(vid.get(vididx++).intValue(), n1); 727 } else if (trackidx == 1) { 728 assertEquals(aud.get(audidx++).intValue(), n1); 729 } else { 730 fail("unexpected track index: " + trackidx); 731 } 732 ex1.advance(); 733 } 734 } 735 736 } finally { 737 if (ex1 != null) { 738 ex1.release(); 739 } 740 if (fd1 != null) { 741 fd1.close(); 742 } 743 } 744 } 745 746 public void testVp9HdrStaticMetadata() throws Exception { 747 final String staticInfo = 748 "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42" + 749 "40 e8 03 64 00 e8 03 2c 01 " ; 750 testHdrStaticMetadata(R.raw.video_1280x720_vp9_hdr_static_3mbps, 751 staticInfo, true /*metadataInContainer*/); 752 } 753 754 public void testAV1HdrStaticMetadata() throws Exception { 755 final String staticInfo = 756 "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42" + 757 "40 e8 03 64 00 e8 03 2c 01 " ; 758 testHdrStaticMetadata(R.raw.video_1280x720_av1_hdr_static_3mbps, 759 staticInfo, false /*metadataInContainer*/); 760 } 761 762 public void testH265HDR10StaticMetadata() throws Exception { 763 // Expected value of MediaFormat.KEY_HDR_STATIC_INFO key. 764 // The associated value is a ByteBuffer. This buffer contains the raw contents of the 765 // Static Metadata Descriptor (including the descriptor ID) of an HDMI Dynamic Range and 766 // Mastering InfoFrame as defined by CTA-861.3. 767 // Media frameworks puts the display primaries in RGB order, here we verify the three 768 // primaries are indeed in this order and fail otherwise. 769 final String staticInfo = 770 "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42" + 771 "40 e8 03 00 00 e8 03 90 01 " ; 772 testHdrStaticMetadata(R.raw.video_1280x720_hevc_hdr10_static_3mbps, 773 staticInfo, false /*metadataInContainer*/); 774 } 775 776 private void testHdrStaticMetadata(int res, String pattern, boolean metadataInContainer) 777 throws Exception { 778 AssetFileDescriptor infd = null; 779 MediaExtractor extractor = null; 780 781 try { 782 infd = mResources.openRawResourceFd(res); 783 extractor = new MediaExtractor(); 784 extractor.setDataSource(infd.getFileDescriptor(), 785 infd.getStartOffset(), infd.getLength()); 786 787 MediaFormat format = null; 788 int trackIndex = -1; 789 for (int i = 0; i < extractor.getTrackCount(); i++) { 790 format = extractor.getTrackFormat(i); 791 if (format.getString(MediaFormat.KEY_MIME).startsWith("video/")) { 792 trackIndex = i; 793 break; 794 } 795 } 796 797 assertTrue("Extractor failed to extract video track", 798 format != null && trackIndex >= 0); 799 if (metadataInContainer) { 800 verifyHdrStaticInfo("Extractor failed to extract static info", format, pattern); 801 } 802 803 extractor.selectTrack(trackIndex); 804 Log.v(TAG, "format " + format); 805 806 String mime = format.getString(MediaFormat.KEY_MIME); 807 // setting profile and level 808 if (MediaFormat.MIMETYPE_VIDEO_HEVC.equals(mime)) { 809 assertEquals("Extractor set wrong profile", 810 MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10, 811 format.getInteger(MediaFormat.KEY_PROFILE)); 812 } else if (MediaFormat.MIMETYPE_VIDEO_VP9.equals(mime)) { 813 // The muxer might not have put VP9 CSD in the mkv, we manually patch 814 // it here so that we only test HDR when decoder supports it. 815 format.setInteger(MediaFormat.KEY_PROFILE, 816 MediaCodecInfo.CodecProfileLevel.VP9Profile2HDR); 817 } else if (MediaFormat.MIMETYPE_VIDEO_AV1.equals(mime)) { 818 // The muxer might not have put AV1 CSD in the webm, we manually patch 819 // it here so that we only test HDR when decoder supports it. 820 format.setInteger(MediaFormat.KEY_PROFILE, 821 MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10HDR10); 822 } else { 823 fail("Codec " + mime + " shouldn't be tested with this test!"); 824 } 825 String[] decoderNames = MediaUtils.getDecoderNames(format); 826 827 if (decoderNames == null || decoderNames.length == 0) { 828 MediaUtils.skipTest("No video codecs supports HDR"); 829 return; 830 } 831 832 final Surface surface = getActivity().getSurfaceHolder().getSurface(); 833 final MediaExtractor finalExtractor = extractor; 834 835 for (String name : decoderNames) { 836 Log.d(TAG, "Testing candicate decoder " + name); 837 CountDownLatch latch = new CountDownLatch(1); 838 extractor.seekTo(0, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); 839 840 MediaCodec decoder = MediaCodec.createByCodecName(name); 841 decoder.setCallback(new MediaCodec.Callback() { 842 boolean mInputEOS; 843 boolean mOutputReceived; 844 845 @Override 846 public void onOutputBufferAvailable( 847 MediaCodec codec, int index, BufferInfo info) { 848 if (mOutputReceived) { 849 return; 850 } 851 852 MediaFormat bufferFormat = codec.getOutputFormat(index); 853 Log.i(TAG, "got output buffer: format " + bufferFormat); 854 855 codec.releaseOutputBuffer(index, false); 856 verifyHdrStaticInfo("Output buffer has wrong static info", 857 bufferFormat, pattern); 858 mOutputReceived = true; 859 latch.countDown(); 860 } 861 862 @Override 863 public void onInputBufferAvailable(MediaCodec codec, int index) { 864 // keep queuing until intput EOS, or first output buffer received. 865 if (mInputEOS || mOutputReceived) { 866 return; 867 } 868 869 ByteBuffer inputBuffer = codec.getInputBuffer(index); 870 871 if (finalExtractor.getSampleTrackIndex() == -1) { 872 codec.queueInputBuffer( 873 index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); 874 mInputEOS = true; 875 } else { 876 int size = finalExtractor.readSampleData(inputBuffer, 0); 877 long timestamp = finalExtractor.getSampleTime(); 878 finalExtractor.advance(); 879 codec.queueInputBuffer(index, 0, size, timestamp, 0); 880 } 881 } 882 883 @Override 884 public void onError(MediaCodec codec, MediaCodec.CodecException e) { 885 Log.i(TAG, "got codec exception", e); 886 fail("received codec error during decode" + e); 887 } 888 889 @Override 890 public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) { 891 Log.i(TAG, "got output format: " + format); 892 verifyHdrStaticInfo("Output format has wrong static info", 893 format, pattern); 894 } 895 }); 896 decoder.configure(format, surface, null/*crypto*/, 0/*flags*/); 897 decoder.start(); 898 try { 899 assertTrue(latch.await(2000, TimeUnit.MILLISECONDS)); 900 } catch (InterruptedException e) { 901 fail("playback interrupted"); 902 } 903 decoder.stop(); 904 decoder.release(); 905 } 906 } finally { 907 if (extractor != null) { 908 extractor.release(); 909 } 910 if (infd != null) { 911 infd.close(); 912 } 913 } 914 } 915 916 private void verifyHdrStaticInfo(String reason, MediaFormat format, String pattern) { 917 ByteBuffer staticMetadataBuffer = format.containsKey("hdr-static-info") ? 918 format.getByteBuffer("hdr-static-info") : null; 919 assertTrue(reason + ": empty", 920 staticMetadataBuffer != null && staticMetadataBuffer.remaining() > 0); 921 assertTrue(reason + ": mismatch", 922 Arrays.equals(loadByteArrayFromString(pattern), staticMetadataBuffer.array())); 923 } 924 925 // helper to load byte[] from a String 926 private byte[] loadByteArrayFromString(final String str) { 927 Pattern pattern = Pattern.compile("[0-9a-fA-F]{2}"); 928 Matcher matcher = pattern.matcher(str); 929 // allocate a large enough byte array first 930 byte[] tempArray = new byte[str.length() / 2]; 931 int i = 0; 932 while (matcher.find()) { 933 tempArray[i++] = (byte)Integer.parseInt(matcher.group(), 16); 934 } 935 return Arrays.copyOfRange(tempArray, 0, i); 936 } 937 938 public void testDecodeFragmented() throws Exception { 939 testDecodeFragmented(R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz, 940 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented); 941 testDecodeFragmented(R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz, 942 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash); 943 } 944 945 private void testDecodeFragmented(int reference, int teststream) throws Exception { 946 AssetFileDescriptor fd1 = null; 947 AssetFileDescriptor fd2 = null; 948 try { 949 fd1 = mResources.openRawResourceFd(reference); 950 MediaExtractor ex1 = new MediaExtractor(); 951 ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength()); 952 953 fd2 = mResources.openRawResourceFd(teststream); 954 MediaExtractor ex2 = new MediaExtractor(); 955 ex2.setDataSource(fd2.getFileDescriptor(), fd2.getStartOffset(), fd2.getLength()); 956 957 assertEquals("different track count", ex1.getTrackCount(), ex2.getTrackCount()); 958 959 ByteBuffer buf1 = ByteBuffer.allocate(1024*1024); 960 ByteBuffer buf2 = ByteBuffer.allocate(1024*1024); 961 962 for (int i = 0; i < ex1.getTrackCount(); i++) { 963 // note: this assumes the tracks are reported in the order in which they appear 964 // in the file. 965 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 966 ex1.selectTrack(i); 967 ex2.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 968 ex2.selectTrack(i); 969 970 while(true) { 971 int n1 = ex1.readSampleData(buf1, 0); 972 int n2 = ex2.readSampleData(buf2, 0); 973 assertEquals("different buffer size on track " + i, n1, n2); 974 975 if (n1 < 0) { 976 break; 977 } 978 // see bug 13008204 979 buf1.limit(n1); 980 buf2.limit(n2); 981 buf1.rewind(); 982 buf2.rewind(); 983 984 assertEquals("limit does not match return value on track " + i, 985 n1, buf1.limit()); 986 assertEquals("limit does not match return value on track " + i, 987 n2, buf2.limit()); 988 989 assertEquals("buffer data did not match on track " + i, buf1, buf2); 990 991 ex1.advance(); 992 ex2.advance(); 993 } 994 ex1.unselectTrack(i); 995 ex2.unselectTrack(i); 996 } 997 } finally { 998 if (fd1 != null) { 999 fd1.close(); 1000 } 1001 if (fd2 != null) { 1002 fd2.close(); 1003 } 1004 } 1005 } 1006 1007 /** 1008 * Verify correct decoding of MPEG-4 AAC-LC mono and stereo streams 1009 */ 1010 public void testDecodeAacLcM4a() throws Exception { 1011 // mono 1012 decodeNtest(R.raw.sinesweep1_1ch_8khz_aot2_mp4, 40.f); 1013 decodeNtest(R.raw.sinesweep1_1ch_11khz_aot2_mp4, 40.f); 1014 decodeNtest(R.raw.sinesweep1_1ch_12khz_aot2_mp4, 40.f); 1015 decodeNtest(R.raw.sinesweep1_1ch_16khz_aot2_mp4, 40.f); 1016 decodeNtest(R.raw.sinesweep1_1ch_22khz_aot2_mp4, 40.f); 1017 decodeNtest(R.raw.sinesweep1_1ch_24khz_aot2_mp4, 40.f); 1018 decodeNtest(R.raw.sinesweep1_1ch_32khz_aot2_mp4, 40.f); 1019 decodeNtest(R.raw.sinesweep1_1ch_44khz_aot2_mp4, 40.f); 1020 decodeNtest(R.raw.sinesweep1_1ch_48khz_aot2_mp4, 40.f); 1021 // stereo 1022 decodeNtest(R.raw.sinesweep_2ch_8khz_aot2_mp4, 40.f); 1023 decodeNtest(R.raw.sinesweep_2ch_11khz_aot2_mp4, 40.f); 1024 decodeNtest(R.raw.sinesweep_2ch_12khz_aot2_mp4, 40.f); 1025 decodeNtest(R.raw.sinesweep_2ch_16khz_aot2_mp4, 40.f); 1026 decodeNtest(R.raw.sinesweep_2ch_22khz_aot2_mp4, 40.f); 1027 decodeNtest(R.raw.sinesweep_2ch_24khz_aot2_mp4, 40.f); 1028 decodeNtest(R.raw.sinesweep_2ch_32khz_aot2_mp4, 40.f); 1029 decodeNtest(R.raw.sinesweep_2ch_44khz_aot2_mp4, 40.f); 1030 decodeNtest(R.raw.sinesweep_2ch_48khz_aot2_mp4, 40.f); 1031 } 1032 1033 /** 1034 * Verify correct decoding of MPEG-4 AAC-LC 5.0 and 5.1 channel streams 1035 */ 1036 public void testDecodeAacLcMcM4a() throws Exception { 1037 AudioParameter decParams = new AudioParameter(); 1038 short[] decSamples = decodeToMemory(decParams, R.raw.noise_6ch_48khz_aot2_mp4, 1039 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1040 checkEnergy(decSamples, decParams, 6); 1041 decParams.reset(); 1042 1043 decSamples = decodeToMemory(decParams, R.raw.noise_5ch_44khz_aot2_mp4, 1044 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1045 checkEnergy(decSamples, decParams, 5); 1046 decParams.reset(); 1047 } 1048 1049 /** 1050 * Verify correct decoding of MPEG-4 HE-AAC mono and stereo streams 1051 */ 1052 public void testDecodeHeAacM4a() throws Exception { 1053 AudioParameter decParams = new AudioParameter(); 1054 // mono 1055 short[] decSamples = decodeToMemory(decParams, R.raw.noise_1ch_24khz_aot5_dr_sbr_sig1_mp4, 1056 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1057 checkEnergy(decSamples, decParams, 1); 1058 decParams.reset(); 1059 1060 decSamples = decodeToMemory(decParams, R.raw.noise_1ch_24khz_aot5_ds_sbr_sig1_mp4, 1061 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1062 checkEnergy(decSamples, decParams, 1); 1063 decParams.reset(); 1064 1065 decSamples = decodeToMemory(decParams, R.raw.noise_1ch_32khz_aot5_dr_sbr_sig2_mp4, 1066 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1067 checkEnergy(decSamples, decParams, 1); 1068 decParams.reset(); 1069 1070 decSamples = decodeToMemory(decParams, R.raw.noise_1ch_44khz_aot5_dr_sbr_sig0_mp4, 1071 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1072 checkEnergy(decSamples, decParams, 1); 1073 decParams.reset(); 1074 1075 decSamples = decodeToMemory(decParams, R.raw.noise_1ch_44khz_aot5_ds_sbr_sig2_mp4, 1076 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1077 checkEnergy(decSamples, decParams, 1); 1078 decParams.reset(); 1079 1080 // stereo 1081 decSamples = decodeToMemory(decParams, R.raw.noise_2ch_24khz_aot5_dr_sbr_sig2_mp4, 1082 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1083 checkEnergy(decSamples, decParams, 2); 1084 decParams.reset(); 1085 1086 decSamples = decodeToMemory(decParams, R.raw.noise_2ch_32khz_aot5_ds_sbr_sig2_mp4, 1087 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1088 checkEnergy(decSamples, decParams, 2); 1089 decParams.reset(); 1090 1091 decSamples = decodeToMemory(decParams, R.raw.noise_2ch_48khz_aot5_dr_sbr_sig1_mp4, 1092 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1093 checkEnergy(decSamples, decParams, 2); 1094 decParams.reset(); 1095 1096 decSamples = decodeToMemory(decParams, R.raw.noise_2ch_48khz_aot5_ds_sbr_sig1_mp4, 1097 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1098 checkEnergy(decSamples, decParams, 2); 1099 decParams.reset(); 1100 } 1101 1102 /** 1103 * Verify correct decoding of MPEG-4 HE-AAC 5.0 and 5.1 channel streams 1104 */ 1105 public void testDecodeHeAacMcM4a() throws Exception { 1106 AudioParameter decParams = new AudioParameter(); 1107 short[] decSamples = decodeToMemory(decParams, R.raw.noise_5ch_48khz_aot5_dr_sbr_sig1_mp4, 1108 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1109 checkEnergy(decSamples, decParams, 5); 1110 decParams.reset(); 1111 1112 decSamples = decodeToMemory(decParams, R.raw.noise_6ch_44khz_aot5_dr_sbr_sig2_mp4, 1113 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1114 checkEnergy(decSamples, decParams, 6); 1115 decParams.reset(); 1116 } 1117 1118 /** 1119 * Verify correct decoding of MPEG-4 HE-AAC v2 stereo streams 1120 */ 1121 public void testDecodeHeAacV2M4a() throws Exception { 1122 AudioParameter decParams = new AudioParameter(); 1123 short[] decSamples = decodeToMemory(decParams, R.raw.noise_2ch_24khz_aot29_dr_sbr_sig0_mp4, 1124 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1125 checkEnergy(decSamples, decParams, 2); 1126 1127 decSamples = decodeToMemory(decParams, R.raw.noise_2ch_44khz_aot29_dr_sbr_sig1_mp4, 1128 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1129 checkEnergy(decSamples, decParams, 2); 1130 1131 decSamples = decodeToMemory(decParams, R.raw.noise_2ch_48khz_aot29_dr_sbr_sig2_mp4, 1132 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1133 checkEnergy(decSamples, decParams, 2); 1134 } 1135 1136 /** 1137 * Verify correct decoding of MPEG-4 AAC-ELD mono and stereo streams 1138 */ 1139 public void testDecodeAacEldM4a() throws Exception { 1140 // mono 1141 decodeNtest(R.raw.sinesweep1_1ch_16khz_aot39_fl480_mp4, 40.f); 1142 decodeNtest(R.raw.sinesweep1_1ch_22khz_aot39_fl512_mp4, 40.f); 1143 decodeNtest(R.raw.sinesweep1_1ch_24khz_aot39_fl480_mp4, 40.f); 1144 decodeNtest(R.raw.sinesweep1_1ch_32khz_aot39_fl512_mp4, 40.f); 1145 decodeNtest(R.raw.sinesweep1_1ch_44khz_aot39_fl480_mp4, 40.f); 1146 decodeNtest(R.raw.sinesweep1_1ch_48khz_aot39_fl512_mp4, 40.f); 1147 1148 // stereo 1149 decodeNtest(R.raw.sinesweep_2ch_16khz_aot39_fl512_mp4, 40.f); 1150 decodeNtest(R.raw.sinesweep_2ch_22khz_aot39_fl480_mp4, 40.f); 1151 decodeNtest(R.raw.sinesweep_2ch_24khz_aot39_fl512_mp4, 40.f); 1152 decodeNtest(R.raw.sinesweep_2ch_32khz_aot39_fl480_mp4, 40.f); 1153 decodeNtest(R.raw.sinesweep_2ch_44khz_aot39_fl512_mp4, 40.f); 1154 decodeNtest(R.raw.sinesweep_2ch_48khz_aot39_fl480_mp4, 40.f); 1155 1156 AudioParameter decParams = new AudioParameter(); 1157 // mono 1158 short[] decSamples = decodeToMemory(decParams, R.raw.noise_1ch_16khz_aot39_ds_sbr_fl512_mp4, 1159 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1160 checkEnergy(decSamples, decParams, 1); 1161 decParams.reset(); 1162 1163 decSamples = decodeToMemory(decParams, R.raw.noise_1ch_24khz_aot39_ds_sbr_fl512_mp4, 1164 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1165 checkEnergy(decSamples, decParams, 1); 1166 decParams.reset(); 1167 1168 decSamples = decodeToMemory(decParams, R.raw.noise_1ch_32khz_aot39_dr_sbr_fl480_mp4, 1169 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1170 checkEnergy(decSamples, decParams, 1); 1171 decParams.reset(); 1172 1173 decSamples = decodeToMemory(decParams, R.raw.noise_1ch_44khz_aot39_ds_sbr_fl512_mp4, 1174 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1175 checkEnergy(decSamples, decParams, 1); 1176 decParams.reset(); 1177 1178 decSamples = decodeToMemory(decParams, R.raw.noise_1ch_48khz_aot39_dr_sbr_fl480_mp4, 1179 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1180 checkEnergy(decSamples, decParams, 1); 1181 decParams.reset(); 1182 1183 // stereo 1184 decSamples = decodeToMemory(decParams, R.raw.noise_2ch_22khz_aot39_ds_sbr_fl512_mp4, 1185 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1186 checkEnergy(decSamples, decParams, 2); 1187 decParams.reset(); 1188 1189 decSamples = decodeToMemory(decParams, R.raw.noise_2ch_32khz_aot39_ds_sbr_fl512_mp4, 1190 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1191 checkEnergy(decSamples, decParams, 2); 1192 decParams.reset(); 1193 1194 decSamples = decodeToMemory(decParams, R.raw.noise_2ch_44khz_aot39_dr_sbr_fl480_mp4, 1195 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1196 checkEnergy(decSamples, decParams, 2); 1197 decParams.reset(); 1198 1199 decSamples = decodeToMemory(decParams, R.raw.noise_2ch_48khz_aot39_ds_sbr_fl512_mp4, 1200 RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1201 checkEnergy(decSamples, decParams, 2); 1202 decParams.reset(); 1203 } 1204 1205 /** 1206 * Perform a segmented energy analysis on given audio signal samples and run several tests on 1207 * the energy values. 1208 * 1209 * The main purpose is to verify whether an AAC decoder implementation applies Spectral Band 1210 * Replication (SBR) and Parametric Stereo (PS) correctly. Both tools are inherent parts to the 1211 * MPEG-4 HE-AAC and HE-AAC v2 audio codecs. 1212 * 1213 * In addition, this test can verify the correct decoding of multi-channel (e.g. 5.1 channel) 1214 * streams or the creation of a mixdown signal. 1215 * 1216 * Note: This test procedure is not an MPEG Conformance Test and can not serve as a replacement. 1217 * 1218 * @param decSamples the decoded audio samples to be tested 1219 * @param decParams the audio parameters of the given audio samples (decSamples) 1220 * @param encNch the encoded number of audio channels (number of channels of the original 1221 * input) 1222 * @param nrgRatioThresh threshold to classify the energy ratios ]0.0, 1.0[ 1223 * @throws RuntimeException 1224 */ 1225 protected void checkEnergy(short[] decSamples, AudioParameter decParams, int encNch, 1226 float nrgRatioThresh) throws RuntimeException 1227 { 1228 final int nSegPerBlk = 4; // the number of segments per block 1229 final int nCh = decParams.getNumChannels(); // the number of input channels 1230 final int nBlkSmp = decParams.getSamplingRate(); // length of one (LB/HB) block [samples] 1231 final int nSegSmp = nBlkSmp / nSegPerBlk; // length of one segment [samples] 1232 final int smplPerChan = decSamples.length / nCh; // actual # samples per channel (total) 1233 1234 final int nSegSmpTot = nSegSmp * nCh; // actual # samples per segment (all ch) 1235 final int nSegChOffst = 2 * nSegPerBlk; // signal offset between chans [segments] 1236 final int procNch = Math.min(nCh, encNch); // the number of channels to be analyzed 1237 if (encNch > 4) { 1238 assertTrue(String.format("multichannel content (%dch) was downmixed (%dch)", 1239 encNch, nCh), procNch > 4); 1240 } 1241 assertTrue(String.format("got less channels(%d) than encoded (%d)", nCh, encNch), 1242 nCh >= encNch); 1243 1244 final int encEffNch = (encNch > 5) ? encNch-1 : encNch; // all original configs with more 1245 // ... than five channel have an LFE */ 1246 final int expSmplPerChan = Math.max(encEffNch, 2) * nSegChOffst * nSegSmp; 1247 final boolean isDmx = nCh < encNch; // flag telling that input is dmx signal 1248 int effProcNch = procNch; // the num analyzed channels with signal 1249 1250 assertTrue("got less input samples than expected", smplPerChan >= expSmplPerChan); 1251 1252 // get the signal offset by counting zero samples at the very beginning (over all channels) 1253 final int zeroSigThresh = 1; // sample value threshold for signal search 1254 int signalStart = smplPerChan; // receives the number of samples that 1255 // ... are in front of the actual signal 1256 int noiseStart = signalStart; // receives the number of null samples 1257 // ... (per chan) at the very beginning 1258 for (int smpl = 0; smpl < decSamples.length; smpl++) { 1259 int value = Math.abs(decSamples[smpl]); 1260 if (value > 0 && noiseStart == signalStart) { 1261 noiseStart = smpl / nCh; // store start of prepended noise 1262 } // ... (can be same as signalStart) 1263 if (value > zeroSigThresh) { 1264 signalStart = smpl / nCh; // store signal start offset [samples] 1265 break; 1266 } 1267 } 1268 signalStart = (signalStart > noiseStart+1) ? signalStart : noiseStart; 1269 assertTrue ("no signal found in any channel!", signalStart < smplPerChan); 1270 final int totSeg = (smplPerChan-signalStart) / nSegSmp; // max num seg that fit into signal 1271 final int totSmp = nSegSmp * totSeg; // max num relevant samples (per channel) 1272 assertTrue("no segments left to test after signal search", totSeg > 0); 1273 1274 // get the energies and the channel offsets by searching for the first segment above the 1275 // energy threshold 1276 final double zeroMaxNrgRatio = 0.001f; // ratio of zeroNrgThresh to the max nrg 1277 double zeroNrgThresh = nSegSmp * nSegSmp; // threshold to classify segment energies 1278 double totMaxNrg = 0.0f; // will store the max seg nrg over all ch 1279 double[][] nrg = new double[procNch][totSeg]; // array receiving the segment energies 1280 int[] offset = new int[procNch]; // array for channel offsets 1281 boolean[] sigSeg = new boolean[totSeg]; // array receiving the segment ... 1282 // ... energy status over all channels 1283 for (int ch = 0; ch < procNch; ch++) { 1284 offset[ch] = -1; 1285 for (int seg = 0; seg < totSeg; seg++) { 1286 final int smpStart = (signalStart * nCh) + (seg * nSegSmpTot) + ch; 1287 final int smpStop = smpStart + nSegSmpTot; 1288 for (int smpl = smpStart; smpl < smpStop; smpl += nCh) { 1289 nrg[ch][seg] += decSamples[smpl] * decSamples[smpl]; // accumulate segment nrg 1290 } 1291 if (nrg[ch][seg] > zeroNrgThresh && offset[ch] < 0) { // store 1st segment (index) 1292 offset[ch] = seg / nSegChOffst; // ... per ch which has energy above the 1293 } // ... threshold to get the ch offsets 1294 if (nrg[ch][seg] > totMaxNrg) { 1295 totMaxNrg = nrg[ch][seg]; // store the max segment nrg over all ch 1296 } 1297 sigSeg[seg] |= nrg[ch][seg] > zeroNrgThresh; // store whether the channel has 1298 // ... energy in this segment 1299 } 1300 if (offset[ch] < 0) { // if one channel has no signal it is 1301 effProcNch -= 1; // ... most probably the LFE 1302 offset[ch] = effProcNch; // the LFE is no effective channel 1303 } 1304 if (ch == 0) { // recalculate the zero signal threshold 1305 zeroNrgThresh = zeroMaxNrgRatio * totMaxNrg; // ... based on the 1st channels max 1306 } // ... energy for all subsequent checks 1307 } 1308 // check the channel mapping 1309 assertTrue("more than one LFE detected", effProcNch >= procNch - 1); 1310 assertTrue(String.format("less samples decoded than expected: %d < %d", 1311 decSamples.length-(signalStart * nCh), totSmp * effProcNch), 1312 decSamples.length-(signalStart * nCh) >= totSmp * effProcNch); 1313 if (procNch >= 5) { // for multi-channel signals the only 1314 final int[] frontChMap1 = {2, 0, 1}; // valid front channel orders are L, R, C 1315 final int[] frontChMap2 = {0, 1, 2}; // or C, L, R (L=left, R=right, C=center) 1316 if ( !(Arrays.equals(Arrays.copyOfRange(offset, 0, 3), frontChMap1) 1317 || Arrays.equals(Arrays.copyOfRange(offset, 0, 3), frontChMap2)) ) { 1318 fail("wrong front channel mapping"); 1319 } 1320 } 1321 // check whether every channel occurs exactly once 1322 int[] chMap = new int[nCh]; // mapping array to sort channels 1323 for (int ch = 0; ch < effProcNch; ch++) { 1324 int occurred = 0; 1325 for (int idx = 0; idx < procNch; idx++) { 1326 if (offset[idx] == ch) { 1327 occurred += 1; 1328 chMap[ch] = idx; // create mapping table to address chans 1329 } // ... from front to back 1330 } // the LFE must be last 1331 assertTrue(String.format("channel %d occurs %d times in the mapping", ch, occurred), 1332 occurred == 1); 1333 } 1334 1335 // go over all segment energies in all channels and check them 1336 double refMinNrg = zeroNrgThresh; // reference min energy for the 1st ch; 1337 // others will be compared against 1st 1338 for (int ch = 0; ch < procNch; ch++) { 1339 int idx = chMap[ch]; // resolve channel mapping 1340 final int ofst = offset[idx] * nSegChOffst; // signal offset [segments] 1341 if (ch < effProcNch && ofst < totSeg) { 1342 int nrgSegEnd; // the last segment that has energy 1343 int nrgSeg; // the number of segments with energy 1344 if ((encNch <= 2) && (ch == 0)) { // the first channel of a mono or ... 1345 nrgSeg = totSeg; // stereo signal has full signal ... 1346 } else { // all others have one LB + one HB block 1347 nrgSeg = Math.min(totSeg, (2 * nSegPerBlk) + ofst) - ofst; 1348 } 1349 nrgSegEnd = ofst + nrgSeg; 1350 // find min and max energy of all segments that should have signal 1351 double minNrg = nrg[idx][ofst]; // channels minimum segment energy 1352 double maxNrg = nrg[idx][ofst]; // channels maximum segment energy 1353 for (int seg = ofst+1; seg < nrgSegEnd; seg++) { // values of 1st segment 1354 if (nrg[idx][seg] < minNrg) minNrg = nrg[idx][seg]; // ... already assigned 1355 if (nrg[idx][seg] > maxNrg) maxNrg = nrg[idx][seg]; 1356 } 1357 assertTrue(String.format("max energy of channel %d is zero", ch), 1358 maxNrg > 0.0f); 1359 assertTrue(String.format("channel %d has not enough energy", ch), 1360 minNrg >= refMinNrg); // check the channels minimum energy 1361 if (ch == 0) { // use 85% of 1st channels min energy as 1362 refMinNrg = minNrg * 0.85f; // ... reference the other chs must meet 1363 } else if (isDmx && (ch == 1)) { // in case of mixdown signal the energy 1364 refMinNrg *= 0.50f; // ... can be lower depending on the 1365 } // ... downmix equation 1366 // calculate and check the energy ratio 1367 final double nrgRatio = minNrg / maxNrg; 1368 assertTrue(String.format("energy ratio of channel %d below threshold", ch), 1369 nrgRatio >= nrgRatioThresh); 1370 if (!isDmx) { 1371 if (nrgSegEnd < totSeg) { 1372 // consider that some noise can extend into the subsequent segment 1373 // allow this to be at max 20% of the channels minimum energy 1374 assertTrue(String.format("min energy after noise above threshold (%.2f)", 1375 nrg[idx][nrgSegEnd]), 1376 nrg[idx][nrgSegEnd] < minNrg * 0.20f); 1377 nrgSegEnd += 1; 1378 } 1379 } else { // ignore all subsequent segments 1380 nrgSegEnd = totSeg; // ... in case of a mixdown signal 1381 } 1382 // zero-out the verified energies to simplify the subsequent check 1383 for (int seg = ofst; seg < nrgSegEnd; seg++) nrg[idx][seg] = 0.0f; 1384 } 1385 // check zero signal parts 1386 for (int seg = 0; seg < totSeg; seg++) { 1387 assertTrue(String.format("segment %d in channel %d has signal where should " + 1388 "be none (%.2f)", seg, ch, nrg[idx][seg]), nrg[idx][seg] < zeroNrgThresh); 1389 } 1390 } 1391 // test whether each segment has energy in at least one channel 1392 for (int seg = 0; seg < totSeg; seg++) { 1393 assertTrue(String.format("no channel has energy in segment %d", seg), sigSeg[seg]); 1394 } 1395 } 1396 1397 private void checkEnergy(short[] decSamples, AudioParameter decParams, int encNch) 1398 throws RuntimeException { 1399 checkEnergy(decSamples, decParams, encNch, 0.50f); // default energy ratio threshold: 0.50 1400 } 1401 1402 /** 1403 * Calculate the RMS of the difference signal between a given signal and the reference samples 1404 * located in mMasterBuffer. 1405 * @param signal the decoded samples to test 1406 * @return RMS of error signal 1407 * @throws RuntimeException 1408 */ 1409 private double getRmsError(short[] signal) throws RuntimeException { 1410 long totalErrorSquared = 0; 1411 int stride = mMasterBuffer.length / signal.length; 1412 assertEquals("wrong data size", mMasterBuffer.length, signal.length * stride); 1413 1414 for (int i = 0; i < signal.length; i++) { 1415 short sample = signal[i]; 1416 short mastersample = mMasterBuffer[i * stride]; 1417 int d = sample - mastersample; 1418 totalErrorSquared += d * d; 1419 } 1420 long avgErrorSquared = (totalErrorSquared / signal.length); 1421 return Math.sqrt(avgErrorSquared); 1422 } 1423 1424 /** 1425 * Decode a given input stream and compare the output against the reference signal. The RMS of 1426 * the error signal must be below the given threshold (maxerror). 1427 * Important note about the test signals: this method expects test signals to have been 1428 * "stretched" relative to the reference signal. The reference, sinesweepraw, is 3s long at 1429 * 44100Hz. For instance for comparing this reference to a test signal at 8000Hz, the test 1430 * signal needs to be 44100/8000 = 5.5125 times longer, containing frequencies 5.5125 1431 * times lower than the reference. 1432 * @param testinput the file to decode 1433 * @param maxerror the maximum allowed root mean squared error 1434 * @throws Exception 1435 */ 1436 private void decodeNtest(int testinput, float maxerror) throws Exception { 1437 String localTag = TAG + "#decodeNtest"; 1438 1439 AudioParameter decParams = new AudioParameter(); 1440 short[] decoded = decodeToMemory(decParams, testinput, RESET_MODE_NONE, CONFIG_MODE_NONE, 1441 -1, null); 1442 double rmse = getRmsError(decoded); 1443 1444 assertTrue("decoding error too big: " + rmse, rmse <= maxerror); 1445 Log.v(localTag, String.format("rms = %f (max = %f)", rmse, maxerror)); 1446 } 1447 1448 private void monoTest(int res, int expectedLength) throws Exception { 1449 short [] mono = decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1450 if (mono.length == expectedLength) { 1451 // expected 1452 } else if (mono.length == expectedLength * 2) { 1453 // the decoder output 2 channels instead of 1, check that the left and right channel 1454 // are identical 1455 for (int i = 0; i < mono.length; i += 2) { 1456 assertEquals("mismatched samples at " + i, mono[i], mono[i+1]); 1457 } 1458 } else { 1459 fail("wrong number of samples: " + mono.length); 1460 } 1461 1462 short [] mono2 = decodeToMemory(res, RESET_MODE_RECONFIGURE, CONFIG_MODE_NONE, -1, null); 1463 1464 assertEquals("count different after reconfigure: ", mono.length, mono2.length); 1465 for (int i = 0; i < mono.length; i++) { 1466 assertEquals("samples at " + i + " don't match", mono[i], mono2[i]); 1467 } 1468 1469 short [] mono3 = decodeToMemory(res, RESET_MODE_FLUSH, CONFIG_MODE_NONE, -1, null); 1470 1471 assertEquals("count different after flush: ", mono.length, mono3.length); 1472 for (int i = 0; i < mono.length; i++) { 1473 assertEquals("samples at " + i + " don't match", mono[i], mono3[i]); 1474 } 1475 } 1476 1477 /** 1478 * @param testinput the file to decode 1479 * @param maxerror the maximum allowed root mean squared error 1480 * @throws IOException 1481 */ 1482 private void decode(int testinput, float maxerror) throws IOException { 1483 1484 short[] decoded = decodeToMemory(testinput, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 1485 1486 assertEquals("wrong data size", mMasterBuffer.length, decoded.length); 1487 1488 double rmse = getRmsError(decoded); 1489 1490 assertTrue("decoding error too big: " + rmse, rmse <= maxerror); 1491 1492 int[] resetModes = new int[] { RESET_MODE_NONE, RESET_MODE_RECONFIGURE, 1493 RESET_MODE_FLUSH, RESET_MODE_EOS_FLUSH }; 1494 int[] configModes = new int[] { CONFIG_MODE_NONE, CONFIG_MODE_QUEUE }; 1495 1496 for (int conf : configModes) { 1497 for (int reset : resetModes) { 1498 if (conf == CONFIG_MODE_NONE && reset == RESET_MODE_NONE) { 1499 // default case done outside of loop 1500 continue; 1501 } 1502 if (conf == CONFIG_MODE_QUEUE && !hasAudioCsd(testinput)) { 1503 continue; 1504 } 1505 1506 String params = String.format("(using reset: %d, config: %s)", reset, conf); 1507 short[] decoded2 = decodeToMemory(testinput, reset, conf, -1, null); 1508 assertEquals("count different with reconfigure" + params, 1509 decoded.length, decoded2.length); 1510 for (int i = 0; i < decoded.length; i++) { 1511 assertEquals("samples don't match" + params, decoded[i], decoded2[i]); 1512 } 1513 } 1514 } 1515 } 1516 1517 private boolean hasAudioCsd(int testinput) throws IOException { 1518 AssetFileDescriptor fd = null; 1519 try { 1520 1521 fd = mResources.openRawResourceFd(testinput); 1522 MediaExtractor extractor = new MediaExtractor(); 1523 extractor.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength()); 1524 MediaFormat format = extractor.getTrackFormat(0); 1525 1526 return format.containsKey(CSD_KEYS[0]); 1527 1528 } finally { 1529 if (fd != null) { 1530 fd.close(); 1531 } 1532 } 1533 } 1534 1535 // Class handling all audio parameters relevant for testing 1536 protected static class AudioParameter { 1537 1538 public AudioParameter() { 1539 this.reset(); 1540 } 1541 1542 public void reset() { 1543 this.numChannels = 0; 1544 this.samplingRate = 0; 1545 } 1546 1547 public int getNumChannels() { 1548 return this.numChannels; 1549 } 1550 1551 public int getSamplingRate() { 1552 return this.samplingRate; 1553 } 1554 1555 public void setNumChannels(int numChannels) { 1556 this.numChannels = numChannels; 1557 } 1558 1559 public void setSamplingRate(int samplingRate) { 1560 this.samplingRate = samplingRate; 1561 } 1562 1563 private int numChannels; 1564 private int samplingRate; 1565 } 1566 1567 private short[] decodeToMemory(int testinput, int resetMode, int configMode, 1568 int eossample, List<Long> timestamps) throws IOException { 1569 1570 AudioParameter audioParams = new AudioParameter(); 1571 return decodeToMemory(audioParams, testinput, resetMode, configMode, eossample, timestamps); 1572 } 1573 1574 private short[] decodeToMemory(AudioParameter audioParams, int testinput, int resetMode, 1575 int configMode, int eossample, List<Long> timestamps) 1576 throws IOException 1577 { 1578 String localTag = TAG + "#decodeToMemory"; 1579 Log.v(localTag, String.format("reset = %d; config: %s", resetMode, configMode)); 1580 short [] decoded = new short[0]; 1581 int decodedIdx = 0; 1582 1583 AssetFileDescriptor testFd = mResources.openRawResourceFd(testinput); 1584 1585 MediaExtractor extractor; 1586 MediaCodec codec; 1587 ByteBuffer[] codecInputBuffers; 1588 ByteBuffer[] codecOutputBuffers; 1589 1590 extractor = new MediaExtractor(); 1591 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 1592 testFd.getLength()); 1593 testFd.close(); 1594 1595 assertEquals("wrong number of tracks", 1, extractor.getTrackCount()); 1596 MediaFormat format = extractor.getTrackFormat(0); 1597 String mime = format.getString(MediaFormat.KEY_MIME); 1598 assertTrue("not an audio file", mime.startsWith("audio/")); 1599 1600 MediaFormat configFormat = format; 1601 codec = MediaCodec.createDecoderByType(mime); 1602 if (configMode == CONFIG_MODE_QUEUE && format.containsKey(CSD_KEYS[0])) { 1603 configFormat = MediaFormat.createAudioFormat(mime, 1604 format.getInteger(MediaFormat.KEY_SAMPLE_RATE), 1605 format.getInteger(MediaFormat.KEY_CHANNEL_COUNT)); 1606 1607 configFormat.setLong(MediaFormat.KEY_DURATION, 1608 format.getLong(MediaFormat.KEY_DURATION)); 1609 String[] keys = new String[] { "max-input-size", "encoder-delay", "encoder-padding" }; 1610 for (String k : keys) { 1611 if (format.containsKey(k)) { 1612 configFormat.setInteger(k, format.getInteger(k)); 1613 } 1614 } 1615 } 1616 Log.v(localTag, "configuring with " + configFormat); 1617 codec.configure(configFormat, null /* surface */, null /* crypto */, 0 /* flags */); 1618 1619 codec.start(); 1620 codecInputBuffers = codec.getInputBuffers(); 1621 codecOutputBuffers = codec.getOutputBuffers(); 1622 1623 if (resetMode == RESET_MODE_RECONFIGURE) { 1624 codec.stop(); 1625 codec.configure(configFormat, null /* surface */, null /* crypto */, 0 /* flags */); 1626 codec.start(); 1627 codecInputBuffers = codec.getInputBuffers(); 1628 codecOutputBuffers = codec.getOutputBuffers(); 1629 } else if (resetMode == RESET_MODE_FLUSH) { 1630 codec.flush(); 1631 } 1632 1633 extractor.selectTrack(0); 1634 1635 if (configMode == CONFIG_MODE_QUEUE) { 1636 queueConfig(codec, format); 1637 } 1638 1639 // start decoding 1640 final long kTimeOutUs = 5000; 1641 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 1642 boolean sawInputEOS = false; 1643 boolean sawOutputEOS = false; 1644 int noOutputCounter = 0; 1645 int samplecounter = 0; 1646 while (!sawOutputEOS && noOutputCounter < 50) { 1647 noOutputCounter++; 1648 if (!sawInputEOS) { 1649 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs); 1650 1651 if (inputBufIndex >= 0) { 1652 ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; 1653 1654 int sampleSize = 1655 extractor.readSampleData(dstBuf, 0 /* offset */); 1656 1657 long presentationTimeUs = 0; 1658 1659 if (sampleSize < 0 && eossample > 0) { 1660 fail("test is broken: never reached eos sample"); 1661 } 1662 if (sampleSize < 0) { 1663 Log.d(TAG, "saw input EOS."); 1664 sawInputEOS = true; 1665 sampleSize = 0; 1666 } else { 1667 if (samplecounter == eossample) { 1668 sawInputEOS = true; 1669 } 1670 samplecounter++; 1671 presentationTimeUs = extractor.getSampleTime(); 1672 } 1673 codec.queueInputBuffer( 1674 inputBufIndex, 1675 0 /* offset */, 1676 sampleSize, 1677 presentationTimeUs, 1678 sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); 1679 1680 if (!sawInputEOS) { 1681 extractor.advance(); 1682 } 1683 } 1684 } 1685 1686 int res = codec.dequeueOutputBuffer(info, kTimeOutUs); 1687 1688 if (res >= 0) { 1689 //Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs); 1690 1691 if (info.size > 0) { 1692 noOutputCounter = 0; 1693 if (timestamps != null) { 1694 timestamps.add(info.presentationTimeUs); 1695 } 1696 } 1697 if (info.size > 0 && 1698 resetMode != RESET_MODE_NONE && resetMode != RESET_MODE_EOS_FLUSH) { 1699 // once we've gotten some data out of the decoder, reset and start again 1700 if (resetMode == RESET_MODE_RECONFIGURE) { 1701 codec.stop(); 1702 codec.configure(configFormat, null /* surface */, null /* crypto */, 1703 0 /* flags */); 1704 codec.start(); 1705 codecInputBuffers = codec.getInputBuffers(); 1706 codecOutputBuffers = codec.getOutputBuffers(); 1707 if (configMode == CONFIG_MODE_QUEUE) { 1708 queueConfig(codec, format); 1709 } 1710 } else /* resetMode == RESET_MODE_FLUSH */ { 1711 codec.flush(); 1712 } 1713 resetMode = RESET_MODE_NONE; 1714 extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 1715 sawInputEOS = false; 1716 samplecounter = 0; 1717 if (timestamps != null) { 1718 timestamps.clear(); 1719 } 1720 continue; 1721 } 1722 1723 int outputBufIndex = res; 1724 ByteBuffer buf = codecOutputBuffers[outputBufIndex]; 1725 1726 if (decodedIdx + (info.size / 2) >= decoded.length) { 1727 decoded = Arrays.copyOf(decoded, decodedIdx + (info.size / 2)); 1728 } 1729 1730 buf.position(info.offset); 1731 for (int i = 0; i < info.size; i += 2) { 1732 decoded[decodedIdx++] = buf.getShort(); 1733 } 1734 1735 codec.releaseOutputBuffer(outputBufIndex, false /* render */); 1736 1737 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 1738 Log.d(TAG, "saw output EOS."); 1739 if (resetMode == RESET_MODE_EOS_FLUSH) { 1740 resetMode = RESET_MODE_NONE; 1741 codec.flush(); 1742 extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 1743 sawInputEOS = false; 1744 samplecounter = 0; 1745 decoded = new short[0]; 1746 decodedIdx = 0; 1747 if (timestamps != null) { 1748 timestamps.clear(); 1749 } 1750 } else { 1751 sawOutputEOS = true; 1752 } 1753 } 1754 } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 1755 codecOutputBuffers = codec.getOutputBuffers(); 1756 1757 Log.d(TAG, "output buffers have changed."); 1758 } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 1759 MediaFormat oformat = codec.getOutputFormat(); 1760 audioParams.setNumChannels(oformat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)); 1761 audioParams.setSamplingRate(oformat.getInteger(MediaFormat.KEY_SAMPLE_RATE)); 1762 Log.d(TAG, "output format has changed to " + oformat); 1763 } else { 1764 Log.d(TAG, "dequeueOutputBuffer returned " + res); 1765 } 1766 } 1767 if (noOutputCounter >= 50) { 1768 fail("decoder stopped outputing data"); 1769 } 1770 1771 codec.stop(); 1772 codec.release(); 1773 return decoded; 1774 } 1775 1776 private static void queueConfig(MediaCodec codec, MediaFormat format) { 1777 for (String csdKey : CSD_KEYS) { 1778 if (!format.containsKey(csdKey)) { 1779 continue; 1780 } 1781 ByteBuffer[] codecInputBuffers = codec.getInputBuffers(); 1782 int inputBufIndex = codec.dequeueInputBuffer(-1); 1783 if (inputBufIndex < 0) { 1784 fail("failed to queue configuration buffer " + csdKey); 1785 } else { 1786 ByteBuffer csd = (ByteBuffer) format.getByteBuffer(csdKey).rewind(); 1787 Log.v(TAG + "#queueConfig", String.format("queueing %s:%s", csdKey, csd)); 1788 codecInputBuffers[inputBufIndex].put(csd); 1789 codec.queueInputBuffer( 1790 inputBufIndex, 1791 0 /* offset */, 1792 csd.limit(), 1793 0 /* presentation time (us) */, 1794 MediaCodec.BUFFER_FLAG_CODEC_CONFIG); 1795 } 1796 } 1797 } 1798 1799 public void testDecodeWithEOSOnLastBuffer() throws Exception { 1800 testDecodeWithEOSOnLastBuffer(R.raw.sinesweepm4a); 1801 testDecodeWithEOSOnLastBuffer(R.raw.sinesweepmp3lame); 1802 testDecodeWithEOSOnLastBuffer(R.raw.sinesweepmp3smpb); 1803 testDecodeWithEOSOnLastBuffer(R.raw.sinesweepopus); 1804 testDecodeWithEOSOnLastBuffer(R.raw.sinesweepopusmp4); 1805 testDecodeWithEOSOnLastBuffer(R.raw.sinesweepwav); 1806 testDecodeWithEOSOnLastBuffer(R.raw.sinesweepflacmkv); 1807 testDecodeWithEOSOnLastBuffer(R.raw.sinesweepflac); 1808 testDecodeWithEOSOnLastBuffer(R.raw.sinesweepflacmp4); 1809 testDecodeWithEOSOnLastBuffer(R.raw.sinesweepogg); 1810 testDecodeWithEOSOnLastBuffer(R.raw.sinesweepoggmkv); 1811 testDecodeWithEOSOnLastBuffer(R.raw.sinesweepoggmp4); 1812 } 1813 1814 /* setting EOS on the last full input buffer should be equivalent to setting EOS on an empty 1815 * input buffer after all the full ones. */ 1816 private void testDecodeWithEOSOnLastBuffer(int res) throws Exception { 1817 int numsamples = countSamples(res); 1818 assertTrue(numsamples != 0); 1819 1820 List<Long> timestamps1 = new ArrayList<Long>(); 1821 short[] decode1 = decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, timestamps1); 1822 1823 List<Long> timestamps2 = new ArrayList<Long>(); 1824 short[] decode2 = decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, numsamples - 1, 1825 timestamps2); 1826 1827 // check that the data and the timestamps are the same for EOS-on-last and EOS-after-last 1828 assertEquals(decode1.length, decode2.length); 1829 assertTrue(Arrays.equals(decode1, decode2)); 1830 assertEquals(timestamps1.size(), timestamps2.size()); 1831 assertTrue(timestamps1.equals(timestamps2)); 1832 1833 // ... and that this is also true when reconfiguring the codec 1834 timestamps2.clear(); 1835 decode2 = decodeToMemory(res, RESET_MODE_RECONFIGURE, CONFIG_MODE_NONE, -1, timestamps2); 1836 assertTrue(Arrays.equals(decode1, decode2)); 1837 assertTrue(timestamps1.equals(timestamps2)); 1838 timestamps2.clear(); 1839 decode2 = decodeToMemory(res, RESET_MODE_RECONFIGURE, CONFIG_MODE_NONE, numsamples - 1, 1840 timestamps2); 1841 assertEquals(decode1.length, decode2.length); 1842 assertTrue(Arrays.equals(decode1, decode2)); 1843 assertTrue(timestamps1.equals(timestamps2)); 1844 1845 // ... and that this is also true when flushing the codec 1846 timestamps2.clear(); 1847 decode2 = decodeToMemory(res, RESET_MODE_FLUSH, CONFIG_MODE_NONE, -1, timestamps2); 1848 assertTrue(Arrays.equals(decode1, decode2)); 1849 assertTrue(timestamps1.equals(timestamps2)); 1850 timestamps2.clear(); 1851 decode2 = decodeToMemory(res, RESET_MODE_FLUSH, CONFIG_MODE_NONE, numsamples - 1, 1852 timestamps2); 1853 assertEquals(decode1.length, decode2.length); 1854 assertTrue(Arrays.equals(decode1, decode2)); 1855 assertTrue(timestamps1.equals(timestamps2)); 1856 } 1857 1858 private int countSamples(int res) throws IOException { 1859 AssetFileDescriptor testFd = mResources.openRawResourceFd(res); 1860 1861 MediaExtractor extractor = new MediaExtractor(); 1862 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 1863 testFd.getLength()); 1864 testFd.close(); 1865 extractor.selectTrack(0); 1866 int numsamples = extractor.getSampleTime() < 0 ? 0 : 1; 1867 while (extractor.advance()) { 1868 numsamples++; 1869 } 1870 return numsamples; 1871 } 1872 1873 private void testDecode(int testVideo, int frameNum) throws Exception { 1874 if (!MediaUtils.checkCodecForResource(mContext, testVideo, 0 /* track */)) { 1875 return; // skip 1876 } 1877 1878 // Decode to Surface. 1879 Surface s = getActivity().getSurfaceHolder().getSurface(); 1880 int frames1 = countFrames(testVideo, RESET_MODE_NONE, -1 /* eosframe */, s); 1881 assertEquals("wrong number of frames decoded", frameNum, frames1); 1882 1883 // Decode to buffer. 1884 int frames2 = countFrames(testVideo, RESET_MODE_NONE, -1 /* eosframe */, null); 1885 assertEquals("different number of frames when using Surface", frames1, frames2); 1886 } 1887 1888 public void testCodecBasicH264() throws Exception { 1889 testDecode(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 240); 1890 } 1891 1892 public void testCodecBasicHEVC() throws Exception { 1893 testDecode( 1894 R.raw.bbb_s1_720x480_mp4_hevc_mp3_1600kbps_30fps_aac_he_6ch_240kbps_48000hz, 300); 1895 } 1896 1897 public void testCodecBasicH263() throws Exception { 1898 testDecode(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 122); 1899 } 1900 1901 public void testCodecBasicMpeg2() throws Exception { 1902 testDecode(R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz, 300); 1903 } 1904 1905 public void testCodecBasicMpeg4() throws Exception { 1906 testDecode(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 249); 1907 } 1908 1909 public void testCodecBasicVP8() throws Exception { 1910 testDecode(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz, 240); 1911 } 1912 1913 public void testCodecBasicVP9() throws Exception { 1914 testDecode(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz, 240); 1915 } 1916 1917 public void testCodecBasicAV1() throws Exception { 1918 testDecode(R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz, 300); 1919 } 1920 1921 public void testH264Decode320x240() throws Exception { 1922 testDecode(R.raw.bbb_s1_320x240_mp4_h264_mp2_800kbps_30fps_aac_lc_5ch_240kbps_44100hz, 300); 1923 } 1924 1925 public void testH264Decode720x480() throws Exception { 1926 testDecode(R.raw.bbb_s1_720x480_mp4_h264_mp3_2mbps_30fps_aac_lc_5ch_320kbps_48000hz, 300); 1927 } 1928 1929 public void testH264Decode30fps1280x720Tv() throws Exception { 1930 if (checkTv()) { 1931 assertTrue(MediaUtils.canDecodeVideo( 1932 MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 30, 1933 AVCProfileHigh, AVCLevel31, 8000000)); 1934 } 1935 } 1936 1937 public void testH264SecureDecode30fps1280x720Tv() throws Exception { 1938 if (checkTv()) { 1939 verifySecureVideoDecodeSupport( 1940 MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 30, 1941 AVCProfileHigh, AVCLevel31, 8000000); 1942 } 1943 } 1944 1945 public void testH264Decode30fps1280x720() throws Exception { 1946 testDecode(R.raw.bbb_s4_1280x720_mp4_h264_mp31_8mbps_30fps_aac_he_mono_40kbps_44100hz, 300); 1947 } 1948 1949 public void testH264Decode60fps1280x720Tv() throws Exception { 1950 if (checkTv()) { 1951 assertTrue(MediaUtils.canDecodeVideo( 1952 MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 60, 1953 AVCProfileHigh, AVCLevel32, 8000000)); 1954 testDecode( 1955 R.raw.bbb_s3_1280x720_mp4_h264_hp32_8mbps_60fps_aac_he_v2_stereo_48kbps_48000hz, 1956 600); 1957 } 1958 } 1959 1960 public void testH264SecureDecode60fps1280x720Tv() throws Exception { 1961 if (checkTv()) { 1962 verifySecureVideoDecodeSupport( 1963 MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 60, 1964 AVCProfileHigh, AVCLevel32, 8000000); 1965 } 1966 } 1967 1968 public void testH264Decode60fps1280x720() throws Exception { 1969 testDecode( 1970 R.raw.bbb_s3_1280x720_mp4_h264_mp32_8mbps_60fps_aac_he_v2_6ch_144kbps_44100hz, 600); 1971 } 1972 1973 public void testH264Decode30fps1920x1080Tv() throws Exception { 1974 if (checkTv()) { 1975 assertTrue(MediaUtils.canDecodeVideo( 1976 MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 30, 1977 AVCProfileHigh, AVCLevel4, 20000000)); 1978 testDecode( 1979 R.raw.bbb_s4_1920x1080_wide_mp4_h264_hp4_20mbps_30fps_aac_lc_6ch_384kbps_44100hz, 1980 150); 1981 } 1982 } 1983 1984 public void testH264SecureDecode30fps1920x1080Tv() throws Exception { 1985 if (checkTv()) { 1986 verifySecureVideoDecodeSupport( 1987 MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 30, 1988 AVCProfileHigh, AVCLevel4, 20000000); 1989 } 1990 } 1991 1992 public void testH264Decode30fps1920x1080() throws Exception { 1993 testDecode( 1994 R.raw.bbb_s4_1920x1080_wide_mp4_h264_mp4_20mbps_30fps_aac_he_5ch_200kbps_44100hz, 1995 150); 1996 } 1997 1998 public void testH264Decode60fps1920x1080Tv() throws Exception { 1999 if (checkTv()) { 2000 assertTrue(MediaUtils.canDecodeVideo( 2001 MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 60, 2002 AVCProfileHigh, AVCLevel42, 20000000)); 2003 testDecode( 2004 R.raw.bbb_s2_1920x1080_mp4_h264_hp42_20mbps_60fps_aac_lc_6ch_384kbps_48000hz, 2005 300); 2006 } 2007 } 2008 2009 public void testH264SecureDecode60fps1920x1080Tv() throws Exception { 2010 if (checkTv()) { 2011 verifySecureVideoDecodeSupport( 2012 MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 60, 2013 AVCProfileHigh, AVCLevel42, 20000000); 2014 } 2015 } 2016 2017 public void testH264Decode60fps1920x1080() throws Exception { 2018 testDecode( 2019 R.raw.bbb_s2_1920x1080_mp4_h264_mp42_20mbps_60fps_aac_he_v2_5ch_160kbps_48000hz, 2020 300); 2021 testDecode( 2022 R.raw.bbb_s2_1920x1080_mkv_h264_mp42_20mbps_60fps_aac_he_v2_5ch_160kbps_48000hz, 2023 300); 2024 } 2025 2026 public void testH265Decode25fps1280x720() throws Exception { 2027 testDecode( 2028 R.raw.video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz, 240); 2029 } 2030 2031 public void testVP8Decode320x180() throws Exception { 2032 testDecode(R.raw.bbb_s1_320x180_webm_vp8_800kbps_30fps_opus_5ch_320kbps_48000hz, 300); 2033 } 2034 2035 public void testVP8Decode640x360() throws Exception { 2036 testDecode(R.raw.bbb_s1_640x360_webm_vp8_2mbps_30fps_vorbis_5ch_320kbps_48000hz, 300); 2037 } 2038 2039 public void testVP8Decode30fps1280x720Tv() throws Exception { 2040 if (checkTv()) { 2041 assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_VP8, 1280, 720, 30)); 2042 } 2043 } 2044 2045 public void testVP8Decode30fps1280x720() throws Exception { 2046 testDecode(R.raw.bbb_s4_1280x720_webm_vp8_8mbps_30fps_opus_mono_64kbps_48000hz, 300); 2047 } 2048 2049 public void testVP8Decode60fps1280x720Tv() throws Exception { 2050 if (checkTv()) { 2051 assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_VP8, 1280, 720, 60)); 2052 } 2053 } 2054 2055 public void testVP8Decode60fps1280x720() throws Exception { 2056 testDecode(R.raw.bbb_s3_1280x720_webm_vp8_8mbps_60fps_opus_6ch_384kbps_48000hz, 600); 2057 } 2058 2059 public void testVP8Decode30fps1920x1080Tv() throws Exception { 2060 if (checkTv()) { 2061 assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_VP8, 1920, 1080, 30)); 2062 } 2063 } 2064 2065 public void testVP8Decode30fps1920x1080() throws Exception { 2066 testDecode( 2067 R.raw.bbb_s4_1920x1080_wide_webm_vp8_20mbps_30fps_vorbis_6ch_384kbps_44100hz, 150); 2068 } 2069 2070 public void testVP8Decode60fps1920x1080Tv() throws Exception { 2071 if (checkTv()) { 2072 assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_VP8, 1920, 1080, 60)); 2073 } 2074 } 2075 2076 public void testVP8Decode60fps1920x1080() throws Exception { 2077 testDecode(R.raw.bbb_s2_1920x1080_webm_vp8_20mbps_60fps_vorbis_6ch_384kbps_48000hz, 300); 2078 } 2079 2080 public void testVP9Decode320x180() throws Exception { 2081 testDecode(R.raw.bbb_s1_320x180_webm_vp9_0p11_600kbps_30fps_vorbis_mono_64kbps_48000hz, 300); 2082 } 2083 2084 public void testVP9Decode640x360() throws Exception { 2085 testDecode( 2086 R.raw.bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz, 2087 300); 2088 } 2089 2090 public void testVP9Decode30fps1280x720Tv() throws Exception { 2091 if (checkTv()) { 2092 assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_VP9, 1280, 720, 30)); 2093 } 2094 } 2095 2096 public void testVP9Decode30fps1280x720() throws Exception { 2097 testDecode( 2098 R.raw.bbb_s4_1280x720_webm_vp9_0p31_4mbps_30fps_opus_stereo_128kbps_48000hz, 300); 2099 } 2100 2101 public void testVP9Decode60fps1920x1080() throws Exception { 2102 testDecode( 2103 R.raw.bbb_s2_1920x1080_webm_vp9_0p41_10mbps_60fps_vorbis_6ch_384kbps_22050hz, 300); 2104 } 2105 2106 public void testVP9Decode30fps3840x2160() throws Exception { 2107 testDecode( 2108 R.raw.bbb_s4_3840x2160_webm_vp9_0p5_20mbps_30fps_vorbis_6ch_384kbps_24000hz, 150); 2109 } 2110 2111 public void testVP9Decode60fps3840x2160() throws Exception { 2112 testDecode( 2113 R.raw.bbb_s2_3840x2160_webm_vp9_0p51_20mbps_60fps_vorbis_6ch_384kbps_32000hz, 300); 2114 } 2115 2116 public void testAV1Decode320x180() throws Exception { 2117 testDecode(R.raw.video_320x180_webm_av1_200kbps_30fps_vorbis_stereo_128kbps_48000hz, 300); 2118 } 2119 2120 public void testAV1Decode640x360() throws Exception { 2121 testDecode( 2122 R.raw.video_640x360_webm_av1_470kbps_30fps_vorbis_stereo_128kbps_48000hz, 2123 300); 2124 } 2125 2126 public void testAV1Decode30fps1280x720() throws Exception { 2127 testDecode( 2128 R.raw.video_1280x720_webm_av1_2000kbps_30fps_vorbis_stereo_128kbps_48000hz, 300); 2129 } 2130 2131 public void testAV1Decode60fps1920x1080() throws Exception { 2132 testDecode( 2133 R.raw.video_1920x1080_webm_av1_7000kbps_60fps_vorbis_stereo_128kbps_48000hz, 300); 2134 } 2135 2136 public void testAV1Decode30fps3840x2160() throws Exception { 2137 testDecode( 2138 R.raw.video_3840x2160_webm_av1_11000kbps_30fps_vorbis_stereo_128kbps_48000hz, 150); 2139 } 2140 2141 public void testAV1Decode60fps3840x2160() throws Exception { 2142 testDecode( 2143 R.raw.video_3840x2160_webm_av1_18000kbps_60fps_vorbis_stereo_128kbps_48000hz, 300); 2144 } 2145 2146 public void testHEVCDecode352x288() throws Exception { 2147 testDecode( 2148 R.raw.bbb_s1_352x288_mp4_hevc_mp2_600kbps_30fps_aac_he_stereo_96kbps_48000hz, 300); 2149 } 2150 2151 public void testHEVCDecode720x480() throws Exception { 2152 testDecode( 2153 R.raw.bbb_s1_720x480_mp4_hevc_mp3_1600kbps_30fps_aac_he_6ch_240kbps_48000hz, 300); 2154 } 2155 2156 public void testHEVCDecode30fps1280x720Tv() throws Exception { 2157 if (checkTv()) { 2158 assertTrue(MediaUtils.canDecodeVideo( 2159 MediaFormat.MIMETYPE_VIDEO_HEVC, 1280, 720, 30, 2160 HEVCProfileMain, HEVCMainTierLevel31, 4000000)); 2161 } 2162 } 2163 2164 public void testHEVCDecode30fps1280x720() throws Exception { 2165 testDecode( 2166 R.raw.bbb_s4_1280x720_mp4_hevc_mp31_4mbps_30fps_aac_he_stereo_80kbps_32000hz, 300); 2167 } 2168 2169 public void testHEVCDecode30fps1920x1080Tv() throws Exception { 2170 if (checkTv()) { 2171 assertTrue(MediaUtils.canDecodeVideo( 2172 MediaFormat.MIMETYPE_VIDEO_HEVC, 1920, 1080, 30, 2173 HEVCProfileMain, HEVCMainTierLevel41, 5000000)); 2174 } 2175 } 2176 2177 public void testHEVCDecode60fps1920x1080() throws Exception { 2178 testDecode( 2179 R.raw.bbb_s2_1920x1080_mp4_hevc_mp41_10mbps_60fps_aac_lc_6ch_384kbps_22050hz, 300); 2180 } 2181 2182 public void testHEVCDecode30fps3840x2160() throws Exception { 2183 testDecode( 2184 R.raw.bbb_s4_3840x2160_mp4_hevc_mp5_20mbps_30fps_aac_lc_6ch_384kbps_24000hz, 150); 2185 } 2186 2187 public void testHEVCDecode60fps3840x2160() throws Exception { 2188 testDecode( 2189 R.raw.bbb_s2_3840x2160_mp4_hevc_mp51_20mbps_60fps_aac_lc_6ch_384kbps_32000hz, 300); 2190 } 2191 2192 public void testMpeg2Decode352x288() throws Exception { 2193 testDecode(R.raw.video_352x288_mp4_mpeg2_1000kbps_30fps_aac_stereo_128kbps_48000hz, 300); 2194 } 2195 2196 public void testMpeg2Decode720x480() throws Exception { 2197 testDecode(R.raw.video_720x480_mp4_mpeg2_2000kbps_30fps_aac_stereo_128kbps_48000hz, 300); 2198 } 2199 2200 public void testMpeg2Decode30fps1280x720Tv() throws Exception { 2201 if (checkTv()) { 2202 assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_MPEG2, 1280, 720, 30)); 2203 } 2204 } 2205 2206 public void testMpeg2Decode30fps1280x720() throws Exception { 2207 testDecode(R.raw.video_1280x720_mp4_mpeg2_6000kbps_30fps_aac_stereo_128kbps_48000hz, 150); 2208 } 2209 2210 public void testMpeg2Decode30fps1920x1080Tv() throws Exception { 2211 if (checkTv()) { 2212 assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_MPEG2, 1920, 1080, 30)); 2213 } 2214 } 2215 2216 public void testMpeg2Decode30fps1920x1080() throws Exception { 2217 testDecode(R.raw.video_1920x1080_mp4_mpeg2_12000kbps_30fps_aac_stereo_128kbps_48000hz, 150); 2218 } 2219 2220 public void testMpeg2Decode30fps3840x2160() throws Exception { 2221 testDecode(R.raw.video_3840x2160_mp4_mpeg2_20000kbps_30fps_aac_stereo_128kbps_48000hz, 150); 2222 } 2223 2224 private void testCodecEarlyEOS(int resid, int eosFrame) throws Exception { 2225 if (!MediaUtils.checkCodecForResource(mContext, resid, 0 /* track */)) { 2226 return; // skip 2227 } 2228 Surface s = getActivity().getSurfaceHolder().getSurface(); 2229 int frames1 = countFrames(resid, RESET_MODE_NONE, eosFrame, s); 2230 assertEquals("wrong number of frames decoded", eosFrame, frames1); 2231 } 2232 2233 public void testCodecEarlyEOSH263() throws Exception { 2234 testCodecEarlyEOS( 2235 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 2236 64 /* eosframe */); 2237 } 2238 2239 public void testCodecEarlyEOSH264() throws Exception { 2240 testCodecEarlyEOS( 2241 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 2242 120 /* eosframe */); 2243 } 2244 2245 public void testCodecEarlyEOSHEVC() throws Exception { 2246 testCodecEarlyEOS( 2247 R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz, 2248 120 /* eosframe */); 2249 } 2250 2251 public void testCodecEarlyEOSMpeg2() throws Exception { 2252 testCodecEarlyEOS( 2253 R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz, 2254 120 /* eosframe */); 2255 } 2256 2257 public void testCodecEarlyEOSMpeg4() throws Exception { 2258 testCodecEarlyEOS( 2259 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 2260 120 /* eosframe */); 2261 } 2262 2263 public void testCodecEarlyEOSVP8() throws Exception { 2264 testCodecEarlyEOS( 2265 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz, 2266 120 /* eosframe */); 2267 } 2268 2269 public void testCodecEarlyEOSVP9() throws Exception { 2270 testCodecEarlyEOS( 2271 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz, 2272 120 /* eosframe */); 2273 } 2274 2275 public void testCodecEarlyEOSAV1() throws Exception { 2276 testCodecEarlyEOS( 2277 R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz, 2278 120 /* eosframe */); 2279 } 2280 2281 public void testCodecResetsH264WithoutSurface() throws Exception { 2282 testCodecResets( 2283 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, null); 2284 } 2285 2286 public void testCodecResetsH264WithSurface() throws Exception { 2287 Surface s = getActivity().getSurfaceHolder().getSurface(); 2288 testCodecResets( 2289 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, s); 2290 } 2291 2292 public void testCodecResetsHEVCWithoutSurface() throws Exception { 2293 testCodecResets( 2294 R.raw.bbb_s1_720x480_mp4_hevc_mp3_1600kbps_30fps_aac_he_6ch_240kbps_48000hz, null); 2295 } 2296 2297 public void testCodecResetsHEVCWithSurface() throws Exception { 2298 Surface s = getActivity().getSurfaceHolder().getSurface(); 2299 testCodecResets( 2300 R.raw.bbb_s1_720x480_mp4_hevc_mp3_1600kbps_30fps_aac_he_6ch_240kbps_48000hz, s); 2301 } 2302 2303 public void testCodecResetsMpeg2WithoutSurface() throws Exception { 2304 testCodecResets( 2305 R.raw.video_1280x720_mp4_mpeg2_6000kbps_30fps_aac_stereo_128kbps_48000hz, null); 2306 } 2307 2308 public void testCodecResetsMpeg2WithSurface() throws Exception { 2309 Surface s = getActivity().getSurfaceHolder().getSurface(); 2310 testCodecResets( 2311 R.raw.video_176x144_mp4_mpeg2_105kbps_25fps_aac_stereo_128kbps_44100hz, s); 2312 } 2313 2314 public void testCodecResetsH263WithoutSurface() throws Exception { 2315 testCodecResets( 2316 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, null); 2317 } 2318 2319 public void testCodecResetsH263WithSurface() throws Exception { 2320 Surface s = getActivity().getSurfaceHolder().getSurface(); 2321 testCodecResets( 2322 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, s); 2323 } 2324 2325 public void testCodecResetsMpeg4WithoutSurface() throws Exception { 2326 testCodecResets( 2327 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, null); 2328 } 2329 2330 public void testCodecResetsMpeg4WithSurface() throws Exception { 2331 Surface s = getActivity().getSurfaceHolder().getSurface(); 2332 testCodecResets( 2333 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, s); 2334 } 2335 2336 public void testCodecResetsVP8WithoutSurface() throws Exception { 2337 testCodecResets( 2338 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz, null); 2339 } 2340 2341 public void testCodecResetsVP8WithSurface() throws Exception { 2342 Surface s = getActivity().getSurfaceHolder().getSurface(); 2343 testCodecResets( 2344 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz, s); 2345 } 2346 2347 public void testCodecResetsVP9WithoutSurface() throws Exception { 2348 testCodecResets( 2349 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz, null); 2350 } 2351 2352 public void testCodecResetsAV1WithoutSurface() throws Exception { 2353 testCodecResets( 2354 R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz, null); 2355 } 2356 2357 public void testCodecResetsVP9WithSurface() throws Exception { 2358 Surface s = getActivity().getSurfaceHolder().getSurface(); 2359 testCodecResets( 2360 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz, s); 2361 } 2362 2363 public void testCodecResetsAV1WithSurface() throws Exception { 2364 Surface s = getActivity().getSurfaceHolder().getSurface(); 2365 testCodecResets( 2366 R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz, s); 2367 } 2368 2369 // public void testCodecResetsOgg() throws Exception { 2370 // testCodecResets(R.raw.sinesweepogg, null); 2371 // } 2372 2373 public void testCodecResetsMp3() throws Exception { 2374 testCodecReconfig(R.raw.sinesweepmp3lame); 2375 // NOTE: replacing testCodecReconfig call soon 2376 // testCodecResets(R.raw.sinesweepmp3lame, null); 2377 } 2378 2379 public void testCodecResetsM4a() throws Exception { 2380 testCodecReconfig(R.raw.sinesweepm4a); 2381 // NOTE: replacing testCodecReconfig call soon 2382 // testCodecResets(R.raw.sinesweepm4a, null); 2383 } 2384 2385 private void testCodecReconfig(int audio) throws Exception { 2386 int size1 = countSize(audio, RESET_MODE_NONE, -1 /* eosframe */); 2387 int size2 = countSize(audio, RESET_MODE_RECONFIGURE, -1 /* eosframe */); 2388 assertEquals("different output size when using reconfigured codec", size1, size2); 2389 } 2390 2391 private void testCodecResets(int video, Surface s) throws Exception { 2392 if (!MediaUtils.checkCodecForResource(mContext, video, 0 /* track */)) { 2393 return; // skip 2394 } 2395 2396 int frames1 = countFrames(video, RESET_MODE_NONE, -1 /* eosframe */, s); 2397 int frames2 = countFrames(video, RESET_MODE_RECONFIGURE, -1 /* eosframe */, s); 2398 int frames3 = countFrames(video, RESET_MODE_FLUSH, -1 /* eosframe */, s); 2399 assertEquals("different number of frames when using reconfigured codec", frames1, frames2); 2400 assertEquals("different number of frames when using flushed codec", frames1, frames3); 2401 } 2402 2403 private static void verifySecureVideoDecodeSupport( 2404 String mime, int width, int height, float rate, int profile, int level, int bitrate) { 2405 MediaFormat baseFormat = new MediaFormat(); 2406 baseFormat.setString(MediaFormat.KEY_MIME, mime); 2407 baseFormat.setFeatureEnabled(CodecCapabilities.FEATURE_SecurePlayback, true); 2408 2409 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); 2410 format.setFeatureEnabled(CodecCapabilities.FEATURE_SecurePlayback, true); 2411 format.setFloat(MediaFormat.KEY_FRAME_RATE, rate); 2412 format.setInteger(MediaFormat.KEY_PROFILE, profile); 2413 format.setInteger(MediaFormat.KEY_LEVEL, level); 2414 format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); 2415 2416 MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS); 2417 if (mcl.findDecoderForFormat(baseFormat) == null) { 2418 MediaUtils.skipTest("no secure decoder for " + mime); 2419 return; 2420 } 2421 assertNotNull("no decoder for " + format, mcl.findDecoderForFormat(format)); 2422 } 2423 2424 private static MediaCodec createDecoder(String mime) { 2425 try { 2426 if (false) { 2427 // change to force testing software codecs 2428 MediaFormat format = new MediaFormat(); 2429 format.setString(MediaFormat.KEY_MIME, mime); 2430 String[] codecs = 2431 MediaUtils.getDecoderNames(Boolean.TRUE /* isGoog */, format); 2432 if (codecs.length > 0) { 2433 return MediaCodec.createByCodecName(codecs[0]); 2434 } else { 2435 return null; 2436 } 2437 } 2438 return MediaCodec.createDecoderByType(mime); 2439 } catch (Exception e) { 2440 return null; 2441 } 2442 } 2443 2444 private static MediaCodec createDecoder(MediaFormat format) { 2445 return MediaUtils.getDecoder(format); 2446 } 2447 2448 // for video 2449 private int countFrames(int video, int resetMode, int eosframe, Surface s) 2450 throws Exception { 2451 AssetFileDescriptor testFd = mResources.openRawResourceFd(video); 2452 MediaExtractor extractor = new MediaExtractor(); 2453 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 2454 testFd.getLength()); 2455 extractor.selectTrack(0); 2456 2457 int numframes = decodeWithChecks(null /* decoderName */, extractor, 2458 CHECKFLAG_RETURN_OUTPUTFRAMES | CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH, 2459 resetMode, s, eosframe, null, null); 2460 2461 extractor.release(); 2462 testFd.close(); 2463 return numframes; 2464 } 2465 2466 // for audio 2467 private int countSize(int audio, int resetMode, int eosframe) 2468 throws Exception { 2469 AssetFileDescriptor testFd = mResources.openRawResourceFd(audio); 2470 MediaExtractor extractor = new MediaExtractor(); 2471 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 2472 testFd.getLength()); 2473 extractor.selectTrack(0); 2474 2475 // fails CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH 2476 int outputSize = decodeWithChecks(null /* decoderName */, extractor, 2477 CHECKFLAG_RETURN_OUTPUTSIZE, resetMode, null, 2478 eosframe, null, null); 2479 2480 extractor.release(); 2481 testFd.close(); 2482 return outputSize; 2483 } 2484 2485 /* 2486 * Test all decoders' EOS behavior. 2487 */ 2488 private void testEOSBehavior(int movie, int stopatsample) throws Exception { 2489 testEOSBehavior(movie, new int[] {stopatsample}); 2490 } 2491 2492 /* 2493 * Test all decoders' EOS behavior. 2494 */ 2495 private void testEOSBehavior(int movie, int[] stopAtSample) throws Exception { 2496 Surface s = null; 2497 AssetFileDescriptor testFd = mResources.openRawResourceFd(movie); 2498 MediaExtractor extractor = new MediaExtractor(); 2499 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 2500 testFd.getLength()); 2501 extractor.selectTrack(0); // consider variable looping on track 2502 MediaFormat format = extractor.getTrackFormat(0); 2503 2504 String[] decoderNames = MediaUtils.getDecoderNames(format); 2505 for (String decoderName: decoderNames) { 2506 List<Long> outputChecksums = new ArrayList<Long>(); 2507 List<Long> outputTimestamps = new ArrayList<Long>(); 2508 Arrays.sort(stopAtSample); 2509 int last = stopAtSample.length - 1; 2510 2511 // decode reference (longest sequence to stop at + 100) and 2512 // store checksums/pts in outputChecksums and outputTimestamps 2513 // (will fail CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH) 2514 decodeWithChecks(decoderName, extractor, 2515 CHECKFLAG_SETCHECKSUM | CHECKFLAG_SETPTS | CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH, 2516 RESET_MODE_NONE, s, 2517 stopAtSample[last] + 100, outputChecksums, outputTimestamps); 2518 2519 // decode stopAtSample requests in reverse order (longest to 2520 // shortest) and compare to reference checksums/pts in 2521 // outputChecksums and outputTimestamps 2522 for (int i = last; i >= 0; --i) { 2523 if (true) { // reposition extractor 2524 extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 2525 } else { // create new extractor 2526 extractor.release(); 2527 extractor = new MediaExtractor(); 2528 extractor.setDataSource(testFd.getFileDescriptor(), 2529 testFd.getStartOffset(), testFd.getLength()); 2530 extractor.selectTrack(0); // consider variable looping on track 2531 } 2532 decodeWithChecks(decoderName, extractor, 2533 CHECKFLAG_COMPARECHECKSUM | CHECKFLAG_COMPAREPTS 2534 | CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH 2535 | CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH, 2536 RESET_MODE_NONE, s, 2537 stopAtSample[i], outputChecksums, outputTimestamps); 2538 } 2539 extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 2540 } 2541 2542 extractor.release(); 2543 testFd.close(); 2544 } 2545 2546 private static final int CHECKFLAG_SETCHECKSUM = 1 << 0; 2547 private static final int CHECKFLAG_COMPARECHECKSUM = 1 << 1; 2548 private static final int CHECKFLAG_SETPTS = 1 << 2; 2549 private static final int CHECKFLAG_COMPAREPTS = 1 << 3; 2550 private static final int CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH = 1 << 4; 2551 private static final int CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH = 1 << 5; 2552 private static final int CHECKFLAG_RETURN_OUTPUTFRAMES = 1 << 6; 2553 private static final int CHECKFLAG_RETURN_OUTPUTSIZE = 1 << 7; 2554 2555 /** 2556 * Decodes frames with parameterized checks and return values. 2557 * If decoderName is provided, mediacodec will create that decoder. Otherwise, 2558 * mediacodec will use the default decoder provided by platform. 2559 * The integer return can be selected through the checkFlags variable. 2560 */ 2561 private static int decodeWithChecks( 2562 String decoderName, MediaExtractor extractor, 2563 int checkFlags, int resetMode, Surface surface, int stopAtSample, 2564 List<Long> outputChecksums, List<Long> outputTimestamps) 2565 throws Exception { 2566 int trackIndex = extractor.getSampleTrackIndex(); 2567 MediaFormat format = extractor.getTrackFormat(trackIndex); 2568 String mime = format.getString(MediaFormat.KEY_MIME); 2569 boolean isAudio = mime.startsWith("audio/"); 2570 ByteBuffer[] codecInputBuffers; 2571 ByteBuffer[] codecOutputBuffers; 2572 2573 MediaCodec codec = 2574 decoderName == null ? createDecoder(format) : MediaCodec.createByCodecName(decoderName); 2575 Log.i("@@@@", "using codec: " + codec.getName()); 2576 codec.configure(format, surface, null /* crypto */, 0 /* flags */); 2577 codec.start(); 2578 codecInputBuffers = codec.getInputBuffers(); 2579 codecOutputBuffers = codec.getOutputBuffers(); 2580 2581 if (resetMode == RESET_MODE_RECONFIGURE) { 2582 codec.stop(); 2583 codec.configure(format, surface, null /* crypto */, 0 /* flags */); 2584 codec.start(); 2585 codecInputBuffers = codec.getInputBuffers(); 2586 codecOutputBuffers = codec.getOutputBuffers(); 2587 } else if (resetMode == RESET_MODE_FLUSH) { 2588 codec.flush(); 2589 2590 // We must always queue CSD after a flush that is potentially 2591 // before we receive output format has changed. 2592 queueConfig(codec, format); 2593 } 2594 2595 // start decode loop 2596 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 2597 2598 MediaFormat outFormat = codec.getOutputFormat(); 2599 long kTimeOutUs = 5000; // 5ms timeout 2600 String outMime = format.getString(MediaFormat.KEY_MIME); 2601 if ((surface == null) && (outMime != null) && outMime.startsWith("video/")) { 2602 int outWidth = outFormat.getInteger(MediaFormat.KEY_WIDTH); 2603 int outHeight = outFormat.getInteger(MediaFormat.KEY_HEIGHT); 2604 // in the 4K decoding case in byte buffer mode, set kTimeOutUs to 10ms as decode may 2605 // involve a memcpy 2606 if (outWidth * outHeight >= 8000000) { 2607 kTimeOutUs = 10000; 2608 } 2609 } 2610 2611 boolean sawInputEOS = false; 2612 boolean sawOutputEOS = false; 2613 int deadDecoderCounter = 0; 2614 int samplenum = 0; 2615 int numframes = 0; 2616 int outputSize = 0; 2617 int width = 0; 2618 int height = 0; 2619 boolean dochecksum = false; 2620 ArrayList<Long> timestamps = new ArrayList<Long>(); 2621 if ((checkFlags & CHECKFLAG_SETPTS) != 0) { 2622 outputTimestamps.clear(); 2623 } 2624 if ((checkFlags & CHECKFLAG_SETCHECKSUM) != 0) { 2625 outputChecksums.clear(); 2626 } 2627 boolean advanceDone = true; 2628 while (!sawOutputEOS && deadDecoderCounter < 100) { 2629 // handle input 2630 if (!sawInputEOS) { 2631 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs); 2632 2633 if (inputBufIndex >= 0) { 2634 ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; 2635 2636 int sampleSize = 2637 extractor.readSampleData(dstBuf, 0 /* offset */); 2638 assertEquals("end of stream should match extractor.advance()", sampleSize >= 0, 2639 advanceDone); 2640 long presentationTimeUs = extractor.getSampleTime(); 2641 advanceDone = extractor.advance(); 2642 // int flags = extractor.getSampleFlags(); 2643 // Log.i("@@@@", "read sample " + samplenum + ":" + 2644 // extractor.getSampleFlags() 2645 // + " @ " + extractor.getSampleTime() + " size " + 2646 // sampleSize); 2647 2648 if (sampleSize < 0) { 2649 assertFalse("advance succeeded after failed read", advanceDone); 2650 Log.d(TAG, "saw input EOS."); 2651 sawInputEOS = true; 2652 assertEquals("extractor.readSampleData() must return -1 at end of stream", 2653 -1, sampleSize); 2654 assertEquals("extractor.getSampleTime() must return -1 at end of stream", 2655 -1, presentationTimeUs); 2656 sampleSize = 0; // required otherwise queueInputBuffer 2657 // returns invalid. 2658 } else { 2659 timestamps.add(presentationTimeUs); 2660 samplenum++; // increment before comparing with stopAtSample 2661 if (samplenum == stopAtSample) { 2662 Log.d(TAG, "saw input EOS (stop at sample)."); 2663 sawInputEOS = true; // tag this sample as EOS 2664 } 2665 } 2666 codec.queueInputBuffer( 2667 inputBufIndex, 2668 0 /* offset */, 2669 sampleSize, 2670 presentationTimeUs, 2671 sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); 2672 } else { 2673 assertEquals( 2674 "codec.dequeueInputBuffer() unrecognized return value: " + inputBufIndex, 2675 MediaCodec.INFO_TRY_AGAIN_LATER, inputBufIndex); 2676 } 2677 } 2678 2679 // handle output 2680 int outputBufIndex = codec.dequeueOutputBuffer(info, kTimeOutUs); 2681 2682 deadDecoderCounter++; 2683 if (outputBufIndex >= 0) { 2684 if (info.size > 0) { // Disregard 0-sized buffers at the end. 2685 deadDecoderCounter = 0; 2686 if (resetMode != RESET_MODE_NONE) { 2687 // once we've gotten some data out of the decoder, reset 2688 // and start again 2689 if (resetMode == RESET_MODE_RECONFIGURE) { 2690 codec.stop(); 2691 codec.configure(format, surface /* surface */, null /* crypto */, 2692 0 /* flags */); 2693 codec.start(); 2694 codecInputBuffers = codec.getInputBuffers(); 2695 codecOutputBuffers = codec.getOutputBuffers(); 2696 } else if (resetMode == RESET_MODE_FLUSH) { 2697 codec.flush(); 2698 } else { 2699 fail("unknown resetMode: " + resetMode); 2700 } 2701 // restart at beginning, clear resetMode 2702 resetMode = RESET_MODE_NONE; 2703 extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 2704 sawInputEOS = false; 2705 numframes = 0; 2706 timestamps.clear(); 2707 if ((checkFlags & CHECKFLAG_SETPTS) != 0) { 2708 outputTimestamps.clear(); 2709 } 2710 if ((checkFlags & CHECKFLAG_SETCHECKSUM) != 0) { 2711 outputChecksums.clear(); 2712 } 2713 continue; 2714 } 2715 if ((checkFlags & CHECKFLAG_COMPAREPTS) != 0) { 2716 assertTrue("number of frames (" + numframes 2717 + ") exceeds number of reference timestamps", 2718 numframes < outputTimestamps.size()); 2719 assertEquals("frame ts mismatch at frame " + numframes, 2720 (long) outputTimestamps.get(numframes), info.presentationTimeUs); 2721 } else if ((checkFlags & CHECKFLAG_SETPTS) != 0) { 2722 outputTimestamps.add(info.presentationTimeUs); 2723 } 2724 if ((checkFlags & (CHECKFLAG_SETCHECKSUM | CHECKFLAG_COMPARECHECKSUM)) != 0) { 2725 long sum = 0; // note: checksum is 0 if buffer format unrecognized 2726 if (dochecksum) { 2727 Image image = codec.getOutputImage(outputBufIndex); 2728 // use image to do crc if it's available 2729 // fall back to buffer if image is not available 2730 if (image != null) { 2731 sum = checksum(image); 2732 } else { 2733 // TODO: add stride - right now just use info.size (as before) 2734 //sum = checksum(codecOutputBuffers[outputBufIndex], width, height, 2735 // stride); 2736 ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufIndex); 2737 outputBuffer.position(info.offset); 2738 sum = checksum(outputBuffer, info.size); 2739 } 2740 } 2741 if ((checkFlags & CHECKFLAG_COMPARECHECKSUM) != 0) { 2742 assertTrue("number of frames (" + numframes 2743 + ") exceeds number of reference checksums", 2744 numframes < outputChecksums.size()); 2745 Log.d(TAG, "orig checksum: " + outputChecksums.get(numframes) 2746 + " new checksum: " + sum); 2747 assertEquals("frame data mismatch at frame " + numframes, 2748 (long) outputChecksums.get(numframes), sum); 2749 } else if ((checkFlags & CHECKFLAG_SETCHECKSUM) != 0) { 2750 outputChecksums.add(sum); 2751 } 2752 } 2753 if ((checkFlags & CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH) != 0) { 2754 assertTrue("output timestamp " + info.presentationTimeUs 2755 + " without corresponding input timestamp" 2756 , timestamps.remove(info.presentationTimeUs)); 2757 } 2758 outputSize += info.size; 2759 numframes++; 2760 } 2761 // Log.d(TAG, "got frame, size " + info.size + "/" + 2762 // info.presentationTimeUs + 2763 // "/" + numframes + "/" + info.flags); 2764 codec.releaseOutputBuffer(outputBufIndex, true /* render */); 2765 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 2766 Log.d(TAG, "saw output EOS."); 2767 sawOutputEOS = true; 2768 } 2769 } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 2770 codecOutputBuffers = codec.getOutputBuffers(); 2771 Log.d(TAG, "output buffers have changed."); 2772 } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 2773 MediaFormat oformat = codec.getOutputFormat(); 2774 if (oformat.containsKey(MediaFormat.KEY_COLOR_FORMAT) && 2775 oformat.containsKey(MediaFormat.KEY_WIDTH) && 2776 oformat.containsKey(MediaFormat.KEY_HEIGHT)) { 2777 int colorFormat = oformat.getInteger(MediaFormat.KEY_COLOR_FORMAT); 2778 width = oformat.getInteger(MediaFormat.KEY_WIDTH); 2779 height = oformat.getInteger(MediaFormat.KEY_HEIGHT); 2780 dochecksum = isRecognizedFormat(colorFormat); // only checksum known raw 2781 // buf formats 2782 Log.d(TAG, "checksum fmt: " + colorFormat + " dim " + width + "x" + height); 2783 } else { 2784 dochecksum = false; // check with audio later 2785 width = height = 0; 2786 Log.d(TAG, "output format has changed to (unknown video) " + oformat); 2787 } 2788 } else { 2789 assertEquals( 2790 "codec.dequeueOutputBuffer() unrecognized return index: " 2791 + outputBufIndex, 2792 MediaCodec.INFO_TRY_AGAIN_LATER, outputBufIndex); 2793 } 2794 } 2795 codec.stop(); 2796 codec.release(); 2797 2798 assertTrue("last frame didn't have EOS", sawOutputEOS); 2799 if ((checkFlags & CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH) != 0) { 2800 assertEquals("I!=O", samplenum, numframes); 2801 if (stopAtSample != 0) { 2802 assertEquals("did not stop with right number of frames", stopAtSample, numframes); 2803 } 2804 } 2805 return (checkFlags & CHECKFLAG_RETURN_OUTPUTSIZE) != 0 ? outputSize : 2806 (checkFlags & CHECKFLAG_RETURN_OUTPUTFRAMES) != 0 ? numframes : 2807 0; 2808 } 2809 2810 public void testEOSBehaviorH264() throws Exception { 2811 // this video has an I frame at 44 2812 testEOSBehavior( 2813 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 2814 new int[] {1, 44, 45, 55}); 2815 } 2816 public void testEOSBehaviorHEVC() throws Exception { 2817 testEOSBehavior( 2818 R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz, 2819 new int[] {1, 17, 23, 49}); 2820 } 2821 2822 public void testEOSBehaviorMpeg2() throws Exception { 2823 testEOSBehavior(R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz, 17); 2824 testEOSBehavior(R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz, 23); 2825 testEOSBehavior(R.raw.video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz, 49); 2826 } 2827 2828 public void testEOSBehaviorH263() throws Exception { 2829 // this video has an I frame every 12 frames. 2830 testEOSBehavior( 2831 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 2832 new int[] {1, 24, 25, 48, 50}); 2833 } 2834 2835 public void testEOSBehaviorMpeg4() throws Exception { 2836 // this video has an I frame every 12 frames 2837 testEOSBehavior( 2838 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 2839 new int[] {1, 24, 25, 48, 50, 2}); 2840 } 2841 2842 public void testEOSBehaviorVP8() throws Exception { 2843 // this video has an I frame at 46 2844 testEOSBehavior( 2845 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz, 2846 new int[] {1, 46, 47, 57, 45}); 2847 } 2848 2849 public void testEOSBehaviorVP9() throws Exception { 2850 // this video has an I frame at 44 2851 testEOSBehavior( 2852 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz, 2853 new int[] {1, 44, 45, 55, 43}); 2854 } 2855 2856 public void testEOSBehaviorAV1() throws Exception { 2857 // this video has an I frame at 44 2858 testEOSBehavior( 2859 R.raw.video_480x360_webm_av1_400kbps_30fps_vorbis_stereo_128kbps_48000hz, 2860 new int[] {1, 44, 45, 55, 43}); 2861 } 2862 2863 /* from EncodeDecodeTest */ 2864 private static boolean isRecognizedFormat(int colorFormat) { 2865 // Log.d(TAG, "color format: " + String.format("0x%08x", colorFormat)); 2866 switch (colorFormat) { 2867 // these are the formats we know how to handle for this test 2868 case CodecCapabilities.COLOR_FormatYUV420Planar: 2869 case CodecCapabilities.COLOR_FormatYUV420PackedPlanar: 2870 case CodecCapabilities.COLOR_FormatYUV420SemiPlanar: 2871 case CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar: 2872 case CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar: 2873 case CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar: 2874 /* 2875 * TODO: Check newer formats or ignore. 2876 * OMX_SEC_COLOR_FormatNV12Tiled = 0x7FC00002 2877 * OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka = 0x7FA30C03: N4/N7_2 2878 * OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m = 0x7FA30C04: N5 2879 */ 2880 return true; 2881 default: 2882 return false; 2883 } 2884 } 2885 2886 private static long checksum(ByteBuffer buf, int size) { 2887 int cap = buf.capacity(); 2888 assertTrue("checksum() params are invalid: size = " + size + " cap = " + cap, 2889 size > 0 && size <= cap); 2890 CRC32 crc = new CRC32(); 2891 if (buf.hasArray()) { 2892 crc.update(buf.array(), buf.position() + buf.arrayOffset(), size); 2893 } else { 2894 int pos = buf.position(); 2895 final int rdsize = Math.min(4096, size); 2896 byte bb[] = new byte[rdsize]; 2897 int chk; 2898 for (int i = 0; i < size; i += chk) { 2899 chk = Math.min(rdsize, size - i); 2900 buf.get(bb, 0, chk); 2901 crc.update(bb, 0, chk); 2902 } 2903 buf.position(pos); 2904 } 2905 return crc.getValue(); 2906 } 2907 2908 private static long checksum(ByteBuffer buf, int width, int height, int stride) { 2909 int cap = buf.capacity(); 2910 assertTrue("checksum() params are invalid: w x h , s = " 2911 + width + " x " + height + " , " + stride + " cap = " + cap, 2912 width > 0 && width <= stride && height > 0 && height * stride <= cap); 2913 // YUV 4:2:0 should generally have a data storage height 1.5x greater 2914 // than the declared image height, representing the UV planes. 2915 // 2916 // We only check Y frame for now. Somewhat unknown with tiling effects. 2917 // 2918 //long tm = System.nanoTime(); 2919 final int lineinterval = 1; // line sampling frequency 2920 CRC32 crc = new CRC32(); 2921 if (buf.hasArray()) { 2922 byte b[] = buf.array(); 2923 int offs = buf.arrayOffset(); 2924 for (int i = 0; i < height; i += lineinterval) { 2925 crc.update(b, i * stride + offs, width); 2926 } 2927 } else { // almost always ends up here due to direct buffers 2928 int pos = buf.position(); 2929 if (true) { // this {} is 80x times faster than else {} below. 2930 byte[] bb = new byte[width]; // local line buffer 2931 for (int i = 0; i < height; i += lineinterval) { 2932 buf.position(pos + i * stride); 2933 buf.get(bb, 0, width); 2934 crc.update(bb, 0, width); 2935 } 2936 } else { 2937 for (int i = 0; i < height; i += lineinterval) { 2938 buf.position(pos + i * stride); 2939 for (int j = 0; j < width; ++j) { 2940 crc.update(buf.get()); 2941 } 2942 } 2943 } 2944 buf.position(pos); 2945 } 2946 //tm = System.nanoTime() - tm; 2947 //Log.d(TAG, "checksum time " + tm); 2948 return crc.getValue(); 2949 } 2950 2951 private static long checksum(Image image) { 2952 int format = image.getFormat(); 2953 assertEquals("unsupported image format", ImageFormat.YUV_420_888, format); 2954 2955 CRC32 crc = new CRC32(); 2956 2957 int imageWidth = image.getWidth(); 2958 int imageHeight = image.getHeight(); 2959 2960 Image.Plane[] planes = image.getPlanes(); 2961 for (int i = 0; i < planes.length; ++i) { 2962 ByteBuffer buf = planes[i].getBuffer(); 2963 2964 int width, height, rowStride, pixelStride, x, y; 2965 rowStride = planes[i].getRowStride(); 2966 pixelStride = planes[i].getPixelStride(); 2967 if (i == 0) { 2968 width = imageWidth; 2969 height = imageHeight; 2970 } else { 2971 width = imageWidth / 2; 2972 height = imageHeight /2; 2973 } 2974 // local contiguous pixel buffer 2975 byte[] bb = new byte[width * height]; 2976 if (buf.hasArray()) { 2977 byte b[] = buf.array(); 2978 int offs = buf.arrayOffset(); 2979 if (pixelStride == 1) { 2980 for (y = 0; y < height; ++y) { 2981 System.arraycopy(bb, y * width, b, y * rowStride + offs, width); 2982 } 2983 } else { 2984 // do it pixel-by-pixel 2985 for (y = 0; y < height; ++y) { 2986 int lineOffset = offs + y * rowStride; 2987 for (x = 0; x < width; ++x) { 2988 bb[y * width + x] = b[lineOffset + x * pixelStride]; 2989 } 2990 } 2991 } 2992 } else { // almost always ends up here due to direct buffers 2993 int pos = buf.position(); 2994 if (pixelStride == 1) { 2995 for (y = 0; y < height; ++y) { 2996 buf.position(pos + y * rowStride); 2997 buf.get(bb, y * width, width); 2998 } 2999 } else { 3000 // local line buffer 3001 byte[] lb = new byte[rowStride]; 3002 // do it pixel-by-pixel 3003 for (y = 0; y < height; ++y) { 3004 buf.position(pos + y * rowStride); 3005 // we're only guaranteed to have pixelStride * (width - 1) + 1 bytes 3006 buf.get(lb, 0, pixelStride * (width - 1) + 1); 3007 for (x = 0; x < width; ++x) { 3008 bb[y * width + x] = lb[x * pixelStride]; 3009 } 3010 } 3011 } 3012 buf.position(pos); 3013 } 3014 crc.update(bb, 0, width * height); 3015 } 3016 3017 return crc.getValue(); 3018 } 3019 3020 public void testFlush() throws Exception { 3021 testFlush(R.raw.loudsoftwav); 3022 testFlush(R.raw.loudsoftogg); 3023 testFlush(R.raw.loudsoftoggmkv); 3024 testFlush(R.raw.loudsoftoggmp4); 3025 testFlush(R.raw.loudsoftmp3); 3026 testFlush(R.raw.loudsoftaac); 3027 testFlush(R.raw.loudsoftfaac); 3028 testFlush(R.raw.loudsoftitunes); 3029 } 3030 3031 private void testFlush(int resource) throws Exception { 3032 3033 AssetFileDescriptor testFd = mResources.openRawResourceFd(resource); 3034 3035 MediaExtractor extractor; 3036 MediaCodec codec; 3037 ByteBuffer[] codecInputBuffers; 3038 ByteBuffer[] codecOutputBuffers; 3039 3040 extractor = new MediaExtractor(); 3041 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 3042 testFd.getLength()); 3043 testFd.close(); 3044 3045 assertEquals("wrong number of tracks", 1, extractor.getTrackCount()); 3046 MediaFormat format = extractor.getTrackFormat(0); 3047 String mime = format.getString(MediaFormat.KEY_MIME); 3048 assertTrue("not an audio file", mime.startsWith("audio/")); 3049 3050 codec = MediaCodec.createDecoderByType(mime); 3051 assertNotNull("couldn't find codec " + mime, codec); 3052 3053 codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */); 3054 codec.start(); 3055 codecInputBuffers = codec.getInputBuffers(); 3056 codecOutputBuffers = codec.getOutputBuffers(); 3057 3058 extractor.selectTrack(0); 3059 3060 // decode a bit of the first part of the file, and verify the amplitude 3061 short maxvalue1 = getAmplitude(extractor, codec); 3062 3063 // flush the codec and seek the extractor a different position, then decode a bit more 3064 // and check the amplitude 3065 extractor.seekTo(8000000, 0); 3066 codec.flush(); 3067 short maxvalue2 = getAmplitude(extractor, codec); 3068 3069 assertTrue("first section amplitude too low", maxvalue1 > 20000); 3070 assertTrue("second section amplitude too high", maxvalue2 < 5000); 3071 codec.stop(); 3072 codec.release(); 3073 3074 } 3075 3076 private short getAmplitude(MediaExtractor extractor, MediaCodec codec) { 3077 short maxvalue = 0; 3078 int numBytesDecoded = 0; 3079 final long kTimeOutUs = 5000; 3080 ByteBuffer[] codecInputBuffers = codec.getInputBuffers(); 3081 ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers(); 3082 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 3083 3084 while(numBytesDecoded < 44100 * 2) { 3085 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs); 3086 3087 if (inputBufIndex >= 0) { 3088 ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; 3089 3090 int sampleSize = extractor.readSampleData(dstBuf, 0 /* offset */); 3091 long presentationTimeUs = extractor.getSampleTime(); 3092 3093 codec.queueInputBuffer( 3094 inputBufIndex, 3095 0 /* offset */, 3096 sampleSize, 3097 presentationTimeUs, 3098 0 /* flags */); 3099 3100 extractor.advance(); 3101 } 3102 int res = codec.dequeueOutputBuffer(info, kTimeOutUs); 3103 3104 if (res >= 0) { 3105 3106 int outputBufIndex = res; 3107 ByteBuffer buf = codecOutputBuffers[outputBufIndex]; 3108 3109 buf.position(info.offset); 3110 for (int i = 0; i < info.size; i += 2) { 3111 short sample = buf.getShort(); 3112 if (maxvalue < sample) { 3113 maxvalue = sample; 3114 } 3115 int idx = (numBytesDecoded + i) / 2; 3116 } 3117 3118 numBytesDecoded += info.size; 3119 3120 codec.releaseOutputBuffer(outputBufIndex, false /* render */); 3121 } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 3122 codecOutputBuffers = codec.getOutputBuffers(); 3123 } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 3124 MediaFormat oformat = codec.getOutputFormat(); 3125 } 3126 } 3127 return maxvalue; 3128 } 3129 3130 /* return true if a particular video feature is supported for the given mimetype */ 3131 private boolean isVideoFeatureSupported(String mimeType, String feature) { 3132 MediaFormat format = MediaFormat.createVideoFormat( mimeType, 1920, 1080); 3133 format.setFeatureEnabled(feature, true); 3134 MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS); 3135 String codecName = mcl.findDecoderForFormat(format); 3136 return (codecName == null) ? false : true; 3137 } 3138 3139 3140 /** 3141 * Test tunneled video playback mode if supported 3142 */ 3143 public void testTunneledVideoPlayback() throws Exception { 3144 if (!isVideoFeatureSupported(MediaFormat.MIMETYPE_VIDEO_AVC, 3145 CodecCapabilities.FEATURE_TunneledPlayback)) { 3146 MediaUtils.skipTest(TAG, "No tunneled video playback codec found!"); 3147 return; 3148 } 3149 3150 AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE); 3151 mMediaCodecPlayer = new MediaCodecTunneledPlayer( 3152 getActivity().getSurfaceHolder(), true, am.generateAudioSessionId()); 3153 3154 Uri audioUri = Uri.parse(dynamicConfig.getValue(AUDIO_URL_KEY)); 3155 Uri videoUri = Uri.parse(dynamicConfig.getValue(VIDEO_URL_KEY)); 3156 mMediaCodecPlayer.setAudioDataSource(audioUri, null); 3157 mMediaCodecPlayer.setVideoDataSource(videoUri, null); 3158 assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start()); 3159 assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare()); 3160 3161 // starts video playback 3162 mMediaCodecPlayer.startThread(); 3163 3164 final long durationMs = mMediaCodecPlayer.getDuration(); 3165 final long timeOutMs = System.currentTimeMillis() + durationMs + 5 * 1000; // add 5 sec 3166 while (!mMediaCodecPlayer.isEnded()) { 3167 // Log.d(TAG, "currentPosition: " + mMediaCodecPlayer.getCurrentPosition() 3168 // + " duration: " + mMediaCodecPlayer.getDuration()); 3169 assertTrue("Tunneled video playback timeout exceeded", 3170 timeOutMs > System.currentTimeMillis()); 3171 Thread.sleep(SLEEP_TIME_MS); 3172 if (mMediaCodecPlayer.getCurrentPosition() >= mMediaCodecPlayer.getDuration()) { 3173 Log.d(TAG, "testTunneledVideoPlayback -- current pos = " + 3174 mMediaCodecPlayer.getCurrentPosition() + 3175 ">= duration = " + mMediaCodecPlayer.getDuration()); 3176 break; 3177 } 3178 } 3179 // mMediaCodecPlayer.reset() handled in TearDown(); 3180 } 3181 3182 /** 3183 * Test tunneled video playback flush if supported 3184 */ 3185 public void testTunneledVideoFlush() throws Exception { 3186 if (!isVideoFeatureSupported(MediaFormat.MIMETYPE_VIDEO_AVC, 3187 CodecCapabilities.FEATURE_TunneledPlayback)) { 3188 MediaUtils.skipTest(TAG, "No tunneled video playback codec found!"); 3189 return; 3190 } 3191 3192 AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE); 3193 mMediaCodecPlayer = new MediaCodecTunneledPlayer( 3194 getActivity().getSurfaceHolder(), true, am.generateAudioSessionId()); 3195 3196 Uri audioUri = Uri.parse(dynamicConfig.getValue(AUDIO_URL_KEY)); 3197 Uri videoUri = Uri.parse(dynamicConfig.getValue(VIDEO_URL_KEY)); 3198 mMediaCodecPlayer.setAudioDataSource(audioUri, null); 3199 mMediaCodecPlayer.setVideoDataSource(videoUri, null); 3200 assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start()); 3201 assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare()); 3202 3203 // starts video playback 3204 mMediaCodecPlayer.startThread(); 3205 Thread.sleep(SLEEP_TIME_MS); 3206 mMediaCodecPlayer.pause(); 3207 mMediaCodecPlayer.flush(); 3208 // mMediaCodecPlayer.reset() handled in TearDown(); 3209 } 3210 3211 /** 3212 * Returns list of CodecCapabilities advertising support for the given MIME type. 3213 */ 3214 private static List<CodecCapabilities> getCodecCapabilitiesForMimeType(String mimeType) { 3215 int numCodecs = MediaCodecList.getCodecCount(); 3216 List<CodecCapabilities> caps = new ArrayList<CodecCapabilities>(); 3217 for (int i = 0; i < numCodecs; i++) { 3218 MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); 3219 if (codecInfo.isAlias()) { 3220 continue; 3221 } 3222 if (codecInfo.isEncoder()) { 3223 continue; 3224 } 3225 3226 String[] types = codecInfo.getSupportedTypes(); 3227 for (int j = 0; j < types.length; j++) { 3228 if (types[j].equalsIgnoreCase(mimeType)) { 3229 caps.add(codecInfo.getCapabilitiesForType(mimeType)); 3230 } 3231 } 3232 } 3233 return caps; 3234 } 3235 3236 /** 3237 * Returns true if there exists a codec supporting the given MIME type that meets the 3238 * minimum specification for VR high performance requirements. 3239 * 3240 * The requirements are as follows: 3241 * - At least 243000 blocks per second (where blocks are defined as 16x16 -- note this 3242 * is equivalent to 1920x1080@30fps) 3243 * - Feature adaptive-playback present 3244 */ 3245 private static boolean doesMimeTypeHaveMinimumSpecVrReadyCodec(String mimeType) { 3246 List<CodecCapabilities> caps = getCodecCapabilitiesForMimeType(mimeType); 3247 for (CodecCapabilities c : caps) { 3248 if (!c.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback)) { 3249 continue; 3250 } 3251 3252 if (!c.getVideoCapabilities().areSizeAndRateSupported(1920, 1080, 30.0)) { 3253 continue; 3254 } 3255 3256 return true; 3257 } 3258 3259 return false; 3260 } 3261 3262 /** 3263 * Returns true if there exists a codec supporting the given MIME type that meets VR high 3264 * performance requirements. 3265 * 3266 * The requirements are as follows: 3267 * - At least 972000 blocks per second (where blocks are defined as 16x16 -- note this 3268 * is equivalent to 3840x2160@30fps) 3269 * - At least 4 concurrent instances 3270 * - Feature adaptive-playback present 3271 */ 3272 private static boolean doesMimeTypeHaveVrReadyCodec(String mimeType) { 3273 List<CodecCapabilities> caps = getCodecCapabilitiesForMimeType(mimeType); 3274 for (CodecCapabilities c : caps) { 3275 if (c.getMaxSupportedInstances() < 4) { 3276 continue; 3277 } 3278 3279 if (!c.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback)) { 3280 continue; 3281 } 3282 3283 if (!c.getVideoCapabilities().areSizeAndRateSupported(3840, 2160, 30.0)) { 3284 continue; 3285 } 3286 3287 return true; 3288 } 3289 3290 return false; 3291 } 3292 3293 public void testVrHighPerformanceH264() throws Exception { 3294 if (!supportsVrHighPerformance()) { 3295 MediaUtils.skipTest(TAG, "FEATURE_VR_MODE_HIGH_PERFORMANCE not present"); 3296 return; 3297 } 3298 3299 boolean h264IsReady = doesMimeTypeHaveVrReadyCodec(MediaFormat.MIMETYPE_VIDEO_AVC); 3300 assertTrue("Did not find a VR ready H.264 decoder", h264IsReady); 3301 } 3302 3303 public void testVrHighPerformanceHEVC() throws Exception { 3304 if (!supportsVrHighPerformance()) { 3305 MediaUtils.skipTest(TAG, "FEATURE_VR_MODE_HIGH_PERFORMANCE not present"); 3306 return; 3307 } 3308 3309 // Test minimum mandatory requirements. 3310 assertTrue(doesMimeTypeHaveMinimumSpecVrReadyCodec(MediaFormat.MIMETYPE_VIDEO_HEVC)); 3311 3312 boolean hevcIsReady = doesMimeTypeHaveVrReadyCodec(MediaFormat.MIMETYPE_VIDEO_HEVC); 3313 if (!hevcIsReady) { 3314 Log.d(TAG, "HEVC isn't required to be VR ready"); 3315 return; 3316 } 3317 } 3318 3319 public void testVrHighPerformanceVP9() throws Exception { 3320 if (!supportsVrHighPerformance()) { 3321 MediaUtils.skipTest(TAG, "FEATURE_VR_MODE_HIGH_PERFORMANCE not present"); 3322 return; 3323 } 3324 3325 // Test minimum mandatory requirements. 3326 assertTrue(doesMimeTypeHaveMinimumSpecVrReadyCodec(MediaFormat.MIMETYPE_VIDEO_VP9)); 3327 3328 boolean vp9IsReady = doesMimeTypeHaveVrReadyCodec(MediaFormat.MIMETYPE_VIDEO_VP9); 3329 if (!vp9IsReady) { 3330 Log.d(TAG, "VP9 isn't required to be VR ready"); 3331 return; 3332 } 3333 } 3334 3335 private boolean supportsVrHighPerformance() { 3336 PackageManager pm = mContext.getPackageManager(); 3337 return pm.hasSystemFeature(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE); 3338 } 3339 } 3340