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.AudioPresentation;
26import android.media.MediaCodec;
27import android.media.MediaFormat;
28import android.media.MediaHTTPService;
29import android.net.Uri;
30import android.os.IBinder;
31import android.os.IHwBinder;
32import android.os.PersistableBundle;
33
34import com.android.internal.util.Preconditions;
35
36import java.io.FileDescriptor;
37import java.io.IOException;
38import java.lang.annotation.Retention;
39import java.lang.annotation.RetentionPolicy;
40import java.nio.ByteBuffer;
41import java.nio.ByteOrder;
42import java.util.ArrayList;
43import java.util.HashMap;
44import java.util.List;
45import java.util.Map;
46import 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 */
78final 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