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