Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 435e59d9 authored by Chong Zhang's avatar Chong Zhang
Browse files

Add BitmapParams option to getFrameAtTime/getScaledFrameAtTime

Add versions of these two methods that accepts BitmapParams.
Does not change spec or behavior of existing getFrameAtTime/
getScaledFrameAtTime.

bug: 135718180
bug: 138114267
test: CTS MediaMetadataRetrieverTest; manual testing with
thumbnails saved to storage and examine the outputs.

Change-Id: I75e96dde072c94ac950f0be637cdd096e7543f10
parent 843c2951
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -25383,6 +25383,7 @@ package android.media {
    method public android.graphics.Bitmap getFrameAtIndex(int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
    method public android.graphics.Bitmap getFrameAtIndex(int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
    method public android.graphics.Bitmap getFrameAtIndex(int);
    method public android.graphics.Bitmap getFrameAtIndex(int);
    method public android.graphics.Bitmap getFrameAtTime(long, int);
    method public android.graphics.Bitmap getFrameAtTime(long, int);
    method public android.graphics.Bitmap getFrameAtTime(long, int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
    method public android.graphics.Bitmap getFrameAtTime(long);
    method public android.graphics.Bitmap getFrameAtTime(long);
    method public android.graphics.Bitmap getFrameAtTime();
    method public android.graphics.Bitmap getFrameAtTime();
    method @NonNull public java.util.List<android.graphics.Bitmap> getFramesAtIndex(int, int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
    method @NonNull public java.util.List<android.graphics.Bitmap> getFramesAtIndex(int, int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
@@ -25392,6 +25393,7 @@ package android.media {
    method public android.graphics.Bitmap getPrimaryImage(@NonNull android.media.MediaMetadataRetriever.BitmapParams);
    method public android.graphics.Bitmap getPrimaryImage(@NonNull android.media.MediaMetadataRetriever.BitmapParams);
    method public android.graphics.Bitmap getPrimaryImage();
    method public android.graphics.Bitmap getPrimaryImage();
    method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int);
    method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int);
    method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
    method public void release();
    method public void release();
    method public void setDataSource(String) throws java.lang.IllegalArgumentException;
    method public void setDataSource(String) throws java.lang.IllegalArgumentException;
    method public void setDataSource(String, java.util.Map<java.lang.String,java.lang.String>) throws java.lang.IllegalArgumentException;
    method public void setDataSource(String, java.util.Map<java.lang.String,java.lang.String>) throws java.lang.IllegalArgumentException;
+100 −10
Original line number Original line Diff line number Diff line
@@ -226,6 +226,44 @@ public class MediaMetadataRetriever implements AutoCloseable {
     */
     */
    public native String extractMetadata(int keyCode);
    public native String extractMetadata(int keyCode);


    /**
     * This method is similar to {@link #getFrameAtTime(long, int, BitmapParams)}
     * except that the device will choose the actual {@link Bitmap.Config} to use.
     *
     * @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. {@link Bitmap#getConfig()} can
     *         be used to query the actual {@link Bitmap.Config}.
     *
     * @see {@link #getFrameAtTime(long, int, BitmapParams)}
     */
    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*/, null);
    }

    /**
    /**
     * Call this method after setDataSource(). This method finds a
     * Call this method after setDataSource(). This method finds a
     * representative frame close to the given time position by considering
     * representative frame close to the given time position by considering
@@ -255,16 +293,60 @@ public class MediaMetadataRetriever implements AutoCloseable {
     * {@link #OPTION_CLOSEST} often has larger performance overhead compared
     * {@link #OPTION_CLOSEST} often has larger performance overhead compared
     * to the other options if there is no sync frame located at timeUs.
     * to the other options if there is no sync frame located at timeUs.
     *
     *
     * @param params BitmapParams that controls the returned bitmap config
     *        (such as pixel formats).
     *
     * @return A Bitmap containing a representative video frame, which
     * @return A Bitmap containing a representative video frame, which
     *         can be null, if such a frame cannot be retrieved.
     *         can be null, if such a frame cannot be retrieved.
     *
     * @see {@link #getFrameAtTime(long, int)}
     */
     */
    public Bitmap getFrameAtTime(long timeUs, @Option int option) {
    public Bitmap getFrameAtTime(
            long timeUs, @Option int option, @NonNull BitmapParams params) {
        if (option < OPTION_PREVIOUS_SYNC ||
        if (option < OPTION_PREVIOUS_SYNC ||
            option > OPTION_CLOSEST) {
            option > OPTION_CLOSEST) {
            throw new IllegalArgumentException("Unsupported option: " + option);
            throw new IllegalArgumentException("Unsupported option: " + option);
        }
        }


        return _getFrameAtTime(timeUs, option, -1 /*dst_width*/, -1 /*dst_height*/);
        return _getFrameAtTime(timeUs, option, -1 /*dst_width*/, -1 /*dst_height*/, params);
    }

    /**
     * This method is similar to {@link #getScaledFrameAtTime(long, int, int, int, BitmapParams)}
     * except that the device will choose the actual {@link Bitmap.Config} to use.
     *
     * @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 containing a representative video frame, which can be null,
     *         if such a frame cannot be retrieved. {@link Bitmap#getConfig()} can
     *         be used to query the actual {@link Bitmap.Config}.
     * @throws IllegalArgumentException if passed in invalid option or width by height
     *         is less than or equal to 0.
     * @see {@link #getScaledFrameAtTime(long, int, int, int, BitmapParams)}
     */
    public Bitmap getScaledFrameAtTime(
            long timeUs, @Option int option, int dstWidth, int dstHeight) {
        validate(option, dstWidth, dstHeight);
        return _getFrameAtTime(timeUs, option, dstWidth, dstHeight, null);
    }
    }


    /**
    /**
@@ -297,15 +379,23 @@ public class MediaMetadataRetriever implements AutoCloseable {
     *
     *
     * @param dstWidth expected output bitmap width
     * @param dstWidth expected output bitmap width
     * @param dstHeight expected output bitmap height
     * @param dstHeight expected output bitmap height
     * @param params BitmapParams that controls the returned bitmap config
     *        (such as pixel formats).
     *
     * @return A Bitmap of size not larger than dstWidth by dstHeight containing a
     * @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.
     *         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
     * @throws IllegalArgumentException if passed in invalid option or width by height
     *         is less than or equal to 0.
     *         is less than or equal to 0.
     * @see {@link #getScaledFrameAtTime(long, int, int, int)}
     */
     */
    public Bitmap getScaledFrameAtTime(
    public Bitmap getScaledFrameAtTime(long timeUs, @Option int option,
            long timeUs, @Option int option, int dstWidth, int dstHeight) {
            int dstWidth, int dstHeight, @NonNull BitmapParams params) {
        if (option < OPTION_PREVIOUS_SYNC ||
        validate(option, dstWidth, dstHeight);
            option > OPTION_CLOSEST) {
        return _getFrameAtTime(timeUs, option, dstWidth, dstHeight, params);
    }

    private void validate(@Option int option, int dstWidth, int dstHeight) {
        if (option < OPTION_PREVIOUS_SYNC || option > OPTION_CLOSEST) {
            throw new IllegalArgumentException("Unsupported option: " + option);
            throw new IllegalArgumentException("Unsupported option: " + option);
        }
        }
        if (dstWidth <= 0) {
        if (dstWidth <= 0) {
@@ -314,8 +404,6 @@ public class MediaMetadataRetriever implements AutoCloseable {
        if (dstHeight <= 0) {
        if (dstHeight <= 0) {
            throw new IllegalArgumentException("Invalid height: " + dstHeight);
            throw new IllegalArgumentException("Invalid height: " + dstHeight);
        }
        }

        return _getFrameAtTime(timeUs, option, dstWidth, dstHeight);
    }
    }


    /**
    /**
@@ -365,10 +453,12 @@ public class MediaMetadataRetriever implements AutoCloseable {
     * @see #getFrameAtTime(long, int)
     * @see #getFrameAtTime(long, int)
     */
     */
    public Bitmap getFrameAtTime() {
    public Bitmap getFrameAtTime() {
        return _getFrameAtTime(-1, OPTION_CLOSEST_SYNC, -1 /*dst_width*/, -1 /*dst_height*/);
        return _getFrameAtTime(
                -1, OPTION_CLOSEST_SYNC, -1 /*dst_width*/, -1 /*dst_height*/, null);
    }
    }


    private native Bitmap _getFrameAtTime(long timeUs, int option, int width, int height);
    private native Bitmap _getFrameAtTime(
            long timeUs, int option, int width, int height, @Nullable BitmapParams params);


    public static final class BitmapParams {
    public static final class BitmapParams {
        private Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
        private Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
+13 −6
Original line number Original line Diff line number Diff line
@@ -350,9 +350,10 @@ static jobject getBitmapFromVideoFrame(
    return jBitmap;
    return jBitmap;
}
}


static int getColorFormat(JNIEnv *env, jobject options) {
static int getColorFormat(JNIEnv *env, jobject options,
        int defaultPreferred = HAL_PIXEL_FORMAT_RGBA_8888) {
    if (options == NULL) {
    if (options == NULL) {
        return HAL_PIXEL_FORMAT_RGBA_8888;
        return defaultPreferred;
    }
    }


    ScopedLocalRef<jobject> inConfig(env, env->GetObjectField(options, fields.inPreferredConfig));
    ScopedLocalRef<jobject> inConfig(env, env->GetObjectField(options, fields.inPreferredConfig));
@@ -383,7 +384,8 @@ static SkColorType setOutColorType(JNIEnv *env, int colorFormat, jobject options
}
}


static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
        JNIEnv *env, jobject thiz, jlong timeUs, jint option, jint dst_width, jint dst_height)
        JNIEnv *env, jobject thiz, jlong timeUs, jint option,
        jint dst_width, jint dst_height, jobject params)
{
{
    ALOGV("getFrameAtTime: %lld us option: %d dst width: %d heigh: %d",
    ALOGV("getFrameAtTime: %lld us option: %d dst width: %d heigh: %d",
            (long long)timeUs, option, dst_width, dst_height);
            (long long)timeUs, option, dst_width, dst_height);
@@ -392,10 +394,13 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
        jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
        jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
        return NULL;
        return NULL;
    }
    }
    // For getFrameAtTime family of calls, default to HAL_PIXEL_FORMAT_RGB_565
    // to keep the behavior consistent with older releases
    int colorFormat = getColorFormat(env, params, HAL_PIXEL_FORMAT_RGB_565);


    // Call native method to retrieve a video frame
    // Call native method to retrieve a video frame
    VideoFrame *videoFrame = NULL;
    VideoFrame *videoFrame = NULL;
    sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option);
    sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option, colorFormat);
    // TODO: Using unsecurePointer() has some associated security pitfalls
    // TODO: Using unsecurePointer() has some associated security pitfalls
    //       (see declaration for details).
    //       (see declaration for details).
    //       Either document why it is safe in this case or address the
    //       Either document why it is safe in this case or address the
@@ -408,7 +413,9 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
        return NULL;
        return NULL;
    }
    }


    return getBitmapFromVideoFrame(env, videoFrame, dst_width, dst_height, kRGB_565_SkColorType);
    SkColorType outColorType = setOutColorType(env, colorFormat, params);

    return getBitmapFromVideoFrame(env, videoFrame, dst_width, dst_height, outColorType);
}
}


static jobject android_media_MediaMetadataRetriever_getImageAtIndex(
static jobject android_media_MediaMetadataRetriever_getImageAtIndex(
@@ -739,7 +746,7 @@ static const JNINativeMethod nativeMethods[] = {
                (void *)android_media_MediaMetadataRetriever_setDataSourceFD},
                (void *)android_media_MediaMetadataRetriever_setDataSourceFD},
        {"_setDataSource",   "(Landroid/media/MediaDataSource;)V",
        {"_setDataSource",   "(Landroid/media/MediaDataSource;)V",
                (void *)android_media_MediaMetadataRetriever_setDataSourceCallback},
                (void *)android_media_MediaMetadataRetriever_setDataSourceCallback},
        {"_getFrameAtTime", "(JIII)Landroid/graphics/Bitmap;",
        {"_getFrameAtTime", "(JIIILandroid/media/MediaMetadataRetriever$BitmapParams;)Landroid/graphics/Bitmap;",
                (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
                (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
        {
        {
            "_getImageAtIndex",
            "_getImageAtIndex",