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

Commit a89f6e1b authored by Chong Zhang's avatar Chong Zhang
Browse files

heif: add option for specifying bitmap pixel format

Add an option similar to BitmapFactory.Options to the bitmap
extraction APIs added in P to allow the app to specify bitmap's
pixel format. MediaMetadataRetriever's old getFrameAtTime()
only allows extraction in RGB565, for image use case the bitdepth
could be too low.

Also change return type of getFramesAtIndex to List as
Lint is complaining about returning raw arrays.

bug: 63633199
bug: 73886998

Change-Id: I40f0a421c767483e32c7744180dc5a187681e066
parent 3a1b5d18
Loading
Loading
Loading
Loading
+11 −4
Original line number Diff line number Diff line
@@ -24015,13 +24015,13 @@ package android.media {
    ctor public MediaMetadataRetriever();
    method public java.lang.String extractMetadata(int);
    method public byte[] getEmbeddedPicture();
    method public android.graphics.Bitmap getFrameAtIndex(int);
    method public android.graphics.Bitmap getFrameAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams);
    method public android.graphics.Bitmap getFrameAtTime(long, int);
    method public android.graphics.Bitmap getFrameAtTime(long);
    method public android.graphics.Bitmap getFrameAtTime();
    method public android.graphics.Bitmap[] getFramesAtIndex(int, int);
    method public android.graphics.Bitmap getImageAtIndex(int);
    method public android.graphics.Bitmap getPrimaryImage();
    method public java.util.List<android.graphics.Bitmap> getFramesAtIndex(int, int, android.media.MediaMetadataRetriever.BitmapParams);
    method public android.graphics.Bitmap getImageAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams);
    method public android.graphics.Bitmap getPrimaryImage(android.media.MediaMetadataRetriever.BitmapParams);
    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;
@@ -24067,6 +24067,13 @@ package android.media {
    field public static final int OPTION_PREVIOUS_SYNC = 0; // 0x0
  }
  public static final class MediaMetadataRetriever.BitmapParams {
    ctor public MediaMetadataRetriever.BitmapParams();
    method public android.graphics.Bitmap.Config getActualConfig();
    method public android.graphics.Bitmap.Config getPreferredConfig();
    method public void setPreferredConfig(android.graphics.Bitmap.Config);
  }
  public final class MediaMuxer {
    ctor public MediaMuxer(java.lang.String, int) throws java.io.IOException;
    ctor public MediaMuxer(java.io.FileDescriptor, int) throws java.io.IOException;
+95 −19
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
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;
@@ -30,7 +32,7 @@ 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;

/**
@@ -367,27 +369,79 @@ public class MediaMetadataRetriever

    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).
     *        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 #getFramesAtIndex(int, int)
     * @see #getFramesAtIndex(int, int, BitmapParams)
     */
    public Bitmap getFrameAtIndex(int frameIndex) {
        Bitmap[] bitmaps = getFramesAtIndex(frameIndex, 1);
        if (bitmaps == null || bitmaps.length < 1) {
    public Bitmap getFrameAtIndex(int frameIndex, @Nullable BitmapParams params) {
        List<Bitmap> bitmaps = getFramesAtIndex(frameIndex, 1, params);
        if (bitmaps == null || bitmaps.size() < 1) {
            return null;
        }
        return bitmaps[0];
        return bitmaps.get(0);
    }

    /**
@@ -395,24 +449,31 @@ public class MediaMetadataRetriever
     * 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)} for efficiency.
     * 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).
     *        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
     *         stream doesn't contain at least numFrames starting at frameIndex.

     * @return An array of Bitmaps containing the requested video frames. The returned
     * @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)
     * @see #getFrameAtIndex(int, BitmapParams)
     */
    public Bitmap[] getFramesAtIndex(int frameIndex, int numFrames) {
    public List<Bitmap> getFramesAtIndex(
            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");
        }
@@ -424,24 +485,32 @@ public class MediaMetadataRetriever
            throw new IllegalArgumentException("Invalid frameIndex or numFrames: "
                + frameIndex + ", " + numFrames);
        }
        return _getFrameAtIndex(frameIndex, numFrames);
        return _getFrameAtIndex(frameIndex, numFrames, params);
    }
    private native Bitmap[] _getFrameAtIndex(int frameIndex, int numFrames);
    private native List<Bitmap> _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, with negative value indicating
     *        the primary 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 #getPrimaryImage
     * @see #getPrimaryImage(BitmapParams)
     */
    public Bitmap getImageAtIndex(int imageIndex) {
    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");
        }
@@ -451,24 +520,31 @@ public class MediaMetadataRetriever
            throw new IllegalArgumentException("Invalid image index: " + imageCount);
        }

        return _getImageAtIndex(imageIndex);
        return _getImageAtIndex(imageIndex, params);
    }

    /**
     * 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).
     *        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)
     * @see #getImageAtIndex(int, BitmapParams)
     */
    public Bitmap getPrimaryImage() {
        return getImageAtIndex(-1);
    public Bitmap getPrimaryImage(@Nullable BitmapParams params) {
        return getImageAtIndex(-1, params);
    }

    private native Bitmap _getImageAtIndex(int imageIndex);
    private native Bitmap _getImageAtIndex(int imageIndex, @Nullable BitmapParams params);

    /**
     * Call this method after setDataSource(). This method finds the optional
+159 −46
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include <media/IMediaHTTPService.h>
#include <media/mediametadataretriever.h>
#include <media/mediascanner.h>
#include <nativehelper/ScopedLocalRef.h>
#include <private/media/VideoFrame.h>

#include "jni.h"
@@ -45,6 +46,12 @@ struct fields_t {
    jmethodID createScaledBitmapMethod;
    jclass configClazz;  // Must be a global ref
    jmethodID createConfigMethod;
    jclass bitmapParamsClazz; // Must be a global ref
    jfieldID inPreferredConfig;
    jfieldID outActualConfig;
    jclass arrayListClazz; // Must be a global ref
    jmethodID arrayListInit;
    jmethodID arrayListAdd;
};

static fields_t fields;
@@ -254,16 +261,18 @@ static void rotate(T *dst, const T *src, size_t width, size_t height, int angle)
}

static jobject getBitmapFromVideoFrame(
        JNIEnv *env, VideoFrame *videoFrame, jint dst_width, jint dst_height) {
        JNIEnv *env, VideoFrame *videoFrame, jint dst_width, jint dst_height,
        SkColorType outColorType) {
    ALOGV("getBitmapFromVideoFrame: dimension = %dx%d and bytes = %d",
            videoFrame->mDisplayWidth,
            videoFrame->mDisplayHeight,
            videoFrame->mSize);

    jobject config = env->CallStaticObjectMethod(
    ScopedLocalRef<jobject> config(env,
            env->CallStaticObjectMethod(
                    fields.configClazz,
                    fields.createConfigMethod,
                        GraphicsJNI::colorTypeToLegacyBitmapConfig(kRGB_565_SkColorType));
                    GraphicsJNI::colorTypeToLegacyBitmapConfig(outColorType)));

    uint32_t width, height, displayWidth, displayHeight;
    bool swapWidthAndHeight = false;
@@ -285,7 +294,7 @@ static jobject getBitmapFromVideoFrame(
                            fields.createBitmapMethod,
                            width,
                            height,
                            config);
                            config.get());
    if (jBitmap == NULL) {
        if (env->ExceptionCheck()) {
            env->ExceptionClear();
@@ -297,11 +306,19 @@ static jobject getBitmapFromVideoFrame(
    SkBitmap bitmap;
    GraphicsJNI::getSkBitmap(env, jBitmap, &bitmap);

    if (outColorType == kRGB_565_SkColorType) {
        rotate((uint16_t*)bitmap.getPixels(),
               (uint16_t*)((char*)videoFrame + sizeof(VideoFrame)),
               videoFrame->mWidth,
               videoFrame->mHeight,
               videoFrame->mRotationAngle);
    } else {
        rotate((uint32_t*)bitmap.getPixels(),
               (uint32_t*)((char*)videoFrame + sizeof(VideoFrame)),
               videoFrame->mWidth,
               videoFrame->mHeight,
               videoFrame->mRotationAngle);
    }

    if (dst_width <= 0 || dst_height <= 0) {
        dst_width = displayWidth;
@@ -323,12 +340,46 @@ static jobject getBitmapFromVideoFrame(
                                dst_width,
                                dst_height,
                                true);

        env->DeleteLocalRef(jBitmap);
        return scaledBitmap;
    }

    return jBitmap;
}

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

    ScopedLocalRef<jobject> inConfig(env, env->GetObjectField(options, fields.inPreferredConfig));
    SkColorType prefColorType = GraphicsJNI::getNativeBitmapColorType(env, inConfig.get());

    if (prefColorType == kRGB_565_SkColorType) {
        return HAL_PIXEL_FORMAT_RGB_565;
    }
    return HAL_PIXEL_FORMAT_RGBA_8888;
}

static SkColorType setOutColorType(JNIEnv *env, int colorFormat, jobject options) {
    SkColorType outColorType = kN32_SkColorType;
    if (colorFormat == HAL_PIXEL_FORMAT_RGB_565) {
        outColorType = kRGB_565_SkColorType;
    }

    if (options != NULL) {
        ScopedLocalRef<jobject> config(env,
                env->CallStaticObjectMethod(
                        fields.configClazz,
                        fields.createConfigMethod,
                        GraphicsJNI::colorTypeToLegacyBitmapConfig(outColorType)));

        env->SetObjectField(options, fields.outActualConfig, config.get());
    }
    return outColorType;
}

static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
        JNIEnv *env, jobject thiz, jlong timeUs, jint option, jint dst_width, jint dst_height)
{
@@ -351,11 +402,11 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
        return NULL;
    }

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

static jobject android_media_MediaMetadataRetriever_getImageAtIndex(
        JNIEnv *env, jobject thiz, jint index)
        JNIEnv *env, jobject thiz, jint index, jobject params)
{
    ALOGV("getImageAtIndex: index %d", index);
    sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
@@ -364,9 +415,11 @@ static jobject android_media_MediaMetadataRetriever_getImageAtIndex(
        return NULL;
    }

    int colorFormat = getColorFormat(env, params);

    // Call native method to retrieve an image
    VideoFrame *videoFrame = NULL;
    sp<IMemory> frameMemory = retriever->getImageAtIndex(index);
    sp<IMemory> frameMemory = retriever->getImageAtIndex(index, colorFormat);
    if (frameMemory != 0) {  // cast the shared structure to a VideoFrame object
        videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
    }
@@ -375,11 +428,13 @@ static jobject android_media_MediaMetadataRetriever_getImageAtIndex(
        return NULL;
    }

    return getBitmapFromVideoFrame(env, videoFrame, -1, -1);
    SkColorType outColorType = setOutColorType(env, colorFormat, params);

    return getBitmapFromVideoFrame(env, videoFrame, -1, -1, outColorType);
}

static jobjectArray android_media_MediaMetadataRetriever_getFrameAtIndex(
        JNIEnv *env, jobject thiz, jint frameIndex, jint numFrames)
static jobject android_media_MediaMetadataRetriever_getFrameAtIndex(
        JNIEnv *env, jobject thiz, jint frameIndex, jint numFrames, jobject params)
{
    ALOGV("getFrameAtIndex: frameIndex %d, numFrames %d", frameIndex, numFrames);
    sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
@@ -389,31 +444,34 @@ static jobjectArray android_media_MediaMetadataRetriever_getFrameAtIndex(
        return NULL;
    }

    int colorFormat = getColorFormat(env, params);

    std::vector<sp<IMemory> > frames;
    status_t err = retriever->getFrameAtIndex(&frames, frameIndex, numFrames);
    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());
        return NULL;
    }

    jobjectArray bitmapArrayObj = env->NewObjectArray(
            frames.size(), fields.bitmapClazz, NULL);
    if (bitmapArrayObj == NULL) {
        ALOGE("can't create bitmap array object");
    jobject arrayList = env->NewObject(fields.arrayListClazz, fields.arrayListInit);
    if (arrayList == NULL) {
        ALOGE("can't create bitmap array list object");
        return NULL;
    }

    SkColorType outColorType = setOutColorType(env, colorFormat, params);

    for (size_t i = 0; i < frames.size(); i++) {
        if (frames[i] == NULL || frames[i]->pointer() == NULL) {
            ALOGE("video frame at index %zu is a NULL pointer", frameIndex + i);
            continue;
        }
        VideoFrame *videoFrame = static_cast<VideoFrame *>(frames[i]->pointer());
        jobject bitmapObj = getBitmapFromVideoFrame(env, videoFrame, -1, -1);
        env->SetObjectArrayElement(bitmapArrayObj, i, bitmapObj);
        jobject bitmapObj = getBitmapFromVideoFrame(env, videoFrame, -1, -1, outColorType);
        env->CallBooleanMethod(arrayList, fields.arrayListAdd, bitmapObj);
        env->DeleteLocalRef(bitmapObj);
    }
    return bitmapArrayObj;
    return arrayList;
}

static jbyteArray android_media_MediaMetadataRetriever_getEmbeddedPicture(
@@ -488,21 +546,21 @@ static void android_media_MediaMetadataRetriever_native_finalize(JNIEnv *env, jo
// first time an instance of this class is used.
static void android_media_MediaMetadataRetriever_native_init(JNIEnv *env)
{
    jclass clazz = env->FindClass(kClassPathName);
    if (clazz == NULL) {
    ScopedLocalRef<jclass> clazz(env, env->FindClass(kClassPathName));
    if (clazz.get() == NULL) {
        return;
    }

    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
    fields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
    if (fields.context == NULL) {
        return;
    }

    jclass bitmapClazz = env->FindClass("android/graphics/Bitmap");
    if (bitmapClazz == NULL) {
    clazz.reset(env->FindClass("android/graphics/Bitmap"));
    if (clazz.get() == NULL) {
        return;
    }
    fields.bitmapClazz = (jclass) env->NewGlobalRef(bitmapClazz);
    fields.bitmapClazz = (jclass) env->NewGlobalRef(clazz.get());
    if (fields.bitmapClazz == NULL) {
        return;
    }
@@ -521,11 +579,11 @@ static void android_media_MediaMetadataRetriever_native_init(JNIEnv *env)
        return;
    }

    jclass configClazz = env->FindClass("android/graphics/Bitmap$Config");
    if (configClazz == NULL) {
    clazz.reset(env->FindClass("android/graphics/Bitmap$Config"));
    if (clazz.get() == NULL) {
        return;
    }
    fields.configClazz = (jclass) env->NewGlobalRef(configClazz);
    fields.configClazz = (jclass) env->NewGlobalRef(clazz.get());
    if (fields.configClazz == NULL) {
        return;
    }
@@ -535,6 +593,42 @@ static void android_media_MediaMetadataRetriever_native_init(JNIEnv *env)
    if (fields.createConfigMethod == NULL) {
        return;
    }

    clazz.reset(env->FindClass("android/media/MediaMetadataRetriever$BitmapParams"));
    if (clazz.get() == NULL) {
        return;
    }
    fields.bitmapParamsClazz = (jclass) env->NewGlobalRef(clazz.get());
    if (fields.bitmapParamsClazz == NULL) {
        return;
    }
    fields.inPreferredConfig = env->GetFieldID(fields.bitmapParamsClazz,
            "inPreferredConfig", "Landroid/graphics/Bitmap$Config;");
    if (fields.inPreferredConfig == NULL) {
        return;
    }
    fields.outActualConfig = env->GetFieldID(fields.bitmapParamsClazz,
            "outActualConfig", "Landroid/graphics/Bitmap$Config;");
    if (fields.outActualConfig == NULL) {
        return;
    }

    clazz.reset(env->FindClass("java/util/ArrayList"));
    if (clazz.get() == NULL) {
        return;
    }
    fields.arrayListClazz = (jclass) env->NewGlobalRef(clazz.get());
    if (fields.arrayListClazz == NULL) {
        return;
    }
    fields.arrayListInit = env->GetMethodID(clazz.get(), "<init>", "()V");
    if (fields.arrayListInit == NULL) {
        return;
    }
    fields.arrayListAdd = env->GetMethodID(clazz.get(), "add", "(Ljava/lang/Object;)Z");
    if (fields.arrayListAdd == NULL) {
        return;
    }
}

static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobject thiz)
@@ -556,17 +650,36 @@ static const JNINativeMethod nativeMethods[] = {
            (void *)android_media_MediaMetadataRetriever_setDataSourceAndHeaders
        },

        {"setDataSource",   "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaMetadataRetriever_setDataSourceFD},
        {"_setDataSource",   "(Landroid/media/MediaDataSource;)V", (void *)android_media_MediaMetadataRetriever_setDataSourceCallback},
        {"_getFrameAtTime", "(JIII)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
        {"_getImageAtIndex", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getImageAtIndex},
        {"_getFrameAtIndex", "(II)[Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtIndex},
        {"extractMetadata", "(I)Ljava/lang/String;", (void *)android_media_MediaMetadataRetriever_extractMetadata},
        {"getEmbeddedPicture", "(I)[B", (void *)android_media_MediaMetadataRetriever_getEmbeddedPicture},
        {"release",         "()V", (void *)android_media_MediaMetadataRetriever_release},
        {"native_finalize", "()V", (void *)android_media_MediaMetadataRetriever_native_finalize},
        {"native_setup",    "()V", (void *)android_media_MediaMetadataRetriever_native_setup},
        {"native_init",     "()V", (void *)android_media_MediaMetadataRetriever_native_init},
        {"setDataSource",   "(Ljava/io/FileDescriptor;JJ)V",
                (void *)android_media_MediaMetadataRetriever_setDataSourceFD},
        {"_setDataSource",   "(Landroid/media/MediaDataSource;)V",
                (void *)android_media_MediaMetadataRetriever_setDataSourceCallback},
        {"_getFrameAtTime", "(JIII)Landroid/graphics/Bitmap;",
                (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
        {
            "_getImageAtIndex",
            "(ILandroid/media/MediaMetadataRetriever$BitmapParams;)Landroid/graphics/Bitmap;",
            (void *)android_media_MediaMetadataRetriever_getImageAtIndex
        },

        {
            "_getFrameAtIndex",
            "(IILandroid/media/MediaMetadataRetriever$BitmapParams;)Ljava/util/List;",
            (void *)android_media_MediaMetadataRetriever_getFrameAtIndex
        },

        {"extractMetadata", "(I)Ljava/lang/String;",
                (void *)android_media_MediaMetadataRetriever_extractMetadata},
        {"getEmbeddedPicture", "(I)[B",
                (void *)android_media_MediaMetadataRetriever_getEmbeddedPicture},
        {"release",         "()V",
                (void *)android_media_MediaMetadataRetriever_release},
        {"native_finalize", "()V",
                (void *)android_media_MediaMetadataRetriever_native_finalize},
        {"native_setup",    "()V",
                (void *)android_media_MediaMetadataRetriever_native_setup},
        {"native_init",     "()V",
                (void *)android_media_MediaMetadataRetriever_native_init},
};

// This function only registers the native methods, and is called from