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