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