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