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 
17 package android.media;
18 
19 import android.content.ContentResolver;
20 import android.content.Context;
21 import android.content.res.AssetFileDescriptor;
22 import android.graphics.Bitmap;
23 import android.net.Uri;
24 import android.os.IBinder;
25 
26 import java.io.FileDescriptor;
27 import java.io.FileInputStream;
28 import java.io.FileNotFoundException;
29 import java.io.IOException;
30 
31 import 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  */
37 public class MediaMetadataRetriever
38 {
39     static {
40         System.loadLibrary("media_jni");
native_init()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 
MediaMetadataRetriever()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      */
setDataSource(String path)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      */
setDataSource(String uri, Map<String, String> headers)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 
_setDataSource( IBinder httpServiceBinder, String uri, String[] keys, String[] values)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      */
setDataSource(FileDescriptor fd, long offset, long length)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      */
setDataSource(FileDescriptor fd)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      */
setDataSource(Context context, Uri uri)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      */
extractMetadata(int keyCode)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      */
getFrameAtTime(long timeUs, int option)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      */
getFrameAtTime(long timeUs)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      */
getFrameAtTime()294     public Bitmap getFrameAtTime() {
295         return getFrameAtTime(-1, OPTION_CLOSEST_SYNC);
296     }
297 
_getFrameAtTime(long timeUs, int option)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      */
getEmbeddedPicture()308     public byte[] getEmbeddedPicture() {
309         return getEmbeddedPicture(EMBEDDED_PICTURE_TYPE_ANY);
310     }
311 
getEmbeddedPicture(int pictureType)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      */
release()318     public native void release();
native_setup()319     private native void native_setup();
native_init()320     private static native void native_init();
321 
native_finalize()322     private native final void native_finalize();
323 
324     @Override
finalize()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