MediaMetadataRetriever.java revision 425916e4c5305b9bfff5b5d60d203363afcb7b89
1/*
2 * Copyright (C) 2008 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.graphics.Bitmap;
23import android.net.Uri;
24
25import java.io.FileDescriptor;
26import java.io.FileInputStream;
27import java.io.FileNotFoundException;
28import java.io.IOException;
29
30import java.util.Map;
31
32/**
33 * MediaMetadataRetriever class provides a unified interface for retrieving
34 * frame and meta data from an input media file.
35 */
36public class MediaMetadataRetriever
37{
38    static {
39        System.loadLibrary("media_jni");
40        native_init();
41    }
42
43    // The field below is accessed by native methods
44    @SuppressWarnings("unused")
45    private int mNativeContext;
46
47    private static final int EMBEDDED_PICTURE_TYPE_ANY = 0xFFFF;
48
49    public MediaMetadataRetriever() {
50        native_setup();
51    }
52
53    /**
54     * Sets the data source (file pathname) to use. Call this
55     * method before the rest of the methods in this class. This method may be
56     * time-consuming.
57     *
58     * @param path The path of the input media file.
59     * @throws IllegalArgumentException If the path is invalid.
60     */
61    public void setDataSource(String path) throws IllegalArgumentException {
62        if (path == null) {
63            throw new IllegalArgumentException();
64        }
65
66        FileInputStream is = null;
67        try {
68            is = new FileInputStream(path);
69            FileDescriptor fd = is.getFD();
70            setDataSource(fd, 0, 0x7ffffffffffffffL);
71        } catch (FileNotFoundException fileEx) {
72            throw new IllegalArgumentException();
73        } catch (IOException ioEx) {
74            throw new IllegalArgumentException();
75        }
76
77        try {
78            if (is != null) {
79                is.close();
80            }
81        } catch (Exception e) {}
82    }
83
84    /**
85     * Sets the data source (URI) to use. Call this
86     * method before the rest of the methods in this class. This method may be
87     * time-consuming.
88     *
89     * @param uri The URI of the input media.
90     * @param headers the headers to be sent together with the request for the data
91     * @throws IllegalArgumentException If the URI is invalid.
92     */
93    public void setDataSource(String uri,  Map<String, String> headers)
94            throws IllegalArgumentException {
95        int i = 0;
96        String[] keys = new String[headers.size()];
97        String[] values = new String[headers.size()];
98        for (Map.Entry<String, String> entry: headers.entrySet()) {
99            keys[i] = entry.getKey();
100            values[i] = entry.getValue();
101            ++i;
102        }
103        _setDataSource(uri, keys, values);
104    }
105
106    private native void _setDataSource(
107        String uri, String[] keys, String[] values)
108        throws IllegalArgumentException;
109
110    /**
111     * Sets the data source (FileDescriptor) to use.  It is the caller's
112     * responsibility to close the file descriptor. It is safe to do so as soon
113     * as this call returns. Call this method before the rest of the methods in
114     * this class. This method may be time-consuming.
115     *
116     * @param fd the FileDescriptor for the file you want to play
117     * @param offset the offset into the file where the data to be played starts,
118     * in bytes. It must be non-negative
119     * @param length the length in bytes of the data to be played. It must be
120     * non-negative.
121     * @throws IllegalArgumentException if the arguments are invalid
122     */
123    public native void setDataSource(FileDescriptor fd, long offset, long length)
124            throws IllegalArgumentException;
125
126    /**
127     * Sets the data source (FileDescriptor) to use. It is the caller's
128     * responsibility to close the file descriptor. It is safe to do so as soon
129     * as this call returns. Call this method before the rest of the methods in
130     * this class. This method may be time-consuming.
131     *
132     * @param fd the FileDescriptor for the file you want to play
133     * @throws IllegalArgumentException if the FileDescriptor is invalid
134     */
135    public void setDataSource(FileDescriptor fd)
136            throws IllegalArgumentException {
137        // intentionally less than LONG_MAX
138        setDataSource(fd, 0, 0x7ffffffffffffffL);
139    }
140
141    /**
142     * Sets the data source as a content Uri. Call this method before
143     * the rest of the methods in this class. This method may be time-consuming.
144     *
145     * @param context the Context to use when resolving the Uri
146     * @param uri the Content URI of the data you want to play
147     * @throws IllegalArgumentException if the Uri is invalid
148     * @throws SecurityException if the Uri cannot be used due to lack of
149     * permission.
150     */
151    public void setDataSource(Context context, Uri uri)
152        throws IllegalArgumentException, SecurityException {
153        if (uri == null) {
154            throw new IllegalArgumentException();
155        }
156
157        String scheme = uri.getScheme();
158        if(scheme == null || scheme.equals("file")) {
159            setDataSource(uri.getPath());
160            return;
161        }
162
163        AssetFileDescriptor fd = null;
164        try {
165            ContentResolver resolver = context.getContentResolver();
166            try {
167                fd = resolver.openAssetFileDescriptor(uri, "r");
168            } catch(FileNotFoundException e) {
169                throw new IllegalArgumentException();
170            }
171            if (fd == null) {
172                throw new IllegalArgumentException();
173            }
174            FileDescriptor descriptor = fd.getFileDescriptor();
175            if (!descriptor.valid()) {
176                throw new IllegalArgumentException();
177            }
178            // Note: using getDeclaredLength so that our behavior is the same
179            // as previous versions when the content provider is returning
180            // a full file.
181            if (fd.getDeclaredLength() < 0) {
182                setDataSource(descriptor);
183            } else {
184                setDataSource(descriptor, fd.getStartOffset(), fd.getDeclaredLength());
185            }
186            return;
187        } catch (SecurityException ex) {
188        } finally {
189            try {
190                if (fd != null) {
191                    fd.close();
192                }
193            } catch(IOException ioEx) {
194            }
195        }
196        setDataSource(uri.toString());
197    }
198
199    /**
200     * Call this method after setDataSource(). This method retrieves the
201     * meta data value associated with the keyCode.
202     *
203     * The keyCode currently supported is listed below as METADATA_XXX
204     * constants. With any other value, it returns a null pointer.
205     *
206     * @param keyCode One of the constants listed below at the end of the class.
207     * @return The meta data value associate with the given keyCode on success;
208     * null on failure.
209     */
210    public native String extractMetadata(int keyCode);
211
212    /**
213     * Call this method after setDataSource(). This method finds a
214     * representative frame close to the given time position by considering
215     * the given option if possible, and returns it as a bitmap. This is
216     * useful for generating a thumbnail for an input data source or just
217     * obtain and display a frame at the given time position.
218     *
219     * @param timeUs The time position where the frame will be retrieved.
220     * When retrieving the frame at the given time position, there is no
221     * guarantee that the data source has a frame located at the position.
222     * When this happens, a frame nearby will be returned. If timeUs is
223     * negative, time position and option will ignored, and any frame
224     * that the implementation considers as representative may be returned.
225     *
226     * @param option a hint on how the frame is found. Use
227     * {@link #OPTION_PREVIOUS_SYNC} if one wants to retrieve a sync frame
228     * that has a timestamp earlier than or the same as timeUs. Use
229     * {@link #OPTION_NEXT_SYNC} if one wants to retrieve a sync frame
230     * that has a timestamp later than or the same as timeUs. Use
231     * {@link #OPTION_CLOSEST_SYNC} if one wants to retrieve a sync frame
232     * that has a timestamp closest to or the same as timeUs. Use
233     * {@link #OPTION_CLOSEST} if one wants to retrieve a frame that may
234     * or may not be a sync frame but is closest to or the same as timeUs.
235     * {@link #OPTION_CLOSEST} often has larger performance overhead compared
236     * to the other options if there is no sync frame located at timeUs.
237     *
238     * @return A Bitmap containing a representative video frame, which
239     *         can be null, if such a frame cannot be retrieved.
240     */
241    public Bitmap getFrameAtTime(long timeUs, int option) {
242        if (option < OPTION_PREVIOUS_SYNC ||
243            option > OPTION_CLOSEST) {
244            throw new IllegalArgumentException("Unsupported option: " + option);
245        }
246
247        return _getFrameAtTime(timeUs, option);
248    }
249
250    /**
251     * Call this method after setDataSource(). This method finds a
252     * representative frame close to the given time position if possible,
253     * and returns it as a bitmap. This is useful for generating a thumbnail
254     * for an input data source. Call this method if one does not care
255     * how the frame is found as long as it is close to the given time;
256     * otherwise, please call {@link #getFrameAtTime(long, int)}.
257     *
258     * @param timeUs The time position where the frame will be retrieved.
259     * When retrieving the frame at the given time position, there is no
260     * guarentee that the data source has a frame located at the position.
261     * When this happens, a frame nearby will be returned. If timeUs is
262     * negative, time position and option will ignored, and any frame
263     * that the implementation considers as representative may be returned.
264     *
265     * @return A Bitmap containing a representative video frame, which
266     *         can be null, if such a frame cannot be retrieved.
267     *
268     * @see #getFrameAtTime(long, int)
269     */
270    public Bitmap getFrameAtTime(long timeUs) {
271        return getFrameAtTime(timeUs, OPTION_CLOSEST_SYNC);
272    }
273
274    /**
275     * Call this method after setDataSource(). This method finds a
276     * representative frame at any time position if possible,
277     * and returns it as a bitmap. This is useful for generating a thumbnail
278     * for an input data source. Call this method if one does not
279     * care about where the frame is located; otherwise, please call
280     * {@link #getFrameAtTime(long)} or {@link #getFrameAtTime(long, int)}
281     *
282     * @return A Bitmap containing a representative video frame, which
283     *         can be null, if such a frame cannot be retrieved.
284     *
285     * @see #getFrameAtTime(long)
286     * @see #getFrameAtTime(long, int)
287     */
288    public Bitmap getFrameAtTime() {
289        return getFrameAtTime(-1, OPTION_CLOSEST_SYNC);
290    }
291
292    private native Bitmap _getFrameAtTime(long timeUs, int option);
293
294
295    /**
296     * Call this method after setDataSource(). This method finds the optional
297     * graphic or album/cover art associated associated with the data source. If
298     * there are more than one pictures, (any) one of them is returned.
299     *
300     * @return null if no such graphic is found.
301     */
302    public byte[] getEmbeddedPicture() {
303        return getEmbeddedPicture(EMBEDDED_PICTURE_TYPE_ANY);
304    }
305
306    private native byte[] getEmbeddedPicture(int pictureType);
307
308    /**
309     * Call it when one is done with the object. This method releases the memory
310     * allocated internally.
311     */
312    public native void release();
313    private native void native_setup();
314    private static native void native_init();
315
316    private native final void native_finalize();
317
318    @Override
319    protected void finalize() throws Throwable {
320        try {
321            native_finalize();
322        } finally {
323            super.finalize();
324        }
325    }
326
327    /**
328     * Option used in method {@link #getFrameAtTime(long, int)} to get a
329     * frame at a specified location.
330     *
331     * @see #getFrameAtTime(long, int)
332     */
333    /* Do not change these option values without updating their counterparts
334     * in include/media/stagefright/MediaSource.h!
335     */
336    /**
337     * This option is used with {@link #getFrameAtTime(long, int)} to retrieve
338     * a sync (or key) frame associated with a data source that is located
339     * right before or at the given time.
340     *
341     * @see #getFrameAtTime(long, int)
342     */
343    public static final int OPTION_PREVIOUS_SYNC    = 0x00;
344    /**
345     * This option is used with {@link #getFrameAtTime(long, int)} to retrieve
346     * a sync (or key) frame associated with a data source that is located
347     * right after or at the given time.
348     *
349     * @see #getFrameAtTime(long, int)
350     */
351    public static final int OPTION_NEXT_SYNC        = 0x01;
352    /**
353     * This option is used with {@link #getFrameAtTime(long, int)} to retrieve
354     * a sync (or key) frame associated with a data source that is located
355     * closest to (in time) or at the given time.
356     *
357     * @see #getFrameAtTime(long, int)
358     */
359    public static final int OPTION_CLOSEST_SYNC     = 0x02;
360    /**
361     * This option is used with {@link #getFrameAtTime(long, int)} to retrieve
362     * a frame (not necessarily a key frame) associated with a data source that
363     * is located closest to or at the given time.
364     *
365     * @see #getFrameAtTime(long, int)
366     */
367    public static final int OPTION_CLOSEST          = 0x03;
368
369    /*
370     * Do not change these metadata key values without updating their
371     * counterparts in include/media/mediametadataretriever.h!
372     */
373    /**
374     * The metadata key to retrieve the numeric string describing the
375     * order of the audio data source on its original recording.
376     */
377    public static final int METADATA_KEY_CD_TRACK_NUMBER = 0;
378    /**
379     * The metadata key to retrieve the information about the album title
380     * of the data source.
381     */
382    public static final int METADATA_KEY_ALBUM           = 1;
383    /**
384     * The metadata key to retrieve the information about the artist of
385     * the data source.
386     */
387    public static final int METADATA_KEY_ARTIST          = 2;
388    /**
389     * The metadata key to retrieve the information about the author of
390     * the data source.
391     */
392    public static final int METADATA_KEY_AUTHOR          = 3;
393    /**
394     * The metadata key to retrieve the information about the composer of
395     * the data source.
396     */
397    public static final int METADATA_KEY_COMPOSER        = 4;
398    /**
399     * The metadata key to retrieve the date when the data source was created
400     * or modified.
401     */
402    public static final int METADATA_KEY_DATE            = 5;
403    /**
404     * The metadata key to retrieve the content type or genre of the data
405     * source.
406     */
407    public static final int METADATA_KEY_GENRE           = 6;
408    /**
409     * The metadata key to retrieve the data source title.
410     */
411    public static final int METADATA_KEY_TITLE           = 7;
412    /**
413     * The metadata key to retrieve the year when the data source was created
414     * or modified.
415     */
416    public static final int METADATA_KEY_YEAR            = 8;
417    /**
418     * The metadata key to retrieve the playback duration of the data source.
419     */
420    public static final int METADATA_KEY_DURATION        = 9;
421    /**
422     * The metadata key to retrieve the number of tracks, such as audio, video,
423     * text, in the data source, such as a mp4 or 3gpp file.
424     */
425    public static final int METADATA_KEY_NUM_TRACKS      = 10;
426    /**
427     * The metadata key to retrieve the information of the writer (such as
428     * lyricist) of the data source.
429     */
430    public static final int METADATA_KEY_WRITER          = 11;
431    /**
432     * The metadata key to retrieve the mime type of the data source. Some
433     * example mime types include: "video/mp4", "audio/mp4", "audio/amr-wb",
434     * etc.
435     */
436    public static final int METADATA_KEY_MIMETYPE        = 12;
437    /**
438     * The metadata key to retrieve the information about the performers or
439     * artist associated with the data source.
440     */
441    public static final int METADATA_KEY_ALBUMARTIST     = 13;
442    /**
443     * The metadata key to retrieve the numberic string that describes which
444     * part of a set the audio data source comes from.
445     */
446    public static final int METADATA_KEY_DISC_NUMBER     = 14;
447    /**
448     * The metadata key to retrieve the music album compilation status.
449     */
450    public static final int METADATA_KEY_COMPILATION     = 15;
451    /**
452     * If this key exists the media contains audio content.
453     */
454    public static final int METADATA_KEY_HAS_AUDIO       = 16;
455    /**
456     * If this key exists the media contains video content.
457     */
458    public static final int METADATA_KEY_HAS_VIDEO       = 17;
459    /**
460     * If the media contains video, this key retrieves its width.
461     */
462    public static final int METADATA_KEY_VIDEO_WIDTH     = 18;
463    /**
464     * If the media contains video, this key retrieves its height.
465     */
466    public static final int METADATA_KEY_VIDEO_HEIGHT    = 19;
467    /**
468     * This key retrieves the average bitrate (in bits/sec), if available.
469     */
470    public static final int METADATA_KEY_BITRATE         = 20;
471    /**
472     * This key retrieves the language code of text tracks, if available.
473     * If multiple text tracks present, the return value will look like:
474     * "eng:chi"
475     * @hide
476     */
477    public static final int METADATA_KEY_TIMED_TEXT_LANGUAGES      = 21;
478    /**
479     * If this key exists the media is drm-protected.
480     * @hide
481     */
482    public static final int METADATA_KEY_IS_DRM          = 22;
483    /**
484     * This key retrieves the location information, if available.
485     * The location should be specified according to ISO-6709 standard, under
486     * a mp4/3gp box "@xyz". Location with longitude of -90 degrees and latitude
487     * of 180 degrees will be retrieved as "-90.0000+180.0000", for instance.
488     */
489    public static final int METADATA_KEY_LOCATION        = 23;
490    /**
491     * This key retrieves the video rotation angle in degrees, if available.
492     * The video rotation angle may be 0, 90, 180, or 270 degrees.
493     */
494    public static final int METADATA_KEY_VIDEO_ROTATION = 24;
495    // Add more here...
496}
497