1 /* 2 * Copyright (C) 2014 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.res.AssetFileDescriptor; 22 import android.content.res.Resources; 23 import android.cts.util.MediaUtils; 24 import android.media.MediaCodec; 25 import android.media.MediaCodec.BufferInfo; 26 import android.media.MediaCodecInfo; 27 import android.media.MediaExtractor; 28 import android.media.MediaFormat; 29 import android.media.MediaMetadataRetriever; 30 import android.media.MediaPlayer; 31 import android.net.Uri; 32 import android.os.Environment; 33 import android.os.ParcelFileDescriptor; 34 import android.util.Log; 35 import android.view.Surface; 36 import android.webkit.cts.CtsTestServer; 37 38 import java.io.BufferedInputStream; 39 import java.io.ByteArrayOutputStream; 40 import java.io.File; 41 import java.io.FileDescriptor; 42 import java.io.IOException; 43 import java.io.InputStream; 44 import java.io.RandomAccessFile; 45 import java.nio.ByteBuffer; 46 import java.util.ArrayList; 47 import java.util.Arrays; 48 import java.util.List; 49 import java.util.Map; 50 import java.util.Set; 51 import java.util.UUID; 52 53 public class NativeDecoderTest extends MediaPlayerTestBase { 54 private static final String TAG = "DecoderTest"; 55 56 private static final int RESET_MODE_NONE = 0; 57 private static final int RESET_MODE_RECONFIGURE = 1; 58 private static final int RESET_MODE_FLUSH = 2; 59 private static final int RESET_MODE_EOS_FLUSH = 3; 60 61 private static final String[] CSD_KEYS = new String[] { "csd-0", "csd-1" }; 62 63 private static final int CONFIG_MODE_NONE = 0; 64 private static final int CONFIG_MODE_QUEUE = 1; 65 66 private static Resources mResources; 67 short[] mMasterBuffer; 68 69 /** Load jni on initialization */ 70 static { 71 Log.i("@@@", "before loadlibrary"); 72 System.loadLibrary("ctsmediacodec_jni"); 73 Log.i("@@@", "after loadlibrary"); 74 } 75 76 @Override setUp()77 protected void setUp() throws Exception { 78 super.setUp(); 79 mResources = mContext.getResources(); 80 81 } 82 83 // check that native extractor behavior matches java extractor 84 testExtractor()85 public void testExtractor() throws Exception { 86 testExtractor(R.raw.sinesweepogg); 87 testExtractor(R.raw.sinesweepmp3lame); 88 testExtractor(R.raw.sinesweepmp3smpb); 89 testExtractor(R.raw.sinesweepm4a); 90 testExtractor(R.raw.sinesweepflac); 91 testExtractor(R.raw.sinesweepwav); 92 93 testExtractor(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz); 94 testExtractor(R.raw.bbb_s3_1280x720_webm_vp8_8mbps_60fps_opus_6ch_384kbps_48000hz); 95 testExtractor(R.raw.bbb_s4_1280x720_webm_vp9_0p31_4mbps_30fps_opus_stereo_128kbps_48000hz); 96 testExtractor(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz); 97 testExtractor(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz); 98 99 CtsTestServer foo = new CtsTestServer(mContext); 100 testExtractor(foo.getAssetUrl("noiseandchirps.ogg")); 101 testExtractor(foo.getAssetUrl("ringer.mp3")); 102 testExtractor(foo.getRedirectingAssetUrl("ringer.mp3")); 103 } 104 testExtractor(String path)105 private void testExtractor(String path) throws Exception { 106 int[] jsizes = getSampleSizes(path); 107 int[] nsizes = getSampleSizesNativePath(path); 108 109 //Log.i("@@@", Arrays.toString(jsizes)); 110 assertTrue("different samplesizes", Arrays.equals(jsizes, nsizes)); 111 } 112 testExtractor(int res)113 private void testExtractor(int res) throws Exception { 114 AssetFileDescriptor fd = mResources.openRawResourceFd(res); 115 116 int[] jsizes = getSampleSizes( 117 fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength()); 118 int[] nsizes = getSampleSizesNative( 119 fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength()); 120 121 fd.close(); 122 //Log.i("@@@", Arrays.toString(jsizes)); 123 assertTrue("different samplesizes", Arrays.equals(jsizes, nsizes)); 124 } 125 getSampleSizes(String path)126 private static int[] getSampleSizes(String path) throws IOException { 127 MediaExtractor ex = new MediaExtractor(); 128 ex.setDataSource(path); 129 return getSampleSizes(ex); 130 } 131 getSampleSizes(FileDescriptor fd, long offset, long size)132 private static int[] getSampleSizes(FileDescriptor fd, long offset, long size) 133 throws IOException { 134 MediaExtractor ex = new MediaExtractor(); 135 ex.setDataSource(fd, offset, size); 136 return getSampleSizes(ex); 137 } 138 getSampleSizes(MediaExtractor ex)139 private static int[] getSampleSizes(MediaExtractor ex) { 140 ArrayList<Integer> foo = new ArrayList<Integer>(); 141 ByteBuffer buf = ByteBuffer.allocate(1024*1024); 142 int numtracks = ex.getTrackCount(); 143 assertTrue("no tracks", numtracks > 0); 144 foo.add(numtracks); 145 for (int i = 0; i < numtracks; i++) { 146 MediaFormat format = ex.getTrackFormat(i); 147 String mime = format.getString(MediaFormat.KEY_MIME); 148 if (mime.startsWith("audio/")) { 149 foo.add(0); 150 foo.add(format.getInteger(MediaFormat.KEY_SAMPLE_RATE)); 151 foo.add(format.getInteger(MediaFormat.KEY_CHANNEL_COUNT)); 152 foo.add((int)format.getLong(MediaFormat.KEY_DURATION)); 153 } else if (mime.startsWith("video/")) { 154 foo.add(1); 155 foo.add(format.getInteger(MediaFormat.KEY_WIDTH)); 156 foo.add(format.getInteger(MediaFormat.KEY_HEIGHT)); 157 foo.add((int)format.getLong(MediaFormat.KEY_DURATION)); 158 } else { 159 fail("unexpected mime type: " + mime); 160 } 161 ex.selectTrack(i); 162 } 163 while(true) { 164 int n = ex.readSampleData(buf, 0); 165 if (n < 0) { 166 break; 167 } 168 foo.add(n); 169 foo.add(ex.getSampleTrackIndex()); 170 foo.add(ex.getSampleFlags()); 171 foo.add((int)ex.getSampleTime()); // just the low bits should be OK 172 ex.advance(); 173 } 174 175 int [] ret = new int[foo.size()]; 176 for (int i = 0; i < ret.length; i++) { 177 ret[i] = foo.get(i); 178 } 179 return ret; 180 } 181 getSampleSizesNative(int fd, long offset, long size)182 private static native int[] getSampleSizesNative(int fd, long offset, long size); getSampleSizesNativePath(String path)183 private static native int[] getSampleSizesNativePath(String path); 184 185 testDecoder()186 public void testDecoder() throws Exception { 187 int testsRun = 188 testDecoder(R.raw.sinesweepogg) + 189 testDecoder(R.raw.sinesweepmp3lame) + 190 testDecoder(R.raw.sinesweepmp3smpb) + 191 testDecoder(R.raw.sinesweepm4a) + 192 testDecoder(R.raw.sinesweepflac) + 193 testDecoder(R.raw.sinesweepwav) + 194 195 testDecoder(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz) + 196 testDecoder(R.raw.bbb_s1_640x360_webm_vp8_2mbps_30fps_vorbis_5ch_320kbps_48000hz) + 197 testDecoder(R.raw.bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz) + 198 testDecoder(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz) + 199 testDecoder(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz); 200 if (testsRun == 0) { 201 MediaUtils.skipTest("no decoders found"); 202 } 203 } 204 testDecoder(int res)205 private int testDecoder(int res) throws Exception { 206 if (!MediaUtils.hasCodecsForResource(mContext, res)) { 207 return 0; // skip 208 } 209 210 AssetFileDescriptor fd = mResources.openRawResourceFd(res); 211 212 int[] jdata = getDecodedData( 213 fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength()); 214 int[] ndata = getDecodedDataNative( 215 fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength()); 216 217 fd.close(); 218 Log.i("@@@", Arrays.toString(jdata)); 219 Log.i("@@@", Arrays.toString(ndata)); 220 assertEquals("number of samples differs", jdata.length, ndata.length); 221 assertTrue("different decoded data", Arrays.equals(jdata, ndata)); 222 return 1; 223 } 224 getDecodedData(FileDescriptor fd, long offset, long size)225 private static int[] getDecodedData(FileDescriptor fd, long offset, long size) 226 throws IOException { 227 MediaExtractor ex = new MediaExtractor(); 228 ex.setDataSource(fd, offset, size); 229 return getDecodedData(ex); 230 } getDecodedData(MediaExtractor ex)231 private static int[] getDecodedData(MediaExtractor ex) throws IOException { 232 int numtracks = ex.getTrackCount(); 233 assertTrue("no tracks", numtracks > 0); 234 ArrayList<Integer>[] trackdata = new ArrayList[numtracks]; 235 MediaCodec[] codec = new MediaCodec[numtracks]; 236 MediaFormat[] format = new MediaFormat[numtracks]; 237 ByteBuffer[][] inbuffers = new ByteBuffer[numtracks][]; 238 ByteBuffer[][] outbuffers = new ByteBuffer[numtracks][]; 239 for (int i = 0; i < numtracks; i++) { 240 format[i] = ex.getTrackFormat(i); 241 String mime = format[i].getString(MediaFormat.KEY_MIME); 242 if (mime.startsWith("audio/") || mime.startsWith("video/")) { 243 codec[i] = MediaCodec.createDecoderByType(mime); 244 codec[i].configure(format[i], null, null, 0); 245 codec[i].start(); 246 inbuffers[i] = codec[i].getInputBuffers(); 247 outbuffers[i] = codec[i].getOutputBuffers(); 248 trackdata[i] = new ArrayList<Integer>(); 249 } else { 250 fail("unexpected mime type: " + mime); 251 } 252 ex.selectTrack(i); 253 } 254 255 boolean[] sawInputEOS = new boolean[numtracks]; 256 boolean[] sawOutputEOS = new boolean[numtracks]; 257 int eosCount = 0; 258 BufferInfo info = new BufferInfo(); 259 while(eosCount < numtracks) { 260 int t = ex.getSampleTrackIndex(); 261 if (t >= 0) { 262 assertFalse("saw input EOS twice", sawInputEOS[t]); 263 int bufidx = codec[t].dequeueInputBuffer(5000); 264 if (bufidx >= 0) { 265 Log.i("@@@@", "track " + t + " buffer " + bufidx); 266 ByteBuffer buf = inbuffers[t][bufidx]; 267 int sampleSize = ex.readSampleData(buf, 0); 268 Log.i("@@@@", "read " + sampleSize); 269 if (sampleSize < 0) { 270 sampleSize = 0; 271 sawInputEOS[t] = true; 272 Log.i("@@@@", "EOS"); 273 //break; 274 } 275 long presentationTimeUs = ex.getSampleTime(); 276 277 codec[t].queueInputBuffer(bufidx, 0, sampleSize, presentationTimeUs, 278 sawInputEOS[t] ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); 279 ex.advance(); 280 } 281 } else { 282 Log.i("@@@@", "no more input samples"); 283 for (int tt = 0; tt < codec.length; tt++) { 284 if (!sawInputEOS[tt]) { 285 // we ran out of samples without ever signaling EOS to the codec, 286 // so do that now 287 int bufidx = codec[tt].dequeueInputBuffer(5000); 288 if (bufidx >= 0) { 289 codec[tt].queueInputBuffer(bufidx, 0, 0, 0, 290 MediaCodec.BUFFER_FLAG_END_OF_STREAM); 291 sawInputEOS[tt] = true; 292 } 293 } 294 } 295 } 296 297 // see if any of the codecs have data available 298 for (int tt = 0; tt < codec.length; tt++) { 299 if (!sawOutputEOS[tt]) { 300 int status = codec[tt].dequeueOutputBuffer(info, 1); 301 if (status >= 0) { 302 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 303 Log.i("@@@@", "EOS on track " + tt); 304 sawOutputEOS[tt] = true; 305 eosCount++; 306 } 307 Log.i("@@@@", "got decoded buffer for track " + tt + ", size " + info.size); 308 if (info.size > 0) { 309 addSampleData(trackdata[tt], outbuffers[tt][status], info.size, format[tt]); 310 } 311 codec[tt].releaseOutputBuffer(status, false); 312 } else if (status == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 313 Log.i("@@@@", "output buffers changed for track " + tt); 314 outbuffers[tt] = codec[tt].getOutputBuffers(); 315 } else if (status == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 316 format[tt] = codec[tt].getOutputFormat(); 317 Log.i("@@@@", "format changed for track " + t + ": " + format[tt].toString()); 318 } else if (status == MediaCodec.INFO_TRY_AGAIN_LATER) { 319 Log.i("@@@@", "no buffer right now for track " + tt); 320 } else { 321 Log.i("@@@@", "unexpected info code for track " + tt + ": " + status); 322 } 323 } else { 324 Log.i("@@@@", "already at EOS on track " + tt); 325 } 326 } 327 } 328 329 int totalsize = 0; 330 for (int i = 0; i < numtracks; i++) { 331 totalsize += trackdata[i].size(); 332 } 333 int[] trackbytes = new int[totalsize]; 334 int idx = 0; 335 for (int i = 0; i < numtracks; i++) { 336 ArrayList<Integer> src = trackdata[i]; 337 int tracksize = src.size(); 338 for (int j = 0; j < tracksize; j++) { 339 trackbytes[idx++] = src.get(j); 340 } 341 } 342 343 for (int i = 0; i < codec.length; i++) { 344 codec[i].release(); 345 } 346 347 return trackbytes; 348 } 349 addSampleData(ArrayList<Integer> dst, ByteBuffer buf, int size, MediaFormat format)350 static void addSampleData(ArrayList<Integer> dst, 351 ByteBuffer buf, int size, MediaFormat format) throws IOException{ 352 353 Log.i("@@@", "addsample " + dst.size() + "/" + size); 354 int width = format.getInteger(MediaFormat.KEY_WIDTH, size); 355 int stride = format.getInteger(MediaFormat.KEY_STRIDE, width); 356 int height = format.getInteger(MediaFormat.KEY_HEIGHT, 1); 357 byte[] bb = new byte[width * height]; 358 for (int i = 0; i < height; i++) { 359 buf.position(i * stride); 360 buf.get(bb, i * width, width); 361 } 362 // bb is filled with data 363 long sum = adler32(bb); 364 dst.add( (int) (sum & 0xffffffff)); 365 } 366 367 // simple checksum computed over every decoded buffer adler32(byte[] input)368 static long adler32(byte[] input) { 369 int a = 1; 370 int b = 0; 371 for (int i = 0; i < input.length; i++) { 372 int unsignedval = input[i]; 373 if (unsignedval < 0) { 374 unsignedval = 256 + unsignedval; 375 } 376 a += unsignedval; 377 b += a; 378 } 379 a = a % 65521; 380 b = b % 65521; 381 long ret = b * 65536 + a; 382 Log.i("@@@", "adler " + input.length + "/" + ret); 383 return ret; 384 } 385 getDecodedDataNative(int fd, long offset, long size)386 private static native int[] getDecodedDataNative(int fd, long offset, long size) 387 throws IOException; 388 testVideoPlayback()389 public void testVideoPlayback() throws Exception { 390 int testsRun = 391 testVideoPlayback( 392 R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz) + 393 testVideoPlayback( 394 R.raw.bbb_s1_640x360_webm_vp8_2mbps_30fps_vorbis_5ch_320kbps_48000hz) + 395 testVideoPlayback( 396 R.raw.bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz) + 397 testVideoPlayback( 398 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz) + 399 testVideoPlayback( 400 R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz); 401 if (testsRun == 0) { 402 MediaUtils.skipTest("no decoders found"); 403 } 404 } 405 testVideoPlayback(int res)406 private int testVideoPlayback(int res) throws Exception { 407 if (!MediaUtils.checkCodecsForResource(mContext, res)) { 408 return 0; // skip 409 } 410 411 AssetFileDescriptor fd = mResources.openRawResourceFd(res); 412 413 boolean ret = testPlaybackNative(mActivity.getSurfaceHolder().getSurface(), 414 fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength()); 415 assertTrue("native playback error", ret); 416 return 1; 417 } 418 testPlaybackNative(Surface surface, int fd, long startOffset, long length)419 private static native boolean testPlaybackNative(Surface surface, 420 int fd, long startOffset, long length); 421 testMuxerAvc()422 public void testMuxerAvc() throws Exception { 423 // IMPORTANT: this file must not have B-frames 424 testMuxer(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, false); 425 } 426 testMuxerH263()427 public void testMuxerH263() throws Exception { 428 // IMPORTANT: this file must not have B-frames 429 testMuxer(R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_stereo_128kbps_11025hz, false); 430 } 431 testMuxerHevc()432 public void testMuxerHevc() throws Exception { 433 // IMPORTANT: this file must not have B-frames 434 testMuxer(R.raw.video_640x360_mp4_hevc_450kbps_no_b, false); 435 } 436 testMuxerVp8()437 public void testMuxerVp8() throws Exception { 438 testMuxer(R.raw.bbb_s1_640x360_webm_vp8_2mbps_30fps_vorbis_5ch_320kbps_48000hz, true); 439 } 440 testMuxerVp9()441 public void testMuxerVp9() throws Exception { 442 testMuxer( 443 R.raw.video_1280x720_webm_vp9_csd_309kbps_25fps_vorbis_stereo_128kbps_48000hz, 444 true); 445 } 446 testMuxerVp9NoCsd()447 public void testMuxerVp9NoCsd() throws Exception { 448 testMuxer( 449 R.raw.bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz, 450 true); 451 } 452 testMuxerVp9Hdr()453 public void testMuxerVp9Hdr() throws Exception { 454 testMuxer(R.raw.video_256x144_webm_vp9_hdr_83kbps_24fps, true); 455 } 456 457 // We do not support MPEG-2 muxing as of yet SKIP_testMuxerMpeg2()458 public void SKIP_testMuxerMpeg2() throws Exception { 459 // IMPORTANT: this file must not have B-frames 460 testMuxer(R.raw.video_176x144_mp4_mpeg2_105kbps_25fps_aac_stereo_128kbps_44100hz, false); 461 } 462 testMuxerMpeg4()463 public void testMuxerMpeg4() throws Exception { 464 // IMPORTANT: this file must not have B-frames 465 testMuxer(R.raw.video_176x144_mp4_mpeg4_300kbps_25fps_aac_stereo_128kbps_44100hz, false); 466 } 467 testMuxer(int res, boolean webm)468 private void testMuxer(int res, boolean webm) throws Exception { 469 if (!MediaUtils.checkCodecsForResource(mContext, res)) { 470 return; // skip 471 } 472 473 AssetFileDescriptor infd = mResources.openRawResourceFd(res); 474 475 File base = mContext.getExternalFilesDir(null); 476 String tmpFile = base.getPath() + "/tmp.dat"; 477 Log.i("@@@", "using tmp file " + tmpFile); 478 new File(tmpFile).delete(); 479 ParcelFileDescriptor out = ParcelFileDescriptor.open(new File(tmpFile), 480 ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_CREATE); 481 482 assertTrue("muxer failed", testMuxerNative( 483 infd.getParcelFileDescriptor().getFd(), infd.getStartOffset(), infd.getLength(), 484 out.getFd(), webm)); 485 486 // compare the original with the remuxed 487 MediaExtractor org = new MediaExtractor(); 488 org.setDataSource(infd.getFileDescriptor(), 489 infd.getStartOffset(), infd.getLength()); 490 491 MediaExtractor remux = new MediaExtractor(); 492 remux.setDataSource(out.getFileDescriptor()); 493 494 assertEquals("mismatched numer of tracks", org.getTrackCount(), remux.getTrackCount()); 495 // allow duration mismatch for webm files as ffmpeg does not consider the duration of the 496 // last frame while libwebm (and our framework) does. 497 final long maxDurationDiffUs = webm ? 50000 : 0; // 50ms for webm 498 for (int i = 0; i < org.getTrackCount(); i++) { 499 MediaFormat format1 = org.getTrackFormat(i); 500 MediaFormat format2 = remux.getTrackFormat(i); 501 Log.i("@@@", "org: " + format1); 502 Log.i("@@@", "remux: " + format2); 503 assertTrue("different formats", compareFormats(format1, format2, maxDurationDiffUs)); 504 } 505 506 org.release(); 507 remux.release(); 508 509 MediaPlayer player1 = MediaPlayer.create(mContext, res); 510 MediaPlayer player2 = MediaPlayer.create(mContext, Uri.parse("file://" + tmpFile)); 511 assertEquals("duration is different", 512 player1.getDuration(), player2.getDuration(), maxDurationDiffUs * 0.001); 513 player1.release(); 514 player2.release(); 515 new File(tmpFile).delete(); 516 } 517 hexString(ByteBuffer buf)518 private String hexString(ByteBuffer buf) { 519 if (buf == null) { 520 return "(null)"; 521 } 522 final char digits[] = 523 { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; 524 525 StringBuilder hex = new StringBuilder(); 526 for (int i = buf.position(); i < buf.limit(); ++i) { 527 byte c = buf.get(i); 528 hex.append(digits[(c >> 4) & 0xf]); 529 hex.append(digits[c & 0xf]); 530 } 531 return hex.toString(); 532 } 533 534 /** returns: null if key is in neither formats, true if they match and false otherwise */ compareByteBufferInFormats(MediaFormat f1, MediaFormat f2, String key)535 private Boolean compareByteBufferInFormats(MediaFormat f1, MediaFormat f2, String key) { 536 ByteBuffer bufF1 = f1.containsKey(key) ? f1.getByteBuffer(key) : null; 537 ByteBuffer bufF2 = f2.containsKey(key) ? f2.getByteBuffer(key) : null; 538 if (bufF1 == null && bufF2 == null) { 539 return null; 540 } 541 if (bufF1 == null || !bufF1.equals(bufF2)) { 542 Log.i("@@@", "org " + key + ": " + hexString(bufF1)); 543 Log.i("@@@", "rmx " + key + ": " + hexString(bufF2)); 544 return false; 545 } 546 return true; 547 } 548 compareFormats(MediaFormat f1, MediaFormat f2, long maxDurationDiffUs)549 private boolean compareFormats(MediaFormat f1, MediaFormat f2, long maxDurationDiffUs) { 550 final String KEY_DURATION = MediaFormat.KEY_DURATION; 551 552 // allow some difference in durations 553 if (maxDurationDiffUs > 0 554 && f1.containsKey(KEY_DURATION) && f2.containsKey(KEY_DURATION) 555 && Math.abs(f1.getLong(KEY_DURATION) 556 - f2.getLong(KEY_DURATION)) <= maxDurationDiffUs) { 557 f2.setLong(KEY_DURATION, f1.getLong(KEY_DURATION)); 558 } 559 560 // verify hdr-static-info 561 if (Boolean.FALSE.equals(compareByteBufferInFormats(f1, f2, "hdr-static-info"))) { 562 return false; 563 } 564 565 // verify CSDs 566 for (int i = 0;; ++i) { 567 String key = "csd-" + i; 568 Boolean match = compareByteBufferInFormats(f1, f2, key); 569 if (match == null) { 570 break; 571 } else if (match == false) { 572 return false; 573 } 574 } 575 576 // there's no good way to compare two MediaFormats, so compare their string 577 // representation 578 return f1.toString().equals(f2.toString()); 579 } 580 testMuxerNative(int in, long inoffset, long insize, int out, boolean webm)581 private static native boolean testMuxerNative(int in, long inoffset, long insize, 582 int out, boolean webm); 583 testFormat()584 public void testFormat() throws Exception { 585 assertTrue("media format fail, see log for details", testFormatNative()); 586 } 587 testFormatNative()588 private static native boolean testFormatNative(); 589 testPssh()590 public void testPssh() throws Exception { 591 testPssh(R.raw.psshtest); 592 } 593 testPssh(int res)594 private void testPssh(int res) throws Exception { 595 AssetFileDescriptor fd = mResources.openRawResourceFd(res); 596 597 MediaExtractor ex = new MediaExtractor(); 598 ex.setDataSource(fd.getParcelFileDescriptor().getFileDescriptor(), 599 fd.getStartOffset(), fd.getLength()); 600 testPssh(ex); 601 ex.release(); 602 603 boolean ret = testPsshNative( 604 fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength()); 605 assertTrue("native pssh error", ret); 606 } 607 testPssh(MediaExtractor ex)608 private static void testPssh(MediaExtractor ex) { 609 Map<UUID, byte[]> map = ex.getPsshInfo(); 610 Set<UUID> keys = map.keySet(); 611 for (UUID uuid: keys) { 612 Log.i("@@@", "uuid: " + uuid + ", data size " + 613 map.get(uuid).length); 614 } 615 } 616 testPsshNative(int fd, long offset, long size)617 private static native boolean testPsshNative(int fd, long offset, long size); 618 testCryptoInfo()619 public void testCryptoInfo() throws Exception { 620 assertTrue("native cryptoinfo failed, see log for details", testCryptoInfoNative()); 621 } 622 testCryptoInfoNative()623 private static native boolean testCryptoInfoNative(); 624 } 625 626