/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.graphics.Bitmap; import android.net.Uri; import android.os.IBinder; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.Map; /** * MediaMetadataRetriever class provides a unified interface for retrieving * frame and meta data from an input media file. */ public class MediaMetadataRetriever { static { System.loadLibrary("media_jni"); native_init(); } // The field below is accessed by native methods @SuppressWarnings("unused") private long mNativeContext; private static final int EMBEDDED_PICTURE_TYPE_ANY = 0xFFFF; public MediaMetadataRetriever() { native_setup(); } /** * Sets the data source (file pathname) to use. Call this * method before the rest of the methods in this class. This method may be * time-consuming. * * @param path The path of the input media file. * @throws IllegalArgumentException If the path is invalid. */ public void setDataSource(String path) throws IllegalArgumentException { if (path == null) { throw new IllegalArgumentException(); } try (FileInputStream is = new FileInputStream(path)) { FileDescriptor fd = is.getFD(); setDataSource(fd, 0, 0x7ffffffffffffffL); } catch (FileNotFoundException fileEx) { throw new IllegalArgumentException(); } catch (IOException ioEx) { throw new IllegalArgumentException(); } } /** * Sets the data source (URI) to use. Call this * method before the rest of the methods in this class. This method may be * time-consuming. * * @param uri The URI of the input media. * @param headers the headers to be sent together with the request for the data * @throws IllegalArgumentException If the URI is invalid. */ public void setDataSource(String uri, Map headers) throws IllegalArgumentException { int i = 0; String[] keys = new String[headers.size()]; String[] values = new String[headers.size()]; for (Map.Entry entry: headers.entrySet()) { keys[i] = entry.getKey(); values[i] = entry.getValue(); ++i; } _setDataSource( MediaHTTPService.createHttpServiceBinderIfNecessary(uri), uri, keys, values); } private native void _setDataSource( IBinder httpServiceBinder, String uri, String[] keys, String[] values) throws IllegalArgumentException; /** * Sets the data source (FileDescriptor) to use. It is the caller's * responsibility to close the file descriptor. It is safe to do so as soon * as this call returns. Call this method before the rest of the methods in * this class. This method may be time-consuming. * * @param fd the FileDescriptor for the file you want to play * @param offset the offset into the file where the data to be played starts, * in bytes. It must be non-negative * @param length the length in bytes of the data to be played. It must be * non-negative. * @throws IllegalArgumentException if the arguments are invalid */ public native void setDataSource(FileDescriptor fd, long offset, long length) throws IllegalArgumentException; /** * Sets the data source (FileDescriptor) to use. It is the caller's * responsibility to close the file descriptor. It is safe to do so as soon * as this call returns. Call this method before the rest of the methods in * this class. This method may be time-consuming. * * @param fd the FileDescriptor for the file you want to play * @throws IllegalArgumentException if the FileDescriptor is invalid */ public void setDataSource(FileDescriptor fd) throws IllegalArgumentException { // intentionally less than LONG_MAX setDataSource(fd, 0, 0x7ffffffffffffffL); } /** * Sets the data source as a content Uri. Call this method before * the rest of the methods in this class. This method may be time-consuming. * * @param context the Context to use when resolving the Uri * @param uri the Content URI of the data you want to play * @throws IllegalArgumentException if the Uri is invalid * @throws SecurityException if the Uri cannot be used due to lack of * permission. */ public void setDataSource(Context context, Uri uri) throws IllegalArgumentException, SecurityException { if (uri == null) { throw new IllegalArgumentException(); } String scheme = uri.getScheme(); if(scheme == null || scheme.equals("file")) { setDataSource(uri.getPath()); return; } AssetFileDescriptor fd = null; try { ContentResolver resolver = context.getContentResolver(); try { fd = resolver.openAssetFileDescriptor(uri, "r"); } catch(FileNotFoundException e) { throw new IllegalArgumentException(); } if (fd == null) { throw new IllegalArgumentException(); } FileDescriptor descriptor = fd.getFileDescriptor(); if (!descriptor.valid()) { throw new IllegalArgumentException(); } // Note: using getDeclaredLength so that our behavior is the same // as previous versions when the content provider is returning // a full file. if (fd.getDeclaredLength() < 0) { setDataSource(descriptor); } else { setDataSource(descriptor, fd.getStartOffset(), fd.getDeclaredLength()); } return; } catch (SecurityException ex) { } finally { try { if (fd != null) { fd.close(); } } catch(IOException ioEx) { } } setDataSource(uri.toString()); } /** * Sets the data source (MediaDataSource) to use. * * @param dataSource the MediaDataSource for the media you want to play */ public void setDataSource(MediaDataSource dataSource) throws IllegalArgumentException { _setDataSource(dataSource); } private native void _setDataSource(MediaDataSource dataSource) throws IllegalArgumentException; /** * Call this method after setDataSource(). This method retrieves the * meta data value associated with the keyCode. * * The keyCode currently supported is listed below as METADATA_XXX * constants. With any other value, it returns a null pointer. * * @param keyCode One of the constants listed below at the end of the class. * @return The meta data value associate with the given keyCode on success; * null on failure. */ public native String extractMetadata(int keyCode); /** * Call this method after setDataSource(). This method finds a * representative frame close to the given time position by considering * the given option if possible, and returns it as a bitmap. * *

If you don't need a full-resolution * frame (for example, because you need a thumbnail image), use * {@link #getScaledFrameAtTime getScaledFrameAtTime()} instead of this * method.

* * @param timeUs The time position where the frame will be retrieved. * When retrieving the frame at the given time position, there is no * guarantee that the data source has a frame located at the position. * When this happens, a frame nearby will be returned. If timeUs is * negative, time position and option will ignored, and any frame * that the implementation considers as representative may be returned. * * @param option a hint on how the frame is found. Use * {@link #OPTION_PREVIOUS_SYNC} if one wants to retrieve a sync frame * that has a timestamp earlier than or the same as timeUs. Use * {@link #OPTION_NEXT_SYNC} if one wants to retrieve a sync frame * that has a timestamp later than or the same as timeUs. Use * {@link #OPTION_CLOSEST_SYNC} if one wants to retrieve a sync frame * that has a timestamp closest to or the same as timeUs. Use * {@link #OPTION_CLOSEST} if one wants to retrieve a frame that may * or may not be a sync frame but is closest to or the same as timeUs. * {@link #OPTION_CLOSEST} often has larger performance overhead compared * to the other options if there is no sync frame located at timeUs. * * @return A Bitmap containing a representative video frame, which * can be null, if such a frame cannot be retrieved. */ public Bitmap getFrameAtTime(long timeUs, @Option int option) { if (option < OPTION_PREVIOUS_SYNC || option > OPTION_CLOSEST) { throw new IllegalArgumentException("Unsupported option: " + option); } return _getFrameAtTime(timeUs, option, -1 /*dst_width*/, -1 /*dst_height*/); } /** * Retrieve a video frame near a given timestamp scaled to a desired size. * Call this method after setDataSource(). This method finds a representative * frame close to the given time position by considering the given option * if possible, and returns it as a bitmap with same aspect ratio as the source * while scaling it so that it fits into the desired size of dst_width by dst_height. * This is useful for generating a thumbnail for an input data source or just to * obtain a scaled frame at the given time position. * * @param timeUs The time position in microseconds where the frame will be retrieved. * When retrieving the frame at the given time position, there is no * guarantee that the data source has a frame located at the position. * When this happens, a frame nearby will be returned. If timeUs is * negative, time position and option will ignored, and any frame * that the implementation considers as representative may be returned. * * @param option a hint on how the frame is found. Use * {@link #OPTION_PREVIOUS_SYNC} if one wants to retrieve a sync frame * that has a timestamp earlier than or the same as timeUs. Use * {@link #OPTION_NEXT_SYNC} if one wants to retrieve a sync frame * that has a timestamp later than or the same as timeUs. Use * {@link #OPTION_CLOSEST_SYNC} if one wants to retrieve a sync frame * that has a timestamp closest to or the same as timeUs. Use * {@link #OPTION_CLOSEST} if one wants to retrieve a frame that may * or may not be a sync frame but is closest to or the same as timeUs. * {@link #OPTION_CLOSEST} often has larger performance overhead compared * to the other options if there is no sync frame located at timeUs. * * @param dstWidth expected output bitmap width * @param dstHeight expected output bitmap height * @return A Bitmap of size not larger than dstWidth by dstHeight containing a * scaled video frame, which can be null, if such a frame cannot be retrieved. * @throws IllegalArgumentException if passed in invalid option or width by height * is less than or equal to 0. */ public Bitmap getScaledFrameAtTime( long timeUs, @Option int option, int dstWidth, int dstHeight) { if (option < OPTION_PREVIOUS_SYNC || option > OPTION_CLOSEST) { throw new IllegalArgumentException("Unsupported option: " + option); } if (dstWidth <= 0) { throw new IllegalArgumentException("Invalid width: " + dstWidth); } if (dstHeight <= 0) { throw new IllegalArgumentException("Invalid height: " + dstHeight); } return _getFrameAtTime(timeUs, option, dstWidth, dstHeight); } /** * Call this method after setDataSource(). This method finds a * representative frame close to the given time position if possible, * and returns it as a bitmap. Call this method if one does not care * how the frame is found as long as it is close to the given time; * otherwise, please call {@link #getFrameAtTime(long, int)}. * *

If you don't need a full-resolution * frame (for example, because you need a thumbnail image), use * {@link #getScaledFrameAtTime getScaledFrameAtTime()} instead of this * method.

* * @param timeUs The time position where the frame will be retrieved. * When retrieving the frame at the given time position, there is no * guarentee that the data source has a frame located at the position. * When this happens, a frame nearby will be returned. If timeUs is * negative, time position and option will ignored, and any frame * that the implementation considers as representative may be returned. * * @return A Bitmap of size dst_widthxdst_height containing a representative * video frame, which can be null, if such a frame cannot be retrieved. * * @see #getFrameAtTime(long, int) */ public Bitmap getFrameAtTime(long timeUs) { return getFrameAtTime(timeUs, OPTION_CLOSEST_SYNC); } /** * Call this method after setDataSource(). This method finds a * representative frame at any time position if possible, * and returns it as a bitmap. Call this method if one does not * care about where the frame is located; otherwise, please call * {@link #getFrameAtTime(long)} or {@link #getFrameAtTime(long, int)} * *

If you don't need a full-resolution * frame (for example, because you need a thumbnail image), use * {@link #getScaledFrameAtTime getScaledFrameAtTime()} instead of this * method.

* * @return A Bitmap containing a representative video frame, which * can be null, if such a frame cannot be retrieved. * * @see #getFrameAtTime(long) * @see #getFrameAtTime(long, int) */ public Bitmap getFrameAtTime() { return _getFrameAtTime(-1, OPTION_CLOSEST_SYNC, -1 /*dst_width*/, -1 /*dst_height*/); } private native Bitmap _getFrameAtTime(long timeUs, int option, int width, int height); public static final class BitmapParams { private Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888; private Bitmap.Config outActualConfig = Bitmap.Config.ARGB_8888; /** * Create a default BitmapParams object. By default, it uses {@link Bitmap.Config#ARGB_8888} * as the preferred bitmap config. */ public BitmapParams() {} /** * Set the preferred bitmap config for the decoder to decode into. * * If not set, or the request cannot be met, the decoder will output * in {@link Bitmap.Config#ARGB_8888} config by default. * * After decode, the actual config used can be retrieved by {@link #getActualConfig()}. * * @param config the preferred bitmap config to use. */ public void setPreferredConfig(@NonNull Bitmap.Config config) { if (config == null) { throw new IllegalArgumentException("preferred config can't be null"); } inPreferredConfig = config; } /** * Retrieve the preferred bitmap config in the params. * * @return the preferred bitmap config. */ public @NonNull Bitmap.Config getPreferredConfig() { return inPreferredConfig; } /** * Get the actual bitmap config used to decode the bitmap after the decoding. * * @return the actual bitmap config used. */ public @NonNull Bitmap.Config getActualConfig() { return outActualConfig; } } /** * This method retrieves a video frame by its index. It should only be called * after {@link #setDataSource}. * * After the bitmap is returned, you can query the actual parameters that were * used to create the bitmap from the {@code BitmapParams} argument, for instance * to query the bitmap config used for the bitmap with {@link BitmapParams#getActualConfig}. * * @param frameIndex 0-based index of the video frame. The frame index must be that of * a valid frame. The total number of frames available for retrieval can be queried * via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key. * @param params BitmapParams that controls the returned bitmap config (such as pixel formats). * * @throws IllegalStateException if the container doesn't contain video or image sequences. * @throws IllegalArgumentException if the requested frame index does not exist. * * @return A Bitmap containing the requested video frame, or null if the retrieval fails. * * @see #getFrameAtIndex(int) * @see #getFramesAtIndex(int, int, BitmapParams) * @see #getFramesAtIndex(int, int) */ public Bitmap getFrameAtIndex(int frameIndex, @NonNull BitmapParams params) { List bitmaps = getFramesAtIndex(frameIndex, 1, params); return bitmaps.get(0); } /** * This method is similar to {@link #getFrameAtIndex(int, BitmapParams)} except that * the default for {@link BitmapParams} will be used. * * @param frameIndex 0-based index of the video frame. The frame index must be that of * a valid frame. The total number of frames available for retrieval can be queried * via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key. * * @throws IllegalStateException if the container doesn't contain video or image sequences. * @throws IllegalArgumentException if the requested frame index does not exist. * * @return A Bitmap containing the requested video frame, or null if the retrieval fails. * * @see #getFrameAtIndex(int, BitmapParams) * @see #getFramesAtIndex(int, int, BitmapParams) * @see #getFramesAtIndex(int, int) */ public Bitmap getFrameAtIndex(int frameIndex) { List bitmaps = getFramesAtIndex(frameIndex, 1); return bitmaps.get(0); } /** * This method retrieves a consecutive set of video frames starting at the * specified index. It should only be called after {@link #setDataSource}. * * If the caller intends to retrieve more than one consecutive video frames, * this method is preferred over {@link #getFrameAtIndex(int, BitmapParams)} for efficiency. * * After the bitmaps are returned, you can query the actual parameters that were * used to create the bitmaps from the {@code BitmapParams} argument, for instance * to query the bitmap config used for the bitmaps with {@link BitmapParams#getActualConfig}. * * @param frameIndex 0-based index of the first video frame to retrieve. The frame index * must be that of a valid frame. The total number of frames available for retrieval * can be queried via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key. * @param numFrames number of consecutive video frames to retrieve. Must be a positive * value. The stream must contain at least numFrames frames starting at frameIndex. * @param params BitmapParams that controls the returned bitmap config (such as pixel formats). * * @throws IllegalStateException if the container doesn't contain video or image sequences. * @throws IllegalArgumentException if the frameIndex or numFrames is invalid, or the * stream doesn't contain at least numFrames starting at frameIndex. * @return An list of Bitmaps containing the requested video frames. The returned * array could contain less frames than requested if the retrieval fails. * * @see #getFrameAtIndex(int, BitmapParams) * @see #getFrameAtIndex(int) * @see #getFramesAtIndex(int, int) */ public @NonNull List getFramesAtIndex( int frameIndex, int numFrames, @NonNull BitmapParams params) { return getFramesAtIndexInternal(frameIndex, numFrames, params); } /** * This method is similar to {@link #getFramesAtIndex(int, int, BitmapParams)} except that * the default for {@link BitmapParams} will be used. * * @param frameIndex 0-based index of the first video frame to retrieve. The frame index * must be that of a valid frame. The total number of frames available for retrieval * can be queried via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key. * @param numFrames number of consecutive video frames to retrieve. Must be a positive * value. The stream must contain at least numFrames frames starting at frameIndex. * * @throws IllegalStateException if the container doesn't contain video or image sequences. * @throws IllegalArgumentException if the frameIndex or numFrames is invalid, or the * stream doesn't contain at least numFrames starting at frameIndex. * @return An list of Bitmaps containing the requested video frames. The returned * array could contain less frames than requested if the retrieval fails. * * @see #getFrameAtIndex(int, BitmapParams) * @see #getFrameAtIndex(int) * @see #getFramesAtIndex(int, int, BitmapParams) */ public @NonNull List getFramesAtIndex(int frameIndex, int numFrames) { return getFramesAtIndexInternal(frameIndex, numFrames, null); } private @NonNull List getFramesAtIndexInternal( int frameIndex, int numFrames, @Nullable BitmapParams params) { if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO))) { throw new IllegalStateException("Does not contail video or image sequences"); } int frameCount = Integer.parseInt( extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT)); if (frameIndex < 0 || numFrames < 1 || frameIndex >= frameCount || frameIndex > frameCount - numFrames) { throw new IllegalArgumentException("Invalid frameIndex or numFrames: " + frameIndex + ", " + numFrames); } return _getFrameAtIndex(frameIndex, numFrames, params); } private native @NonNull List _getFrameAtIndex( int frameIndex, int numFrames, @Nullable BitmapParams params); /** * This method retrieves a still image by its index. It should only be called * after {@link #setDataSource}. * * After the bitmap is returned, you can query the actual parameters that were * used to create the bitmap from the {@code BitmapParams} argument, for instance * to query the bitmap config used for the bitmap with {@link BitmapParams#getActualConfig}. * * @param imageIndex 0-based index of the image. * @param params BitmapParams that controls the returned bitmap config (such as pixel formats). * * @throws IllegalStateException if the container doesn't contain still images. * @throws IllegalArgumentException if the requested image does not exist. * * @return the requested still image, or null if the image cannot be retrieved. * * @see #getImageAtIndex(int) * @see #getPrimaryImage(BitmapParams) * @see #getPrimaryImage() */ public Bitmap getImageAtIndex(int imageIndex, @NonNull BitmapParams params) { return getImageAtIndexInternal(imageIndex, params); } /** * @hide * * This method retrieves the thumbnail image for a still image if it's available. * It should only be called after {@link #setDataSource}. * * @param imageIndex 0-based index of the image, negative value indicates primary image. * @param params BitmapParams that controls the returned bitmap config (such as pixel formats). * @param targetSize intended size of one edge (wdith or height) of the thumbnail, * this is a heuristic for the framework to decide whether the embedded * thumbnail should be used. * @param maxPixels maximum pixels of thumbnail, this is a heuristic for the frameowrk to * decide whehther the embedded thumnbail (or a downscaled version of it) * should be used. * @return the retrieved thumbnail, or null if no suitable thumbnail is available. */ public native @Nullable Bitmap getThumbnailImageAtIndex( int imageIndex, @NonNull BitmapParams params, int targetSize, int maxPixels); /** * This method is similar to {@link #getImageAtIndex(int, BitmapParams)} except that * the default for {@link BitmapParams} will be used. * * @param imageIndex 0-based index of the image. * * @throws IllegalStateException if the container doesn't contain still images. * @throws IllegalArgumentException if the requested image does not exist. * * @return the requested still image, or null if the image cannot be retrieved. * * @see #getImageAtIndex(int, BitmapParams) * @see #getPrimaryImage(BitmapParams) * @see #getPrimaryImage() */ public Bitmap getImageAtIndex(int imageIndex) { return getImageAtIndexInternal(imageIndex, null); } /** * This method retrieves the primary image of the media content. It should only * be called after {@link #setDataSource}. * * After the bitmap is returned, you can query the actual parameters that were * used to create the bitmap from the {@code BitmapParams} argument, for instance * to query the bitmap config used for the bitmap with {@link BitmapParams#getActualConfig}. * * @param params BitmapParams that controls the returned bitmap config (such as pixel formats). * * @return the primary image, or null if it cannot be retrieved. * * @throws IllegalStateException if the container doesn't contain still images. * * @see #getImageAtIndex(int, BitmapParams) * @see #getImageAtIndex(int) * @see #getPrimaryImage() */ public Bitmap getPrimaryImage(@NonNull BitmapParams params) { return getImageAtIndexInternal(-1, params); } /** * This method is similar to {@link #getPrimaryImage(BitmapParams)} except that * the default for {@link BitmapParams} will be used. * * @return the primary image, or null if it cannot be retrieved. * * @throws IllegalStateException if the container doesn't contain still images. * * @see #getImageAtIndex(int, BitmapParams) * @see #getImageAtIndex(int) * @see #getPrimaryImage(BitmapParams) */ public Bitmap getPrimaryImage() { return getImageAtIndexInternal(-1, null); } private Bitmap getImageAtIndexInternal(int imageIndex, @Nullable BitmapParams params) { if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE))) { throw new IllegalStateException("Does not contail still images"); } String imageCount = extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT); if (imageIndex >= Integer.parseInt(imageCount)) { throw new IllegalArgumentException("Invalid image index: " + imageCount); } return _getImageAtIndex(imageIndex, params); } private native Bitmap _getImageAtIndex(int imageIndex, @Nullable BitmapParams params); /** * Call this method after setDataSource(). This method finds the optional * graphic or album/cover art associated associated with the data source. If * there are more than one pictures, (any) one of them is returned. * * @return null if no such graphic is found. */ public byte[] getEmbeddedPicture() { return getEmbeddedPicture(EMBEDDED_PICTURE_TYPE_ANY); } private native byte[] getEmbeddedPicture(int pictureType); /** * Call it when one is done with the object. This method releases the memory * allocated internally. */ public native void release(); private native void native_setup(); private static native void native_init(); private native final void native_finalize(); @Override protected void finalize() throws Throwable { try { native_finalize(); } finally { super.finalize(); } } /** * Option used in method {@link #getFrameAtTime(long, int)} to get a * frame at a specified location. * * @see #getFrameAtTime(long, int) */ /* Do not change these option values without updating their counterparts * in include/media/MediaSource.h! */ /** * This option is used with {@link #getFrameAtTime(long, int)} to retrieve * a sync (or key) frame associated with a data source that is located * right before or at the given time. * * @see #getFrameAtTime(long, int) */ public static final int OPTION_PREVIOUS_SYNC = 0x00; /** * This option is used with {@link #getFrameAtTime(long, int)} to retrieve * a sync (or key) frame associated with a data source that is located * right after or at the given time. * * @see #getFrameAtTime(long, int) */ public static final int OPTION_NEXT_SYNC = 0x01; /** * This option is used with {@link #getFrameAtTime(long, int)} to retrieve * a sync (or key) frame associated with a data source that is located * closest to (in time) or at the given time. * * @see #getFrameAtTime(long, int) */ public static final int OPTION_CLOSEST_SYNC = 0x02; /** * This option is used with {@link #getFrameAtTime(long, int)} to retrieve * a frame (not necessarily a key frame) associated with a data source that * is located closest to or at the given time. * * @see #getFrameAtTime(long, int) */ public static final int OPTION_CLOSEST = 0x03; /** @hide */ @IntDef(flag = true, prefix = { "OPTION_" }, value = { OPTION_PREVIOUS_SYNC, OPTION_NEXT_SYNC, OPTION_CLOSEST_SYNC, OPTION_CLOSEST, }) @Retention(RetentionPolicy.SOURCE) public @interface Option {} /* * Do not change these metadata key values without updating their * counterparts in include/media/mediametadataretriever.h! */ /** * The metadata key to retrieve the numeric string describing the * order of the audio data source on its original recording. */ public static final int METADATA_KEY_CD_TRACK_NUMBER = 0; /** * The metadata key to retrieve the information about the album title * of the data source. */ public static final int METADATA_KEY_ALBUM = 1; /** * The metadata key to retrieve the information about the artist of * the data source. */ public static final int METADATA_KEY_ARTIST = 2; /** * The metadata key to retrieve the information about the author of * the data source. */ public static final int METADATA_KEY_AUTHOR = 3; /** * The metadata key to retrieve the information about the composer of * the data source. */ public static final int METADATA_KEY_COMPOSER = 4; /** * The metadata key to retrieve the date when the data source was created * or modified. */ public static final int METADATA_KEY_DATE = 5; /** * The metadata key to retrieve the content type or genre of the data * source. */ public static final int METADATA_KEY_GENRE = 6; /** * The metadata key to retrieve the data source title. */ public static final int METADATA_KEY_TITLE = 7; /** * The metadata key to retrieve the year when the data source was created * or modified. */ public static final int METADATA_KEY_YEAR = 8; /** * The metadata key to retrieve the playback duration of the data source. */ public static final int METADATA_KEY_DURATION = 9; /** * The metadata key to retrieve the number of tracks, such as audio, video, * text, in the data source, such as a mp4 or 3gpp file. */ public static final int METADATA_KEY_NUM_TRACKS = 10; /** * The metadata key to retrieve the information of the writer (such as * lyricist) of the data source. */ public static final int METADATA_KEY_WRITER = 11; /** * The metadata key to retrieve the mime type of the data source. Some * example mime types include: "video/mp4", "audio/mp4", "audio/amr-wb", * etc. */ public static final int METADATA_KEY_MIMETYPE = 12; /** * The metadata key to retrieve the information about the performers or * artist associated with the data source. */ public static final int METADATA_KEY_ALBUMARTIST = 13; /** * The metadata key to retrieve the numberic string that describes which * part of a set the audio data source comes from. */ public static final int METADATA_KEY_DISC_NUMBER = 14; /** * The metadata key to retrieve the music album compilation status. */ public static final int METADATA_KEY_COMPILATION = 15; /** * If this key exists the media contains audio content. */ public static final int METADATA_KEY_HAS_AUDIO = 16; /** * If this key exists the media contains video content. */ public static final int METADATA_KEY_HAS_VIDEO = 17; /** * If the media contains video, this key retrieves its width. */ public static final int METADATA_KEY_VIDEO_WIDTH = 18; /** * If the media contains video, this key retrieves its height. */ public static final int METADATA_KEY_VIDEO_HEIGHT = 19; /** * This key retrieves the average bitrate (in bits/sec), if available. */ public static final int METADATA_KEY_BITRATE = 20; /** * This key retrieves the language code of text tracks, if available. * If multiple text tracks present, the return value will look like: * "eng:chi" * @hide */ public static final int METADATA_KEY_TIMED_TEXT_LANGUAGES = 21; /** * If this key exists the media is drm-protected. * @hide */ public static final int METADATA_KEY_IS_DRM = 22; /** * This key retrieves the location information, if available. * The location should be specified according to ISO-6709 standard, under * a mp4/3gp box "@xyz". Location with longitude of -90 degrees and latitude * of 180 degrees will be retrieved as "-90.0000+180.0000", for instance. */ public static final int METADATA_KEY_LOCATION = 23; /** * This key retrieves the video rotation angle in degrees, if available. * The video rotation angle may be 0, 90, 180, or 270 degrees. */ public static final int METADATA_KEY_VIDEO_ROTATION = 24; /** * This key retrieves the original capture framerate, if it's * available. The capture framerate will be a floating point * number. */ public static final int METADATA_KEY_CAPTURE_FRAMERATE = 25; /** * If this key exists the media contains still image content. */ public static final int METADATA_KEY_HAS_IMAGE = 26; /** * If the media contains still images, this key retrieves the number * of still images. */ public static final int METADATA_KEY_IMAGE_COUNT = 27; /** * If the media contains still images, this key retrieves the image * index of the primary image. */ public static final int METADATA_KEY_IMAGE_PRIMARY = 28; /** * If the media contains still images, this key retrieves the width * of the primary image. */ public static final int METADATA_KEY_IMAGE_WIDTH = 29; /** * If the media contains still images, this key retrieves the height * of the primary image. */ public static final int METADATA_KEY_IMAGE_HEIGHT = 30; /** * If the media contains still images, this key retrieves the rotation * angle (in degrees clockwise) of the primary image. The image rotation * angle must be one of 0, 90, 180, or 270 degrees. */ public static final int METADATA_KEY_IMAGE_ROTATION = 31; /** * If the media contains video and this key exists, it retrieves the * total number of frames in the video sequence. */ public static final int METADATA_KEY_VIDEO_FRAME_COUNT = 32; /** * @hide */ public static final int METADATA_KEY_EXIF_OFFSET = 33; /** * @hide */ public static final int METADATA_KEY_EXIF_LENGTH = 34; // Add more here... }