• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.AudioPresentation;
26 import android.media.MediaCodec;
27 import android.media.MediaFormat;
28 import android.media.MediaHTTPService;
29 import android.net.Uri;
30 import android.os.IBinder;
31 import android.os.IHwBinder;
32 import android.os.PersistableBundle;
33 
34 import com.android.internal.util.Preconditions;
35 
36 import java.io.FileDescriptor;
37 import java.io.IOException;
38 import java.lang.annotation.Retention;
39 import java.lang.annotation.RetentionPolicy;
40 import java.nio.ByteBuffer;
41 import java.nio.ByteOrder;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.HashMap;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.UUID;
48 import java.util.stream.Collectors;
49 
50 /**
51  * MediaExtractor facilitates extraction of demuxed, typically encoded,  media data
52  * from a data source.
53  * <p>It is generally used like this:
54  * <pre>
55  * MediaExtractor extractor = new MediaExtractor();
56  * extractor.setDataSource(...);
57  * int numTracks = extractor.getTrackCount();
58  * for (int i = 0; i &lt; numTracks; ++i) {
59  *   MediaFormat format = extractor.getTrackFormat(i);
60  *   String mime = format.getString(MediaFormat.KEY_MIME);
61  *   if (weAreInterestedInThisTrack) {
62  *     extractor.selectTrack(i);
63  *   }
64  * }
65  * ByteBuffer inputBuffer = ByteBuffer.allocate(...)
66  * while (extractor.readSampleData(inputBuffer, ...) &gt;= 0) {
67  *   int trackIndex = extractor.getSampleTrackIndex();
68  *   long presentationTimeUs = extractor.getSampleTime();
69  *   ...
70  *   extractor.advance();
71  * }
72  *
73  * extractor.release();
74  * extractor = null;
75  * </pre>
76  *
77  * <p>This class requires the {@link android.Manifest.permission#INTERNET} permission
78  * when used with network-based content.
79  */
80 final public class MediaExtractor {
MediaExtractor()81     public MediaExtractor() {
82         native_setup();
83     }
84 
85     /**
86      * Sets the data source (MediaDataSource) to use.
87      *
88      * @param dataSource the MediaDataSource for the media you want to extract from
89      *
90      * @throws IllegalArgumentException if dataSource is invalid.
91      */
setDataSource(@onNull MediaDataSource dataSource)92     public native final void setDataSource(@NonNull MediaDataSource dataSource)
93         throws IOException;
94 
95     /**
96      * Sets the data source as a content Uri.
97      *
98      * @param context the Context to use when resolving the Uri
99      * @param uri the Content URI of the data you want to extract from.
100      *
101      * <p>When <code>uri</code> refers to a network file the
102      * {@link android.Manifest.permission#INTERNET} permission is required.
103      *
104      * @param headers the headers to be sent together with the request for the data.
105      *        This can be {@code null} if no specific headers are to be sent with the
106      *        request.
107      */
setDataSource( @onNull Context context, @NonNull Uri uri, @Nullable Map<String, String> headers)108     public final void setDataSource(
109             @NonNull Context context, @NonNull Uri uri, @Nullable Map<String, String> headers)
110         throws IOException {
111         String scheme = uri.getScheme();
112         if (scheme == null || scheme.equals("file")) {
113             setDataSource(uri.getPath());
114             return;
115         }
116 
117         AssetFileDescriptor fd = null;
118         try {
119             ContentResolver resolver = context.getContentResolver();
120             fd = resolver.openAssetFileDescriptor(uri, "r");
121             if (fd == null) {
122                 return;
123             }
124             // Note: using getDeclaredLength so that our behavior is the same
125             // as previous versions when the content provider is returning
126             // a full file.
127             if (fd.getDeclaredLength() < 0) {
128                 setDataSource(fd.getFileDescriptor());
129             } else {
130                 setDataSource(
131                         fd.getFileDescriptor(),
132                         fd.getStartOffset(),
133                         fd.getDeclaredLength());
134             }
135             return;
136         } catch (SecurityException ex) {
137         } catch (IOException ex) {
138         } finally {
139             if (fd != null) {
140                 fd.close();
141             }
142         }
143 
144         setDataSource(uri.toString(), headers);
145     }
146 
147     /**
148      * Sets the data source (file-path or http URL) to use.
149      *
150      * @param path the path of the file, or the http URL
151      *
152      * <p>When <code>path</code> refers to a network file the
153      * {@link android.Manifest.permission#INTERNET} permission is required.
154      *
155      * @param headers the headers associated with the http request for the stream you want to play.
156      *        This can be {@code null} if no specific headers are to be sent with the
157      *        request.
158      */
setDataSource(@onNull String path, @Nullable Map<String, String> headers)159     public final void setDataSource(@NonNull String path, @Nullable Map<String, String> headers)
160         throws IOException {
161         String[] keys = null;
162         String[] values = null;
163 
164         if (headers != null) {
165             keys = new String[headers.size()];
166             values = new String[headers.size()];
167 
168             int i = 0;
169             for (Map.Entry<String, String> entry: headers.entrySet()) {
170                 keys[i] = entry.getKey();
171                 values[i] = entry.getValue();
172                 ++i;
173             }
174         }
175 
176         nativeSetDataSource(
177                 MediaHTTPService.createHttpServiceBinderIfNecessary(path),
178                 path,
179                 keys,
180                 values);
181     }
182 
nativeSetDataSource( @onNull IBinder httpServiceBinder, @NonNull String path, @Nullable String[] keys, @Nullable String[] values)183     private native final void nativeSetDataSource(
184             @NonNull IBinder httpServiceBinder,
185             @NonNull String path,
186             @Nullable String[] keys,
187             @Nullable String[] values) throws IOException;
188 
189     /**
190      * Sets the data source (file-path or http URL) to use.
191      *
192      * @param path the path of the file, or the http URL of the stream
193      *
194      * <p>When <code>path</code> refers to a local file, the file may actually be opened by a
195      * process other than the calling application.  This implies that the pathname
196      * should be an absolute path (as any other process runs with unspecified current working
197      * directory), and that the pathname should reference a world-readable file.
198      * As an alternative, the application could first open the file for reading,
199      * and then use the file descriptor form {@link #setDataSource(FileDescriptor)}.
200      *
201      * <p>When <code>path</code> refers to a network file the
202      * {@link android.Manifest.permission#INTERNET} permission is required.
203      */
setDataSource(@onNull String path)204     public final void setDataSource(@NonNull String path) throws IOException {
205         nativeSetDataSource(
206                 MediaHTTPService.createHttpServiceBinderIfNecessary(path),
207                 path,
208                 null,
209                 null);
210     }
211 
212     /**
213      * Sets the data source (AssetFileDescriptor) to use. It is the caller's
214      * responsibility to close the file descriptor. It is safe to do so as soon
215      * as this call returns.
216      *
217      * @param afd the AssetFileDescriptor for the file you want to extract from.
218      */
setDataSource(@onNull AssetFileDescriptor afd)219     public final void setDataSource(@NonNull AssetFileDescriptor afd)
220             throws IOException, IllegalArgumentException, IllegalStateException {
221         Preconditions.checkNotNull(afd);
222         // Note: using getDeclaredLength so that our behavior is the same
223         // as previous versions when the content provider is returning
224         // a full file.
225         if (afd.getDeclaredLength() < 0) {
226             setDataSource(afd.getFileDescriptor());
227         } else {
228             setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength());
229         }
230     }
231 
232     /**
233      * Sets the data source (FileDescriptor) to use. It is the caller's responsibility
234      * to close the file descriptor. It is safe to do so as soon as this call returns.
235      *
236      * @param fd the FileDescriptor for the file you want to extract from.
237      */
setDataSource(@onNull FileDescriptor fd)238     public final void setDataSource(@NonNull FileDescriptor fd) throws IOException {
239         setDataSource(fd, 0, 0x7ffffffffffffffL);
240     }
241 
242     /**
243      * Sets the data source (FileDescriptor) to use.  The FileDescriptor must be
244      * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
245      * to close the file descriptor. It is safe to do so as soon as this call returns.
246      *
247      * @param fd the FileDescriptor for the file you want to extract from.
248      * @param offset the offset into the file where the data to be extracted starts, in bytes
249      * @param length the length in bytes of the data to be extracted
250      */
setDataSource( @onNull FileDescriptor fd, long offset, long length)251     public native final void setDataSource(
252             @NonNull FileDescriptor fd, long offset, long length) throws IOException;
253 
254     /**
255      * Sets the MediaCas instance to use. This should be called after a
256      * successful setDataSource() if at least one track reports mime type
257      * of {@link android.media.MediaFormat#MIMETYPE_AUDIO_SCRAMBLED}
258      * or {@link android.media.MediaFormat#MIMETYPE_VIDEO_SCRAMBLED}.
259      * Stream parsing will not proceed until a valid MediaCas object
260      * is provided.
261      *
262      * @param mediaCas the MediaCas object to use.
263      */
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&hellip;: for listed containers<br>
588      *      MPEG4, AAC&hellip;: 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      *  Return Metrics data about the current media container.
776      *
777      * @return a {@link PersistableBundle} containing the set of attributes and values
778      * available for the media container being handled by this instance
779      * of MediaExtractor.
780      * The attributes are descibed in {@link MetricsConstants}.
781      *
782      *  Additional vendor-specific fields may also be present in
783      *  the return value.
784      */
785 
786     public PersistableBundle getMetrics() {
787         PersistableBundle bundle = native_getMetrics();
788         return bundle;
789     }
790 
791     private native PersistableBundle native_getMetrics();
792 
793     private static native final void native_init();
794     private native final void native_setup();
795     private native final void native_finalize();
796 
797     static {
798         System.loadLibrary("media_jni");
799         native_init();
800     }
801 
802     private MediaCas mMediaCas;
803 
804     private long mNativeContext;
805 
806     public final static class MetricsConstants
807     {
808         private MetricsConstants() {}
809 
810         /**
811          * Key to extract the container format
812          * from the {@link MediaExtractor#getMetrics} return value.
813          * The value is a String.
814          */
815         public static final String FORMAT = "android.media.mediaextractor.fmt";
816 
817         /**
818          * Key to extract the container MIME type
819          * from the {@link MediaExtractor#getMetrics} return value.
820          * The value is a String.
821          */
822         public static final String MIME_TYPE = "android.media.mediaextractor.mime";
823 
824         /**
825          * Key to extract the number of tracks in the container
826          * from the {@link MediaExtractor#getMetrics} return value.
827          * The value is an integer.
828          */
829         public static final String TRACKS = "android.media.mediaextractor.ntrk";
830 
831     }
832 
833 }
834