MediaMetadataRetriever.java revision ee35aff74494e6c0b718e219427af6a6c573b928
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.FileNotFoundException;
27import java.io.IOException;
28
29/**
30 * MediaMetadataRetriever class provides a unified interface for retrieving
31 * frame and meta data from an input media file.
32 * {@hide}
33 */
34public class MediaMetadataRetriever
35{
36    static {
37        System.loadLibrary("media_jni");
38        native_init();
39    }
40
41    // The field below is accessed by native methods
42    @SuppressWarnings("unused")
43    private int mNativeContext;
44
45    private static final int EMBEDDED_PICTURE_TYPE_ANY = 0xFFFF;
46
47    public MediaMetadataRetriever() {
48        native_setup();
49    }
50
51    /**
52     * Call this method before setDataSource() so that the mode becomes
53     * effective for subsequent operations. This method can be called only once
54     * at the beginning if the intended mode of operation for a
55     * MediaMetadataRetriever object remains the same for its whole lifetime,
56     * and thus it is unnecessary to call this method each time setDataSource()
57     * is called. If this is not never called (which is allowed), by default the
58     * intended mode of operation is to both capture frame and retrieve meta
59     * data (i.e., MODE_GET_METADATA_ONLY | MODE_CAPTURE_FRAME_ONLY).
60     * Often, this may not be what one wants, since doing this has negative
61     * performance impact on execution time of a call to setDataSource(), since
62     * both types of operations may be time consuming.
63     *
64     * @param mode The intended mode of operation. Can be any combination of
65     * MODE_GET_METADATA_ONLY and MODE_CAPTURE_FRAME_ONLY:
66     * 1. MODE_GET_METADATA_ONLY & MODE_CAPTURE_FRAME_ONLY:
67     *    For neither frame capture nor meta data retrieval
68     * 2. MODE_GET_METADATA_ONLY: For meta data retrieval only
69     * 3. MODE_CAPTURE_FRAME_ONLY: For frame capture only
70     * 4. MODE_GET_METADATA_ONLY | MODE_CAPTURE_FRAME_ONLY:
71     *    For both frame capture and meta data retrieval
72     */
73    public native void setMode(int mode);
74
75    /**
76     * Sets the data source (file pathname) to use. Call this
77     * method before the rest of the methods in this class. This method may be
78     * time-consuming.
79     *
80     * @param path The path of the input media file.
81     * @throws IllegalArgumentException If the path is invalid.
82     */
83    public native void setDataSource(String path) throws IllegalArgumentException;
84
85    /**
86     * Sets the data source (FileDescriptor) to use.  It is the caller's
87     * responsibility to close the file descriptor. It is safe to do so as soon
88     * as this call returns. Call this method before the rest of the methods in
89     * this class. This method may be time-consuming.
90     *
91     * @param fd the FileDescriptor for the file you want to play
92     * @param offset the offset into the file where the data to be played starts,
93     * in bytes. It must be non-negative
94     * @param length the length in bytes of the data to be played. It must be
95     * non-negative.
96     * @throws IllegalArgumentException if the arguments are invalid
97     */
98    public native void setDataSource(FileDescriptor fd, long offset, long length)
99            throws IllegalArgumentException;
100
101    /**
102     * Sets the data source (FileDescriptor) to use. It is the caller's
103     * responsibility to close the file descriptor. It is safe to do so as soon
104     * as this call returns. Call this method before the rest of the methods in
105     * this class. This method may be time-consuming.
106     *
107     * @param fd the FileDescriptor for the file you want to play
108     * @throws IllegalArgumentException if the FileDescriptor is invalid
109     */
110    public void setDataSource(FileDescriptor fd)
111            throws IllegalArgumentException {
112        // intentionally less than LONG_MAX
113        setDataSource(fd, 0, 0x7ffffffffffffffL);
114    }
115
116    /**
117     * Sets the data source as a content Uri. Call this method before
118     * the rest of the methods in this class. This method may be time-consuming.
119     *
120     * @param context the Context to use when resolving the Uri
121     * @param uri the Content URI of the data you want to play
122     * @throws IllegalArgumentException if the Uri is invalid
123     * @throws SecurityException if the Uri cannot be used due to lack of
124     * permission.
125     */
126    public void setDataSource(Context context, Uri uri)
127        throws IllegalArgumentException, SecurityException {
128        if (uri == null) {
129            throw new IllegalArgumentException();
130        }
131
132        String scheme = uri.getScheme();
133        if(scheme == null || scheme.equals("file")) {
134            setDataSource(uri.getPath());
135            return;
136        }
137
138        AssetFileDescriptor fd = null;
139        try {
140            ContentResolver resolver = context.getContentResolver();
141            try {
142                fd = resolver.openAssetFileDescriptor(uri, "r");
143            } catch(FileNotFoundException e) {
144                throw new IllegalArgumentException();
145            }
146            if (fd == null) {
147                throw new IllegalArgumentException();
148            }
149            FileDescriptor descriptor = fd.getFileDescriptor();
150            if (!descriptor.valid()) {
151                throw new IllegalArgumentException();
152            }
153            // Note: using getDeclaredLength so that our behavior is the same
154            // as previous versions when the content provider is returning
155            // a full file.
156            if (fd.getDeclaredLength() < 0) {
157                setDataSource(descriptor);
158            } else {
159                setDataSource(descriptor, fd.getStartOffset(), fd.getDeclaredLength());
160            }
161            return;
162        } catch (SecurityException ex) {
163        } finally {
164            try {
165                if (fd != null) {
166                    fd.close();
167                }
168            } catch(IOException ioEx) {
169            }
170        }
171        setDataSource(uri.toString());
172    }
173
174    /**
175     * Call this method after setDataSource(). This method retrieves the
176     * meta data value associated with the keyCode.
177     *
178     * The keyCode currently supported is listed below as METADATA_XXX
179     * constants. With any other value, it returns a null pointer.
180     *
181     * @param keyCode One of the constants listed below at the end of the class.
182     * @return The meta data value associate with the given keyCode on success;
183     * null on failure.
184     */
185    public native String extractMetadata(int keyCode);
186
187    /**
188     * Call this method after setDataSource(). This method finds a
189     * representative frame close to the given time position by considering
190     * the given option if possible, and returns it as a bitmap. This is
191     * useful for generating a thumbnail for an input data source or just
192     * obtain and display a frame at the given time position.
193     *
194     * @param timeUs The time position where the frame will be retrieved.
195     * When retrieving the frame at the given time position, there is no
196     * guarantee that the data source has a frame located at the position.
197     * When this happens, a frame nearby will be returned. If timeUs is
198     * negative, time position and option will ignored, and any frame
199     * that the implementation considers as representative may be returned.
200     *
201     * @param option a hint on how the frame is found. Use
202     * {@link OPTION_PREVIOUS_SYNC} if one wants to retrieve a sync frame
203     * that has a timestamp earlier than or the same as timeUs. Use
204     * {@link OPTION_NEXT_SYNC} if one wants to retrieve a sync frame
205     * that has a timestamp later than or the same as timeUs. Use
206     * {@link OPTION_CLOSEST_SYNC} if one wants to retrieve a sync frame
207     * that has a timestamp closest to or the same as timeUs. Use
208     * {@link OPTION_CLOSEST} if one wants to retrieve a frame that may
209     * or may not be a sync frame but is closest to or the same as timeUs.
210     * {@link OPTION_CLOSEST} often has larger performance overhead compared
211     * to the other options if there is no sync frame located at timeUs.
212     *
213     * @return A Bitmap containing a representative video frame, which
214     *         can be null, if such a frame cannot be retrieved.
215     */
216    public Bitmap getFrameAtTime(long timeUs, int option) {
217        if (option < OPTION_PREVIOUS_SYNC ||
218            option > OPTION_CLOSEST) {
219            throw new IllegalArgumentException("Unsupported option: " + option);
220        }
221
222        return _getFrameAtTime(timeUs, option);
223    }
224
225    /**
226     * Call this method after setDataSource(). This method finds a
227     * representative frame close to the given time position if possible,
228     * and returns it as a bitmap. This is useful for generating a thumbnail
229     * for an input data source. Call this method if one does not care
230     * how the frame is found as long as it is close to the given time;
231     * otherwise, please call {@link getFrameAtTime(long, int)}.
232     *
233     * @param timeUs The time position where the frame will be retrieved.
234     * When retrieving the frame at the given time position, there is no
235     * guarentee that the data source has a frame located at the position.
236     * When this happens, a frame nearby will be returned. If timeUs is
237     * negative, time position and option will ignored, and any frame
238     * that the implementation considers as representative may be returned.
239     *
240     * @return A Bitmap containing a representative video frame, which
241     *         can be null, if such a frame cannot be retrieved.
242     *
243     * @see #getFrameAtTime(long, int)
244     */
245    public Bitmap getFrameAtTime(long timeUs) {
246        return getFrameAtTime(timeUs, OPTION_CLOSEST_SYNC);
247    }
248
249    /**
250     * Call this method after setDataSource(). This method finds a
251     * representative frame at any time position if possible,
252     * and returns it as a bitmap. This is useful for generating a thumbnail
253     * for an input data source. Call this method if one does not
254     * care about where the frame is located; otherwise, please call
255     * {@link getFrameAtTime(long)} or {@link getFrameAtTime(long, int)}
256     *
257     * @return A Bitmap containing a representative video frame, which
258     *         can be null, if such a frame cannot be retrieved.
259     *
260     * @see #getFrameAtTime(long)
261     * @see #getFrameAtTime(long, int)
262     */
263    public Bitmap getFrameAtTime() {
264        return getFrameAtTime(-1, OPTION_CLOSEST_SYNC);
265    }
266
267    private native Bitmap _getFrameAtTime(long timeUs, int option);
268
269
270    /**
271     * Call this method after setDataSource(). This method finds the optional
272     * graphic or album art associated (embedded or external url linked) the
273     * related data source.
274     *
275     * @return null if no such graphic is found.
276     */
277    public byte[] getEmbeddedPicture() {
278        return getEmbeddedPicture(EMBEDDED_PICTURE_TYPE_ANY);
279    }
280
281    private native byte[] getEmbeddedPicture(int pictureType);
282
283    /**
284     * Call it when one is done with the object. This method releases the memory
285     * allocated internally.
286     */
287    public native void release();
288    private native void native_setup();
289    private static native void native_init();
290
291    private native final void native_finalize();
292
293    @Override
294    protected void finalize() throws Throwable {
295        try {
296            native_finalize();
297        } finally {
298            super.finalize();
299        }
300    }
301
302    public static final int MODE_GET_METADATA_ONLY  = 0x01;
303    public static final int MODE_CAPTURE_FRAME_ONLY = 0x02;
304
305    /**
306     * Option used in method {@link getFrameAtTime(long, int)} to get a
307     * frame at a specified location.
308     *
309     * @see #getFrameAtTime(long, int)
310     */
311    /* Do not change these values without updating their counterparts
312     * in include/media/stagefright/MediaSource.h!
313     */
314    public static final int OPTION_PREVIOUS_SYNC    = 0x00;
315    public static final int OPTION_NEXT_SYNC        = 0x01;
316    public static final int OPTION_CLOSEST_SYNC     = 0x02;
317    public static final int OPTION_CLOSEST          = 0x03;
318
319    /*
320     * Do not change these values without updating their counterparts
321     * in include/media/mediametadataretriever.h!
322     */
323    public static final int METADATA_KEY_CD_TRACK_NUMBER = 0;
324    public static final int METADATA_KEY_ALBUM           = 1;
325    public static final int METADATA_KEY_ARTIST          = 2;
326    public static final int METADATA_KEY_AUTHOR          = 3;
327    public static final int METADATA_KEY_COMPOSER        = 4;
328    public static final int METADATA_KEY_DATE            = 5;
329    public static final int METADATA_KEY_GENRE           = 6;
330    public static final int METADATA_KEY_TITLE           = 7;
331    public static final int METADATA_KEY_YEAR            = 8;
332    public static final int METADATA_KEY_DURATION        = 9;
333    public static final int METADATA_KEY_NUM_TRACKS      = 10;
334    public static final int METADATA_KEY_IS_DRM_CRIPPLED = 11;
335    public static final int METADATA_KEY_CODEC           = 12;
336    public static final int METADATA_KEY_RATING          = 13;
337    public static final int METADATA_KEY_COMMENT         = 14;
338    public static final int METADATA_KEY_COPYRIGHT       = 15;
339    public static final int METADATA_KEY_BIT_RATE        = 16;
340    public static final int METADATA_KEY_FRAME_RATE      = 17;
341    public static final int METADATA_KEY_VIDEO_FORMAT    = 18;
342    public static final int METADATA_KEY_VIDEO_HEIGHT    = 19;
343    public static final int METADATA_KEY_VIDEO_WIDTH     = 20;
344    public static final int METADATA_KEY_WRITER          = 21;
345    public static final int METADATA_KEY_MIMETYPE        = 22;
346    public static final int METADATA_KEY_DISCNUMBER      = 23;
347    public static final int METADATA_KEY_ALBUMARTIST     = 24;
348    public static final int METADATA_KEY_COMPILATION     = 25;
349    // Add more here...
350}
351