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