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