Loading api/current.txt +11 −4 Original line number Diff line number Diff line Loading @@ -24052,13 +24052,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; Loading Loading @@ -24104,6 +24104,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; media/java/android/media/MediaMetadataRetriever.java +95 −19 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; /** Loading Loading @@ -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); } /** Loading @@ -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"); } Loading @@ -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"); } Loading @@ -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 Loading media/jni/android_media_MediaMetadataRetriever.cpp +159 −46 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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; Loading Loading @@ -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; Loading @@ -285,7 +294,7 @@ static jobject getBitmapFromVideoFrame( fields.createBitmapMethod, width, height, config); config.get()); if (jBitmap == NULL) { if (env->ExceptionCheck()) { env->ExceptionClear(); Loading @@ -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; Loading @@ -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) { Loading @@ -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); Loading @@ -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()); } Loading @@ -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); Loading @@ -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( Loading Loading @@ -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; } Loading @@ -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; } Loading @@ -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) Loading @@ -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 Loading Loading
api/current.txt +11 −4 Original line number Diff line number Diff line Loading @@ -24052,13 +24052,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; Loading Loading @@ -24104,6 +24104,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;
media/java/android/media/MediaMetadataRetriever.java +95 −19 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; /** Loading Loading @@ -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); } /** Loading @@ -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"); } Loading @@ -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"); } Loading @@ -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 Loading
media/jni/android_media_MediaMetadataRetriever.cpp +159 −46 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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; Loading Loading @@ -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; Loading @@ -285,7 +294,7 @@ static jobject getBitmapFromVideoFrame( fields.createBitmapMethod, width, height, config); config.get()); if (jBitmap == NULL) { if (env->ExceptionCheck()) { env->ExceptionClear(); Loading @@ -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; Loading @@ -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) { Loading @@ -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); Loading @@ -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()); } Loading @@ -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); Loading @@ -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( Loading Loading @@ -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; } Loading @@ -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; } Loading @@ -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) Loading @@ -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 Loading