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.media.MediaCodec; 24 import android.media.MediaCodecInfo; 25 import android.media.MediaExtractor; 26 import android.media.MediaFormat; 27 import android.util.Log; 28 import android.view.Surface; 29 30 import java.io.BufferedInputStream; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.nio.ByteBuffer; 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.zip.CRC32; 37 38 public class DecoderTest extends MediaPlayerTestBase { 39 private static final String TAG = "DecoderTest"; 40 41 private static final int RESET_MODE_NONE = 0; 42 private static final int RESET_MODE_RECONFIGURE = 1; 43 private static final int RESET_MODE_FLUSH = 2; 44 45 private Resources mResources; 46 short[] mMasterBuffer; 47 48 @Override 49 protected void setUp() throws Exception { 50 super.setUp(); 51 mResources = mContext.getResources(); 52 53 // read master file into memory 54 AssetFileDescriptor masterFd = mResources.openRawResourceFd(R.raw.sinesweepraw); 55 long masterLength = masterFd.getLength(); 56 mMasterBuffer = new short[(int) (masterLength / 2)]; 57 InputStream is = masterFd.createInputStream(); 58 BufferedInputStream bis = new BufferedInputStream(is); 59 for (int i = 0; i < mMasterBuffer.length; i++) { 60 int lo = bis.read(); 61 int hi = bis.read(); 62 if (hi >= 128) { 63 hi -= 256; 64 } 65 int sample = hi * 256 + lo; 66 mMasterBuffer[i] = (short) sample; 67 } 68 bis.close(); 69 masterFd.close(); 70 } 71 72 // The allowed errors in the following tests are the actual maximum measured 73 // errors with the standard decoders, plus 10%. 74 // This should allow for some variation in decoders, while still detecting 75 // phase and delay errors, channel swap, etc. 76 public void testDecodeMp3Lame() throws Exception { 77 decode(R.raw.sinesweepmp3lame, 804.f); 78 } 79 public void testDecodeMp3Smpb() throws Exception { 80 decode(R.raw.sinesweepmp3smpb, 413.f); 81 } 82 public void testDecodeM4a() throws Exception { 83 decode(R.raw.sinesweepm4a, 124.f); 84 } 85 public void testDecodeOgg() throws Exception { 86 decode(R.raw.sinesweepogg, 168.f); 87 } 88 public void testDecodeWav() throws Exception { 89 decode(R.raw.sinesweepwav, 0.0f); 90 } 91 public void testDecodeFlac() throws Exception { 92 decode(R.raw.sinesweepflac, 0.0f); 93 } 94 95 public void testDecodeMonoMp3() throws Exception { 96 monoTest(R.raw.monotestmp3); 97 } 98 99 public void testDecodeMonoM4a() throws Exception { 100 monoTest(R.raw.monotestm4a); 101 } 102 103 public void testDecodeMonoOgg() throws Exception { 104 monoTest(R.raw.monotestogg); 105 } 106 107 private void monoTest(int res) throws Exception { 108 short [] mono = decodeToMemory(res, RESET_MODE_NONE); 109 if (mono.length == 44100) { 110 // expected 111 } else if (mono.length == 88200) { 112 // the decoder output 2 channels instead of 1, check that the left and right channel 113 // are identical 114 for (int i = 0; i < mono.length; i += 2) { 115 assertEquals("mismatched samples at " + i, mono[i], mono[i+1]); 116 } 117 } else { 118 fail("wrong number of samples: " + mono.length); 119 } 120 121 // we should get the same data when reconfiguring the codec 122 short [] mono2 = decodeToMemory(res, RESET_MODE_RECONFIGURE); 123 assertTrue(Arrays.equals(mono, mono2)); 124 125 // NOTE: coming soon 126 // and when flushing it 127 // short [] mono3 = decodeToMemory(res, RESET_MODE_FLUSH); 128 // assertTrue(Arrays.equals(mono, mono3)); 129 } 130 131 /** 132 * @param testinput the file to decode 133 * @param maxerror the maximum allowed root mean squared error 134 * @throws IOException 135 */ 136 private void decode(int testinput, float maxerror) throws IOException { 137 138 short [] decoded = decodeToMemory(testinput, RESET_MODE_NONE); 139 140 assertEquals("wrong data size", mMasterBuffer.length, decoded.length); 141 142 long totalErrorSquared = 0; 143 144 for (int i = 0; i < decoded.length; i++) { 145 short sample = decoded[i]; 146 short mastersample = mMasterBuffer[i]; 147 int d = sample - mastersample; 148 totalErrorSquared += d * d; 149 } 150 151 long avgErrorSquared = (totalErrorSquared / decoded.length); 152 double rmse = Math.sqrt(avgErrorSquared); 153 assertTrue("decoding error too big: " + rmse, rmse <= maxerror); 154 155 short [] decoded2 = decodeToMemory(testinput, RESET_MODE_RECONFIGURE); 156 assertEquals("count different with reconfigure", decoded.length, decoded2.length); 157 for (int i = 0; i < decoded.length; i++) { 158 assertEquals("samples don't match", decoded[i], decoded2[i]); 159 } 160 161 // NOTE: coming soon 162 // short [] decoded3 = decodeToMemory(testinput, RESET_MODE_FLUSH); 163 // assertEquals("count different with flush", decoded.length, decoded3.length); 164 // for (int i = 0; i < decoded.length; i++) { 165 // assertEquals("samples don't match", decoded[i], decoded3[i]); 166 // } 167 } 168 169 private short[] decodeToMemory(int testinput, int resetMode) throws IOException { 170 171 short [] decoded = new short[0]; 172 int decodedIdx = 0; 173 174 AssetFileDescriptor testFd = mResources.openRawResourceFd(testinput); 175 176 MediaExtractor extractor; 177 MediaCodec codec; 178 ByteBuffer[] codecInputBuffers; 179 ByteBuffer[] codecOutputBuffers; 180 181 extractor = new MediaExtractor(); 182 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 183 testFd.getLength()); 184 testFd.close(); 185 186 assertEquals("wrong number of tracks", 1, extractor.getTrackCount()); 187 MediaFormat format = extractor.getTrackFormat(0); 188 String mime = format.getString(MediaFormat.KEY_MIME); 189 assertTrue("not an audio file", mime.startsWith("audio/")); 190 191 codec = MediaCodec.createDecoderByType(mime); 192 codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */); 193 codec.start(); 194 codecInputBuffers = codec.getInputBuffers(); 195 codecOutputBuffers = codec.getOutputBuffers(); 196 197 if (resetMode == RESET_MODE_RECONFIGURE) { 198 codec.stop(); 199 codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */); 200 codec.start(); 201 codecInputBuffers = codec.getInputBuffers(); 202 codecOutputBuffers = codec.getOutputBuffers(); 203 } else if (resetMode == RESET_MODE_FLUSH) { 204 codec.flush(); 205 } 206 207 extractor.selectTrack(0); 208 209 // start decoding 210 final long kTimeOutUs = 5000; 211 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 212 boolean sawInputEOS = false; 213 boolean sawOutputEOS = false; 214 int noOutputCounter = 0; 215 while (!sawOutputEOS && noOutputCounter < 50) { 216 noOutputCounter++; 217 if (!sawInputEOS) { 218 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs); 219 220 if (inputBufIndex >= 0) { 221 ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; 222 223 int sampleSize = 224 extractor.readSampleData(dstBuf, 0 /* offset */); 225 226 long presentationTimeUs = 0; 227 228 if (sampleSize < 0) { 229 Log.d(TAG, "saw input EOS."); 230 sawInputEOS = true; 231 sampleSize = 0; 232 } else { 233 presentationTimeUs = extractor.getSampleTime(); 234 } 235 236 codec.queueInputBuffer( 237 inputBufIndex, 238 0 /* offset */, 239 sampleSize, 240 presentationTimeUs, 241 sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); 242 243 if (!sawInputEOS) { 244 extractor.advance(); 245 } 246 } 247 } 248 249 int res = codec.dequeueOutputBuffer(info, kTimeOutUs); 250 251 if (res >= 0) { 252 //Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs); 253 254 if (info.size > 0) { 255 noOutputCounter = 0; 256 } 257 if (info.size > 0 && resetMode != RESET_MODE_NONE) { 258 // once we've gotten some data out of the decoder, reset and start again 259 if (resetMode == RESET_MODE_RECONFIGURE) { 260 codec.stop(); 261 codec.configure(format, null /* surface */, null /* crypto */, 262 0 /* flags */); 263 codec.start(); 264 codecInputBuffers = codec.getInputBuffers(); 265 codecOutputBuffers = codec.getOutputBuffers(); 266 } else /* resetMode == RESET_MODE_FLUSH */ { 267 codec.flush(); 268 } 269 resetMode = RESET_MODE_NONE; 270 extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 271 sawInputEOS = false; 272 continue; 273 } 274 275 int outputBufIndex = res; 276 ByteBuffer buf = codecOutputBuffers[outputBufIndex]; 277 278 if (decodedIdx + (info.size / 2) >= decoded.length) { 279 decoded = Arrays.copyOf(decoded, decodedIdx + (info.size / 2)); 280 } 281 282 for (int i = 0; i < info.size; i += 2) { 283 decoded[decodedIdx++] = buf.getShort(i); 284 } 285 286 codec.releaseOutputBuffer(outputBufIndex, false /* render */); 287 288 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 289 Log.d(TAG, "saw output EOS."); 290 sawOutputEOS = true; 291 } 292 } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 293 codecOutputBuffers = codec.getOutputBuffers(); 294 295 Log.d(TAG, "output buffers have changed."); 296 } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 297 MediaFormat oformat = codec.getOutputFormat(); 298 299 Log.d(TAG, "output format has changed to " + oformat); 300 } else { 301 Log.d(TAG, "dequeueOutputBuffer returned " + res); 302 } 303 } 304 305 codec.stop(); 306 codec.release(); 307 return decoded; 308 } 309 310 public void testCodecBasicH264() throws Exception { 311 Surface s = getActivity().getSurfaceHolder().getSurface(); 312 int frames1 = countFrames( 313 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 314 RESET_MODE_NONE, -1 /* eosframe */, s); 315 assertEquals("wrong number of frames decoded", 240, frames1); 316 317 int frames2 = countFrames( 318 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 319 RESET_MODE_NONE, -1 /* eosframe */, null); 320 assertEquals("different number of frames when using Surface", frames1, frames2); 321 } 322 323 public void testCodecBasicH263() throws Exception { 324 Surface s = getActivity().getSurfaceHolder().getSurface(); 325 int frames1 = countFrames( 326 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 327 RESET_MODE_NONE, -1 /* eosframe */, s); 328 assertEquals("wrong number of frames decoded", 122, frames1); 329 330 int frames2 = countFrames( 331 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 332 RESET_MODE_NONE, -1 /* eosframe */, null); 333 assertEquals("different number of frames when using Surface", frames1, frames2); 334 } 335 336 public void testCodecBasicMpeg4() throws Exception { 337 Surface s = getActivity().getSurfaceHolder().getSurface(); 338 int frames1 = countFrames( 339 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 340 RESET_MODE_NONE, -1 /* eosframe */, s); 341 assertEquals("wrong number of frames decoded", 249, frames1); 342 343 int frames2 = countFrames( 344 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 345 RESET_MODE_NONE, -1 /* eosframe */, null); 346 assertEquals("different number of frames when using Surface", frames1, frames2); 347 } 348 349 public void testCodecBasicVP8() throws Exception { 350 Surface s = getActivity().getSurfaceHolder().getSurface(); 351 int frames1 = countFrames( 352 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 353 RESET_MODE_NONE, -1 /* eosframe */, s); 354 assertEquals("wrong number of frames decoded", 240, frames1); 355 356 int frames2 = countFrames( 357 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 358 RESET_MODE_NONE, -1 /* eosframe */, null); 359 assertEquals("different number of frames when using Surface", frames1, frames2); 360 } 361 362 public void testCodecBasicVP9() throws Exception { 363 Surface s = getActivity().getSurfaceHolder().getSurface(); 364 int frames1 = countFrames( 365 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 366 RESET_MODE_NONE, -1 /* eosframe */, s); 367 assertEquals("wrong number of frames decoded", 240, frames1); 368 369 int frames2 = countFrames( 370 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 371 RESET_MODE_NONE, -1 /* eosframe */, null); 372 assertEquals("different number of frames when using Surface", frames1, frames2); 373 } 374 375 public void testCodecEarlyEOSH263() throws Exception { 376 Surface s = getActivity().getSurfaceHolder().getSurface(); 377 int frames1 = countFrames( 378 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 379 RESET_MODE_NONE, 64 /* eosframe */, s); 380 assertEquals("wrong number of frames decoded", 64, frames1); 381 } 382 383 public void testCodecEarlyEOSH264() throws Exception { 384 Surface s = getActivity().getSurfaceHolder().getSurface(); 385 int frames1 = countFrames( 386 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 387 RESET_MODE_NONE, 120 /* eosframe */, s); 388 assertEquals("wrong number of frames decoded", 120, frames1); 389 } 390 391 public void testCodecEarlyEOSMpeg4() throws Exception { 392 Surface s = getActivity().getSurfaceHolder().getSurface(); 393 int frames1 = countFrames( 394 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 395 RESET_MODE_NONE, 120 /* eosframe */, s); 396 assertEquals("wrong number of frames decoded", 120, frames1); 397 } 398 399 public void testCodecEarlyEOSVP8() throws Exception { 400 Surface s = getActivity().getSurfaceHolder().getSurface(); 401 int frames1 = countFrames( 402 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 403 RESET_MODE_NONE, 120 /* eosframe */, s); 404 assertEquals("wrong number of frames decoded", 120, frames1); 405 } 406 407 public void testCodecEarlyEOSVP9() throws Exception { 408 Surface s = getActivity().getSurfaceHolder().getSurface(); 409 int frames1 = countFrames( 410 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 411 RESET_MODE_NONE, 120 /* eosframe */, s); 412 assertEquals("wrong number of frames decoded", 120, frames1); 413 } 414 415 public void testCodecResetsH264WithoutSurface() throws Exception { 416 testCodecResets( 417 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, null); 418 } 419 420 public void testCodecResetsH264WithSurface() throws Exception { 421 Surface s = getActivity().getSurfaceHolder().getSurface(); 422 testCodecResets( 423 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, s); 424 } 425 426 public void testCodecResetsH263WithoutSurface() throws Exception { 427 testCodecResets( 428 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, null); 429 } 430 431 public void testCodecResetsH263WithSurface() throws Exception { 432 Surface s = getActivity().getSurfaceHolder().getSurface(); 433 testCodecResets( 434 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, s); 435 } 436 437 public void testCodecResetsMpeg4WithoutSurface() throws Exception { 438 testCodecResets( 439 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, null); 440 } 441 442 public void testCodecResetsMpeg4WithSurface() throws Exception { 443 Surface s = getActivity().getSurfaceHolder().getSurface(); 444 testCodecResets( 445 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, s); 446 } 447 448 public void testCodecResetsVP8WithoutSurface() throws Exception { 449 testCodecResets( 450 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, null); 451 } 452 453 public void testCodecResetsVP8WithSurface() throws Exception { 454 Surface s = getActivity().getSurfaceHolder().getSurface(); 455 testCodecResets( 456 R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, s); 457 } 458 459 public void testCodecResetsVP9WithoutSurface() throws Exception { 460 testCodecResets( 461 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, null); 462 } 463 464 public void testCodecResetsVP9WithSurface() throws Exception { 465 Surface s = getActivity().getSurfaceHolder().getSurface(); 466 testCodecResets( 467 R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, s); 468 } 469 470 // public void testCodecResetsOgg() throws Exception { 471 // testCodecResets(R.raw.sinesweepogg, null); 472 // } 473 474 public void testCodecResetsMp3() throws Exception { 475 testCodecReconfig(R.raw.sinesweepmp3lame, null); 476 // NOTE: replacing testCodecReconfig call soon 477 // testCodecResets(R.raw.sinesweepmp3lame, null); 478 } 479 480 public void testCodecResetsM4a() throws Exception { 481 testCodecReconfig(R.raw.sinesweepm4a, null); 482 // NOTE: replacing testCodecReconfig call soon 483 // testCodecResets(R.raw.sinesweepm4a, null); 484 } 485 486 private void testCodecReconfig(int video, Surface s) throws Exception { 487 int frames1 = countFrames(video, RESET_MODE_NONE, -1 /* eosframe */, s); 488 int frames2 = countFrames(video, RESET_MODE_RECONFIGURE, -1 /* eosframe */, s); 489 assertEquals("different number of frames when using reconfigured codec", frames1, frames2); 490 } 491 492 private void testCodecResets(int video, Surface s) throws Exception { 493 int frames1 = countFrames(video, RESET_MODE_NONE, -1 /* eosframe */, s); 494 int frames2 = countFrames(video, RESET_MODE_RECONFIGURE, -1 /* eosframe */, s); 495 int frames3 = countFrames(video, RESET_MODE_FLUSH, -1 /* eosframe */, s); 496 assertEquals("different number of frames when using reconfigured codec", frames1, frames2); 497 assertEquals("different number of frames when using flushed codec", frames1, frames3); 498 } 499 500 private MediaCodec createDecoder(String mime) { 501 if (false) { 502 // change to force testing software codecs 503 if (mime.contains("avc")) { 504 return MediaCodec.createByCodecName("OMX.google.h264.decoder"); 505 } else if (mime.contains("3gpp")) { 506 return MediaCodec.createByCodecName("OMX.google.h263.decoder"); 507 } else if (mime.contains("mp4v")) { 508 return MediaCodec.createByCodecName("OMX.google.mpeg4.decoder"); 509 } else if (mime.contains("vp8")) { 510 return MediaCodec.createByCodecName("OMX.google.vp8.decoder"); 511 } else if (mime.contains("vp9")) { 512 return MediaCodec.createByCodecName("OMX.google.vp9.decoder"); 513 } 514 } 515 return MediaCodec.createDecoderByType(mime); 516 } 517 518 private int countFrames(int video, int resetMode, int eosframe, Surface s) 519 throws Exception { 520 int numframes = 0; 521 522 AssetFileDescriptor testFd = mResources.openRawResourceFd(video); 523 524 MediaExtractor extractor; 525 MediaCodec codec = null; 526 ByteBuffer[] codecInputBuffers; 527 ByteBuffer[] codecOutputBuffers; 528 529 extractor = new MediaExtractor(); 530 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 531 testFd.getLength()); 532 533 MediaFormat format = extractor.getTrackFormat(0); 534 String mime = format.getString(MediaFormat.KEY_MIME); 535 boolean isAudio = mime.startsWith("audio/"); 536 537 codec = createDecoder(mime); 538 539 assertNotNull("couldn't find codec", codec); 540 Log.i("@@@@", "using codec: " + codec.getName()); 541 codec.configure(format, s /* surface */, null /* crypto */, 0 /* flags */); 542 codec.start(); 543 codecInputBuffers = codec.getInputBuffers(); 544 codecOutputBuffers = codec.getOutputBuffers(); 545 546 if (resetMode == RESET_MODE_RECONFIGURE) { 547 codec.stop(); 548 codec.configure(format, s /* surface */, null /* crypto */, 0 /* flags */); 549 codec.start(); 550 codecInputBuffers = codec.getInputBuffers(); 551 codecOutputBuffers = codec.getOutputBuffers(); 552 } else if (resetMode == RESET_MODE_FLUSH) { 553 codec.flush(); 554 } 555 556 Log.i("@@@@", "format: " + format); 557 558 extractor.selectTrack(0); 559 560 // start decoding 561 final long kTimeOutUs = 5000; 562 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 563 boolean sawInputEOS = false; 564 boolean sawOutputEOS = false; 565 int deadDecoderCounter = 0; 566 int samplecounter = 0; 567 ArrayList<Long> timestamps = new ArrayList<Long>(); 568 while (!sawOutputEOS && deadDecoderCounter < 100) { 569 if (!sawInputEOS) { 570 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs); 571 572 if (inputBufIndex >= 0) { 573 ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; 574 575 int sampleSize = 576 extractor.readSampleData(dstBuf, 0 /* offset */); 577 578 long presentationTimeUs = 0; 579 580 if (sampleSize < 0) { 581 Log.d(TAG, "saw input EOS."); 582 sawInputEOS = true; 583 sampleSize = 0; 584 } else { 585 presentationTimeUs = extractor.getSampleTime(); 586 samplecounter++; 587 if (samplecounter == eosframe) { 588 sawInputEOS = true; 589 } 590 } 591 592 timestamps.add(presentationTimeUs); 593 594 int flags = extractor.getSampleFlags(); 595 596 codec.queueInputBuffer( 597 inputBufIndex, 598 0 /* offset */, 599 sampleSize, 600 presentationTimeUs, 601 sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); 602 603 if (!sawInputEOS) { 604 extractor.advance(); 605 } 606 } 607 } 608 609 int res = codec.dequeueOutputBuffer(info, kTimeOutUs); 610 611 deadDecoderCounter++; 612 if (res >= 0) { 613 //Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs); 614 615 // Some decoders output a 0-sized buffer at the end. Disregard those. 616 if (info.size > 0) { 617 deadDecoderCounter = 0; 618 if (resetMode != RESET_MODE_NONE) { 619 // once we've gotten some data out of the decoder, reset and start again 620 if (resetMode == RESET_MODE_RECONFIGURE) { 621 codec.stop(); 622 codec.configure(format, s /* surface */, null /* crypto */, 623 0 /* flags */); 624 codec.start(); 625 codecInputBuffers = codec.getInputBuffers(); 626 codecOutputBuffers = codec.getOutputBuffers(); 627 } else /* resetMode == RESET_MODE_FLUSH */ { 628 codec.flush(); 629 } 630 resetMode = RESET_MODE_NONE; 631 extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC); 632 sawInputEOS = false; 633 numframes = 0; 634 timestamps.clear(); 635 continue; 636 } 637 638 if (isAudio) { 639 // for audio, count the number of bytes that were decoded, not the number 640 // of access units 641 numframes += info.size; 642 } else { 643 // for video, count the number of video frames and check the timestamp 644 numframes++; 645 assertTrue("invalid timestamp", timestamps.remove(info.presentationTimeUs)); 646 } 647 } 648 int outputBufIndex = res; 649 codec.releaseOutputBuffer(outputBufIndex, true /* render */); 650 651 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 652 Log.d(TAG, "saw output EOS."); 653 sawOutputEOS = true; 654 } 655 } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 656 codecOutputBuffers = codec.getOutputBuffers(); 657 658 Log.d(TAG, "output buffers have changed."); 659 } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 660 MediaFormat oformat = codec.getOutputFormat(); 661 662 Log.d(TAG, "output format has changed to " + oformat); 663 } else { 664 Log.d(TAG, "no output"); 665 } 666 } 667 668 codec.stop(); 669 codec.release(); 670 testFd.close(); 671 return numframes; 672 } 673 674 public void testEOSBehaviorH264() throws Exception { 675 // this video has an I frame at 44 676 testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 44); 677 testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 45); 678 testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 55); 679 } 680 681 public void testEOSBehaviorH263() throws Exception { 682 // this video has an I frame every 12 frames. 683 testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 24); 684 testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 25); 685 testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 48); 686 testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 50); 687 } 688 689 public void testEOSBehaviorMpeg4() throws Exception { 690 // this video has an I frame every 12 frames 691 testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 24); 692 testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 25); 693 testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 48); 694 testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 50); 695 testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 2); 696 } 697 698 public void testEOSBehaviorVP8() throws Exception { 699 // this video has an I frame at 46 700 testEOSBehavior(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 46); 701 testEOSBehavior(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 47); 702 testEOSBehavior(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 57); 703 testEOSBehavior(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 45); 704 } 705 706 public void testEOSBehaviorVP9() throws Exception { 707 // this video has an I frame at 44 708 testEOSBehavior(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 44); 709 testEOSBehavior(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 45); 710 testEOSBehavior(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 55); 711 testEOSBehavior(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 43); 712 } 713 714 private void testEOSBehavior(int movie, int stopatsample) throws Exception { 715 716 int numframes = 0; 717 718 long [] checksums = new long[stopatsample]; 719 720 AssetFileDescriptor testFd = mResources.openRawResourceFd(movie); 721 722 MediaExtractor extractor; 723 MediaCodec codec = null; 724 ByteBuffer[] codecInputBuffers; 725 ByteBuffer[] codecOutputBuffers; 726 727 extractor = new MediaExtractor(); 728 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 729 testFd.getLength()); 730 731 MediaFormat format = extractor.getTrackFormat(0); 732 String mime = format.getString(MediaFormat.KEY_MIME); 733 boolean isAudio = mime.startsWith("audio/"); 734 735 codec = createDecoder(mime); 736 737 assertNotNull("couldn't find codec", codec); 738 Log.i("@@@@", "using codec: " + codec.getName()); 739 codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */); 740 codec.start(); 741 codecInputBuffers = codec.getInputBuffers(); 742 codecOutputBuffers = codec.getOutputBuffers(); 743 744 extractor.selectTrack(0); 745 746 // start decoding 747 final long kTimeOutUs = 5000; 748 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 749 boolean sawInputEOS = false; 750 boolean sawOutputEOS = false; 751 int deadDecoderCounter = 0; 752 int samplenum = 0; 753 boolean dochecksum = false; 754 while (!sawOutputEOS && deadDecoderCounter < 100) { 755 if (!sawInputEOS) { 756 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs); 757 758 if (inputBufIndex >= 0) { 759 ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; 760 761 int sampleSize = 762 extractor.readSampleData(dstBuf, 0 /* offset */); 763 // Log.i("@@@@", "read sample " + samplenum + ":" + extractor.getSampleFlags() 764 // + " @ " + extractor.getSampleTime() + " size " + sampleSize); 765 samplenum++; 766 767 long presentationTimeUs = 0; 768 769 if (sampleSize < 0 || samplenum >= (stopatsample + 100)) { 770 Log.d(TAG, "saw input EOS."); 771 sawInputEOS = true; 772 sampleSize = 0; 773 } else { 774 presentationTimeUs = extractor.getSampleTime(); 775 } 776 777 int flags = extractor.getSampleFlags(); 778 779 codec.queueInputBuffer( 780 inputBufIndex, 781 0 /* offset */, 782 sampleSize, 783 presentationTimeUs, 784 sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); 785 786 if (!sawInputEOS) { 787 extractor.advance(); 788 } 789 } 790 } 791 792 int res = codec.dequeueOutputBuffer(info, kTimeOutUs); 793 794 deadDecoderCounter++; 795 if (res >= 0) { 796 797 // Some decoders output a 0-sized buffer at the end. Disregard those. 798 if (info.size > 0) { 799 deadDecoderCounter = 0; 800 801 if (isAudio) { 802 // for audio, count the number of bytes that were decoded, not the number 803 // of access units 804 numframes += info.size; 805 } else { 806 // for video, count the number of video frames 807 long sum = dochecksum ? checksum(codecOutputBuffers[res], info.size) : 0; 808 if (numframes < checksums.length) { 809 checksums[numframes] = sum; 810 } 811 numframes++; 812 } 813 } 814 // Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs + 815 // "/" + numframes + "/" + info.flags); 816 817 int outputBufIndex = res; 818 codec.releaseOutputBuffer(outputBufIndex, true /* render */); 819 820 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 821 Log.d(TAG, "saw output EOS."); 822 sawOutputEOS = true; 823 } 824 } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 825 codecOutputBuffers = codec.getOutputBuffers(); 826 827 Log.d(TAG, "output buffers have changed."); 828 } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 829 MediaFormat oformat = codec.getOutputFormat(); 830 int colorFormat = oformat.getInteger(MediaFormat.KEY_COLOR_FORMAT); 831 dochecksum = isRecognizedFormat(colorFormat); 832 Log.d(TAG, "output format has changed to " + oformat); 833 } else { 834 Log.d(TAG, "no output"); 835 } 836 } 837 838 codec.stop(); 839 codec.release(); 840 extractor.release(); 841 842 843 // We now have checksums for every frame. 844 // Now decode again, but signal EOS right before an index frame, and ensure the frames 845 // prior to that are the same. 846 847 extractor = new MediaExtractor(); 848 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 849 testFd.getLength()); 850 851 codec = createDecoder(mime); 852 codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */); 853 codec.start(); 854 codecInputBuffers = codec.getInputBuffers(); 855 codecOutputBuffers = codec.getOutputBuffers(); 856 857 extractor.selectTrack(0); 858 859 // start decoding 860 info = new MediaCodec.BufferInfo(); 861 sawInputEOS = false; 862 sawOutputEOS = false; 863 deadDecoderCounter = 0; 864 samplenum = 0; 865 numframes = 0; 866 dochecksum = false; 867 while (!sawOutputEOS && deadDecoderCounter < 100) { 868 if (!sawInputEOS) { 869 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs); 870 871 if (inputBufIndex >= 0) { 872 ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; 873 874 int sampleSize = 875 extractor.readSampleData(dstBuf, 0 /* offset */); 876 // Log.i("@@@@", "read sample " + samplenum + ":" + extractor.getSampleFlags() 877 // + " @ " + extractor.getSampleTime() + " size " + sampleSize); 878 samplenum++; 879 880 long presentationTimeUs = extractor.getSampleTime(); 881 882 if (sampleSize < 0 || samplenum >= stopatsample) { 883 Log.d(TAG, "saw input EOS."); 884 sawInputEOS = true; 885 if (sampleSize < 0) { 886 sampleSize = 0; 887 } 888 } 889 890 int flags = extractor.getSampleFlags(); 891 892 codec.queueInputBuffer( 893 inputBufIndex, 894 0 /* offset */, 895 sampleSize, 896 presentationTimeUs, 897 sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); 898 899 if (!sawInputEOS) { 900 extractor.advance(); 901 } 902 } 903 } 904 905 int res = codec.dequeueOutputBuffer(info, kTimeOutUs); 906 907 deadDecoderCounter++; 908 if (res >= 0) { 909 910 // Some decoders output a 0-sized buffer at the end. Disregard those. 911 if (info.size > 0) { 912 deadDecoderCounter = 0; 913 914 if (isAudio) { 915 // for audio, count the number of bytes that were decoded, not the number 916 // of access units 917 numframes += info.size; 918 } else { 919 // for video, count the number of video frames 920 long sum = dochecksum ? checksum(codecOutputBuffers[res], info.size) : 0; 921 if (numframes < checksums.length) { 922 assertEquals("frame data mismatch at frame " + numframes, 923 checksums[numframes], sum); 924 } 925 numframes++; 926 } 927 } 928 // Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs + 929 // "/" + numframes + "/" + info.flags); 930 931 int outputBufIndex = res; 932 codec.releaseOutputBuffer(outputBufIndex, true /* render */); 933 934 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 935 Log.d(TAG, "saw output EOS."); 936 sawOutputEOS = true; 937 } 938 } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 939 codecOutputBuffers = codec.getOutputBuffers(); 940 941 Log.d(TAG, "output buffers have changed."); 942 } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 943 MediaFormat oformat = codec.getOutputFormat(); 944 int colorFormat = oformat.getInteger(MediaFormat.KEY_COLOR_FORMAT); 945 dochecksum = isRecognizedFormat(colorFormat); 946 Log.d(TAG, "output format has changed to " + oformat); 947 } else { 948 Log.d(TAG, "no output"); 949 } 950 } 951 952 codec.stop(); 953 codec.release(); 954 extractor.release(); 955 956 assertEquals("I!=O", samplenum, numframes); 957 assertTrue("last frame didn't have EOS", sawOutputEOS); 958 assertEquals(stopatsample, numframes); 959 960 testFd.close(); 961 } 962 963 /* from EncodeDecodeTest */ 964 private static boolean isRecognizedFormat(int colorFormat) { 965 switch (colorFormat) { 966 // these are the formats we know how to handle for this test 967 case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar: 968 case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar: 969 case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar: 970 case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar: 971 case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar: 972 return true; 973 default: 974 return false; 975 } 976 } 977 978 private long checksum(ByteBuffer buf, int size) { 979 assertTrue(size != 0); 980 assertTrue(size <= buf.capacity()); 981 CRC32 crc = new CRC32(); 982 int pos = buf.position(); 983 buf.rewind(); 984 for (int i = 0; i < buf.capacity(); i++) { 985 crc.update(buf.get()); 986 } 987 buf.position(pos); 988 return crc.getValue(); 989 } 990 991 public void testFlush() throws Exception { 992 testFlush(R.raw.loudsoftwav); 993 testFlush(R.raw.loudsoftogg); 994 testFlush(R.raw.loudsoftmp3); 995 testFlush(R.raw.loudsoftaac); 996 testFlush(R.raw.loudsoftfaac); 997 testFlush(R.raw.loudsoftitunes); 998 } 999 1000 private void testFlush(int resource) throws Exception { 1001 1002 AssetFileDescriptor testFd = mResources.openRawResourceFd(resource); 1003 1004 MediaExtractor extractor; 1005 MediaCodec codec; 1006 ByteBuffer[] codecInputBuffers; 1007 ByteBuffer[] codecOutputBuffers; 1008 1009 extractor = new MediaExtractor(); 1010 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 1011 testFd.getLength()); 1012 testFd.close(); 1013 1014 assertEquals("wrong number of tracks", 1, extractor.getTrackCount()); 1015 MediaFormat format = extractor.getTrackFormat(0); 1016 String mime = format.getString(MediaFormat.KEY_MIME); 1017 assertTrue("not an audio file", mime.startsWith("audio/")); 1018 1019 codec = MediaCodec.createDecoderByType(mime); 1020 codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */); 1021 codec.start(); 1022 codecInputBuffers = codec.getInputBuffers(); 1023 codecOutputBuffers = codec.getOutputBuffers(); 1024 1025 extractor.selectTrack(0); 1026 1027 // decode a bit of the first part of the file, and verify the amplitude 1028 short maxvalue1 = getAmplitude(extractor, codec); 1029 1030 // flush the codec and seek the extractor a different position, then decode a bit more 1031 // and check the amplitude 1032 extractor.seekTo(8000000, 0); 1033 codec.flush(); 1034 short maxvalue2 = getAmplitude(extractor, codec); 1035 1036 assertTrue("first section amplitude too low", maxvalue1 > 20000); 1037 assertTrue("second section amplitude too high", maxvalue2 < 5000); 1038 codec.stop(); 1039 codec.release(); 1040 1041 } 1042 1043 private short getAmplitude(MediaExtractor extractor, MediaCodec codec) { 1044 short maxvalue = 0; 1045 int numBytesDecoded = 0; 1046 final long kTimeOutUs = 5000; 1047 ByteBuffer[] codecInputBuffers = codec.getInputBuffers(); 1048 ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers(); 1049 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 1050 1051 while(numBytesDecoded < 44100 * 2) { 1052 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs); 1053 1054 if (inputBufIndex >= 0) { 1055 ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; 1056 1057 int sampleSize = extractor.readSampleData(dstBuf, 0 /* offset */); 1058 long presentationTimeUs = extractor.getSampleTime(); 1059 1060 codec.queueInputBuffer( 1061 inputBufIndex, 1062 0 /* offset */, 1063 sampleSize, 1064 presentationTimeUs, 1065 0 /* flags */); 1066 1067 extractor.advance(); 1068 } 1069 int res = codec.dequeueOutputBuffer(info, kTimeOutUs); 1070 1071 if (res >= 0) { 1072 1073 int outputBufIndex = res; 1074 ByteBuffer buf = codecOutputBuffers[outputBufIndex]; 1075 1076 for (int i = 0; i < info.size; i += 2) { 1077 short sample = buf.getShort(i); 1078 if (maxvalue < sample) { 1079 maxvalue = sample; 1080 } 1081 int idx = (numBytesDecoded + i) / 2; 1082 } 1083 1084 numBytesDecoded += info.size; 1085 1086 codec.releaseOutputBuffer(outputBufIndex, false /* render */); 1087 } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 1088 codecOutputBuffers = codec.getOutputBuffers(); 1089 } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 1090 MediaFormat oformat = codec.getOutputFormat(); 1091 } 1092 } 1093 return maxvalue; 1094 } 1095 1096 } 1097 1098