Prepare for publishing MediaMetadataRetriever as public API

step one:
o replaced captureFrame with getFrameAtTime
o removed getMode

bug - 2433195

Change-Id: I38a8cecef29014692f0b08b8818326e3ebb40a12
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 7e7f6d1..07e506a 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -681,10 +681,12 @@
                         METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL),
                      (status_t)OK);
 
-            sp<IMemory> mem = retriever->captureFrame();
+            sp<IMemory> mem =
+                    retriever->getFrameAtTime(-1,
+                                    MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
 
             if (mem != NULL) {
-                printf("captureFrame(%s) => OK\n", filename);
+                printf("getFrameAtTime(%s) => OK\n", filename);
 
                 VideoFrame *frame = (VideoFrame *)mem->pointer();
 
@@ -704,7 +706,7 @@
                 if (mem != NULL) {
                     printf("extractAlbumArt(%s) => OK\n", filename);
                 } else {
-                    printf("both captureFrame and extractAlbumArt "
+                    printf("both getFrameAtTime and extractAlbumArt "
                            "failed on file '%s'.\n", filename);
                 }
             }
diff --git a/include/media/IMediaMetadataRetriever.h b/include/media/IMediaMetadataRetriever.h
index 9baba8e..e517cf0 100644
--- a/include/media/IMediaMetadataRetriever.h
+++ b/include/media/IMediaMetadataRetriever.h
@@ -33,8 +33,7 @@
     virtual status_t        setDataSource(const char* srcUrl) = 0;
     virtual status_t        setDataSource(int fd, int64_t offset, int64_t length) = 0;
     virtual status_t        setMode(int mode) = 0;
-    virtual status_t        getMode(int* mode) const = 0;
-    virtual sp<IMemory>     captureFrame() = 0;
+    virtual sp<IMemory>     getFrameAtTime(int64_t timeUs, int option) = 0;
     virtual sp<IMemory>     extractAlbumArt() = 0;
     virtual const char*     extractMetadata(int keyCode) = 0;
 };
diff --git a/include/media/MediaMetadataRetrieverInterface.h b/include/media/MediaMetadataRetrieverInterface.h
index ff57774..717849d 100644
--- a/include/media/MediaMetadataRetrieverInterface.h
+++ b/include/media/MediaMetadataRetrieverInterface.h
@@ -33,8 +33,7 @@
     virtual status_t    setDataSource(const char *url) = 0;
     virtual status_t    setDataSource(int fd, int64_t offset, int64_t length) = 0;
     virtual status_t    setMode(int mode) = 0;
-    virtual status_t    getMode(int* mode) const = 0;
-    virtual VideoFrame* captureFrame() = 0;
+    virtual VideoFrame* getFrameAtTime(int64_t timeUs, int option) = 0;
     virtual MediaAlbumArt* extractAlbumArt() = 0;
     virtual const char* extractMetadata(int keyCode) = 0;
 };
@@ -67,7 +66,7 @@
                         }
 
     virtual status_t    getMode(int* mode) const { *mode = mMode; return NO_ERROR; }
-    virtual VideoFrame* captureFrame() { return NULL; }
+    virtual VideoFrame* getFrameAtTime(int64_t timeUs, int option) { return NULL; }
     virtual MediaAlbumArt* extractAlbumArt() { return NULL; }
     virtual const char* extractMetadata(int keyCode) { return NULL; }
 
diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h
index dbbcc49..ed54b37 100644
--- a/include/media/mediametadataretriever.h
+++ b/include/media/mediametadataretriever.h
@@ -81,8 +81,7 @@
     status_t setDataSource(const char* dataSourceUrl);
     status_t setDataSource(int fd, int64_t offset, int64_t length);
     status_t setMode(int mode);
-    status_t getMode(int* mode);
-    sp<IMemory> captureFrame();
+    sp<IMemory> getFrameAtTime(int64_t timeUs, int option);
     sp<IMemory> extractAlbumArt();
     const char* extractMetadata(int keyCode);
 
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 681751b..c92fc23 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -71,12 +71,6 @@
     public native void setMode(int mode);
     
     /**
-     * @return the current mode of operation. A negative return value indicates
-     * some runtime error has occurred.
-     */
-    public native int getMode();
-
-    /**
      * Sets the data source (file pathname) to use. Call this
      * method before the rest of the methods in this class. This method may be
      * time-consuming.
@@ -190,13 +184,94 @@
 
     /**
      * Call this method after setDataSource(). This method finds a
-     * representative frame if successful and returns it as a bitmap. This is
-     * useful for generating a thumbnail for an input media source.
-     * 
+     * representative frame close to the given time position by considering
+     * the given option if possible, and returns it as a bitmap. This is
+     * useful for generating a thumbnail for an input data source or just
+     * obtain and display a frame at the given time position.
+     *
+     * @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.
      */
-    public native Bitmap captureFrame();
+    public Bitmap getFrameAtTime(long timeUs, int option) {
+        if (option < OPTION_PREVIOUS_SYNC ||
+            option > OPTION_CLOSEST) {
+            throw new IllegalArgumentException("Unsupported option: " + option);
+        }
+
+        return _getFrameAtTime(timeUs, option);
+    }
+
+    /**
+     * Call this method after setDataSource(). This method finds a
+     * representative frame close to the given time position if possible,
+     * and returns it as a bitmap. This is useful for generating a thumbnail
+     * for an input data source. Call this method if one does not care
+     * how the frame is found as long as it is close to the given time;
+     * otherwise, please call {@link getFrameAtTime(long, int)}.
+     *
+     * @param timeUs The time position where the frame will be retrieved.
+     * When retrieving the frame at the given time position, there is no
+     * guarentee 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.
+     *
+     * @return A Bitmap containing a representative video frame, which
+     *         can be null, if such a frame cannot be retrieved.
+     *
+     * @see #getFrameAtTime(long, int)
+     */
+    public Bitmap getFrameAtTime(long timeUs) {
+        return getFrameAtTime(timeUs, OPTION_CLOSEST_SYNC);
+    }
+
+    /**
+     * Call this method after setDataSource(). This method finds a
+     * representative frame at any time position if possible,
+     * and returns it as a bitmap. This is useful for generating a thumbnail
+     * for an input data source. Call this method if one does not
+     * care about where the frame is located; otherwise, please call
+     * {@link getFrameAtTime(long)} or {@link getFrameAtTime(long, int)}
+     *
+     * @return A Bitmap containing a representative video frame, which
+     *         can be null, if such a frame cannot be retrieved.
+     *
+     * @see #getFrameAtTime(long)
+     * @see #getFrameAtTime(long, int)
+     */
+    public Bitmap getFrameAtTime() {
+        return getFrameAtTime(-1, OPTION_CLOSEST_SYNC);
+    }
+
+    /**
+     * FIXME
+     * To be removed and replaced by getFrameAt().
+     */
+    public Bitmap captureFrame() {
+        return _getFrameAtTime(-1, OPTION_CLOSEST_SYNC);
+    }
+
+    private native Bitmap _getFrameAtTime(long timeUs, int option);
+
     
     /**
      * Call this method after setDataSource(). This method finds the optional
@@ -229,6 +304,20 @@
     public static final int MODE_GET_METADATA_ONLY  = 0x01;
     public static final int MODE_CAPTURE_FRAME_ONLY = 0x02;
 
+    /**
+     * Option used in method {@link getFrameAtTime(long, int)} to get a
+     * frame at a specified location.
+     *
+     * @see #getFrameAtTime(long, int)
+     */
+    /* Do not change these values without updating their counterparts
+     * in include/media/stagefright/MediaSource.h!
+     */
+    public static final int OPTION_PREVIOUS_SYNC    = 0x00;
+    public static final int OPTION_NEXT_SYNC        = 0x01;
+    public static final int OPTION_CLOSEST_SYNC     = 0x02;
+    public static final int OPTION_CLOSEST          = 0x03;
+
     /*
      * Do not change these values without updating their counterparts
      * in include/media/mediametadataretriever.h!
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 63e9dc8..5904bfe 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -142,22 +142,9 @@
     process_media_retriever_call(env, retriever->setMode(mode), "java/lang/RuntimeException", "setMode failed");
 }
 
-static int android_media_MediaMetadataRetriever_getMode(JNIEnv *env, jobject thiz)
+static jobject android_media_MediaMetadataRetriever_getFrameAtTime(JNIEnv *env, jobject thiz, jlong timeUs, jint option)
 {
-    LOGV("getMode");
-    MediaMetadataRetriever* retriever = getRetriever(env, thiz);
-    if (retriever == 0) {
-        jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
-        return -1;  // Error
-    }
-    int mode = -1;
-    retriever->getMode(&mode);
-    return mode;
-}
-
-static jobject android_media_MediaMetadataRetriever_captureFrame(JNIEnv *env, jobject thiz)
-{
-    LOGV("captureFrame");
+    LOGV("getFrameAtTime: %lld us option: %d", timeUs, option);
     MediaMetadataRetriever* retriever = getRetriever(env, thiz);
     if (retriever == 0) {
         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
@@ -166,12 +153,12 @@
 
     // Call native method to retrieve a video frame
     VideoFrame *videoFrame = NULL;
-    sp<IMemory> frameMemory = retriever->captureFrame();
+    sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option);
     if (frameMemory != 0) {  // cast the shared structure to a VideoFrame object
         videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
     }
     if (videoFrame == NULL) {
-        LOGE("captureFrame: videoFrame is a NULL pointer");
+        LOGE("getFrameAtTime: videoFrame is a NULL pointer");
         return NULL;
     }
 
@@ -213,7 +200,7 @@
     // Create a SkBitmap to hold the pixels
     SkBitmap *bitmap = new SkBitmap();
     if (bitmap == NULL) {
-        LOGE("captureFrame: cannot instantiate a SkBitmap object.");
+        LOGE("getFrameAtTime: cannot instantiate a SkBitmap object.");
         return NULL;
     }
     bitmap->setConfig(SkBitmap::kRGB_565_Config, videoFrame->mDisplayWidth, videoFrame->mDisplayHeight);
@@ -366,8 +353,7 @@
         {"setDataSource",   "(Ljava/lang/String;)V", (void *)android_media_MediaMetadataRetriever_setDataSource},
         {"setDataSource",   "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaMetadataRetriever_setDataSourceFD},
         {"setMode",         "(I)V", (void *)android_media_MediaMetadataRetriever_setMode},
-        {"getMode",         "()I",  (void *)android_media_MediaMetadataRetriever_getMode},
-        {"captureFrame",    "()Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_captureFrame},
+        {"_getFrameAtTime", "(JI)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
         {"extractMetadata", "(I)Ljava/lang/String;", (void *)android_media_MediaMetadataRetriever_extractMetadata},
         {"extractAlbumArt", "()[B", (void *)android_media_MediaMetadataRetriever_extractAlbumArt},
         {"release",         "()V", (void *)android_media_MediaMetadataRetriever_release},
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index e529d25..0193e25 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -82,8 +82,7 @@
     SET_DATA_SOURCE_URL,
     SET_DATA_SOURCE_FD,
     SET_MODE,
-    GET_MODE,
-    CAPTURE_FRAME,
+    GET_FRAME_AT_TIME,
     EXTRACT_ALBUM_ART,
     EXTRACT_METADATA,
 };
@@ -133,23 +132,17 @@
         return reply.readInt32();
     }
 
-    status_t getMode(int* mode) const
+    sp<IMemory> getFrameAtTime(int64_t timeUs, int option)
     {
+        LOGV("getTimeAtTime: time(%lld us) and option(%d)", timeUs, option);
         Parcel data, reply;
         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
-        remote()->transact(GET_MODE, data, &reply);
-        *mode = reply.readInt32();
-        return reply.readInt32();
-    }
-
-    sp<IMemory> captureFrame()
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
+        data.writeInt64(timeUs);
+        data.writeInt32(option);
 #ifndef DISABLE_GROUP_SCHEDULE_HACK
         sendSchedPolicy(data);
 #endif
-        remote()->transact(CAPTURE_FRAME, data, &reply);
+        remote()->transact(GET_FRAME_AT_TIME, data, &reply);
         status_t ret = reply.readInt32();
         if (ret != NO_ERROR) {
             return NULL;
@@ -222,20 +215,15 @@
             reply->writeInt32(setMode(mode));
             return NO_ERROR;
         } break;
-        case GET_MODE: {
+        case GET_FRAME_AT_TIME: {
             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
-            int mode;
-            status_t status = getMode(&mode);
-            reply->writeInt32(mode);
-            reply->writeInt32(status);
-            return NO_ERROR;
-        } break;
-        case CAPTURE_FRAME: {
-            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
+            int64_t timeUs = data.readInt64();
+            int option = data.readInt32();
+            LOGV("getTimeAtTime: time(%lld us) and option(%d)", timeUs, option);
 #ifndef DISABLE_GROUP_SCHEDULE_HACK
             setSchedPolicy(data);
 #endif
-            sp<IMemory> bitmap = captureFrame();
+            sp<IMemory> bitmap = getFrameAtTime(timeUs, option);
             if (bitmap != 0) {  // Don't send NULL across the binder interface
                 reply->writeInt32(NO_ERROR);
                 reply->writeStrongBinder(bitmap->asBinder());
diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp
index e2712ba..39b5bc3 100644
--- a/media/libmedia/mediametadataretriever.cpp
+++ b/media/libmedia/mediametadataretriever.cpp
@@ -134,26 +134,15 @@
     return mRetriever->setMode(mode);
 }
 
-status_t MediaMetadataRetriever::getMode(int* mode)
+sp<IMemory> MediaMetadataRetriever::getFrameAtTime(int64_t timeUs, int option)
 {
-    LOGV("getMode");
-    Mutex::Autolock _l(mLock);
-    if (mRetriever == 0) {
-        LOGE("retriever is not initialized");
-        return INVALID_OPERATION;
-    }
-    return mRetriever->getMode(mode);
-}
-
-sp<IMemory> MediaMetadataRetriever::captureFrame()
-{
-    LOGV("captureFrame");
+    LOGV("getFrameAtTime: time(%lld us) option(%d)", timeUs, option);
     Mutex::Autolock _l(mLock);
     if (mRetriever == 0) {
         LOGE("retriever is not initialized");
         return NULL;
     }
-    return mRetriever->captureFrame();
+    return mRetriever->getFrameAtTime(timeUs, option);
 }
 
 const char* MediaMetadataRetriever::extractMetadata(int keyCode)
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index b069345..abaec02 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -196,33 +196,16 @@
     return NO_ERROR;
 }
 
-status_t MetadataRetrieverClient::getMode(int* mode) const
+sp<IMemory> MetadataRetrieverClient::getFrameAtTime(int64_t timeUs, int option)
 {
-    LOGV("getMode");
-    Mutex::Autolock lock(mLock);
-
-    // TODO:
-    // This may not be necessary.
-    // If setDataSource() has not been called, return the cached value
-    // otherwise, return the value retrieved from the retriever
-    if (mRetriever == NULL) {
-        *mode = mMode;
-    } else {
-        mRetriever->getMode(mode);
-    }
-    return NO_ERROR;
-}
-
-sp<IMemory> MetadataRetrieverClient::captureFrame()
-{
-    LOGV("captureFrame");
+    LOGV("getFrameAtTime: time(%lld us) option(%d)", timeUs, option);
     Mutex::Autolock lock(mLock);
     mThumbnail.clear();
     if (mRetriever == NULL) {
         LOGE("retriever is not initialized");
         return NULL;
     }
-    VideoFrame *frame = mRetriever->captureFrame();
+    VideoFrame *frame = mRetriever->getFrameAtTime(timeUs, option);
     if (frame == NULL) {
         LOGE("failed to capture a video frame");
         return NULL;
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h
index 4aab94f..8b4c0c78 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.h
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.h
@@ -44,8 +44,7 @@
     virtual status_t                setDataSource(const char *url);
     virtual status_t                setDataSource(int fd, int64_t offset, int64_t length);
     virtual status_t                setMode(int mode);
-    virtual status_t                getMode(int* mode) const;
-    virtual sp<IMemory>             captureFrame();
+    virtual sp<IMemory>             getFrameAtTime(int64_t timeUs, int option);
     virtual sp<IMemory>             extractAlbumArt();
     virtual const char*             extractMetadata(int keyCode);
 
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 763a914..c28de93 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -108,7 +108,10 @@
 static VideoFrame *extractVideoFrameWithCodecFlags(
         OMXClient *client,
         const sp<MetaData> &trackMeta,
-        const sp<MediaSource> &source, uint32_t flags) {
+        const sp<MediaSource> &source,
+        uint32_t flags,
+        int64_t frameTimeUs,
+        int seekMode) {
     sp<MediaSource> decoder =
         OMXCodec::Create(
                 client->interface(), source->getFormat(), false, source,
@@ -130,11 +133,22 @@
     // and spurious empty buffers.
 
     MediaSource::ReadOptions options;
+    if (seekMode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC ||
+        seekMode > MediaSource::ReadOptions::SEEK_CLOSEST) {
+
+        LOGE("Unknown seek mode: %d", seekMode);
+        return NULL;
+    }
+
+    MediaSource::ReadOptions::SeekMode mode =
+            static_cast<MediaSource::ReadOptions::SeekMode>(seekMode);
+
     int64_t thumbNailTime;
-    if (trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)) {
-        options.setSeekTo(thumbNailTime);
+    if (frameTimeUs < 0 && trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)) {
+        options.setSeekTo(thumbNailTime, mode);
     } else {
         thumbNailTime = -1;
+        options.setSeekTo(frameTimeUs, mode);
     }
 
     MediaBuffer *buffer = NULL;
@@ -238,9 +252,10 @@
     return frame;
 }
 
-VideoFrame *StagefrightMetadataRetriever::captureFrame() {
-    LOGV("captureFrame");
+VideoFrame *StagefrightMetadataRetriever::getFrameAtTime(
+        int64_t timeUs, int option) {
 
+    LOGV("getFrameAtTime: %lld us option: %d", timeUs, option);
     if (0 == (mMode & METADATA_MODE_FRAME_CAPTURE_ONLY)) {
         LOGV("captureFrame disabled by mode (0x%08x)", mMode);
 
@@ -282,13 +297,15 @@
 
     VideoFrame *frame =
         extractVideoFrameWithCodecFlags(
-                &mClient, trackMeta, source, OMXCodec::kPreferSoftwareCodecs);
+                &mClient, trackMeta, source, OMXCodec::kPreferSoftwareCodecs,
+                timeUs, option);
 
     if (frame == NULL) {
         LOGV("Software decoder failed to extract thumbnail, "
              "trying hardware decoder.");
 
-        frame = extractVideoFrameWithCodecFlags(&mClient, trackMeta, source, 0);
+        frame = extractVideoFrameWithCodecFlags(&mClient, trackMeta, source, 0,
+                        timeUs, option);
     }
 
     return frame;
diff --git a/media/libstagefright/include/StagefrightMetadataRetriever.h b/media/libstagefright/include/StagefrightMetadataRetriever.h
index b80387f..07b1ec8 100644
--- a/media/libstagefright/include/StagefrightMetadataRetriever.h
+++ b/media/libstagefright/include/StagefrightMetadataRetriever.h
@@ -35,7 +35,7 @@
     virtual status_t setDataSource(const char *url);
     virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
 
-    virtual VideoFrame *captureFrame();
+    virtual VideoFrame *getFrameAtTime(int64_t timeUs, int option);
     virtual MediaAlbumArt *extractAlbumArt();
     virtual const char *extractMetadata(int keyCode);