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; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.content.res.AssetFileDescriptor; 25 import android.media.MediaCodec; 26 import android.media.MediaFormat; 27 import android.media.MediaHTTPService; 28 import android.net.Uri; 29 import android.os.IBinder; 30 import android.os.IHwBinder; 31 import android.os.PersistableBundle; 32 33 import com.android.internal.util.Preconditions; 34 35 import java.io.FileDescriptor; 36 import java.io.IOException; 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 import java.nio.ByteBuffer; 40 import java.nio.ByteOrder; 41 import java.util.ArrayList; 42 import java.util.HashMap; 43 import java.util.Map; 44 import java.util.UUID; 45 46 /** 47 * MediaExtractor facilitates extraction of demuxed, typically encoded, media data 48 * from a data source. 49 * <p>It is generally used like this: 50 * <pre> 51 * MediaExtractor extractor = new MediaExtractor(); 52 * extractor.setDataSource(...); 53 * int numTracks = extractor.getTrackCount(); 54 * for (int i = 0; i < numTracks; ++i) { 55 * MediaFormat format = extractor.getTrackFormat(i); 56 * String mime = format.getString(MediaFormat.KEY_MIME); 57 * if (weAreInterestedInThisTrack) { 58 * extractor.selectTrack(i); 59 * } 60 * } 61 * ByteBuffer inputBuffer = ByteBuffer.allocate(...) 62 * while (extractor.readSampleData(inputBuffer, ...) >= 0) { 63 * int trackIndex = extractor.getSampleTrackIndex(); 64 * long presentationTimeUs = extractor.getSampleTime(); 65 * ... 66 * extractor.advance(); 67 * } 68 * 69 * extractor.release(); 70 * extractor = null; 71 * </pre> 72 * 73 * <p>This class requires the {@link android.Manifest.permission#INTERNET} permission 74 * when used with network-based content. 75 */ 76 final public class MediaExtractor { 77 public MediaExtractor() { 78 native_setup(); 79 } 80 81 /** 82 * Sets the data source (MediaDataSource) to use. 83 * 84 * @param dataSource the MediaDataSource for the media you want to extract from 85 * 86 * @throws IllegalArgumentException if dataSource is invalid. 87 */ 88 public native final void setDataSource(@NonNull MediaDataSource dataSource) 89 throws IOException; 90 91 /** 92 * Sets the data source as a content Uri. 93 * 94 * @param context the Context to use when resolving the Uri 95 * @param uri the Content URI of the data you want to extract from. 96 * 97 * <p>When <code>uri</code> refers to a network file the 98 * {@link android.Manifest.permission#INTERNET} permission is required. 99 * 100 * @param headers the headers to be sent together with the request for the data. 101 * This can be {@code null} if no specific headers are to be sent with the 102 * request. 103 */ 104 public final void setDataSource( 105 @NonNull Context context, @NonNull Uri uri, @Nullable Map<String, String> headers) 106 throws IOException { 107 String scheme = uri.getScheme(); 108 if (scheme == null || scheme.equals("file")) { 109 setDataSource(uri.getPath()); 110 return; 111 } 112 113 AssetFileDescriptor fd = null; 114 try { 115 ContentResolver resolver = context.getContentResolver(); 116 fd = resolver.openAssetFileDescriptor(uri, "r"); 117 if (fd == null) { 118 return; 119 } 120 // Note: using getDeclaredLength so that our behavior is the same 121 // as previous versions when the content provider is returning 122 // a full file. 123 if (fd.getDeclaredLength() < 0) { 124 setDataSource(fd.getFileDescriptor()); 125 } else { 126 setDataSource( 127 fd.getFileDescriptor(), 128 fd.getStartOffset(), 129 fd.getDeclaredLength()); 130 } 131 return; 132 } catch (SecurityException ex) { 133 } catch (IOException ex) { 134 } finally { 135 if (fd != null) { 136 fd.close(); 137 } 138 } 139 140 setDataSource(uri.toString(), headers); 141 } 142 143 /** 144 * Sets the data source (file-path or http URL) to use. 145 * 146 * @param path the path of the file, or the http URL 147 * 148 * <p>When <code>path</code> refers to a network file the 149 * {@link android.Manifest.permission#INTERNET} permission is required. 150 * 151 * @param headers the headers associated with the http request for the stream you want to play. 152 * This can be {@code null} if no specific headers are to be sent with the 153 * request. 154 */ 155 public final void setDataSource(@NonNull String path, @Nullable Map<String, String> headers) 156 throws IOException { 157 String[] keys = null; 158 String[] values = null; 159 160 if (headers != null) { 161 keys = new String[headers.size()]; 162 values = new String[headers.size()]; 163 164 int i = 0; 165 for (Map.Entry<String, String> entry: headers.entrySet()) { 166 keys[i] = entry.getKey(); 167 values[i] = entry.getValue(); 168 ++i; 169 } 170 } 171 172 nativeSetDataSource( 173 MediaHTTPService.createHttpServiceBinderIfNecessary(path), 174 path, 175 keys, 176 values); 177 } 178 179 private native final void nativeSetDataSource( 180 @NonNull IBinder httpServiceBinder, 181 @NonNull String path, 182 @Nullable String[] keys, 183 @Nullable String[] values) throws IOException; 184 185 /** 186 * Sets the data source (file-path or http URL) to use. 187 * 188 * @param path the path of the file, or the http URL of the stream 189 * 190 * <p>When <code>path</code> refers to a local file, the file may actually be opened by a 191 * process other than the calling application. This implies that the pathname 192 * should be an absolute path (as any other process runs with unspecified current working 193 * directory), and that the pathname should reference a world-readable file. 194 * As an alternative, the application could first open the file for reading, 195 * and then use the file descriptor form {@link #setDataSource(FileDescriptor)}. 196 * 197 * <p>When <code>path</code> refers to a network file the 198 * {@link android.Manifest.permission#INTERNET} permission is required. 199 */ 200 public final void setDataSource(@NonNull String path) throws IOException { 201 nativeSetDataSource( 202 MediaHTTPService.createHttpServiceBinderIfNecessary(path), 203 path, 204 null, 205 null); 206 } 207 208 /** 209 * Sets the data source (AssetFileDescriptor) to use. It is the caller's 210 * responsibility to close the file descriptor. It is safe to do so as soon 211 * as this call returns. 212 * 213 * @param afd the AssetFileDescriptor for the file you want to extract from. 214 */ 215 public final void setDataSource(@NonNull AssetFileDescriptor afd) 216 throws IOException, IllegalArgumentException, IllegalStateException { 217 Preconditions.checkNotNull(afd); 218 // Note: using getDeclaredLength so that our behavior is the same 219 // as previous versions when the content provider is returning 220 // a full file. 221 if (afd.getDeclaredLength() < 0) { 222 setDataSource(afd.getFileDescriptor()); 223 } else { 224 setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength()); 225 } 226 } 227 228 /** 229 * Sets the data source (FileDescriptor) to use. It is the caller's responsibility 230 * to close the file descriptor. It is safe to do so as soon as this call returns. 231 * 232 * @param fd the FileDescriptor for the file you want to extract from. 233 */ 234 public final void setDataSource(@NonNull FileDescriptor fd) throws IOException { 235 setDataSource(fd, 0, 0x7ffffffffffffffL); 236 } 237 238 /** 239 * Sets the data source (FileDescriptor) to use. The FileDescriptor must be 240 * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility 241 * to close the file descriptor. It is safe to do so as soon as this call returns. 242 * 243 * @param fd the FileDescriptor for the file you want to extract from. 244 * @param offset the offset into the file where the data to be extracted starts, in bytes 245 * @param length the length in bytes of the data to be extracted 246 */ 247 public native final void setDataSource( 248 @NonNull FileDescriptor fd, long offset, long length) throws IOException; 249 250 /** 251 * Sets the MediaCas instance to use. This should be called after a 252 * successful setDataSource() if at least one track reports mime type 253 * of {@link android.media.MediaFormat#MIMETYPE_AUDIO_SCRAMBLED} 254 * or {@link android.media.MediaFormat#MIMETYPE_VIDEO_SCRAMBLED}. 255 * Stream parsing will not proceed until a valid MediaCas object 256 * is provided. 257 * 258 * @param mediaCas the MediaCas object to use. 259 */ 260 public final void setMediaCas(@NonNull MediaCas mediaCas) { 261 mMediaCas = mediaCas; 262 nativeSetMediaCas(mediaCas.getBinder()); 263 } 264 265 private native final void nativeSetMediaCas(@NonNull IHwBinder casBinder); 266 267 /** 268 * Describes the conditional access system used to scramble a track. 269 */ 270 public static final class CasInfo { 271 private final int mSystemId; 272 private final MediaCas.Session mSession; 273 274 CasInfo(int systemId, @Nullable MediaCas.Session session) { 275 mSystemId = systemId; 276 mSession = session; 277 } 278 279 /** 280 * Retrieves the system id of the conditional access system. 281 * 282 * @return CA system id of the CAS used to scramble the track. 283 */ 284 public int getSystemId() { 285 return mSystemId; 286 } 287 288 /** 289 * Retrieves the {@link MediaCas.Session} associated with a track. The 290 * session is needed to initialize a descrambler in order to decode the 291 * scrambled track. 292 * <p> 293 * @see MediaDescrambler#setMediaCasSession 294 * <p> 295 * @return a {@link MediaCas.Session} object associated with a track. 296 */ 297 public MediaCas.Session getSession() { 298 return mSession; 299 } 300 } 301 302 private ArrayList<Byte> toByteArray(@NonNull byte[] data) { 303 ArrayList<Byte> byteArray = new ArrayList<Byte>(data.length); 304 for (int i = 0; i < data.length; i++) { 305 byteArray.add(i, Byte.valueOf(data[i])); 306 } 307 return byteArray; 308 } 309 310 /** 311 * Retrieves the information about the conditional access system used to scramble 312 * a track. 313 * 314 * @param index of the track. 315 * @return an {@link CasInfo} object describing the conditional access system. 316 */ 317 public CasInfo getCasInfo(int index) { 318 Map<String, Object> formatMap = getTrackFormatNative(index); 319 if (formatMap.containsKey(MediaFormat.KEY_CA_SYSTEM_ID)) { 320 int systemId = ((Integer)formatMap.get(MediaFormat.KEY_CA_SYSTEM_ID)).intValue(); 321 MediaCas.Session session = null; 322 if (mMediaCas != null && formatMap.containsKey(MediaFormat.KEY_CA_SESSION_ID)) { 323 ByteBuffer buf = (ByteBuffer) formatMap.get(MediaFormat.KEY_CA_SESSION_ID); 324 buf.rewind(); 325 final byte[] sessionId = new byte[buf.remaining()]; 326 buf.get(sessionId); 327 session = mMediaCas.createFromSessionId(toByteArray(sessionId)); 328 } 329 return new CasInfo(systemId, session); 330 } 331 return null; 332 } 333 334 @Override 335 protected void finalize() { 336 native_finalize(); 337 } 338 339 /** 340 * Make sure you call this when you're done to free up any resources 341 * instead of relying on the garbage collector to do this for you at 342 * some point in the future. 343 */ 344 public native final void release(); 345 346 /** 347 * Count the number of tracks found in the data source. 348 */ 349 public native final int getTrackCount(); 350 351 /** 352 * Extract DRM initialization data if it exists 353 * 354 * @return DRM initialization data in the content, or {@code null} 355 * if no recognizable DRM format is found; 356 * @see DrmInitData 357 */ 358 public DrmInitData getDrmInitData() { 359 Map<String, Object> formatMap = getFileFormatNative(); 360 if (formatMap == null) { 361 return null; 362 } 363 if (formatMap.containsKey("pssh")) { 364 Map<UUID, byte[]> psshMap = getPsshInfo(); 365 final Map<UUID, DrmInitData.SchemeInitData> initDataMap = 366 new HashMap<UUID, DrmInitData.SchemeInitData>(); 367 for (Map.Entry<UUID, byte[]> e: psshMap.entrySet()) { 368 UUID uuid = e.getKey(); 369 byte[] data = e.getValue(); 370 initDataMap.put(uuid, new DrmInitData.SchemeInitData("cenc", data)); 371 } 372 return new DrmInitData() { 373 public SchemeInitData get(UUID schemeUuid) { 374 return initDataMap.get(schemeUuid); 375 } 376 }; 377 } else { 378 int numTracks = getTrackCount(); 379 for (int i = 0; i < numTracks; ++i) { 380 Map<String, Object> trackFormatMap = getTrackFormatNative(i); 381 if (!trackFormatMap.containsKey("crypto-key")) { 382 continue; 383 } 384 ByteBuffer buf = (ByteBuffer) trackFormatMap.get("crypto-key"); 385 buf.rewind(); 386 final byte[] data = new byte[buf.remaining()]; 387 buf.get(data); 388 return new DrmInitData() { 389 public SchemeInitData get(UUID schemeUuid) { 390 return new DrmInitData.SchemeInitData("webm", data); 391 } 392 }; 393 } 394 } 395 return null; 396 } 397 398 /** 399 * Get the PSSH info if present. 400 * @return a map of uuid-to-bytes, with the uuid specifying 401 * the crypto scheme, and the bytes being the data specific to that scheme. 402 * This can be {@code null} if the source does not contain PSSH info. 403 */ 404 @Nullable 405 public Map<UUID, byte[]> getPsshInfo() { 406 Map<UUID, byte[]> psshMap = null; 407 Map<String, Object> formatMap = getFileFormatNative(); 408 if (formatMap != null && formatMap.containsKey("pssh")) { 409 ByteBuffer rawpssh = (ByteBuffer) formatMap.get("pssh"); 410 rawpssh.order(ByteOrder.nativeOrder()); 411 rawpssh.rewind(); 412 formatMap.remove("pssh"); 413 // parse the flat pssh bytebuffer into something more manageable 414 psshMap = new HashMap<UUID, byte[]>(); 415 while (rawpssh.remaining() > 0) { 416 rawpssh.order(ByteOrder.BIG_ENDIAN); 417 long msb = rawpssh.getLong(); 418 long lsb = rawpssh.getLong(); 419 UUID uuid = new UUID(msb, lsb); 420 rawpssh.order(ByteOrder.nativeOrder()); 421 int datalen = rawpssh.getInt(); 422 byte [] psshdata = new byte[datalen]; 423 rawpssh.get(psshdata); 424 psshMap.put(uuid, psshdata); 425 } 426 } 427 return psshMap; 428 } 429 430 @NonNull 431 private native Map<String, Object> getFileFormatNative(); 432 433 /** 434 * Get the track format at the specified index. 435 * 436 * More detail on the representation can be found at {@link android.media.MediaCodec} 437 * <p> 438 * The following table summarizes support for format keys across android releases: 439 * 440 * <table style="width: 0%"> 441 * <thead> 442 * <tr> 443 * <th rowspan=2>OS Version(s)</th> 444 * <td colspan=3>{@code MediaFormat} keys used for</th> 445 * </tr><tr> 446 * <th>All Tracks</th> 447 * <th>Audio Tracks</th> 448 * <th>Video Tracks</th> 449 * </tr> 450 * </thead> 451 * <tbody> 452 * <tr> 453 * <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN}</td> 454 * <td rowspan=8>{@link MediaFormat#KEY_MIME},<br> 455 * {@link MediaFormat#KEY_DURATION},<br> 456 * {@link MediaFormat#KEY_MAX_INPUT_SIZE}</td> 457 * <td rowspan=5>{@link MediaFormat#KEY_SAMPLE_RATE},<br> 458 * {@link MediaFormat#KEY_CHANNEL_COUNT},<br> 459 * {@link MediaFormat#KEY_CHANNEL_MASK},<br> 460 * gapless playback information<sup>.mp3, .mp4</sup>,<br> 461 * {@link MediaFormat#KEY_IS_ADTS}<sup>AAC if streaming</sup>,<br> 462 * codec-specific data<sup>AAC, Vorbis</sup></td> 463 * <td rowspan=2>{@link MediaFormat#KEY_WIDTH},<br> 464 * {@link MediaFormat#KEY_HEIGHT},<br> 465 * codec-specific data<sup>AVC, MPEG4</sup></td> 466 * </tr><tr> 467 * <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}</td> 468 * </tr><tr> 469 * <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}</td> 470 * <td rowspan=3>as above, plus<br> 471 * Pixel aspect ratio information<sup>AVC, *</sup></td> 472 * </tr><tr> 473 * <td>{@link android.os.Build.VERSION_CODES#KITKAT}</td> 474 * </tr><tr> 475 * <td>{@link android.os.Build.VERSION_CODES#KITKAT_WATCH}</td> 476 * </tr><tr> 477 * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP}</td> 478 * <td rowspan=2>as above, plus<br> 479 * {@link MediaFormat#KEY_BIT_RATE}<sup>AAC</sup>,<br> 480 * codec-specific data<sup>Opus</sup></td> 481 * <td rowspan=2>as above, plus<br> 482 * {@link MediaFormat#KEY_ROTATION}<sup>.mp4</sup>,<br> 483 * {@link MediaFormat#KEY_BIT_RATE}<sup>MPEG4</sup>,<br> 484 * codec-specific data<sup>HEVC</sup></td> 485 * </tr><tr> 486 * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}</td> 487 * </tr><tr> 488 * <td>{@link android.os.Build.VERSION_CODES#M}</td> 489 * <td>as above, plus<br> 490 * gapless playback information<sup>Opus</sup></td> 491 * <td>as above, plus<br> 492 * {@link MediaFormat#KEY_FRAME_RATE} (integer)</td> 493 * </tr><tr> 494 * <td>{@link android.os.Build.VERSION_CODES#N}</td> 495 * <td>as above, plus<br> 496 * {@link MediaFormat#KEY_TRACK_ID},<br> 497 * <!-- {link MediaFormat#KEY_MAX_BIT_RATE}<sup>#, .mp4</sup>,<br> --> 498 * {@link MediaFormat#KEY_BIT_RATE}<sup>#, .mp4</sup></td> 499 * <td>as above, plus<br> 500 * {@link MediaFormat#KEY_PCM_ENCODING},<br> 501 * {@link MediaFormat#KEY_PROFILE}<sup>AAC</sup></td> 502 * <td>as above, plus<br> 503 * {@link MediaFormat#KEY_HDR_STATIC_INFO}<sup>#, .webm</sup>,<br> 504 * {@link MediaFormat#KEY_COLOR_STANDARD}<sup>#</sup>,<br> 505 * {@link MediaFormat#KEY_COLOR_TRANSFER}<sup>#</sup>,<br> 506 * {@link MediaFormat#KEY_COLOR_RANGE}<sup>#</sup>,<br> 507 * {@link MediaFormat#KEY_PROFILE}<sup>MPEG2, H.263, MPEG4, AVC, HEVC, VP9</sup>,<br> 508 * {@link MediaFormat#KEY_LEVEL}<sup>H.263, MPEG4, AVC, HEVC, VP9</sup>,<br> 509 * codec-specific data<sup>VP9</sup></td> 510 * </tr> 511 * <tr> 512 * <td colspan=4> 513 * <p class=note><strong>Notes:</strong><br> 514 * #: container-specified value only.<br> 515 * .mp4, .webm…: for listed containers<br> 516 * MPEG4, AAC…: for listed codecs 517 * </td> 518 * </tr><tr> 519 * <td colspan=4> 520 * <p class=note>Note that that level information contained in the container many times 521 * does not match the level of the actual bitstream. You may want to clear the level using 522 * {@code MediaFormat.setString(KEY_LEVEL, null)} before using the track format to find a 523 * decoder that can play back a particular track. 524 * </td> 525 * </tr><tr> 526 * <td colspan=4> 527 * <p class=note><strong>*Pixel (sample) aspect ratio</strong> is returned in the following 528 * keys. The display width can be calculated for example as: 529 * <p align=center> 530 * display-width = display-height * crop-width / crop-height * sar-width / sar-height 531 * </td> 532 * </tr><tr> 533 * <th>Format Key</th><th>Value Type</th><th colspan=2>Description</th> 534 * </tr><tr> 535 * <td>{@code "sar-width"}</td><td>Integer</td><td colspan=2>Pixel aspect ratio width</td> 536 * </tr><tr> 537 * <td>{@code "sar-height"}</td><td>Integer</td><td colspan=2>Pixel aspect ratio height</td> 538 * </tr> 539 * </tbody> 540 * </table> 541 * 542 */ 543 @NonNull 544 public MediaFormat getTrackFormat(int index) { 545 return new MediaFormat(getTrackFormatNative(index)); 546 } 547 548 @NonNull 549 private native Map<String, Object> getTrackFormatNative(int index); 550 551 /** 552 * Subsequent calls to {@link #readSampleData}, {@link #getSampleTrackIndex} and 553 * {@link #getSampleTime} only retrieve information for the subset of tracks 554 * selected. 555 * Selecting the same track multiple times has no effect, the track is 556 * only selected once. 557 */ 558 public native void selectTrack(int index); 559 560 /** 561 * Subsequent calls to {@link #readSampleData}, {@link #getSampleTrackIndex} and 562 * {@link #getSampleTime} only retrieve information for the subset of tracks 563 * selected. 564 */ 565 public native void unselectTrack(int index); 566 567 /** 568 * If possible, seek to a sync sample at or before the specified time 569 */ 570 public static final int SEEK_TO_PREVIOUS_SYNC = 0; 571 /** 572 * If possible, seek to a sync sample at or after the specified time 573 */ 574 public static final int SEEK_TO_NEXT_SYNC = 1; 575 /** 576 * If possible, seek to the sync sample closest to the specified time 577 */ 578 public static final int SEEK_TO_CLOSEST_SYNC = 2; 579 580 /** @hide */ 581 @IntDef({ 582 SEEK_TO_PREVIOUS_SYNC, 583 SEEK_TO_NEXT_SYNC, 584 SEEK_TO_CLOSEST_SYNC, 585 }) 586 @Retention(RetentionPolicy.SOURCE) 587 public @interface SeekMode {} 588 589 /** 590 * All selected tracks seek near the requested time according to the 591 * specified mode. 592 */ 593 public native void seekTo(long timeUs, @SeekMode int mode); 594 595 /** 596 * Advance to the next sample. Returns false if no more sample data 597 * is available (end of stream). 598 * 599 * When extracting a local file, the behaviors of {@link #advance} and 600 * {@link #readSampleData} are undefined in presence of concurrent 601 * writes to the same local file; more specifically, end of stream 602 * could be signalled earlier than expected. 603 */ 604 public native boolean advance(); 605 606 /** 607 * Retrieve the current encoded sample and store it in the byte buffer 608 * starting at the given offset. 609 * <p> 610 * <b>Note:</b>As of API 21, on success the position and limit of 611 * {@code byteBuf} is updated to point to the data just read. 612 * @param byteBuf the destination byte buffer 613 * @return the sample size (or -1 if no more samples are available). 614 */ 615 public native int readSampleData(@NonNull ByteBuffer byteBuf, int offset); 616 617 /** 618 * Returns the track index the current sample originates from (or -1 619 * if no more samples are available) 620 */ 621 public native int getSampleTrackIndex(); 622 623 /** 624 * Returns the current sample's presentation time in microseconds. 625 * or -1 if no more samples are available. 626 */ 627 public native long getSampleTime(); 628 629 // Keep these in sync with their equivalents in NuMediaExtractor.h 630 /** 631 * The sample is a sync sample (or in {@link MediaCodec}'s terminology 632 * it is a key frame.) 633 * 634 * @see MediaCodec#BUFFER_FLAG_KEY_FRAME 635 */ 636 public static final int SAMPLE_FLAG_SYNC = 1; 637 638 /** 639 * The sample is (at least partially) encrypted, see also the documentation 640 * for {@link android.media.MediaCodec#queueSecureInputBuffer} 641 */ 642 public static final int SAMPLE_FLAG_ENCRYPTED = 2; 643 644 /** 645 * This indicates that the buffer only contains part of a frame, 646 * and the decoder should batch the data until a buffer without 647 * this flag appears before decoding the frame. 648 * 649 * @see MediaCodec#BUFFER_FLAG_PARTIAL_FRAME 650 */ 651 public static final int SAMPLE_FLAG_PARTIAL_FRAME = 4; 652 653 /** @hide */ 654 @IntDef( 655 flag = true, 656 value = { 657 SAMPLE_FLAG_SYNC, 658 SAMPLE_FLAG_ENCRYPTED, 659 SAMPLE_FLAG_PARTIAL_FRAME, 660 }) 661 @Retention(RetentionPolicy.SOURCE) 662 public @interface SampleFlag {} 663 664 /** 665 * Returns the current sample's flags. 666 */ 667 @SampleFlag 668 public native int getSampleFlags(); 669 670 /** 671 * If the sample flags indicate that the current sample is at least 672 * partially encrypted, this call returns relevant information about 673 * the structure of the sample data required for decryption. 674 * @param info The android.media.MediaCodec.CryptoInfo structure 675 * to be filled in. 676 * @return true iff the sample flags contain {@link #SAMPLE_FLAG_ENCRYPTED} 677 */ 678 public native boolean getSampleCryptoInfo(@NonNull MediaCodec.CryptoInfo info); 679 680 /** 681 * Returns an estimate of how much data is presently cached in memory 682 * expressed in microseconds. Returns -1 if that information is unavailable 683 * or not applicable (no cache). 684 */ 685 public native long getCachedDuration(); 686 687 /** 688 * Returns true iff we are caching data and the cache has reached the 689 * end of the data stream (for now, a future seek may of course restart 690 * the fetching of data). 691 * This API only returns a meaningful result if {@link #getCachedDuration} 692 * indicates the presence of a cache, i.e. does NOT return -1. 693 */ 694 public native boolean hasCacheReachedEndOfStream(); 695 696 /** 697 * Return Metrics data about the current media container. 698 * 699 * @return a {@link PersistableBundle} containing the set of attributes and values 700 * available for the media container being handled by this instance 701 * of MediaExtractor. 702 * The attributes are descibed in {@link MetricsConstants}. 703 * 704 * Additional vendor-specific fields may also be present in 705 * the return value. 706 */ 707 708 public PersistableBundle getMetrics() { 709 PersistableBundle bundle = native_getMetrics(); 710 return bundle; 711 } 712 713 private native PersistableBundle native_getMetrics(); 714 715 private static native final void native_init(); 716 private native final void native_setup(); 717 private native final void native_finalize(); 718 719 static { 720 System.loadLibrary("media_jni"); 721 native_init(); 722 } 723 724 private MediaCas mMediaCas; 725 726 private long mNativeContext; 727 728 public final static class MetricsConstants 729 { 730 private MetricsConstants() {} 731 732 /** 733 * Key to extract the container format 734 * from the {@link MediaExtractor#getMetrics} return value. 735 * The value is a String. 736 */ 737 public static final String FORMAT = "android.media.mediaextractor.fmt"; 738 739 /** 740 * Key to extract the container MIME type 741 * from the {@link MediaExtractor#getMetrics} return value. 742 * The value is a String. 743 */ 744 public static final String MIME_TYPE = "android.media.mediaextractor.mime"; 745 746 /** 747 * Key to extract the number of tracks in the container 748 * from the {@link MediaExtractor#getMetrics} return value. 749 * The value is an integer. 750 */ 751 public static final String TRACKS = "android.media.mediaextractor.ntrk"; 752 753 } 754 755 } 756