Merge "media: Add scaled video thumbnail extractor api." into oc-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index 3b868ad..e299720 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22742,6 +22742,7 @@
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 getScaledFrameAtTime(long, int, int, int);
method public void release();
method public void setDataSource(java.lang.String) throws java.lang.IllegalArgumentException;
method public void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.lang.IllegalArgumentException;
diff --git a/api/system-current.txt b/api/system-current.txt
index fc1b6ed..1b292cc 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -24686,6 +24686,7 @@
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 getScaledFrameAtTime(long, int, int, int);
method public void release();
method public void setDataSource(java.lang.String) throws java.lang.IllegalArgumentException;
method public void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.lang.IllegalArgumentException;
diff --git a/api/test-current.txt b/api/test-current.txt
index f50ef55..7aeb52e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -22878,6 +22878,7 @@
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 getScaledFrameAtTime(long, int, int, int);
method public void release();
method public void setDataSource(java.lang.String) throws java.lang.IllegalArgumentException;
method public void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.lang.IllegalArgumentException;
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 7dd70d4..6664456 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -246,7 +246,7 @@
* {@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
+ * @return A Bitmap containing a representative video frame, which
* can be null, if such a frame cannot be retrieved.
*/
public Bitmap getFrameAtTime(long timeUs, int option) {
@@ -255,7 +255,58 @@
throw new IllegalArgumentException("Unsupported option: " + option);
}
- return _getFrameAtTime(timeUs, option);
+ return _getFrameAtTime(timeUs, option, -1 /*dst_width*/, -1 /*dst_height*/);
+ }
+
+ /**
+ * Retrieve a video frame near a given timestamp scaled to a desired size.
+ * Call this method after setDataSource(). This method finds a representative
+ * frame close to the given time position by considering the given option
+ * if possible, and returns it as a bitmap with same aspect ratio as the source
+ * while scaling it so that it fits into the desired size of dst_width by dst_height.
+ * This is useful for generating a thumbnail for an input data source or just to
+ * obtain a scaled frame at the given time position.
+ *
+ * @param timeUs The time position in microseconds where the frame will be retrieved.
+ * When retrieving the frame at the given time position, there is no
+ * guarantee that the data source has a frame located at the position.
+ * When this happens, a frame nearby will be returned. If timeUs is
+ * negative, time position and option will ignored, and any frame
+ * that the implementation considers as representative may be returned.
+ *
+ * @param option a hint on how the frame is found. Use
+ * {@link #OPTION_PREVIOUS_SYNC} if one wants to retrieve a sync frame
+ * that has a timestamp earlier than or the same as timeUs. Use
+ * {@link #OPTION_NEXT_SYNC} if one wants to retrieve a sync frame
+ * that has a timestamp later than or the same as timeUs. Use
+ * {@link #OPTION_CLOSEST_SYNC} if one wants to retrieve a sync frame
+ * that has a timestamp closest to or the same as timeUs. Use
+ * {@link #OPTION_CLOSEST} if one wants to retrieve a frame that may
+ * or may not be a sync frame but is closest to or the same as timeUs.
+ * {@link #OPTION_CLOSEST} often has larger performance overhead compared
+ * to the other options if there is no sync frame located at timeUs.
+ *
+ * @param dst_width expected output bitmap width
+ * @param dst_height expected output bitmap height
+ * @return A Bitmap of size not larger than dst_width by dst_height containing a
+ * scaled video frame, which can be null, if such a frame cannot be retrieved.
+ * @throws IllegalArgumentException if passed in invalid option or width by height
+ * is less than or equal to 0.
+ */
+ public Bitmap getScaledFrameAtTime(
+ long timeUs, int option, int dst_width, int dst_height) {
+ if (option < OPTION_PREVIOUS_SYNC ||
+ option > OPTION_CLOSEST) {
+ throw new IllegalArgumentException("Unsupported option: " + option);
+ }
+ if (dst_width <= 0) {
+ throw new IllegalArgumentException("Invalid width: " + dst_width);
+ }
+ if (dst_height <= 0) {
+ throw new IllegalArgumentException("Invalid height: " + dst_height);
+ }
+
+ return _getFrameAtTime(timeUs, option, dst_width, dst_height);
}
/**
@@ -273,8 +324,8 @@
* negative, time position and option will ignored, and any frame
* that the implementation considers as representative may be returned.
*
- * @return A Bitmap containing a representative video frame, which
- * can be null, if such a frame cannot be retrieved.
+ * @return A Bitmap of size dst_widthxdst_height containing a representative
+ * video frame, which can be null, if such a frame cannot be retrieved.
*
* @see #getFrameAtTime(long, int)
*/
@@ -297,17 +348,16 @@
* @see #getFrameAtTime(long, int)
*/
public Bitmap getFrameAtTime() {
- return getFrameAtTime(-1, OPTION_CLOSEST_SYNC);
+ return _getFrameAtTime(-1, OPTION_CLOSEST_SYNC, -1 /*dst_width*/, -1 /*dst_height*/);
}
- private native Bitmap _getFrameAtTime(long timeUs, int option);
+ private native Bitmap _getFrameAtTime(long timeUs, int option, int width, int height);
-
/**
* Call this method after setDataSource(). This method finds the optional
* graphic or album/cover art associated associated with the data source. If
* there are more than one pictures, (any) one of them is returned.
- *
+ *
* @return null if no such graphic is found.
*/
public byte[] getEmbeddedPicture() {
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 71f3856..4659ae1 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -244,9 +244,11 @@
}
}
-static jobject android_media_MediaMetadataRetriever_getFrameAtTime(JNIEnv *env, jobject thiz, jlong timeUs, jint option)
+static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
+ JNIEnv *env, jobject thiz, jlong timeUs, jint option, jint dst_width, jint dst_height)
{
- ALOGV("getFrameAtTime: %lld us option: %d", (long long)timeUs, option);
+ ALOGV("getFrameAtTime: %lld us option: %d dst width: %d heigh: %d",
+ (long long)timeUs, option, dst_width, dst_height);
MediaMetadataRetriever* retriever = getRetriever(env, thiz);
if (retriever == 0) {
jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
@@ -274,15 +276,19 @@
fields.createConfigMethod,
GraphicsJNI::colorTypeToLegacyBitmapConfig(kRGB_565_SkColorType));
- uint32_t width, height;
+ uint32_t width, height, displayWidth, displayHeight;
bool swapWidthAndHeight = false;
if (videoFrame->mRotationAngle == 90 || videoFrame->mRotationAngle == 270) {
width = videoFrame->mHeight;
height = videoFrame->mWidth;
swapWidthAndHeight = true;
+ displayWidth = videoFrame->mDisplayHeight;
+ displayHeight = videoFrame->mDisplayWidth;
} else {
width = videoFrame->mWidth;
height = videoFrame->mHeight;
+ displayWidth = videoFrame->mDisplayWidth;
+ displayHeight = videoFrame->mDisplayHeight;
}
jobject jBitmap = env->CallStaticObjectMethod(
@@ -308,22 +314,26 @@
videoFrame->mHeight,
videoFrame->mRotationAngle);
- if (videoFrame->mDisplayWidth != videoFrame->mWidth ||
- videoFrame->mDisplayHeight != videoFrame->mHeight) {
- uint32_t displayWidth = videoFrame->mDisplayWidth;
- uint32_t displayHeight = videoFrame->mDisplayHeight;
- if (swapWidthAndHeight) {
- displayWidth = videoFrame->mDisplayHeight;
- displayHeight = videoFrame->mDisplayWidth;
- }
+ if (dst_width <= 0 || dst_height <= 0) {
+ dst_width = displayWidth;
+ dst_height = displayHeight;
+ } else {
+ float factor = std::min((float)dst_width / (float)displayWidth,
+ (float)dst_height / (float)displayHeight);
+ dst_width = std::round(displayWidth * factor);
+ dst_height = std::round(displayHeight * factor);
+ }
+
+ if ((uint32_t)dst_width != videoFrame->mWidth ||
+ (uint32_t)dst_height != videoFrame->mHeight) {
ALOGV("Bitmap dimension is scaled from %dx%d to %dx%d",
- width, height, displayWidth, displayHeight);
+ width, height, dst_width, dst_height);
jobject scaledBitmap = env->CallStaticObjectMethod(fields.bitmapClazz,
- fields.createScaledBitmapMethod,
- jBitmap,
- displayWidth,
- displayHeight,
- true);
+ fields.createScaledBitmapMethod,
+ jBitmap,
+ dst_width,
+ dst_height,
+ true);
return scaledBitmap;
}
@@ -474,7 +484,7 @@
{"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaMetadataRetriever_setDataSourceFD},
{"_setDataSource", "(Landroid/media/MediaDataSource;)V", (void *)android_media_MediaMetadataRetriever_setDataSourceCallback},
- {"_getFrameAtTime", "(JI)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
+ {"_getFrameAtTime", "(JIII)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
{"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},