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