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