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