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