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.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.HashMap;
     44 import java.util.List;
     45 import java.util.Map;
     46 import java.util.UUID;
     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 &lt; 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, ...) &gt;= 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 final public class 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      */
     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      */
    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      */
    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 
    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      */
    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      */
    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      */
    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      */
    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
    254      * successful setDataSource() if at least one track reports mime type
    255      * of {@link android.media.MediaFormat#MIMETYPE_AUDIO_SCRAMBLED}
    256      * or {@link android.media.MediaFormat#MIMETYPE_VIDEO_SCRAMBLED}.
    257      * Stream parsing will not proceed until a valid MediaCas object
    258      * is provided.
    259      *
    260      * @param mediaCas the MediaCas object to use.
    261      */
    262     public final void setMediaCas(@NonNull MediaCas mediaCas) {
    263         mMediaCas = mediaCas;
    264         nativeSetMediaCas(mediaCas.getBinder());
    265     }
    266 
    267     private native final void nativeSetMediaCas(@NonNull IHwBinder casBinder);
    268 
    269     /**
    270      * Describes the conditional access system used to scramble a track.
    271      */
    272     public static final class CasInfo {
    273         private final int mSystemId;
    274         private final MediaCas.Session mSession;
    275 
    276         CasInfo(int systemId, @Nullable MediaCas.Session session) {
    277             mSystemId = systemId;
    278             mSession = session;
    279         }
    280 
    281         /**
    282          * Retrieves the system id of the conditional access system.
    283          *
    284          * @return CA system id of the CAS used to scramble the track.
    285          */
    286         public int getSystemId() {
    287             return mSystemId;
    288         }
    289 
    290         /**
    291          * Retrieves the {@link MediaCas.Session} associated with a track. The
    292          * session is needed to initialize a descrambler in order to decode the
    293          * scrambled track.
    294          * <p>
    295          * @see MediaDescrambler#setMediaCasSession
    296          * <p>
    297          * @return a {@link MediaCas.Session} object associated with a track.
    298          */
    299         public MediaCas.Session getSession() {
    300             return mSession;
    301         }
    302     }
    303 
    304     private ArrayList<Byte> toByteArray(@NonNull byte[] data) {
    305         ArrayList<Byte> byteArray = new ArrayList<Byte>(data.length);
    306         for (int i = 0; i < data.length; i++) {
    307             byteArray.add(i, Byte.valueOf(data[i]));
    308         }
    309         return byteArray;
    310     }
    311 
    312     /**
    313      * Retrieves the information about the conditional access system used to scramble
    314      * a track.
    315      *
    316      * @param index of the track.
    317      * @return an {@link CasInfo} object describing the conditional access system.
    318      */
    319     public CasInfo getCasInfo(int index) {
    320         Map<String, Object> formatMap = getTrackFormatNative(index);
    321         if (formatMap.containsKey(MediaFormat.KEY_CA_SYSTEM_ID)) {
    322             int systemId = ((Integer)formatMap.get(MediaFormat.KEY_CA_SYSTEM_ID)).intValue();
    323             MediaCas.Session session = null;
    324             if (mMediaCas != null && formatMap.containsKey(MediaFormat.KEY_CA_SESSION_ID)) {
    325                 ByteBuffer buf = (ByteBuffer) formatMap.get(MediaFormat.KEY_CA_SESSION_ID);
    326                 buf.rewind();
    327                 final byte[] sessionId = new byte[buf.remaining()];
    328                 buf.get(sessionId);
    329                 session = mMediaCas.createFromSessionId(toByteArray(sessionId));
    330             }
    331             return new CasInfo(systemId, session);
    332         }
    333         return null;
    334     }
    335 
    336     @Override
    337     protected void finalize() {
    338         native_finalize();
    339     }
    340 
    341     /**
    342      * Make sure you call this when you're done to free up any resources
    343      * instead of relying on the garbage collector to do this for you at
    344      * some point in the future.
    345      */
    346     public native final void release();
    347 
    348     /**
    349      * Count the number of tracks found in the data source.
    350      */
    351     public native final int getTrackCount();
    352 
    353     /**
    354      * Extract DRM initialization data if it exists
    355      *
    356      * @return DRM initialization data in the content, or {@code null}
    357      * if no recognizable DRM format is found;
    358      * @see DrmInitData
    359      */
    360     public DrmInitData getDrmInitData() {
    361         Map<String, Object> formatMap = getFileFormatNative();
    362         if (formatMap == null) {
    363             return null;
    364         }
    365         if (formatMap.containsKey("pssh")) {
    366             Map<UUID, byte[]> psshMap = getPsshInfo();
    367             final Map<UUID, DrmInitData.SchemeInitData> initDataMap =
    368                 new HashMap<UUID, DrmInitData.SchemeInitData>();
    369             for (Map.Entry<UUID, byte[]> e: psshMap.entrySet()) {
    370                 UUID uuid = e.getKey();
    371                 byte[] data = e.getValue();
    372                 initDataMap.put(uuid, new DrmInitData.SchemeInitData("cenc", data));
    373             }
    374             return new DrmInitData() {
    375                 public SchemeInitData get(UUID schemeUuid) {
    376                     return initDataMap.get(schemeUuid);
    377                 }
    378             };
    379         } else {
    380             int numTracks = getTrackCount();
    381             for (int i = 0; i < numTracks; ++i) {
    382                 Map<String, Object> trackFormatMap = getTrackFormatNative(i);
    383                 if (!trackFormatMap.containsKey("crypto-key")) {
    384                     continue;
    385                 }
    386                 ByteBuffer buf = (ByteBuffer) trackFormatMap.get("crypto-key");
    387                 buf.rewind();
    388                 final byte[] data = new byte[buf.remaining()];
    389                 buf.get(data);
    390                 return new DrmInitData() {
    391                     public SchemeInitData get(UUID schemeUuid) {
    392                         return new DrmInitData.SchemeInitData("webm", data);
    393                     }
    394                 };
    395             }
    396         }
    397         return null;
    398     }
    399 
    400     /**
    401      * Get the list of available audio presentations for the track.
    402      * @param trackIndex index of the track.
    403      * @return a list of available audio presentations for a given valid audio track index.
    404      * The list will be empty if the source does not contain any audio presentations.
    405      */
    406     @NonNull
    407     public List<AudioPresentation> getAudioPresentations(int trackIndex) {
    408         return new ArrayList<AudioPresentation>();
    409     }
    410 
    411     /**
    412      * Get the PSSH info if present.
    413      * @return a map of uuid-to-bytes, with the uuid specifying
    414      * the crypto scheme, and the bytes being the data specific to that scheme.
    415      * This can be {@code null} if the source does not contain PSSH info.
    416      */
    417     @Nullable
    418     public Map<UUID, byte[]> getPsshInfo() {
    419         Map<UUID, byte[]> psshMap = null;
    420         Map<String, Object> formatMap = getFileFormatNative();
    421         if (formatMap != null && formatMap.containsKey("pssh")) {
    422             ByteBuffer rawpssh = (ByteBuffer) formatMap.get("pssh");
    423             rawpssh.order(ByteOrder.nativeOrder());
    424             rawpssh.rewind();
    425             formatMap.remove("pssh");
    426             // parse the flat pssh bytebuffer into something more manageable
    427             psshMap = new HashMap<UUID, byte[]>();
    428             while (rawpssh.remaining() > 0) {
    429                 rawpssh.order(ByteOrder.BIG_ENDIAN);
    430                 long msb = rawpssh.getLong();
    431                 long lsb = rawpssh.getLong();
    432                 UUID uuid = new UUID(msb, lsb);
    433                 rawpssh.order(ByteOrder.nativeOrder());
    434                 int datalen = rawpssh.getInt();
    435                 byte [] psshdata = new byte[datalen];
    436                 rawpssh.get(psshdata);
    437                 psshMap.put(uuid, psshdata);
    438             }
    439         }
    440         return psshMap;
    441     }
    442 
    443     @NonNull
    444     private native Map<String, Object> getFileFormatNative();
    445 
    446     /**
    447      * Get the track format at the specified index.
    448      *
    449      * More detail on the representation can be found at {@link android.media.MediaCodec}
    450      * <p>
    451      * The following table summarizes support for format keys across android releases:
    452      *
    453      * <table style="width: 0%">
    454      *  <thead>
    455      *   <tr>
    456      *    <th rowspan=2>OS Version(s)</th>
    457      *    <td colspan=3>{@code MediaFormat} keys used for</th>
    458      *   </tr><tr>
    459      *    <th>All Tracks</th>
    460      *    <th>Audio Tracks</th>
    461      *    <th>Video Tracks</th>
    462      *   </tr>
    463      *  </thead>
    464      *  <tbody>
    465      *   <tr>
    466      *    <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN}</td>
    467      *    <td rowspan=8>{@link MediaFormat#KEY_MIME},<br>
    468      *        {@link MediaFormat#KEY_DURATION},<br>
    469      *        {@link MediaFormat#KEY_MAX_INPUT_SIZE}</td>
    470      *    <td rowspan=5>{@link MediaFormat#KEY_SAMPLE_RATE},<br>
    471      *        {@link MediaFormat#KEY_CHANNEL_COUNT},<br>
    472      *        {@link MediaFormat#KEY_CHANNEL_MASK},<br>
    473      *        gapless playback information<sup>.mp3, .mp4</sup>,<br>
    474      *        {@link MediaFormat#KEY_IS_ADTS}<sup>AAC if streaming</sup>,<br>
    475      *        codec-specific data<sup>AAC, Vorbis</sup></td>
    476      *    <td rowspan=2>{@link MediaFormat#KEY_WIDTH},<br>
    477      *        {@link MediaFormat#KEY_HEIGHT},<br>
    478      *        codec-specific data<sup>AVC, MPEG4</sup></td>
    479      *   </tr><tr>
    480      *    <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}</td>
    481      *   </tr><tr>
    482      *    <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}</td>
    483      *    <td rowspan=3>as above, plus<br>
    484      *        Pixel aspect ratio information<sup>AVC, *</sup></td>
    485      *   </tr><tr>
    486      *    <td>{@link android.os.Build.VERSION_CODES#KITKAT}</td>
    487      *   </tr><tr>
    488      *    <td>{@link android.os.Build.VERSION_CODES#KITKAT_WATCH}</td>
    489      *   </tr><tr>
    490      *    <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP}</td>
    491      *    <td rowspan=2>as above, plus<br>
    492      *        {@link MediaFormat#KEY_BIT_RATE}<sup>AAC</sup>,<br>
    493      *        codec-specific data<sup>Opus</sup></td>
    494      *    <td rowspan=2>as above, plus<br>
    495      *        {@link MediaFormat#KEY_ROTATION}<sup>.mp4</sup>,<br>
    496      *        {@link MediaFormat#KEY_BIT_RATE}<sup>MPEG4</sup>,<br>
    497      *        codec-specific data<sup>HEVC</sup></td>
    498      *   </tr><tr>
    499      *    <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}</td>
    500      *   </tr><tr>
    501      *    <td>{@link android.os.Build.VERSION_CODES#M}</td>
    502      *    <td>as above, plus<br>
    503      *        gapless playback information<sup>Opus</sup></td>
    504      *    <td>as above, plus<br>
    505      *        {@link MediaFormat#KEY_FRAME_RATE} (integer)</td>
    506      *   </tr><tr>
    507      *    <td>{@link android.os.Build.VERSION_CODES#N}</td>
    508      *    <td>as above, plus<br>
    509      *        {@link MediaFormat#KEY_TRACK_ID},<br>
    510      *        <!-- {link MediaFormat#KEY_MAX_BIT_RATE}<sup>#, .mp4</sup>,<br> -->
    511      *        {@link MediaFormat#KEY_BIT_RATE}<sup>#, .mp4</sup></td>
    512      *    <td>as above, plus<br>
    513      *        {@link MediaFormat#KEY_PCM_ENCODING},<br>
    514      *        {@link MediaFormat#KEY_PROFILE}<sup>AAC</sup></td>
    515      *    <td>as above, plus<br>
    516      *        {@link MediaFormat#KEY_HDR_STATIC_INFO}<sup>#, .webm</sup>,<br>
    517      *        {@link MediaFormat#KEY_COLOR_STANDARD}<sup>#</sup>,<br>
    518      *        {@link MediaFormat#KEY_COLOR_TRANSFER}<sup>#</sup>,<br>
    519      *        {@link MediaFormat#KEY_COLOR_RANGE}<sup>#</sup>,<br>
    520      *        {@link MediaFormat#KEY_PROFILE}<sup>MPEG2, H.263, MPEG4, AVC, HEVC, VP9</sup>,<br>
    521      *        {@link MediaFormat#KEY_LEVEL}<sup>H.263, MPEG4, AVC, HEVC, VP9</sup>,<br>
    522      *        codec-specific data<sup>VP9</sup></td>
    523      *   </tr>
    524      *   <tr>
    525      *    <td colspan=4>
    526      *     <p class=note><strong>Notes:</strong><br>
    527      *      #: container-specified value only.<br>
    528      *      .mp4, .webm&hellip;: for listed containers<br>
    529      *      MPEG4, AAC&hellip;: for listed codecs
    530      *    </td>
    531      *   </tr><tr>
    532      *    <td colspan=4>
    533      *     <p class=note>Note that that level information contained in the container many times
    534      *     does not match the level of the actual bitstream. You may want to clear the level using
    535      *     {@code MediaFormat.setString(KEY_LEVEL, null)} before using the track format to find a
    536      *     decoder that can play back a particular track.
    537      *    </td>
    538      *   </tr><tr>
    539      *    <td colspan=4>
    540      *     <p class=note><strong>*Pixel (sample) aspect ratio</strong> is returned in the following
    541      *     keys. The display width can be calculated for example as:
    542      *     <p align=center>
    543      *     display-width = display-height * crop-width / crop-height * sar-width / sar-height
    544      *    </td>
    545      *   </tr><tr>
    546      *    <th>Format Key</th><th>Value Type</th><th colspan=2>Description</th>
    547      *   </tr><tr>
    548      *    <td>{@code "sar-width"}</td><td>Integer</td><td colspan=2>Pixel aspect ratio width</td>
    549      *   </tr><tr>
    550      *    <td>{@code "sar-height"}</td><td>Integer</td><td colspan=2>Pixel aspect ratio height</td>
    551      *   </tr>
    552      *  </tbody>
    553      * </table>
    554      *
    555      */
    556     @NonNull
    557     public MediaFormat getTrackFormat(int index) {
    558         return new MediaFormat(getTrackFormatNative(index));
    559     }
    560 
    561     @NonNull
    562     private native Map<String, Object> getTrackFormatNative(int index);
    563 
    564     /**
    565      * Subsequent calls to {@link #readSampleData}, {@link #getSampleTrackIndex} and
    566      * {@link #getSampleTime} only retrieve information for the subset of tracks
    567      * selected.
    568      * Selecting the same track multiple times has no effect, the track is
    569      * only selected once.
    570      */
    571     public native void selectTrack(int index);
    572 
    573     /**
    574      * Subsequent calls to {@link #readSampleData}, {@link #getSampleTrackIndex} and
    575      * {@link #getSampleTime} only retrieve information for the subset of tracks
    576      * selected.
    577      */
    578     public native void unselectTrack(int index);
    579 
    580     /**
    581      * If possible, seek to a sync sample at or before the specified time
    582      */
    583     public static final int SEEK_TO_PREVIOUS_SYNC       = 0;
    584     /**
    585      * If possible, seek to a sync sample at or after the specified time
    586      */
    587     public static final int SEEK_TO_NEXT_SYNC           = 1;
    588     /**
    589      * If possible, seek to the sync sample closest to the specified time
    590      */
    591     public static final int SEEK_TO_CLOSEST_SYNC        = 2;
    592 
    593     /** @hide */
    594     @IntDef({
    595         SEEK_TO_PREVIOUS_SYNC,
    596         SEEK_TO_NEXT_SYNC,
    597         SEEK_TO_CLOSEST_SYNC,
    598     })
    599     @Retention(RetentionPolicy.SOURCE)
    600     public @interface SeekMode {}
    601 
    602     /**
    603      * All selected tracks seek near the requested time according to the
    604      * specified mode.
    605      */
    606     public native void seekTo(long timeUs, @SeekMode int mode);
    607 
    608     /**
    609      * Advance to the next sample. Returns false if no more sample data
    610      * is available (end of stream).
    611      *
    612      * When extracting a local file, the behaviors of {@link #advance} and
    613      * {@link #readSampleData} are undefined in presence of concurrent
    614      * writes to the same local file; more specifically, end of stream
    615      * could be signalled earlier than expected.
    616      */
    617     public native boolean advance();
    618 
    619     /**
    620      * Retrieve the current encoded sample and store it in the byte buffer
    621      * starting at the given offset.
    622      * <p>
    623      * <b>Note:</b>As of API 21, on success the position and limit of
    624      * {@code byteBuf} is updated to point to the data just read.
    625      * @param byteBuf the destination byte buffer
    626      * @return the sample size (or -1 if no more samples are available).
    627      */
    628     public native int readSampleData(@NonNull ByteBuffer byteBuf, int offset);
    629 
    630     /**
    631      * Returns the track index the current sample originates from (or -1
    632      * if no more samples are available)
    633      */
    634     public native int getSampleTrackIndex();
    635 
    636     /**
    637      * Returns the current sample's presentation time in microseconds.
    638      * or -1 if no more samples are available.
    639      */
    640     public native long getSampleTime();
    641 
    642     /**
    643      * @return size of the current sample in bytes or -1 if no more
    644      * samples are available.
    645      */
    646     public native long getSampleSize();
    647 
    648     // Keep these in sync with their equivalents in NuMediaExtractor.h
    649     /**
    650      * The sample is a sync sample (or in {@link MediaCodec}'s terminology
    651      * it is a key frame.)
    652      *
    653      * @see MediaCodec#BUFFER_FLAG_KEY_FRAME
    654      */
    655     public static final int SAMPLE_FLAG_SYNC      = 1;
    656 
    657     /**
    658      * The sample is (at least partially) encrypted, see also the documentation
    659      * for {@link android.media.MediaCodec#queueSecureInputBuffer}
    660      */
    661     public static final int SAMPLE_FLAG_ENCRYPTED = 2;
    662 
    663     /**
    664      * This indicates that the buffer only contains part of a frame,
    665      * and the decoder should batch the data until a buffer without
    666      * this flag appears before decoding the frame.
    667      *
    668      * @see MediaCodec#BUFFER_FLAG_PARTIAL_FRAME
    669      */
    670     public static final int SAMPLE_FLAG_PARTIAL_FRAME = 4;
    671 
    672     /** @hide */
    673     @IntDef(
    674         flag = true,
    675         value = {
    676             SAMPLE_FLAG_SYNC,
    677             SAMPLE_FLAG_ENCRYPTED,
    678             SAMPLE_FLAG_PARTIAL_FRAME,
    679     })
    680     @Retention(RetentionPolicy.SOURCE)
    681     public @interface SampleFlag {}
    682 
    683     /**
    684      * Returns the current sample's flags.
    685      */
    686     @SampleFlag
    687     public native int getSampleFlags();
    688 
    689     /**
    690      * If the sample flags indicate that the current sample is at least
    691      * partially encrypted, this call returns relevant information about
    692      * the structure of the sample data required for decryption.
    693      * @param info The android.media.MediaCodec.CryptoInfo structure
    694      *             to be filled in.
    695      * @return true iff the sample flags contain {@link #SAMPLE_FLAG_ENCRYPTED}
    696      */
    697     public native boolean getSampleCryptoInfo(@NonNull MediaCodec.CryptoInfo info);
    698 
    699     /**
    700      * Returns an estimate of how much data is presently cached in memory
    701      * expressed in microseconds. Returns -1 if that information is unavailable
    702      * or not applicable (no cache).
    703      */
    704     public native long getCachedDuration();
    705 
    706     /**
    707      * Returns true iff we are caching data and the cache has reached the
    708      * end of the data stream (for now, a future seek may of course restart
    709      * the fetching of data).
    710      * This API only returns a meaningful result if {@link #getCachedDuration}
    711      * indicates the presence of a cache, i.e. does NOT return -1.
    712      */
    713     public native boolean hasCacheReachedEndOfStream();
    714 
    715     /**
    716      *  Return Metrics data about the current media container.
    717      *
    718      * @return a {@link PersistableBundle} containing the set of attributes and values
    719      * available for the media container being handled by this instance
    720      * of MediaExtractor.
    721      * The attributes are descibed in {@link MetricsConstants}.
    722      *
    723      *  Additional vendor-specific fields may also be present in
    724      *  the return value.
    725      */
    726 
    727     public PersistableBundle getMetrics() {
    728         PersistableBundle bundle = native_getMetrics();
    729         return bundle;
    730     }
    731 
    732     private native PersistableBundle native_getMetrics();
    733 
    734     private static native final void native_init();
    735     private native final void native_setup();
    736     private native final void native_finalize();
    737 
    738     static {
    739         System.loadLibrary("media_jni");
    740         native_init();
    741     }
    742 
    743     private MediaCas mMediaCas;
    744 
    745     private long mNativeContext;
    746 
    747     public final static class MetricsConstants
    748     {
    749         private MetricsConstants() {}
    750 
    751         /**
    752          * Key to extract the container format
    753          * from the {@link MediaExtractor#getMetrics} return value.
    754          * The value is a String.
    755          */
    756         public static final String FORMAT = "android.media.mediaextractor.fmt";
    757 
    758         /**
    759          * Key to extract the container MIME type
    760          * from the {@link MediaExtractor#getMetrics} return value.
    761          * The value is a String.
    762          */
    763         public static final String MIME_TYPE = "android.media.mediaextractor.mime";
    764 
    765         /**
    766          * Key to extract the number of tracks in the container
    767          * from the {@link MediaExtractor#getMetrics} return value.
    768          * The value is an integer.
    769          */
    770         public static final String TRACKS = "android.media.mediaextractor.ntrk";
    771 
    772     }
    773 
    774 }
    775