From 6c8512978af64592305e967c838d1b22d348e297 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 14 Mar 2018 18:56:56 -0700 Subject: [PATCH] heif: address api review comments - renaming of MediaFormat keys related to grid config - add methods to MediaMetadataRetriever to get image/frame with default bitmap config - fix java doc bug: 74831433 Change-Id: Iec607e615d34bea0620070592e4adcfc04cbccae --- api/current.txt | 10 +- media/java/android/media/MediaFormat.java | 46 +++--- .../android/media/MediaMetadataRetriever.java | 143 +++++++++++++++--- .../android_media_MediaMetadataRetriever.cpp | 7 +- 4 files changed, 152 insertions(+), 54 deletions(-) diff --git a/api/current.txt b/api/current.txt index 59e7c46a59a92..b1365389afd84 100644 --- a/api/current.txt +++ b/api/current.txt @@ -23777,10 +23777,8 @@ package android.media { field public static final java.lang.String KEY_DURATION = "durationUs"; field public static final java.lang.String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level"; field public static final java.lang.String KEY_FRAME_RATE = "frame-rate"; - field public static final java.lang.String KEY_GRID_COLS = "grid-cols"; - field public static final java.lang.String KEY_GRID_HEIGHT = "grid-height"; + field public static final java.lang.String KEY_GRID_COLUMNS = "grid-cols"; field public static final java.lang.String KEY_GRID_ROWS = "grid-rows"; - field public static final java.lang.String KEY_GRID_WIDTH = "grid-width"; field public static final java.lang.String KEY_HDR_STATIC_INFO = "hdr-static-info"; field public static final java.lang.String KEY_HEIGHT = "height"; field public static final java.lang.String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period"; @@ -23809,6 +23807,8 @@ package android.media { field public static final java.lang.String KEY_SLICE_HEIGHT = "slice-height"; field public static final java.lang.String KEY_STRIDE = "stride"; field public static final java.lang.String KEY_TEMPORAL_LAYERING = "ts-schema"; + field public static final java.lang.String KEY_TILE_HEIGHT = "tile-height"; + field public static final java.lang.String KEY_TILE_WIDTH = "tile-width"; field public static final java.lang.String KEY_TRACK_ID = "track-id"; field public static final java.lang.String KEY_WIDTH = "width"; field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm"; @@ -24058,12 +24058,16 @@ package android.media { method public java.lang.String extractMetadata(int); method public byte[] getEmbeddedPicture(); method public android.graphics.Bitmap getFrameAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams); + method public android.graphics.Bitmap getFrameAtIndex(int); method public android.graphics.Bitmap getFrameAtTime(long, int); method public android.graphics.Bitmap getFrameAtTime(long); method public android.graphics.Bitmap getFrameAtTime(); method public java.util.List getFramesAtIndex(int, int, android.media.MediaMetadataRetriever.BitmapParams); + method public java.util.List getFramesAtIndex(int, int); method public android.graphics.Bitmap getImageAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams); + method public android.graphics.Bitmap getImageAtIndex(int); method public android.graphics.Bitmap getPrimaryImage(android.media.MediaMetadataRetriever.BitmapParams); + method public android.graphics.Bitmap getPrimaryImage(); method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int); method public void release(); method public void setDataSource(java.lang.String) throws java.lang.IllegalArgumentException; diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index b50aa477d5112..3bfbcc2039d3d 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -105,10 +105,10 @@ import java.util.Map; * {@link #KEY_HEIGHT}Integer * {@link #KEY_COLOR_FORMAT}Integerset by the user * for encoders, readable in the output format of decoders - * {@link #KEY_GRID_WIDTH}Integerrequired if the image has grid - * {@link #KEY_GRID_HEIGHT}Integerrequired if the image has grid + * {@link #KEY_TILE_WIDTH}Integerrequired if the image has grid + * {@link #KEY_TILE_HEIGHT}Integerrequired if the image has grid * {@link #KEY_GRID_ROWS}Integerrequired if the image has grid - * {@link #KEY_GRID_COLS}Integerrequired if the image has grid + * {@link #KEY_GRID_COLUMNS}Integerrequired if the image has grid * */ public final class MediaFormat { @@ -150,17 +150,17 @@ public final class MediaFormat { * The track's MediaFormat will come with {@link #KEY_WIDTH} and * {@link #KEY_HEIGHT} keys, which describes the width and height * of the image. If the image doesn't contain grid (i.e. none of - * {@link #KEY_GRID_WIDTH}, {@link #KEY_GRID_HEIGHT}, - * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLS} are present}), the + * {@link #KEY_TILE_WIDTH}, {@link #KEY_TILE_HEIGHT}, + * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLUMNS} are present}), the * track will contain a single sample of coded data for the entire image, * and the image width and height should be used to set up the decoder. * * If the image does come with grid, each sample from the track will * contain one tile in the grid, of which the size is described by - * {@link #KEY_GRID_WIDTH} and {@link #KEY_GRID_HEIGHT}. This size + * {@link #KEY_TILE_WIDTH} and {@link #KEY_TILE_HEIGHT}. This size * (instead of {@link #KEY_WIDTH} and {@link #KEY_HEIGHT}) should be * used to set up the decoder. The track contains {@link #KEY_GRID_ROWS} - * by {@link #KEY_GRID_COLS} samples in row-major, top-row first, + * by {@link #KEY_GRID_COLUMNS} samples in row-major, top-row first, * left-to-right order. The output image should be reconstructed by * first tiling the decoding results of the tiles in the correct order, * then trimming (before rotation is applied) on the bottom and right @@ -275,28 +275,28 @@ public final class MediaFormat { public static final String KEY_FRAME_RATE = "frame-rate"; /** - * A key describing the grid width of the content in a {@link #MIMETYPE_IMAGE_ANDROID_HEIC} - * track. The associated value is an integer. + * A key describing the width (in pixels) of each tile of the content in a + * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer. * * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks. * - * @see #KEY_GRID_HEIGHT + * @see #KEY_TILE_HEIGHT * @see #KEY_GRID_ROWS - * @see #KEY_GRID_COLS + * @see #KEY_GRID_COLUMNS */ - public static final String KEY_GRID_WIDTH = "grid-width"; + public static final String KEY_TILE_WIDTH = "tile-width"; /** - * A key describing the grid height of the content in a {@link #MIMETYPE_IMAGE_ANDROID_HEIC} - * track. The associated value is an integer. + * A key describing the height (in pixels) of each tile of the content in a + * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer. * * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks. * - * @see #KEY_GRID_WIDTH + * @see #KEY_TILE_WIDTH * @see #KEY_GRID_ROWS - * @see #KEY_GRID_COLS + * @see #KEY_GRID_COLUMNS */ - public static final String KEY_GRID_HEIGHT = "grid-height"; + public static final String KEY_TILE_HEIGHT = "tile-height"; /** * A key describing the number of grid rows in the content in a @@ -304,9 +304,9 @@ public final class MediaFormat { * * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks. * - * @see #KEY_GRID_WIDTH - * @see #KEY_GRID_HEIGHT - * @see #KEY_GRID_COLS + * @see #KEY_TILE_WIDTH + * @see #KEY_TILE_HEIGHT + * @see #KEY_GRID_COLUMNS */ public static final String KEY_GRID_ROWS = "grid-rows"; @@ -316,11 +316,11 @@ public final class MediaFormat { * * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks. * - * @see #KEY_GRID_WIDTH - * @see #KEY_GRID_HEIGHT + * @see #KEY_TILE_WIDTH + * @see #KEY_TILE_HEIGHT * @see #KEY_GRID_ROWS */ - public static final String KEY_GRID_COLS = "grid-cols"; + public static final String KEY_GRID_COLUMNS = "grid-cols"; /** * A key describing the raw audio sample encoding/format. diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index 1aeed6da3a317..0955dd633c1cf 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -427,20 +427,40 @@ public class MediaMetadataRetriever * 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). - * If null, default config will be chosen. * * @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, @Nullable BitmapParams params) { + public Bitmap getFrameAtIndex(int frameIndex, @NonNull BitmapParams params) { List bitmaps = getFramesAtIndex(frameIndex, 1, params); - if (bitmaps == null || bitmaps.size() < 1) { - return null; - } + 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); } @@ -461,7 +481,6 @@ public class MediaMetadataRetriever * @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). - * If null, default config will be chosen. * * @throws IllegalStateException if the container doesn't contain video or image sequences. * @throws IllegalArgumentException if the frameIndex or numFrames is invalid, or the @@ -471,8 +490,40 @@ public class MediaMetadataRetriever * array could contain less frames than requested if the retrieval fails. * * @see #getFrameAtIndex(int, BitmapParams) + * @see #getFrameAtIndex(int) + * @see #getFramesAtIndex(int, int) */ - public List getFramesAtIndex( + 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"); @@ -487,7 +538,8 @@ public class MediaMetadataRetriever } return _getFrameAtIndex(frameIndex, numFrames, params); } - private native List _getFrameAtIndex( + + private native @NonNull List _getFrameAtIndex( int frameIndex, int numFrames, @Nullable BitmapParams params); /** @@ -498,29 +550,39 @@ public class MediaMetadataRetriever * 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, with negative value indicating - * the primary image. + * @param imageIndex 0-based index of the image. * @param params BitmapParams that controls the returned bitmap config (such as pixel formats). - * If null, default config will be chosen. * * @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, @Nullable BitmapParams params) { - if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE))) { - throw new IllegalStateException("Does not contail still images"); - } + public Bitmap getImageAtIndex(int imageIndex, @NonNull BitmapParams params) { + return getImageAtIndexInternal(imageIndex, params); + } - String imageCount = extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT); - if (imageIndex >= Integer.parseInt(imageCount)) { - throw new IllegalArgumentException("Invalid image index: " + imageCount); - } - - return _getImageAtIndex(imageIndex, params); + /** + * 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); } /** @@ -532,16 +594,46 @@ public class MediaMetadataRetriever * 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). - * If null, default config will be chosen. * * @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(@Nullable BitmapParams params) { - return getImageAtIndex(-1, params); + 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); @@ -788,7 +880,8 @@ public class MediaMetadataRetriever public static final int METADATA_KEY_IMAGE_HEIGHT = 30; /** * If the media contains still images, this key retrieves the rotation - * of the primary image. + * 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; /** diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp index 3a6714279bb79..4f6763e55ce58 100644 --- a/media/jni/android_media_MediaMetadataRetriever.cpp +++ b/media/jni/android_media_MediaMetadataRetriever.cpp @@ -449,13 +449,14 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtIndex( std::vector > frames; status_t err = retriever->getFrameAtIndex(&frames, frameIndex, numFrames, colorFormat); if (err != OK || frames.size() == 0) { - ALOGE("failed to get frames from retriever, err=%d, size=%zu", - err, frames.size()); + jniThrowException(env, + "java/lang/IllegalStateException", "No frames from retriever"); return NULL; } jobject arrayList = env->NewObject(fields.arrayListClazz, fields.arrayListInit); if (arrayList == NULL) { - ALOGE("can't create bitmap array list object"); + jniThrowException(env, + "java/lang/IllegalStateException", "Can't create bitmap array"); return NULL; }