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