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