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