MediaExtractor.java revision f2855b3df5994e165b29025c4c49d8e7d634c034
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.content.ContentResolver;
20import android.content.Context;
21import android.content.res.AssetFileDescriptor;
22import android.media.MediaCodec;
23import android.net.Uri;
24import java.io.FileDescriptor;
25import java.io.IOException;
26import java.nio.ByteBuffer;
27import java.util.Map;
28
29/**
30 * MediaExtractor facilitates extraction of demuxed, typically encoded,  media data
31 * from a data source.
32 * <p>It is generally used like this:
33 * <pre>
34 * MediaExtractor extractor = new MediaExtractor();
35 * extractor.setDataSource(...);
36 * int numTracks = extractor.countTracks();
37 * for (int i = 0; i &lt; numTracks; ++i) {
38 *   Map%lt;String, Object&gt; format = extractor.getTrackFormat(i);
39 *   String mime = (String)format.get("mime");
40 *   if (weAreInterestedInThisTrack) {
41 *     extractor.selectTrack(i);
42 *   }
43 * }
44 * ByteBuffer inputBuffer = ByteBuffer.allocate(...)
45 * while (extractor.readSampleData(inputBuffer, ...) &gt;= 0) {
46 *   int trackIndex = extractor.getTrackIndex();
47 *   long presentationTimeUs = extractor.getSampleTime();
48 *   ...
49 *   extractor.advance();
50 * }
51 *
52 * extractor.release();
53 * extractor = null;
54 * </pre>
55*/
56final public class MediaExtractor {
57    public MediaExtractor() {
58        native_setup();
59    }
60
61    /**
62     * Sets the data source as a content Uri.
63     *
64     * @param context the Context to use when resolving the Uri
65     * @param uri the Content URI of the data you want to extract from.
66     * @param headers the headers to be sent together with the request for the data
67     */
68    public final void setDataSource(
69            Context context, Uri uri, Map<String, String> headers)
70        throws IOException {
71        String scheme = uri.getScheme();
72        if(scheme == null || scheme.equals("file")) {
73            setDataSource(uri.getPath());
74            return;
75        }
76
77        AssetFileDescriptor fd = null;
78        try {
79            ContentResolver resolver = context.getContentResolver();
80            fd = resolver.openAssetFileDescriptor(uri, "r");
81            if (fd == null) {
82                return;
83            }
84            // Note: using getDeclaredLength so that our behavior is the same
85            // as previous versions when the content provider is returning
86            // a full file.
87            if (fd.getDeclaredLength() < 0) {
88                setDataSource(fd.getFileDescriptor());
89            } else {
90                setDataSource(
91                        fd.getFileDescriptor(),
92                        fd.getStartOffset(),
93                        fd.getDeclaredLength());
94            }
95            return;
96        } catch (SecurityException ex) {
97        } catch (IOException ex) {
98        } finally {
99            if (fd != null) {
100                fd.close();
101            }
102        }
103
104        setDataSource(uri.toString(), headers);
105    }
106
107    /**
108     * Sets the data source (file-path or http URL) to use.
109     *
110     * @param path the path of the file, or the http URL
111     * @param headers the headers associated with the http request for the stream you want to play
112     */
113    public final void setDataSource(String path, Map<String, String> headers) {
114        String[] keys = null;
115        String[] values = null;
116
117        if (headers != null) {
118            keys = new String[headers.size()];
119            values = new String[headers.size()];
120
121            int i = 0;
122            for (Map.Entry<String, String> entry: headers.entrySet()) {
123                keys[i] = entry.getKey();
124                values[i] = entry.getValue();
125                ++i;
126            }
127        }
128        setDataSource(path, keys, values);
129    }
130
131    private native final void setDataSource(
132            String path, String[] keys, String[] values);
133
134    /**
135     * Sets the data source (file-path or http URL) to use.
136     *
137     * @param path the path of the file, or the http URL of the stream
138     *
139     * <p>When <code>path</code> refers to a local file, the file may actually be opened by a
140     * process other than the calling application.  This implies that the pathname
141     * should be an absolute path (as any other process runs with unspecified current working
142     * directory), and that the pathname should reference a world-readable file.
143     * As an alternative, the application could first open the file for reading,
144     * and then use the file descriptor form {@link #setDataSource(FileDescriptor)}.
145     */
146    public final void setDataSource(String path) {
147        setDataSource(path, null, null);
148    }
149
150    /**
151     * Sets the data source (FileDescriptor) to use. It is the caller's responsibility
152     * to close the file descriptor. It is safe to do so as soon as this call returns.
153     *
154     * @param fd the FileDescriptor for the file you want to extract from.
155     */
156    public final void setDataSource(FileDescriptor fd) {
157        setDataSource(fd, 0, 0x7ffffffffffffffL);
158    }
159
160    /**
161     * Sets the data source (FileDescriptor) to use.  The FileDescriptor must be
162     * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
163     * to close the file descriptor. It is safe to do so as soon as this call returns.
164     *
165     * @param fd the FileDescriptor for the file you want to extract from.
166     * @param offset the offset into the file where the data to be extracted starts, in bytes
167     * @param length the length in bytes of the data to be extracted
168     */
169    public native final void setDataSource(
170            FileDescriptor fd, long offset, long length);
171
172    @Override
173    protected void finalize() {
174        native_finalize();
175    }
176
177    /** Make sure you call this when you're done to free up any resources
178     *  instead of relying on the garbage collector to do this for you at
179     *   some point in the future.
180    */
181    public native final void release();
182
183    /** Count the number of tracks found in the data source.
184     */
185    public native int countTracks();
186
187    /** Get the track format at the specified index.
188      * More detail on the representation can be found at {@link android.media.MediaCodec}
189    */
190    public native Map<String, Object> getTrackFormat(int index);
191
192    /** Subsequent calls to {@link #readSampleData}, {@link #getSampleTrackIndex} and
193     *  {@link #getSampleTime} only retrieve information for the subset of tracks
194     *  selected.
195     *  Selecting the same track multiple times has no effect, the track is
196     *  only selected once.
197    */
198    public native void selectTrack(int index);
199
200    /** Subsequent calls to {@link #readSampleData}, {@link #getSampleTrackIndex} and
201     *  {@link #getSampleTime} only retrieve information for the subset of tracks
202     *  selected.
203    */
204    public native void unselectTrack(int index);
205
206    /** If possible, seek to a sync sample at or before the specified time */
207    public static final int SEEK_TO_PREVIOUS_SYNC       = 0;
208    /** If possible, seek to a sync sample at or after the specified time */
209    public static final int SEEK_TO_NEXT_SYNC           = 1;
210    /** If possible, seek to the sync sample closest to the specified time */
211    public static final int SEEK_TO_CLOSEST_SYNC        = 2;
212    /** If possible, seek to a sample closest to the specified time, which may
213      * NOT be a sync sample!
214      */
215    public static final int SEEK_TO_CLOSEST             = 3;
216
217    /** All selected tracks seek near the requested time according to the
218      * specified mode.
219      */
220    public native void seekTo(long timeUs, int mode);
221
222    /** Advance to the next sample. Returns false if no more sample data
223     *  is available (end of stream).
224     */
225    public native boolean advance();
226
227    /** Retrieve the current encoded sample and store it in the byte buffer
228     *  starting at the given offset. Returns the sample size (or -1 if
229     *  no more samples are available).
230    */
231    public native int readSampleData(ByteBuffer byteBuf, int offset);
232
233    /** Returns the track index the current sample originates from (or -1
234     *  if no more samples are available)
235    */
236    public native int getSampleTrackIndex();
237
238    /** Returns the current sample's presentation time in microseconds.
239     *  or -1 if no more samples are available.
240    */
241    public native long getSampleTime();
242
243    // Keep these in sync with their equivalents in NuMediaExtractor.h
244    /** The sample is a sync sample */
245    public static final int SAMPLE_FLAG_SYNC      = 1;
246
247    /** The sample is (at least partially) encrypted, see also the documentation
248     *  for {@link android.media.MediaCodec#queueSecureInputBuffer}
249    */
250    public static final int SAMPLE_FLAG_ENCRYPTED = 2;
251
252    /** Returns the current sample's flags. */
253    public native int getSampleFlags();
254
255    /** If the sample flags indicate that the current sample is at least
256     *  partially encrypted, this call returns relevant information about
257     *  the structure of the sample data required for decryption.
258     *  @param info The android.media.MediaCodec.CryptoInfo structure
259     *              to be filled in.
260     *  @return true iff the sample flags contain {@link #SAMPLE_FLAG_ENCRYPTED}
261    */
262    public native boolean getSampleCryptoInfo(MediaCodec.CryptoInfo info);
263
264    /** Returns an estimate of how much data is presently cached in memory
265        expressed in microseconds. Returns -1 if that information is unavailable
266        or not applicable (no cache).
267     */
268    public native long getCachedDuration();
269
270    /** Returns true iff we are caching data and the cache has reached the
271     *  end of the data stream (for now, a future seek may of course restart
272     *  the fetching of data).
273     *  This API only returns a meaningful result if {link #getCachedDuration}
274     *  indicates the presence of a cache, i.e. does NOT return -1.
275    */
276    public native boolean hasCacheReachedEndOfStream();
277
278    private static native final void native_init();
279    private native final void native_setup();
280    private native final void native_finalize();
281
282    static {
283        System.loadLibrary("media_jni");
284        native_init();
285    }
286
287    private int mNativeContext;
288}
289