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 com.android.cts.media.R; 20 21 import android.content.res.AssetFileDescriptor; 22 import android.content.res.Resources; 23 import android.graphics.ImageFormat; 24 import android.media.Image; 25 import android.media.MediaCodec; 26 import android.media.MediaCodecInfo; 27 import android.media.MediaExtractor; 28 import android.media.MediaFormat; 29 import android.util.Log; 30 import android.view.Surface; 31 32 import java.io.BufferedInputStream; 33 import java.io.IOException; 34 import java.io.InputStream; 35 import java.nio.ByteBuffer; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.List; 39 import java.util.zip.CRC32; 40 41 public class DecoderTest extends MediaPlayerTestBase { 42 private static final String TAG = "DecoderTest"; 43 44 private static final int RESET_MODE_NONE = 0; 45 private static final int RESET_MODE_RECONFIGURE = 1; 46 private static final int RESET_MODE_FLUSH = 2; 47 private static final int RESET_MODE_EOS_FLUSH = 3; 48 49 private static final String[] CSD_KEYS = new String[] { "csd-0", "csd-1" }; 50 51 private static final int CONFIG_MODE_NONE = 0; 52 private static final int CONFIG_MODE_QUEUE = 1; 53 54 private Resources mResources; 55 short[] mMasterBuffer; 56 57 @Override 58 protected void setUp() throws Exception { 59 super.setUp(); 60 mResources = mContext.getResources(); 61 62 // read master file into memory 63 AssetFileDescriptor masterFd = mResources.openRawResourceFd(R.raw.sinesweepraw); 64 long masterLength = masterFd.getLength(); 65 mMasterBuffer = new short[(int) (masterLength / 2)]; 66 InputStream is = masterFd.createInputStream(); 67 BufferedInputStream bis = new BufferedInputStream(is); 68 for (int i = 0; i < mMasterBuffer.length; i++) { 69 int lo = bis.read(); 70 int hi = bis.read(); 71 if (hi >= 128) { 72 hi -= 256; 73 } 74 int sample = hi * 256 + lo; 75 mMasterBuffer[i] = (short) sample; 76 } 77 bis.close(); 78 masterFd.close(); 79 } 80 81 // TODO: add similar tests for other audio and video formats 82 public void testBug11696552() throws Exception { 83 MediaCodec mMediaCodec = MediaCodec.createDecoderByType("audio/mp4a-latm"); 84 MediaFormat mFormat = MediaFormat.createAudioFormat("audio/mp4a-latm", 48000, 2); 85 mFormat.setByteBuffer("csd-0", ByteBuffer.wrap( new byte [] {0x13, 0x10} )); 86 mFormat.setInteger(MediaFormat.KEY_IS_ADTS, 1); 87 mMediaCodec.configure(mFormat, null, null, 0); 88 mMediaCodec.start(); 89 int index = mMediaCodec.dequeueInputBuffer(250000); 90 mMediaCodec.queueInputBuffer(index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM ); 91 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 92 mMediaCodec.dequeueOutputBuffer(info, 250000); 93 } 94 95 // The allowed errors in the following tests are the actual maximum measured 96 // errors with the standard decoders, plus 10%. 97 // This should allow for some variation in decoders, while still detecting 98 // phase and delay errors, channel swap, etc. 99 public void testDecodeMp3Lame() throws Exception { 100 decode(R.raw.sinesweepmp3lame, 804.f); 101 testTimeStampOrdering(R.raw.sinesweepmp3lame); 102 } 103 public void testDecodeMp3Smpb() throws Exception { 104 decode(R.raw.sinesweepmp3smpb, 413.f); 105 testTimeStampOrdering(R.raw.sinesweepmp3smpb); 106 } 107 public void testDecodeM4a() throws Exception { 108 decode(R.raw.sinesweepm4a, 124.f); 109 testTimeStampOrdering(R.raw.sinesweepm4a); 110 } 111 public void testDecodeOgg() throws Exception { 112 decode(R.raw.sinesweepogg, 168.f); 113 testTimeStampOrdering(R.raw.sinesweepogg); 114 } 115 public void testDecodeWav() throws Exception { 116 decode(R.raw.sinesweepwav, 0.0f); 117 testTimeStampOrdering(R.raw.sinesweepwav); 118 } 119 public void testDecodeFlac() throws Exception { 120 decode(R.raw.sinesweepflac, 0.0f); 121 testTimeStampOrdering(R.raw.sinesweepflac); 122 } 123 124 public void testDecodeMonoMp3() throws Exception { 125 monoTest(R.raw.monotestmp3); 126 testTimeStampOrdering(R.raw.monotestmp3); 127 } 128 129 public void testDecodeMonoM4a() throws Exception { 130 monoTest(R.raw.monotestm4a); 131 testTimeStampOrdering(R.raw.monotestm4a); 132 } 133 134 public void testDecodeMonoOgg() throws Exception { 135 monoTest(R.raw.monotestogg); 136 testTimeStampOrdering(R.raw.monotestogg); 137 } 138 139 public void testDecodeAacTs() throws Exception { 140 testTimeStampOrdering(R.raw.sinesweeptsaac); 141 } 142 143 public void testDecode51M4a() throws Exception { 144 decodeToMemory(R.raw.sinesweep51m4a, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 145 } 146 147 private void testTimeStampOrdering(int res) throws Exception { 148 List<Long> timestamps = new ArrayList<Long>(); 149 decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, timestamps); 150 Long lastTime = Long.MIN_VALUE; 151 for (int i = 0; i < timestamps.size(); i++) { 152 Long thisTime = timestamps.get(i); 153 assertTrue("timetravel occurred: " + lastTime + " > " + thisTime, thisTime >= lastTime); 154 lastTime = thisTime; 155 } 156 } 157 158 public void testTrackSelection() throws Exception { 159 testTrackSelection(R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz); 160 testTrackSelection( 161 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented); 162 testTrackSelection( 163 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash); 164 } 165 166 public void testBFrames() throws Exception { 167 testBFrames(R.raw.video_h264_main_b_frames); 168 testBFrames(R.raw.video_h264_main_b_frames_frag); 169 } 170 171 public void testBFrames(int res) throws Exception { 172 AssetFileDescriptor fd = mResources.openRawResourceFd(res); 173 MediaExtractor ex = new MediaExtractor(); 174 ex.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength()); 175 MediaFormat format = ex.getTrackFormat(0); 176 String mime = format.getString(MediaFormat.KEY_MIME); 177 assertTrue("not a video track. Wrong test file?", mime.startsWith("video/")); 178 MediaCodec dec = MediaCodec.createDecoderByType(mime); 179 Surface s = getActivity().getSurfaceHolder().getSurface(); 180 dec.configure(format, s, null, 0); 181 dec.start(); 182 ByteBuffer[] buf = dec.getInputBuffers(); 183 ex.selectTrack(0); 184 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 185 long lastPresentationTimeUsFromExtractor = -1; 186 long lastPresentationTimeUsFromDecoder = -1; 187 boolean inputoutoforder = false; 188 while(true) { 189 int flags = ex.getSampleFlags(); 190 long time = ex.getSampleTime(); 191 if (time >= 0 && time < lastPresentationTimeUsFromExtractor) { 192 inputoutoforder = true; 193 } 194 lastPresentationTimeUsFromExtractor = time; 195 int bufidx = dec.dequeueInputBuffer(5000); 196 if (bufidx >= 0) { 197 int n = ex.readSampleData(buf[bufidx], 0); 198 if (n < 0) { 199 flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM; 200 time = 0; 201 n = 0; 202 } 203 dec.queueInputBuffer(bufidx, 0, n, time, flags); 204 ex.advance(); 205 } 206 int status = dec.dequeueOutputBuffer(info, 5000); 207 if (status >= 0) { 208 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 209 break; 210 } 211 assertTrue("out of order timestamp from decoder", 212 info.presentationTimeUs > lastPresentationTimeUsFromDecoder); 213 dec.releaseOutputBuffer(status, true); 214 lastPresentationTimeUsFromDecoder = info.presentationTimeUs; 215 } 216 } 217 assertTrue("extractor timestamps were ordered, wrong test file?", inputoutoforder); 218 dec.release(); 219 ex.release(); 220 } 221 222 private void testTrackSelection(int resid) throws Exception { 223 AssetFileDescriptor fd1 = null; 224 try { 225 fd1 = mResources.openRawResourceFd(resid); 226 MediaExtractor ex1 = new MediaExtractor(); 227 ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength()); 228 229 ByteBuffer buf1 = ByteBuffer.allocate(1024*1024); 230 ArrayList<Integer> vid = new ArrayList<Integer>(); 231 ArrayList<Integer> aud = new ArrayList<Integer>(); 232 233 // scan the file once and build lists of audio and video samples 234 ex1.selectTrack(0); 235 ex1.selectTrack(1); 236 while(true) { 237 int n1 = ex1.readSampleData(buf1, 0); 238 if (n1 < 0) { 239 break; 240 } 241 int idx = ex1.getSampleTrackIndex(); 242 if (idx == 0) { 243 vid.add(n1); 244 } else if (idx == 1) { 245 aud.add(n1); 246 } else { 247 fail("unexpected track index: " + idx); 248 } 249 ex1.advance(); 250 } 251 252 // read the video track once, then rewind and do it again, and 253 // verify we get the right samples 254 ex1.release(); 255 ex1 = new MediaExtractor(); 256 ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength()); 257 ex1.selectTrack(0); 258 for (int i = 0; i < 2; i++) { 259 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 260 int idx = 0; 261 while(true) { 262 int n1 = ex1.readSampleData(buf1, 0); 263 if (n1 < 0) { 264 assertEquals(vid.size(), idx); 265 break; 266 } 267 assertEquals(vid.get(idx++).intValue(), n1); 268 ex1.advance(); 269 } 270 } 271 272 // read the audio track once, then rewind and do it again, and 273 // verify we get the right samples 274 ex1.release(); 275 ex1 = new MediaExtractor(); 276 ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength()); 277 ex1.selectTrack(1); 278 for (int i = 0; i < 2; i++) { 279 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 280 int idx = 0; 281 while(true) { 282 int n1 = ex1.readSampleData(buf1, 0); 283 if (n1 < 0) { 284 assertEquals(aud.size(), idx); 285 break; 286 } 287 assertEquals(aud.get(idx++).intValue(), n1); 288 ex1.advance(); 289 } 290 } 291 292 // read the video track first, then rewind and get the audio track instead, and 293 // verify we get the right samples 294 ex1.release(); 295 ex1 = new MediaExtractor(); 296 ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength()); 297 for (int i = 0; i < 2; i++) { 298 ex1.selectTrack(i); 299 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 300 int idx = 0; 301 while(true) { 302 int n1 = ex1.readSampleData(buf1, 0); 303 if (i == 0) { 304 if (n1 < 0) { 305 assertEquals(vid.size(), idx); 306 break; 307 } 308 assertEquals(vid.get(idx++).intValue(), n1); 309 } else if (i == 1) { 310 if (n1 < 0) { 311 assertEquals(aud.size(), idx); 312 break; 313 } 314 assertEquals(aud.get(idx++).intValue(), n1); 315 } else { 316 fail("unexpected track index: " + idx); 317 } 318 ex1.advance(); 319 } 320 ex1.unselectTrack(i); 321 } 322 323 // read the video track first, then rewind, enable the audio track in addition 324 // to the video track, and verify we get the right samples 325 ex1.release(); 326 ex1 = new MediaExtractor(); 327 ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength()); 328 for (int i = 0; i < 2; i++) { 329 ex1.selectTrack(i); 330 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 331 int vididx = 0; 332 int audidx = 0; 333 while(true) { 334 int n1 = ex1.readSampleData(buf1, 0); 335 if (n1 < 0) { 336 // we should have read all audio and all video samples at this point 337 assertEquals(vid.size(), vididx); 338 if (i == 1) { 339 assertEquals(aud.size(), audidx); 340 } 341 break; 342 } 343 int trackidx = ex1.getSampleTrackIndex(); 344 if (trackidx == 0) { 345 assertEquals(vid.get(vididx++).intValue(), n1); 346 } else if (trackidx == 1) { 347 assertEquals(aud.get(audidx++).intValue(), n1); 348 } else { 349 fail("unexpected track index: " + trackidx); 350 } 351 ex1.advance(); 352 } 353 } 354 355 // read both tracks from the start, then rewind and verify we get the right 356 // samples both times 357 ex1.release(); 358 ex1 = new MediaExtractor(); 359 ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength()); 360 for (int i = 0; i < 2; i++) { 361 ex1.selectTrack(0); 362 ex1.selectTrack(1); 363 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 364 int vididx = 0; 365 int audidx = 0; 366 while(true) { 367 int n1 = ex1.readSampleData(buf1, 0); 368 if (n1 < 0) { 369 // we should have read all audio and all video samples at this point 370 assertEquals(vid.size(), vididx); 371 assertEquals(aud.size(), audidx); 372 break; 373 } 374 int trackidx = ex1.getSampleTrackIndex(); 375 if (trackidx == 0) { 376 assertEquals(vid.get(vididx++).intValue(), n1); 377 } else if (trackidx == 1) { 378 assertEquals(aud.get(audidx++).intValue(), n1); 379 } else { 380 fail("unexpected track index: " + trackidx); 381 } 382 ex1.advance(); 383 } 384 } 385 386 } finally { 387 if (fd1 != null) { 388 fd1.close(); 389 } 390 } 391 } 392 393 public void testDecodeFragmented() throws Exception { 394 testDecodeFragmented(R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz, 395 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented); 396 testDecodeFragmented(R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz, 397 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash); 398 } 399 400 private void testDecodeFragmented(int reference, int teststream) throws Exception { 401 AssetFileDescriptor fd1 = null; 402 AssetFileDescriptor fd2 = null; 403 try { 404 fd1 = mResources.openRawResourceFd(reference); 405 MediaExtractor ex1 = new MediaExtractor(); 406 ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength()); 407 408 fd2 = mResources.openRawResourceFd(teststream); 409 MediaExtractor ex2 = new MediaExtractor(); 410 ex2.setDataSource(fd2.getFileDescriptor(), fd2.getStartOffset(), fd2.getLength()); 411 412 assertEquals("different track count", ex1.getTrackCount(), ex2.getTrackCount()); 413 414 ByteBuffer buf1 = ByteBuffer.allocate(1024*1024); 415 ByteBuffer buf2 = ByteBuffer.allocate(1024*1024); 416 417 for (int i = 0; i < ex1.getTrackCount(); i++) { 418 // note: this assumes the tracks are reported in the order in which they appear 419 // in the file. 420 ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 421 ex1.selectTrack(i); 422 ex2.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 423 ex2.selectTrack(i); 424 425 while(true) { 426 int n1 = ex1.readSampleData(buf1, 0); 427 int n2 = ex2.readSampleData(buf2, 0); 428 assertEquals("different buffer size on track " + i, n1, n2); 429 430 if (n1 < 0) { 431 break; 432 } 433 // see bug 13008204 434 buf1.limit(n1); 435 buf2.limit(n2); 436 buf1.rewind(); 437 buf2.rewind(); 438 439 assertEquals("limit does not match return value on track " + i, 440 n1, buf1.limit()); 441 assertEquals("limit does not match return value on track " + i, 442 n2, buf2.limit()); 443 444 assertEquals("buffer data did not match on track " + i, buf1, buf2); 445 446 ex1.advance(); 447 ex2.advance(); 448 } 449 ex1.unselectTrack(i); 450 ex2.unselectTrack(i); 451 } 452 } finally { 453 if (fd1 != null) { 454 fd1.close(); 455 } 456 if (fd2 != null) { 457 fd2.close(); 458 } 459 } 460 } 461 462 463 private void monoTest(int res) throws Exception { 464 short [] mono = decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 465 if (mono.length == 44100) { 466 // expected 467 } else if (mono.length == 88200) { 468 // the decoder output 2 channels instead of 1, check that the left and right channel 469 // are identical 470 for (int i = 0; i < mono.length; i += 2) { 471 assertEquals("mismatched samples at " + i, mono[i], mono[i+1]); 472 } 473 } else { 474 fail("wrong number of samples: " + mono.length); 475 } 476 477 short [] mono2 = decodeToMemory(res, RESET_MODE_RECONFIGURE, CONFIG_MODE_NONE, -1, null); 478 assertTrue(Arrays.equals(mono, mono2)); 479 480 short [] mono3 = decodeToMemory(res, RESET_MODE_FLUSH, CONFIG_MODE_NONE, -1, null); 481 assertTrue(Arrays.equals(mono, mono3)); 482 } 483 484 /** 485 * @param testinput the file to decode 486 * @param maxerror the maximum allowed root mean squared error 487 * @throws IOException 488 */ 489 private void decode(int testinput, float maxerror) throws IOException { 490 491 short[] decoded = decodeToMemory(testinput, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null); 492 493 assertEquals("wrong data size", mMasterBuffer.length, decoded.length); 494 495 long totalErrorSquared = 0; 496 497 for (int i = 0; i < decoded.length; i++) { 498 short sample = decoded[i]; 499 short mastersample = mMasterBuffer[i]; 500 int d = sample - mastersample; 501 totalErrorSquared += d * d; 502 } 503 504 long avgErrorSquared = (totalErrorSquared / decoded.length); 505 double rmse = Math.sqrt(avgErrorSquared); 506 assertTrue("decoding error too big: " + rmse, rmse <= maxerror); 507 508 int[] resetModes = new int[] { RESET_MODE_NONE, RESET_MODE_RECONFIGURE, 509 RESET_MODE_FLUSH, RESET_MODE_EOS_FLUSH }; 510 int[] configModes = new int[] { CONFIG_MODE_NONE, CONFIG_MODE_QUEUE }; 511 512 for (int conf : configModes) { 513 for (int reset : resetModes) { 514 if (conf == CONFIG_MODE_NONE && reset == RESET_MODE_NONE) { 515 // default case done outside of loop 516 continue; 517 } 518 if (conf == CONFIG_MODE_QUEUE && !hasAudioCsd(testinput)) { 519 continue; 520 } 521 522 String params = String.format("(using reset: %d, config: %s)", reset, conf); 523 short[] decoded2 = decodeToMemory(testinput, reset, conf, -1, null); 524 assertEquals("count different with reconfigure" + params, 525 decoded.length, decoded2.length); 526 for (int i = 0; i < decoded.length; i++) { 527 assertEquals("samples don't match" + params, decoded[i], decoded2[i]); 528 } 529 } 530 } 531 } 532 533 private boolean hasAudioCsd(int testinput) throws IOException { 534 AssetFileDescriptor fd = null; 535 try { 536 537 fd = mResources.openRawResourceFd(testinput); 538 MediaExtractor extractor = new MediaExtractor(); 539 extractor.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength()); 540 MediaFormat format = extractor.getTrackFormat(0); 541 542 return format.containsKey(CSD_KEYS[0]); 543 544 } finally { 545 if (fd != null) { 546 fd.close(); 547 } 548 } 549 } 550 551 private short[] decodeToMemory(int testinput, int resetMode, int configMode, 552 int eossample, List<Long> timestamps) throws IOException { 553 554 String localTag = TAG + "#decodeToMemory"; 555 Log.v(localTag, String.format("reset = %d; config: %s", resetMode, configMode)); 556 short [] decoded = new short[0]; 557 int decodedIdx = 0; 558 559 AssetFileDescriptor testFd = mResources.openRawResourceFd(testinput); 560 561 MediaExtractor extractor; 562 MediaCodec codec; 563 ByteBuffer[] codecInputBuffers; 564 ByteBuffer[] codecOutputBuffers; 565 566 extractor = new MediaExtractor(); 567 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 568 testFd.getLength()); 569 testFd.close(); 570 571 assertEquals("wrong number of tracks", 1, extractor.getTrackCount()); 572 MediaFormat format = extractor.getTrackFormat(0); 573 String mime = format.getString(MediaFormat.KEY_MIME); 574 assertTrue("not an audio file", mime.startsWith("audio/")); 575 576 MediaFormat configFormat = format; 577 codec = MediaCodec.createDecoderByType(mime); 578 if (configMode == CONFIG_MODE_QUEUE && format.containsKey(CSD_KEYS[0])) { 579 configFormat = MediaFormat.createAudioFormat(mime, 580 format.getInteger(MediaFormat.KEY_SAMPLE_RATE), 581 format.getInteger(MediaFormat.KEY_CHANNEL_COUNT)); 582 583 configFormat.setLong(MediaFormat.KEY_DURATION, 584 format.getLong(MediaFormat.KEY_DURATION)); 585 String[] keys = new String[] { "max-input-size", "encoder-delay", "encoder-padding" }; 586 for (String k : keys) { 587 if (format.containsKey(k)) { 588 configFormat.setInteger(k, format.getInteger(k)); 589 } 590 } 591 } 592 Log.v(localTag, "configuring with " + configFormat); 593 codec.configure(configFormat, null /* surface */, null /* crypto */, 0 /* flags */); 594 595 codec.start(); 596 codecInputBuffers = codec.getInputBuffers(); 597 codecOutputBuffers = codec.getOutputBuffers(); 598 599 if (resetMode == RESET_MODE_RECONFIGURE) { 600 codec.stop(); 601 codec.configure(configFormat, null /* surface */, null /* crypto */, 0 /* flags */); 602 codec.start(); 603 codecInputBuffers = codec.getInputBuffers(); 604 codecOutputBuffers = codec.getOutputBuffers(); 605 } else if (resetMode == RESET_MODE_FLUSH) { 606 codec.flush(); 607 } 608 609 extractor.selectTrack(0); 610 611 if (configMode == CONFIG_MODE_QUEUE) { 612 queueConfig(codec, format); 613 } 614 615 // start decoding 616 final long kTimeOutUs = 5000; 617 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 618 boolean sawInputEOS = false; 619 boolean sawOutputEOS = false; 620 int noOutputCounter = 0; 621 int samplecounter = 0; 622 while (!sawOutputEOS && noOutputCounter < 50) { 623 noOutputCounter++; 624 if (!sawInputEOS) { 625 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs); 626 627 if (inputBufIndex >= 0) { 628 ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; 629 630 int sampleSize = 631 extractor.readSampleData(dstBuf, 0 /* offset */); 632 633 long presentationTimeUs = 0; 634 635 if (sampleSize < 0 && eossample > 0) { 636 fail("test is broken: never reached eos sample"); 637 } 638 if (sampleSize < 0) { 639 Log.d(TAG, "saw input EOS."); 640 sawInputEOS = true; 641 sampleSize = 0; 642 } else { 643 if (samplecounter == eossample) { 644 sawInputEOS = true; 645 } 646 samplecounter++; 647 presentationTimeUs = extractor.getSampleTime(); 648 } 649 codec.queueInputBuffer( 650 inputBufIndex, 651 0 /* offset */, 652 sampleSize, 653 presentationTimeUs, 654 sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); 655 656 if (!sawInputEOS) { 657 extractor.advance(); 658 } 659 } 660 } 661 662 int res = codec.dequeueOutputBuffer(info, kTimeOutUs); 663 664 if (res >= 0) { 665 //Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs); 666 667 if (info.size > 0) { 668 noOutputCounter = 0; 669 if (timestamps != null) { 670 timestamps.add(info.presentationTimeUs); 671 } 672 } 673 if (info.size > 0 && 674 resetMode != RESET_MODE_NONE && resetMode != RESET_MODE_EOS_FLUSH) { 675 // once we've gotten some data out of the decoder, reset and start again 676 if (resetMode == RESET_MODE_RECONFIGURE) { 677 codec.stop(); 678 codec.configure(configFormat, null /* surface */, null /* crypto */, 679 0 /* flags */); 680 codec.start(); 681 codecInputBuffers = codec.getInputBuffers(); 682 codecOutputBuffers = codec.getOutputBuffers(); 683 if (configMode == CONFIG_MODE_QUEUE) { 684 queueConfig(codec, format); 685 } 686 } else /* resetMode == RESET_MODE_FLUSH */ { 687 codec.flush(); 688 } 689 resetMode = RESET_MODE_NONE; 690 extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 691 sawInputEOS = false; 692 samplecounter = 0; 693 if (timestamps != null) { 694 timestamps.clear(); 695 } 696 continue; 697 } 698 699 int outputBufIndex = res; 700 ByteBuffer buf = codecOutputBuffers[outputBufIndex]; 701 702 if (decodedIdx + (info.size / 2) >= decoded.length) { 703 decoded = Arrays.copyOf(decoded, decodedIdx + (info.size / 2)); 704 } 705 706 buf.position(info.offset); 707 for (int i = 0; i < info.size; i += 2) { 708 decoded[decodedIdx++] = buf.getShort(); 709 } 710 711 codec.releaseOutputBuffer(outputBufIndex, false /* render */); 712 713 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 714 Log.d(TAG, "saw output EOS."); 715 if (resetMode == RESET_MODE_EOS_FLUSH) { 716 resetMode = RESET_MODE_NONE; 717 codec.flush(); 718 extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 719 sawInputEOS = false; 720 samplecounter = 0; 721 decoded = new short[0]; 722 decodedIdx = 0; 723 if (timestamps != null) { 724 timestamps.clear(); 725 } 726 } else { 727 sawOutputEOS = true; 728 } 729 } 730 } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 731 codecOutputBuffers = codec.getOutputBuffers(); 732 733 Log.d(TAG, "output buffers have changed."); 734 } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 735 MediaFormat oformat = codec.getOutputFormat(); 736 737 Log.d(TAG, "output format has changed to " + oformat); 738 } else { 739 Log.d(TAG, "dequeueOutputBuffer returned " + res); 740 } 741 } 742 if (noOutputCounter >= 50) { 743 fail("decoder stopped outputing data"); 744 } 745 746 codec.stop(); 747 codec.release(); 748 return decoded; 749 } 750 751 private void queueConfig(MediaCodec codec, MediaFormat format) { 752 for (String csdKey : CSD_KEYS) { 753 if (!format.containsKey(csdKey)) { 754 continue; 755 } 756 ByteBuffer[] codecInputBuffers = codec.getInputBuffers(); 757 int inputBufIndex = codec.dequeueInputBuffer(-1); 758 if (inputBufIndex < 0) { 759 fail("failed to queue configuration buffer " + csdKey); 760 } else { 761 ByteBuffer csd = (ByteBuffer) format.getByteBuffer(csdKey).rewind(); 762 Log.v(TAG + "#queueConfig", String.format("queueing %s:%s", csdKey, csd)); 763 codecInputBuffers[inputBufIndex].put(csd); 764 codec.queueInputBuffer( 765 inputBufIndex, 766 0 /* offset */, 767 csd.limit(), 768 0 /* presentation time (us) */, 769 MediaCodec.BUFFER_FLAG_CODEC_CONFIG); 770 } 771 } 772 } 773 774 public void testDecodeWithEOSOnLastBuffer() throws Exception { 775 testDecodeWithEOSOnLastBuffer(R.raw.sinesweepm4a); 776 testDecodeWithEOSOnLastBuffer(R.raw.sinesweepmp3lame); 777 testDecodeWithEOSOnLastBuffer(R.raw.sinesweepmp3smpb); 778 testDecodeWithEOSOnLastBuffer(R.raw.sinesweepwav); 779 testDecodeWithEOSOnLastBuffer(R.raw.sinesweepflac); 780 testDecodeWithEOSOnLastBuffer(R.raw.sinesweepogg); 781 } 782 783 /* setting EOS on the last full input buffer should be equivalent to setting EOS on an empty 784 * input buffer after all the full ones. */ 785 private void testDecodeWithEOSOnLastBuffer(int res) throws Exception { 786 int numsamples = countSamples(res); 787 assertTrue(numsamples != 0); 788 789 List<Long> timestamps1 = new ArrayList<Long>(); 790 short[] decode1 = decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, timestamps1); 791 792 List<Long> timestamps2 = new ArrayList<Long>(); 793 short[] decode2 = decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, numsamples - 1, 794 timestamps2); 795 796 // check that the data and the timestamps are the same for EOS-on-last and EOS-after-last 797 assertEquals(decode1.length, decode2.length); 798 assertTrue(Arrays.equals(decode1, decode2)); 799 assertEquals(timestamps1.size(), timestamps2.size()); 800 assertTrue(timestamps1.equals(timestamps2)); 801 802 // ... and that this is also true when reconfiguring the codec 803 timestamps2.clear(); 804 decode2 = decodeToMemory(res, RESET_MODE_RECONFIGURE, CONFIG_MODE_NONE, -1, timestamps2); 805 assertTrue(Arrays.equals(decode1, decode2)); 806 assertTrue(timestamps1.equals(timestamps2)); 807 timestamps2.clear(); 808 decode2 = decodeToMemory(res, RESET_MODE_RECONFIGURE, CONFIG_MODE_NONE, numsamples - 1, 809 timestamps2); 810 assertEquals(decode1.length, decode2.length); 811 assertTrue(Arrays.equals(decode1, decode2)); 812 assertTrue(timestamps1.equals(timestamps2)); 813 814 // ... and that this is also true when flushing the codec 815 timestamps2.clear(); 816 decode2 = decodeToMemory(res, RESET_MODE_FLUSH, CONFIG_MODE_NONE, -1, timestamps2); 817 assertTrue(Arrays.equals(decode1, decode2)); 818 assertTrue(timestamps1.equals(timestamps2)); 819 timestamps2.clear(); 820 decode2 = decodeToMemory(res, RESET_MODE_FLUSH, CONFIG_MODE_NONE, numsamples - 1, 821 timestamps2); 822 assertEquals(decode1.length, decode2.length); 823 assertTrue(Arrays.equals(decode1, decode2)); 824 assertTrue(timestamps1.equals(timestamps2)); 825 } 826 827 private int countSamples(int res) throws IOException { 828 AssetFileDescriptor testFd = mResources.openRawResourceFd(res); 829 830 MediaExtractor extractor = new MediaExtractor(); 831 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 832 testFd.getLength()); 833 testFd.close(); 834 extractor.selectTrack(0); 835 int numsamples = 0; 836 while (extractor.advance()) { 837 numsamples++; 838 } 839 return numsamples; 840 } 841 842 public void testCodecBasicH264() throws Exception { 843 Surface s = getActivity().getSurfaceHolder().getSurface(); 844 int frames1 = countFrames( 845 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 846 RESET_MODE_NONE, -1 /* eosframe */, s); 847 assertEquals("wrong number of frames decoded", 240, frames1); 848 849 int frames2 = countFrames( 850 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 851 RESET_MODE_NONE, -1 /* eosframe */, null); 852 assertEquals("different number of frames when using Surface", frames1, frames2); 853 } 854 855 public void testCodecBasicHEVC() throws Exception { 856 Surface s = getActivity().getSurfaceHolder().getSurface(); 857 int frames1 = countFrames( 858 R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz, 859 RESET_MODE_NONE, -1 /* eosframe */, s); 860 assertEquals("wrong number of frames decoded", 300, frames1); 861 862 int frames2 = countFrames( 863 R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz, 864 RESET_MODE_NONE, -1 /* eosframe */, null); 865 assertEquals("different number of frames when using Surface", frames1, frames2); 866 } 867 868 public void testCodecBasicH263() throws Exception { 869 Surface s = getActivity().getSurfaceHolder().getSurface(); 870 int frames1 = countFrames( 871 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 872 RESET_MODE_NONE, -1 /* eosframe */, s); 873 assertEquals("wrong number of frames decoded", 122, frames1); 874 875 int frames2 = countFrames( 876 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 877 RESET_MODE_NONE, -1 /* eosframe */, null); 878 assertEquals("different number of frames when using Surface", frames1, frames2); 879 } 880 881 public void testCodecBasicMpeg4() throws Exception { 882 Surface s = getActivity().getSurfaceHolder().getSurface(); 883 int frames1 = countFrames( 884 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 885 RESET_MODE_NONE, -1 /* eosframe */, s); 886 assertEquals("wrong number of frames decoded", 249, frames1); 887 888 int frames2 = countFrames( 889 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 890 RESET_MODE_NONE, -1 /* eosframe */, null); 891 assertEquals("different number of frames when using Surface", frames1, frames2); 892 } 893 894 public void testCodecBasicVP8() throws Exception { 895 Surface s = getActivity().getSurfaceHolder().getSurface(); 896 int frames1 = countFrames( 897 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 898 RESET_MODE_NONE, -1 /* eosframe */, s); 899 assertEquals("wrong number of frames decoded", 240, frames1); 900 901 int frames2 = countFrames( 902 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 903 RESET_MODE_NONE, -1 /* eosframe */, null); 904 assertEquals("different number of frames when using Surface", frames1, frames2); 905 } 906 907 public void testCodecBasicVP9() throws Exception { 908 Surface s = getActivity().getSurfaceHolder().getSurface(); 909 int frames1 = countFrames( 910 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 911 RESET_MODE_NONE, -1 /* eosframe */, s); 912 assertEquals("wrong number of frames decoded", 240, frames1); 913 914 int frames2 = countFrames( 915 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 916 RESET_MODE_NONE, -1 /* eosframe */, null); 917 assertEquals("different number of frames when using Surface", frames1, frames2); 918 } 919 920 public void testCodecEarlyEOSH263() throws Exception { 921 Surface s = getActivity().getSurfaceHolder().getSurface(); 922 int frames1 = countFrames( 923 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 924 RESET_MODE_NONE, 64 /* eosframe */, s); 925 assertEquals("wrong number of frames decoded", 64, frames1); 926 } 927 928 public void testCodecEarlyEOSH264() throws Exception { 929 Surface s = getActivity().getSurfaceHolder().getSurface(); 930 int frames1 = countFrames( 931 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 932 RESET_MODE_NONE, 120 /* eosframe */, s); 933 assertEquals("wrong number of frames decoded", 120, frames1); 934 } 935 936 public void testCodecEarlyEOSHEVC() throws Exception { 937 Surface s = getActivity().getSurfaceHolder().getSurface(); 938 int frames1 = countFrames( 939 R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz, 940 RESET_MODE_NONE, 120 /* eosframe */, s); 941 assertEquals("wrong number of frames decoded", 120, frames1); 942 } 943 944 public void testCodecEarlyEOSMpeg4() throws Exception { 945 Surface s = getActivity().getSurfaceHolder().getSurface(); 946 int frames1 = countFrames( 947 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 948 RESET_MODE_NONE, 120 /* eosframe */, s); 949 assertEquals("wrong number of frames decoded", 120, frames1); 950 } 951 952 public void testCodecEarlyEOSVP8() throws Exception { 953 Surface s = getActivity().getSurfaceHolder().getSurface(); 954 int frames1 = countFrames( 955 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 956 RESET_MODE_NONE, 120 /* eosframe */, s); 957 assertEquals("wrong number of frames decoded", 120, frames1); 958 } 959 960 public void testCodecEarlyEOSVP9() throws Exception { 961 Surface s = getActivity().getSurfaceHolder().getSurface(); 962 int frames1 = countFrames( 963 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 964 RESET_MODE_NONE, 120 /* eosframe */, s); 965 assertEquals("wrong number of frames decoded", 120, frames1); 966 } 967 968 public void testCodecResetsH264WithoutSurface() throws Exception { 969 testCodecResets( 970 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, null); 971 } 972 973 public void testCodecResetsH264WithSurface() throws Exception { 974 Surface s = getActivity().getSurfaceHolder().getSurface(); 975 testCodecResets( 976 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, s); 977 } 978 979 public void testCodecResetsHEVCWithoutSurface() throws Exception { 980 testCodecResets( 981 R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz, null); 982 } 983 984 public void testCodecResetsHEVCWithSurface() throws Exception { 985 Surface s = getActivity().getSurfaceHolder().getSurface(); 986 testCodecResets( 987 R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz, s); 988 } 989 990 public void testCodecResetsH263WithoutSurface() throws Exception { 991 testCodecResets( 992 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, null); 993 } 994 995 public void testCodecResetsH263WithSurface() throws Exception { 996 Surface s = getActivity().getSurfaceHolder().getSurface(); 997 testCodecResets( 998 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, s); 999 } 1000 1001 public void testCodecResetsMpeg4WithoutSurface() throws Exception { 1002 testCodecResets( 1003 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, null); 1004 } 1005 1006 public void testCodecResetsMpeg4WithSurface() throws Exception { 1007 Surface s = getActivity().getSurfaceHolder().getSurface(); 1008 testCodecResets( 1009 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, s); 1010 } 1011 1012 public void testCodecResetsVP8WithoutSurface() throws Exception { 1013 testCodecResets( 1014 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, null); 1015 } 1016 1017 public void testCodecResetsVP8WithSurface() throws Exception { 1018 Surface s = getActivity().getSurfaceHolder().getSurface(); 1019 testCodecResets( 1020 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, s); 1021 } 1022 1023 public void testCodecResetsVP9WithoutSurface() throws Exception { 1024 testCodecResets( 1025 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, null); 1026 } 1027 1028 public void testCodecResetsVP9WithSurface() throws Exception { 1029 Surface s = getActivity().getSurfaceHolder().getSurface(); 1030 testCodecResets( 1031 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, s); 1032 } 1033 1034 // public void testCodecResetsOgg() throws Exception { 1035 // testCodecResets(R.raw.sinesweepogg, null); 1036 // } 1037 1038 public void testCodecResetsMp3() throws Exception { 1039 testCodecReconfig(R.raw.sinesweepmp3lame); 1040 // NOTE: replacing testCodecReconfig call soon 1041 // testCodecResets(R.raw.sinesweepmp3lame, null); 1042 } 1043 1044 public void testCodecResetsM4a() throws Exception { 1045 testCodecReconfig(R.raw.sinesweepm4a); 1046 // NOTE: replacing testCodecReconfig call soon 1047 // testCodecResets(R.raw.sinesweepm4a, null); 1048 } 1049 1050 private void testCodecReconfig(int audio) throws Exception { 1051 int size1 = countSize(audio, RESET_MODE_NONE, -1 /* eosframe */); 1052 int size2 = countSize(audio, RESET_MODE_RECONFIGURE, -1 /* eosframe */); 1053 assertEquals("different output size when using reconfigured codec", size1, size2); 1054 } 1055 1056 private void testCodecResets(int video, Surface s) throws Exception { 1057 int frames1 = countFrames(video, RESET_MODE_NONE, -1 /* eosframe */, s); 1058 int frames2 = countFrames(video, RESET_MODE_RECONFIGURE, -1 /* eosframe */, s); 1059 int frames3 = countFrames(video, RESET_MODE_FLUSH, -1 /* eosframe */, s); 1060 assertEquals("different number of frames when using reconfigured codec", frames1, frames2); 1061 assertEquals("different number of frames when using flushed codec", frames1, frames3); 1062 } 1063 1064 private static MediaCodec createDecoder(String mime) { 1065 try { 1066 if (false) { 1067 // change to force testing software codecs 1068 if (mime.contains("avc")) { 1069 return MediaCodec.createByCodecName("OMX.google.h264.decoder"); 1070 } else if (mime.contains("hevc")) { 1071 return MediaCodec.createByCodecName("OMX.google.hevc.decoder"); 1072 } else if (mime.contains("3gpp")) { 1073 return MediaCodec.createByCodecName("OMX.google.h263.decoder"); 1074 } else if (mime.contains("mp4v")) { 1075 return MediaCodec.createByCodecName("OMX.google.mpeg4.decoder"); 1076 } else if (mime.contains("vp8")) { 1077 return MediaCodec.createByCodecName("OMX.google.vp8.decoder"); 1078 } else if (mime.contains("vp9")) { 1079 return MediaCodec.createByCodecName("OMX.google.vp9.decoder"); 1080 } 1081 } 1082 return MediaCodec.createDecoderByType(mime); 1083 } catch (Exception e) { 1084 return null; 1085 } 1086 } 1087 1088 // for video 1089 private int countFrames(int video, int resetMode, int eosframe, Surface s) 1090 throws Exception { 1091 AssetFileDescriptor testFd = mResources.openRawResourceFd(video); 1092 MediaExtractor extractor = new MediaExtractor(); 1093 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 1094 testFd.getLength()); 1095 extractor.selectTrack(0); 1096 1097 int numframes = decodeWithChecks(extractor, CHECKFLAG_RETURN_OUTPUTFRAMES 1098 | CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH, resetMode, s, 1099 eosframe, null, null); 1100 1101 extractor.release(); 1102 testFd.close(); 1103 return numframes; 1104 } 1105 1106 // for audio 1107 private int countSize(int audio, int resetMode, int eosframe) 1108 throws Exception { 1109 AssetFileDescriptor testFd = mResources.openRawResourceFd(audio); 1110 MediaExtractor extractor = new MediaExtractor(); 1111 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 1112 testFd.getLength()); 1113 extractor.selectTrack(0); 1114 1115 // fails CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH 1116 int outputSize = decodeWithChecks(extractor, CHECKFLAG_RETURN_OUTPUTSIZE, resetMode, null, 1117 eosframe, null, null); 1118 1119 extractor.release(); 1120 testFd.close(); 1121 return outputSize; 1122 } 1123 1124 private void testEOSBehavior(int movie, int stopatsample) throws Exception { 1125 testEOSBehavior(movie, new int[] {stopatsample}); 1126 } 1127 1128 private void testEOSBehavior(int movie, int[] stopAtSample) throws Exception { 1129 Surface s = null; 1130 AssetFileDescriptor testFd = mResources.openRawResourceFd(movie); 1131 MediaExtractor extractor = new MediaExtractor(); 1132 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 1133 testFd.getLength()); 1134 extractor.selectTrack(0); // consider variable looping on track 1135 List<Long> outputChecksums = new ArrayList<Long>(); 1136 List<Long> outputTimestamps = new ArrayList<Long>(); 1137 Arrays.sort(stopAtSample); 1138 int last = stopAtSample.length - 1; 1139 1140 // decode reference (longest sequence to stop at + 100) and 1141 // store checksums/pts in outputChecksums and outputTimestamps 1142 // (will fail CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH) 1143 decodeWithChecks(extractor, 1144 CHECKFLAG_SETCHECKSUM | CHECKFLAG_SETPTS | CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH, 1145 RESET_MODE_NONE, s, 1146 stopAtSample[last] + 100, outputChecksums, outputTimestamps); 1147 1148 // decode stopAtSample requests in reverse order (longest to 1149 // shortest) and compare to reference checksums/pts in 1150 // outputChecksums and outputTimestamps 1151 for (int i = last; i >= 0; --i) { 1152 if (true) { // reposition extractor 1153 extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 1154 } else { // create new extractor 1155 extractor.release(); 1156 extractor = new MediaExtractor(); 1157 extractor.setDataSource(testFd.getFileDescriptor(), 1158 testFd.getStartOffset(), testFd.getLength()); 1159 extractor.selectTrack(0); // consider variable looping on track 1160 } 1161 decodeWithChecks(extractor, 1162 CHECKFLAG_COMPARECHECKSUM | CHECKFLAG_COMPAREPTS 1163 | CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH 1164 | CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH, 1165 RESET_MODE_NONE, s, 1166 stopAtSample[i], outputChecksums, outputTimestamps); 1167 } 1168 extractor.release(); 1169 testFd.close(); 1170 } 1171 1172 private static final int CHECKFLAG_SETCHECKSUM = 1 << 0; 1173 private static final int CHECKFLAG_COMPARECHECKSUM = 1 << 1; 1174 private static final int CHECKFLAG_SETPTS = 1 << 2; 1175 private static final int CHECKFLAG_COMPAREPTS = 1 << 3; 1176 private static final int CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH = 1 << 4; 1177 private static final int CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH = 1 << 5; 1178 private static final int CHECKFLAG_RETURN_OUTPUTFRAMES = 1 << 6; 1179 private static final int CHECKFLAG_RETURN_OUTPUTSIZE = 1 << 7; 1180 1181 /** 1182 * Decodes frames with parameterized checks and return values. 1183 * The integer return can be selected through the checkFlags variable. 1184 */ 1185 private static int decodeWithChecks(MediaExtractor extractor, int checkFlags, int resetMode, 1186 Surface surface, int stopAtSample, 1187 List<Long> outputChecksums, List<Long> outputTimestamps) 1188 throws Exception { 1189 int trackIndex = extractor.getSampleTrackIndex(); 1190 MediaFormat format = extractor.getTrackFormat(trackIndex); 1191 String mime = format.getString(MediaFormat.KEY_MIME); 1192 boolean isAudio = mime.startsWith("audio/"); 1193 ByteBuffer[] codecInputBuffers; 1194 ByteBuffer[] codecOutputBuffers; 1195 1196 MediaCodec codec = createDecoder(mime); 1197 Log.i("@@@@", "using codec: " + codec.getName()); 1198 codec.configure(format, surface, null /* crypto */, 0 /* flags */); 1199 codec.start(); 1200 codecInputBuffers = codec.getInputBuffers(); 1201 codecOutputBuffers = codec.getOutputBuffers(); 1202 1203 if (resetMode == RESET_MODE_RECONFIGURE) { 1204 codec.stop(); 1205 codec.configure(format, surface, null /* crypto */, 0 /* flags */); 1206 codec.start(); 1207 codecInputBuffers = codec.getInputBuffers(); 1208 codecOutputBuffers = codec.getOutputBuffers(); 1209 } else if (resetMode == RESET_MODE_FLUSH) { 1210 codec.flush(); 1211 } 1212 1213 // start decode loop 1214 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 1215 1216 final long kTimeOutUs = 5000; // 5ms timeout 1217 boolean sawInputEOS = false; 1218 boolean sawOutputEOS = false; 1219 int deadDecoderCounter = 0; 1220 int samplenum = 0; 1221 int numframes = 0; 1222 int outputSize = 0; 1223 int width = 0; 1224 int height = 0; 1225 boolean dochecksum = false; 1226 ArrayList<Long> timestamps = new ArrayList<Long>(); 1227 if ((checkFlags & CHECKFLAG_SETPTS) != 0) { 1228 outputTimestamps.clear(); 1229 } 1230 if ((checkFlags & CHECKFLAG_SETCHECKSUM) != 0) { 1231 outputChecksums.clear(); 1232 } 1233 while (!sawOutputEOS && deadDecoderCounter < 100) { 1234 // handle input 1235 if (!sawInputEOS) { 1236 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs); 1237 1238 if (inputBufIndex >= 0) { 1239 ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; 1240 1241 int sampleSize = 1242 extractor.readSampleData(dstBuf, 0 /* offset */); 1243 long presentationTimeUs = extractor.getSampleTime(); 1244 boolean advanceDone = extractor.advance(); 1245 // int flags = extractor.getSampleFlags(); 1246 // Log.i("@@@@", "read sample " + samplenum + ":" + 1247 // extractor.getSampleFlags() 1248 // + " @ " + extractor.getSampleTime() + " size " + 1249 // sampleSize); 1250 assertEquals("extractor.advance() should match end of stream", sampleSize >= 0, 1251 advanceDone); 1252 1253 if (sampleSize < 0) { 1254 Log.d(TAG, "saw input EOS."); 1255 sawInputEOS = true; 1256 assertEquals("extractor.readSampleData() must return -1 at end of stream", 1257 -1, sampleSize); 1258 assertEquals("extractor.getSampleTime() must return -1 at end of stream", 1259 -1, presentationTimeUs); 1260 sampleSize = 0; // required otherwise queueInputBuffer 1261 // returns invalid. 1262 } else { 1263 timestamps.add(presentationTimeUs); 1264 samplenum++; // increment before comparing with stopAtSample 1265 if (samplenum == stopAtSample) { 1266 Log.d(TAG, "saw input EOS (stop at sample)."); 1267 sawInputEOS = true; // tag this sample as EOS 1268 } 1269 } 1270 codec.queueInputBuffer( 1271 inputBufIndex, 1272 0 /* offset */, 1273 sampleSize, 1274 presentationTimeUs, 1275 sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); 1276 } else { 1277 assertEquals( 1278 "codec.dequeueInputBuffer() unrecognized return value: " + inputBufIndex, 1279 MediaCodec.INFO_TRY_AGAIN_LATER, inputBufIndex); 1280 } 1281 } 1282 1283 // handle output 1284 int outputBufIndex = codec.dequeueOutputBuffer(info, kTimeOutUs); 1285 1286 deadDecoderCounter++; 1287 if (outputBufIndex >= 0) { 1288 if (info.size > 0) { // Disregard 0-sized buffers at the end. 1289 deadDecoderCounter = 0; 1290 if (resetMode != RESET_MODE_NONE) { 1291 // once we've gotten some data out of the decoder, reset 1292 // and start again 1293 if (resetMode == RESET_MODE_RECONFIGURE) { 1294 codec.stop(); 1295 codec.configure(format, surface /* surface */, null /* crypto */, 1296 0 /* flags */); 1297 codec.start(); 1298 codecInputBuffers = codec.getInputBuffers(); 1299 codecOutputBuffers = codec.getOutputBuffers(); 1300 } else if (resetMode == RESET_MODE_FLUSH) { 1301 codec.flush(); 1302 } else { 1303 fail("unknown resetMode: " + resetMode); 1304 } 1305 // restart at beginning, clear resetMode 1306 resetMode = RESET_MODE_NONE; 1307 extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 1308 sawInputEOS = false; 1309 numframes = 0; 1310 timestamps.clear(); 1311 if ((checkFlags & CHECKFLAG_SETPTS) != 0) { 1312 outputTimestamps.clear(); 1313 } 1314 if ((checkFlags & CHECKFLAG_SETCHECKSUM) != 0) { 1315 outputChecksums.clear(); 1316 } 1317 continue; 1318 } 1319 if ((checkFlags & CHECKFLAG_COMPAREPTS) != 0) { 1320 assertTrue("number of frames (" + numframes 1321 + ") exceeds number of reference timestamps", 1322 numframes < outputTimestamps.size()); 1323 assertEquals("frame ts mismatch at frame " + numframes, 1324 (long) outputTimestamps.get(numframes), info.presentationTimeUs); 1325 } else if ((checkFlags & CHECKFLAG_SETPTS) != 0) { 1326 outputTimestamps.add(info.presentationTimeUs); 1327 } 1328 if ((checkFlags & (CHECKFLAG_SETCHECKSUM | CHECKFLAG_COMPARECHECKSUM)) != 0) { 1329 long sum = 0; // note: checksum is 0 if buffer format unrecognized 1330 if (dochecksum) { 1331 Image image = codec.getOutputImage(outputBufIndex); 1332 // use image to do crc if it's available 1333 // fall back to buffer if image is not available 1334 if (image != null) { 1335 sum = checksum(image); 1336 } else { 1337 // TODO: add stride - right now just use info.size (as before) 1338 //sum = checksum(codecOutputBuffers[outputBufIndex], width, height, 1339 // stride); 1340 ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufIndex); 1341 outputBuffer.position(info.offset); 1342 sum = checksum(outputBuffer, info.size); 1343 } 1344 } 1345 if ((checkFlags & CHECKFLAG_COMPARECHECKSUM) != 0) { 1346 assertTrue("number of frames (" + numframes 1347 + ") exceeds number of reference checksums", 1348 numframes < outputChecksums.size()); 1349 Log.d(TAG, "orig checksum: " + outputChecksums.get(numframes) 1350 + " new checksum: " + sum); 1351 assertEquals("frame data mismatch at frame " + numframes, 1352 (long) outputChecksums.get(numframes), sum); 1353 } else if ((checkFlags & CHECKFLAG_SETCHECKSUM) != 0) { 1354 outputChecksums.add(sum); 1355 } 1356 } 1357 if ((checkFlags & CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH) != 0) { 1358 assertTrue("output timestamp " + info.presentationTimeUs 1359 + " without corresponding input timestamp" 1360 , timestamps.remove(info.presentationTimeUs)); 1361 } 1362 outputSize += info.size; 1363 numframes++; 1364 } 1365 // Log.d(TAG, "got frame, size " + info.size + "/" + 1366 // info.presentationTimeUs + 1367 // "/" + numframes + "/" + info.flags); 1368 codec.releaseOutputBuffer(outputBufIndex, true /* render */); 1369 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 1370 Log.d(TAG, "saw output EOS."); 1371 sawOutputEOS = true; 1372 } 1373 } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 1374 codecOutputBuffers = codec.getOutputBuffers(); 1375 Log.d(TAG, "output buffers have changed."); 1376 } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 1377 MediaFormat oformat = codec.getOutputFormat(); 1378 if (oformat.containsKey(MediaFormat.KEY_COLOR_FORMAT) && 1379 oformat.containsKey(MediaFormat.KEY_WIDTH) && 1380 oformat.containsKey(MediaFormat.KEY_HEIGHT)) { 1381 int colorFormat = oformat.getInteger(MediaFormat.KEY_COLOR_FORMAT); 1382 width = oformat.getInteger(MediaFormat.KEY_WIDTH); 1383 height = oformat.getInteger(MediaFormat.KEY_HEIGHT); 1384 dochecksum = isRecognizedFormat(colorFormat); // only checksum known raw 1385 // buf formats 1386 Log.d(TAG, "checksum fmt: " + colorFormat + " dim " + width + "x" + height); 1387 } else { 1388 dochecksum = false; // check with audio later 1389 width = height = 0; 1390 Log.d(TAG, "output format has changed to (unknown video) " + oformat); 1391 } 1392 } else { 1393 assertEquals( 1394 "codec.dequeueOutputBuffer() unrecognized return index: " 1395 + outputBufIndex, 1396 MediaCodec.INFO_TRY_AGAIN_LATER, outputBufIndex); 1397 } 1398 } 1399 codec.stop(); 1400 codec.release(); 1401 1402 assertTrue("last frame didn't have EOS", sawOutputEOS); 1403 if ((checkFlags & CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH) != 0) { 1404 assertEquals("I!=O", samplenum, numframes); 1405 if (stopAtSample != 0) { 1406 assertEquals("did not stop with right number of frames", stopAtSample, numframes); 1407 } 1408 } 1409 return (checkFlags & CHECKFLAG_RETURN_OUTPUTSIZE) != 0 ? outputSize : 1410 (checkFlags & CHECKFLAG_RETURN_OUTPUTFRAMES) != 0 ? numframes : 1411 0; 1412 } 1413 1414 public void testEOSBehaviorH264() throws Exception { 1415 // this video has an I frame at 44 1416 testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 1417 new int[] {44, 45, 55}); 1418 } 1419 public void testEOSBehaviorHEVC() throws Exception { 1420 testEOSBehavior(R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz, 17); 1421 testEOSBehavior(R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz, 23); 1422 testEOSBehavior(R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz, 49); 1423 } 1424 1425 public void testEOSBehaviorH263() throws Exception { 1426 // this video has an I frame every 12 frames. 1427 testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 1428 new int[] {24, 25, 48, 50}); 1429 } 1430 1431 public void testEOSBehaviorMpeg4() throws Exception { 1432 // this video has an I frame every 12 frames 1433 testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 1434 new int[] {24, 25, 48, 50, 2}); 1435 } 1436 1437 public void testEOSBehaviorVP8() throws Exception { 1438 // this video has an I frame at 46 1439 testEOSBehavior(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 1440 new int[] {46, 47, 57, 45}); 1441 } 1442 1443 public void testEOSBehaviorVP9() throws Exception { 1444 // this video has an I frame at 44 1445 testEOSBehavior(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 1446 new int[] {44, 45, 55, 43}); 1447 } 1448 1449 /* from EncodeDecodeTest */ 1450 private static boolean isRecognizedFormat(int colorFormat) { 1451 // Log.d(TAG, "color format: " + String.format("0x%08x", colorFormat)); 1452 switch (colorFormat) { 1453 // these are the formats we know how to handle for this test 1454 case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar: 1455 case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar: 1456 case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar: 1457 case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar: 1458 case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar: 1459 case MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar: 1460 /* 1461 * TODO: Check newer formats or ignore. 1462 * OMX_SEC_COLOR_FormatNV12Tiled = 0x7FC00002 1463 * OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka = 0x7FA30C03: N4/N7_2 1464 * OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m = 0x7FA30C04: N5 1465 */ 1466 return true; 1467 default: 1468 return false; 1469 } 1470 } 1471 1472 private static long checksum(ByteBuffer buf, int size) { 1473 int cap = buf.capacity(); 1474 assertTrue("checksum() params are invalid: size = " + size + " cap = " + cap, 1475 size > 0 && size <= cap); 1476 CRC32 crc = new CRC32(); 1477 if (buf.hasArray()) { 1478 crc.update(buf.array(), buf.position() + buf.arrayOffset(), size); 1479 } else { 1480 int pos = buf.position(); 1481 final int rdsize = Math.min(4096, size); 1482 byte bb[] = new byte[rdsize]; 1483 int chk; 1484 for (int i = 0; i < size; i += chk) { 1485 chk = Math.min(rdsize, size - i); 1486 buf.get(bb, 0, chk); 1487 crc.update(bb, 0, chk); 1488 } 1489 buf.position(pos); 1490 } 1491 return crc.getValue(); 1492 } 1493 1494 private static long checksum(ByteBuffer buf, int width, int height, int stride) { 1495 int cap = buf.capacity(); 1496 assertTrue("checksum() params are invalid: w x h , s = " 1497 + width + " x " + height + " , " + stride + " cap = " + cap, 1498 width > 0 && width <= stride && height > 0 && height * stride <= cap); 1499 // YUV 4:2:0 should generally have a data storage height 1.5x greater 1500 // than the declared image height, representing the UV planes. 1501 // 1502 // We only check Y frame for now. Somewhat unknown with tiling effects. 1503 // 1504 //long tm = System.nanoTime(); 1505 final int lineinterval = 1; // line sampling frequency 1506 CRC32 crc = new CRC32(); 1507 if (buf.hasArray()) { 1508 byte b[] = buf.array(); 1509 int offs = buf.arrayOffset(); 1510 for (int i = 0; i < height; i += lineinterval) { 1511 crc.update(b, i * stride + offs, width); 1512 } 1513 } else { // almost always ends up here due to direct buffers 1514 int pos = buf.position(); 1515 if (true) { // this {} is 80x times faster than else {} below. 1516 byte[] bb = new byte[width]; // local line buffer 1517 for (int i = 0; i < height; i += lineinterval) { 1518 buf.position(pos + i * stride); 1519 buf.get(bb, 0, width); 1520 crc.update(bb, 0, width); 1521 } 1522 } else { 1523 for (int i = 0; i < height; i += lineinterval) { 1524 buf.position(pos + i * stride); 1525 for (int j = 0; j < width; ++j) { 1526 crc.update(buf.get()); 1527 } 1528 } 1529 } 1530 buf.position(pos); 1531 } 1532 //tm = System.nanoTime() - tm; 1533 //Log.d(TAG, "checksum time " + tm); 1534 return crc.getValue(); 1535 } 1536 1537 private static long checksum(Image image) { 1538 int format = image.getFormat(); 1539 assertEquals("unsupported image format", ImageFormat.YUV_420_888, format); 1540 1541 CRC32 crc = new CRC32(); 1542 1543 int imageWidth = image.getWidth(); 1544 int imageHeight = image.getHeight(); 1545 1546 Image.Plane[] planes = image.getPlanes(); 1547 for (int i = 0; i < planes.length; ++i) { 1548 ByteBuffer buf = planes[i].getBuffer(); 1549 1550 int width, height, rowStride, pixelStride, x, y; 1551 rowStride = planes[i].getRowStride(); 1552 pixelStride = planes[i].getPixelStride(); 1553 if (i == 0) { 1554 width = imageWidth; 1555 height = imageHeight; 1556 } else { 1557 width = imageWidth / 2; 1558 height = imageHeight /2; 1559 } 1560 // local contiguous pixel buffer 1561 byte[] bb = new byte[width * height]; 1562 if (buf.hasArray()) { 1563 byte b[] = buf.array(); 1564 int offs = buf.arrayOffset(); 1565 if (pixelStride == 1) { 1566 for (y = 0; y < height; ++y) { 1567 System.arraycopy(bb, y * width, b, y * rowStride + offs, width); 1568 } 1569 } else { 1570 // do it pixel-by-pixel 1571 for (y = 0; y < height; ++y) { 1572 int lineOffset = offs + y * rowStride; 1573 for (x = 0; x < width; ++x) { 1574 bb[y * width + x] = b[lineOffset + x * pixelStride]; 1575 } 1576 } 1577 } 1578 } else { // almost always ends up here due to direct buffers 1579 int pos = buf.position(); 1580 if (pixelStride == 1) { 1581 for (y = 0; y < height; ++y) { 1582 buf.position(pos + y * rowStride); 1583 buf.get(bb, y * width, width); 1584 } 1585 } else { 1586 // local line buffer 1587 byte[] lb = new byte[rowStride]; 1588 // do it pixel-by-pixel 1589 for (y = 0; y < height; ++y) { 1590 buf.position(pos + y * rowStride); 1591 // we're only guaranteed to have pixelStride * (width - 1) + 1 bytes 1592 buf.get(lb, 0, pixelStride * (width - 1) + 1); 1593 for (x = 0; x < width; ++x) { 1594 bb[y * width + x] = lb[x * pixelStride]; 1595 } 1596 } 1597 } 1598 buf.position(pos); 1599 } 1600 crc.update(bb, 0, width * height); 1601 } 1602 1603 return crc.getValue(); 1604 } 1605 1606 public void testFlush() throws Exception { 1607 testFlush(R.raw.loudsoftwav); 1608 testFlush(R.raw.loudsoftogg); 1609 testFlush(R.raw.loudsoftmp3); 1610 testFlush(R.raw.loudsoftaac); 1611 testFlush(R.raw.loudsoftfaac); 1612 testFlush(R.raw.loudsoftitunes); 1613 } 1614 1615 private void testFlush(int resource) throws Exception { 1616 1617 AssetFileDescriptor testFd = mResources.openRawResourceFd(resource); 1618 1619 MediaExtractor extractor; 1620 MediaCodec codec; 1621 ByteBuffer[] codecInputBuffers; 1622 ByteBuffer[] codecOutputBuffers; 1623 1624 extractor = new MediaExtractor(); 1625 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 1626 testFd.getLength()); 1627 testFd.close(); 1628 1629 assertEquals("wrong number of tracks", 1, extractor.getTrackCount()); 1630 MediaFormat format = extractor.getTrackFormat(0); 1631 String mime = format.getString(MediaFormat.KEY_MIME); 1632 assertTrue("not an audio file", mime.startsWith("audio/")); 1633 1634 codec = MediaCodec.createDecoderByType(mime); 1635 assertNotNull("couldn't find codec " + mime, codec); 1636 1637 codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */); 1638 codec.start(); 1639 codecInputBuffers = codec.getInputBuffers(); 1640 codecOutputBuffers = codec.getOutputBuffers(); 1641 1642 extractor.selectTrack(0); 1643 1644 // decode a bit of the first part of the file, and verify the amplitude 1645 short maxvalue1 = getAmplitude(extractor, codec); 1646 1647 // flush the codec and seek the extractor a different position, then decode a bit more 1648 // and check the amplitude 1649 extractor.seekTo(8000000, 0); 1650 codec.flush(); 1651 short maxvalue2 = getAmplitude(extractor, codec); 1652 1653 assertTrue("first section amplitude too low", maxvalue1 > 20000); 1654 assertTrue("second section amplitude too high", maxvalue2 < 5000); 1655 codec.stop(); 1656 codec.release(); 1657 1658 } 1659 1660 private short getAmplitude(MediaExtractor extractor, MediaCodec codec) { 1661 short maxvalue = 0; 1662 int numBytesDecoded = 0; 1663 final long kTimeOutUs = 5000; 1664 ByteBuffer[] codecInputBuffers = codec.getInputBuffers(); 1665 ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers(); 1666 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 1667 1668 while(numBytesDecoded < 44100 * 2) { 1669 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs); 1670 1671 if (inputBufIndex >= 0) { 1672 ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; 1673 1674 int sampleSize = extractor.readSampleData(dstBuf, 0 /* offset */); 1675 long presentationTimeUs = extractor.getSampleTime(); 1676 1677 codec.queueInputBuffer( 1678 inputBufIndex, 1679 0 /* offset */, 1680 sampleSize, 1681 presentationTimeUs, 1682 0 /* flags */); 1683 1684 extractor.advance(); 1685 } 1686 int res = codec.dequeueOutputBuffer(info, kTimeOutUs); 1687 1688 if (res >= 0) { 1689 1690 int outputBufIndex = res; 1691 ByteBuffer buf = codecOutputBuffers[outputBufIndex]; 1692 1693 buf.position(info.offset); 1694 for (int i = 0; i < info.size; i += 2) { 1695 short sample = buf.getShort(); 1696 if (maxvalue < sample) { 1697 maxvalue = sample; 1698 } 1699 int idx = (numBytesDecoded + i) / 2; 1700 } 1701 1702 numBytesDecoded += info.size; 1703 1704 codec.releaseOutputBuffer(outputBufIndex, false /* render */); 1705 } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 1706 codecOutputBuffers = codec.getOutputBuffers(); 1707 } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 1708 MediaFormat oformat = codec.getOutputFormat(); 1709 } 1710 } 1711 return maxvalue; 1712 } 1713 1714 } 1715 1716