OSDN Git Service

Defines MediaPlayer APIs to support multiple audio/video/timedtext
authorInsun Kang <insun@google.com>
Thu, 16 Feb 2012 11:28:27 +0000 (20:28 +0900)
committerInsun Kang <insun@google.com>
Wed, 14 Mar 2012 23:36:52 +0000 (08:36 +0900)
tracks.

o Newly introduced APIs are (MediaPlayer):
  getTrackInfo() / addExternalSource() / enableTrack() / disableTrack().

o Timed text tracks are supported only, for now.

o TODOs:
 - Define the audio/video behavior for enableTrack and disableTrack.
 - Refactoring AwesomePlayer / TimedTextDriver so that all types of
   track index can be managed in the correct order and be ready for
   supporting audio/video tracks.
   (MediaExtractor and MediaSource for text file might be necessary.)

Change-Id: Idb85e1b3f2ed49a64f377d05472dd6663ce94e07

16 files changed:
include/media/mediaplayer.h
include/media/stagefright/MediaDefs.h
include/media/stagefright/timedtext/TimedTextDriver.h
media/libmediaplayerservice/StagefrightPlayer.cpp
media/libstagefright/AwesomePlayer.cpp
media/libstagefright/MediaDefs.cpp
media/libstagefright/include/AwesomePlayer.h
media/libstagefright/timedtext/TimedText3GPPSource.cpp
media/libstagefright/timedtext/TimedText3GPPSource.h
media/libstagefright/timedtext/TimedTextDriver.cpp
media/libstagefright/timedtext/TimedTextPlayer.cpp
media/libstagefright/timedtext/TimedTextPlayer.h
media/libstagefright/timedtext/TimedTextSRTSource.cpp
media/libstagefright/timedtext/TimedTextSRTSource.h
media/libstagefright/timedtext/TimedTextSource.cpp
media/libstagefright/timedtext/TimedTextSource.h

index 662dd13..a68ab4e 100644 (file)
@@ -120,6 +120,9 @@ enum media_info_type {
     MEDIA_INFO_NOT_SEEKABLE = 801,
     // New media metadata is available.
     MEDIA_INFO_METADATA_UPDATE = 802,
+
+    //9xx
+    MEDIA_INFO_TIMED_TEXT_ERROR = 900,
 };
 
 
@@ -140,9 +143,6 @@ enum media_player_states {
 // The same enum space is used for both set and get, in case there are future keys that
 // can be both set and get.  But as of now, all parameters are either set only or get only.
 enum media_parameter_keys {
-    KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX = 1000,                // set only
-    KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE = 1001,     // set only
-
     // Streaming/buffering parameters
     KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS = 1100,            // set only
 
@@ -155,6 +155,23 @@ enum media_parameter_keys {
     KEY_PARAMETER_PLAYBACK_RATE_PERMILLE = 1300,                // set only
 };
 
+// Keep INVOKE_ID_* in sync with MediaPlayer.java.
+enum media_player_invoke_ids {
+    INVOKE_ID_GET_TRACK_INFO = 1,
+    INVOKE_ID_ADD_EXTERNAL_SOURCE = 2,
+    INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3,
+    INVOKE_ID_SELECT_TRACK = 4,
+    INVOKE_ID_UNSELECT_TRACK = 5,
+};
+
+// Keep MEDIA_TRACK_TYPE_* in sync with MediaPlayer.java.
+enum media_track_type {
+    MEDIA_TRACK_TYPE_UNKNOWN = 0,
+    MEDIA_TRACK_TYPE_VIDEO = 1,
+    MEDIA_TRACK_TYPE_AUDIO = 2,
+    MEDIA_TRACK_TYPE_TIMEDTEXT = 3,
+};
+
 // ----------------------------------------------------------------------------
 // ref-counted object for callbacks
 class MediaPlayerListener: virtual public RefBase
index 2eb259e..457d5d7 100644 (file)
@@ -54,6 +54,7 @@ extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG2PS;
 extern const char *MEDIA_MIMETYPE_CONTAINER_WVM;
 
 extern const char *MEDIA_MIMETYPE_TEXT_3GPP;
+extern const char *MEDIA_MIMETYPE_TEXT_SUBRIP;
 
 }  // namespace android
 
index efedb6e..b9752df 100644 (file)
@@ -37,26 +37,26 @@ public:
 
     ~TimedTextDriver();
 
-    // TODO: pause-resume pair seems equivalent to stop-start pair.
-    // Check if it is replaceable with stop-start.
     status_t start();
-    status_t stop();
     status_t pause();
-    status_t resume();
+    status_t selectTrack(int32_t index);
+    status_t unselectTrack(int32_t index);
 
     status_t seekToAsync(int64_t timeUs);
 
     status_t addInBandTextSource(const sp<MediaSource>& source);
-    status_t addOutOfBandTextSource(const Parcel &request);
+    status_t addOutOfBandTextSource(const char *uri, const char *mimeType);
+    // Caller owns the file desriptor and caller is responsible for closing it.
+    status_t addOutOfBandTextSource(
+            int fd, off64_t offset, size_t length, const char *mimeType);
 
-    status_t setTimedTextTrackIndex(int32_t index);
+    void getTrackInfo(Parcel *parcel);
 
 private:
     Mutex mLock;
 
     enum State {
         UNINITIALIZED,
-        STOPPED,
         PLAYING,
         PAUSED,
     };
@@ -67,11 +67,11 @@ private:
 
     // Variables to be guarded by mLock.
     State mState;
-    Vector<sp<TimedTextSource> > mTextInBandVector;
-    Vector<sp<TimedTextSource> > mTextOutOfBandVector;
+    int32_t mCurrentTrackIndex;
+    Vector<sp<TimedTextSource> > mTextSourceVector;
     // -- End of variables to be guarded by mLock
 
-    status_t setTimedTextTrackIndex_l(int32_t index);
+    status_t selectTrack_l(int32_t index);
 
     DISALLOW_EVIL_CONSTRUCTORS(TimedTextDriver);
 };
index 052ebf0..619c149 100644 (file)
@@ -166,7 +166,8 @@ player_type StagefrightPlayer::playerType() {
 }
 
 status_t StagefrightPlayer::invoke(const Parcel &request, Parcel *reply) {
-    return INVALID_OPERATION;
+    ALOGV("invoke()");
+    return mPlayer->invoke(request, reply);
 }
 
 void StagefrightPlayer::setAudioSink(const sp<AudioSink> &audioSink) {
index 9e00bb3..b4cb1ab 100644 (file)
@@ -1114,7 +1114,7 @@ status_t AwesomePlayer::pause_l(bool at_eos) {
         modifyFlags(AUDIO_RUNNING, CLEAR);
     }
 
-    if (mFlags & TEXTPLAYER_STARTED) {
+    if (mFlags & TEXTPLAYER_INITIALIZED) {
         mTextDriver->pause();
         modifyFlags(TEXT_RUNNING, CLEAR);
     }
@@ -1268,32 +1268,6 @@ status_t AwesomePlayer::seekTo(int64_t timeUs) {
     return OK;
 }
 
-status_t AwesomePlayer::setTimedTextTrackIndex(int32_t index) {
-    if (mTextDriver != NULL) {
-        if (index >= 0) { // to turn on a text track
-            status_t err = mTextDriver->setTimedTextTrackIndex(index);
-            if (err != OK) {
-                return err;
-            }
-
-            modifyFlags(TEXT_RUNNING, SET);
-            modifyFlags(TEXTPLAYER_STARTED, SET);
-            return OK;
-        } else { // to turn off the text track display
-            if (mFlags  & TEXT_RUNNING) {
-                modifyFlags(TEXT_RUNNING, CLEAR);
-            }
-            if (mFlags  & TEXTPLAYER_STARTED) {
-                modifyFlags(TEXTPLAYER_STARTED, CLEAR);
-            }
-
-            return mTextDriver->setTimedTextTrackIndex(index);
-        }
-    } else {
-        return INVALID_OPERATION;
-    }
-}
-
 status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
     if (mFlags & CACHE_UNDERRUN) {
         modifyFlags(CACHE_UNDERRUN, CLEAR);
@@ -1315,7 +1289,7 @@ status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
 
     seekAudioIfNecessary_l();
 
-    if (mFlags & TEXTPLAYER_STARTED) {
+    if (mFlags & TEXTPLAYER_INITIALIZED) {
         mTextDriver->seekToAsync(mSeekTimeUs);
     }
 
@@ -1691,8 +1665,8 @@ void AwesomePlayer::onVideoEvent() {
         }
     }
 
-    if ((mFlags & TEXTPLAYER_STARTED) && !(mFlags & (TEXT_RUNNING | SEEK_PREVIEW))) {
-        mTextDriver->resume();
+    if ((mFlags & TEXTPLAYER_INITIALIZED) && !(mFlags & (TEXT_RUNNING | SEEK_PREVIEW))) {
+        mTextDriver->start();
         modifyFlags(TEXT_RUNNING, SET);
     }
 
@@ -2232,20 +2206,6 @@ void AwesomePlayer::postAudioSeekComplete() {
 
 status_t AwesomePlayer::setParameter(int key, const Parcel &request) {
     switch (key) {
-        case KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX:
-        {
-            Mutex::Autolock autoLock(mTimedTextLock);
-            return setTimedTextTrackIndex(request.readInt32());
-        }
-        case KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE:
-        {
-            Mutex::Autolock autoLock(mTimedTextLock);
-            if (mTextDriver == NULL) {
-                mTextDriver = new TimedTextDriver(mListener);
-            }
-
-            return mTextDriver->addOutOfBandTextSource(request);
-        }
         case KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS:
         {
             return setCacheStatCollectFreq(request);
@@ -2294,6 +2254,103 @@ status_t AwesomePlayer::getParameter(int key, Parcel *reply) {
     }
 }
 
+status_t AwesomePlayer::invoke(const Parcel &request, Parcel *reply) {
+    if (NULL == reply) {
+        return android::BAD_VALUE;
+    }
+    int32_t methodId;
+    status_t ret = request.readInt32(&methodId);
+    if (ret != android::OK) {
+        return ret;
+    }
+    switch(methodId) {
+        case INVOKE_ID_GET_TRACK_INFO:
+        {
+            Mutex::Autolock autoLock(mTimedTextLock);
+            if (mTextDriver == NULL) {
+                return INVALID_OPERATION;
+            }
+            mTextDriver->getTrackInfo(reply);
+            return OK;
+        }
+        case INVOKE_ID_ADD_EXTERNAL_SOURCE:
+        {
+            Mutex::Autolock autoLock(mTimedTextLock);
+            if (mTextDriver == NULL) {
+                mTextDriver = new TimedTextDriver(mListener);
+            }
+            // String values written in Parcel are UTF-16 values.
+            String16 uri16 = request.readString16();
+            const char *uri = NULL;
+            if (uri16 != NULL) {
+                uri = String8(uri16).string();
+            }
+            String16 mimeType16 = request.readString16();
+            const char *mimeType = NULL;
+            if (mimeType16 != NULL) {
+                mimeType = String8(mimeType16).string();
+            }
+            return mTextDriver->addOutOfBandTextSource(uri, mimeType);
+        }
+        case INVOKE_ID_ADD_EXTERNAL_SOURCE_FD:
+        {
+            Mutex::Autolock autoLock(mTimedTextLock);
+            if (mTextDriver == NULL) {
+                mTextDriver = new TimedTextDriver(mListener);
+            }
+            int fd         = request.readFileDescriptor();
+            off64_t offset = request.readInt64();
+            size_t length  = request.readInt64();
+            String16 mimeType16 = request.readString16();
+            const char *mimeType = NULL;
+            if (mimeType16 != NULL) {
+                mimeType = String8(mimeType16).string();
+            }
+
+            return mTextDriver->addOutOfBandTextSource(
+                    fd, offset, length, mimeType);
+        }
+        case INVOKE_ID_SELECT_TRACK:
+        {
+            Mutex::Autolock autoLock(mTimedTextLock);
+            if (mTextDriver == NULL) {
+                return INVALID_OPERATION;
+            }
+
+            status_t err = mTextDriver->selectTrack(
+                    request.readInt32());
+            if (err == OK) {
+                modifyFlags(TEXTPLAYER_INITIALIZED, SET);
+                if (mFlags & PLAYING && !(mFlags & TEXT_RUNNING)) {
+                    mTextDriver->start();
+                    modifyFlags(TEXT_RUNNING, SET);
+                }
+            }
+            return err;
+        }
+        case INVOKE_ID_UNSELECT_TRACK:
+        {
+            Mutex::Autolock autoLock(mTimedTextLock);
+            if (mTextDriver == NULL) {
+                return INVALID_OPERATION;
+            }
+            status_t err = mTextDriver->unselectTrack(
+                    request.readInt32());
+            if (err == OK) {
+                modifyFlags(TEXTPLAYER_INITIALIZED, CLEAR);
+                modifyFlags(TEXT_RUNNING, CLEAR);
+            }
+            return err;
+        }
+        default:
+        {
+            return ERROR_UNSUPPORTED;
+        }
+    }
+    // It will not reach here.
+    return OK;
+}
+
 bool AwesomePlayer::isStreamingHTTP() const {
     return mCachedSource != NULL || mWVMExtractor != NULL;
 }
index 444e823..2549de6 100644 (file)
@@ -52,5 +52,6 @@ const char *MEDIA_MIMETYPE_CONTAINER_MPEG2PS = "video/mp2p";
 const char *MEDIA_MIMETYPE_CONTAINER_WVM = "video/wvm";
 
 const char *MEDIA_MIMETYPE_TEXT_3GPP = "text/3gpp-tt";
+const char *MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
 
 }  // namespace android
index 4c7bfa6..06e9468 100644 (file)
@@ -90,6 +90,7 @@ struct AwesomePlayer {
 
     status_t setParameter(int key, const Parcel &request);
     status_t getParameter(int key, Parcel *reply);
+    status_t invoke(const Parcel &request, Parcel *reply);
     status_t setCacheStatCollectFreq(const Parcel &request);
 
     status_t seekTo(int64_t timeUs);
@@ -100,8 +101,6 @@ struct AwesomePlayer {
     void postAudioEOS(int64_t delayUs = 0ll);
     void postAudioSeekComplete();
 
-    status_t setTimedTextTrackIndex(int32_t index);
-
     status_t dump(int fd, const Vector<String16> &args) const;
 
 private:
@@ -136,7 +135,7 @@ private:
         INCOGNITO           = 0x8000,
 
         TEXT_RUNNING        = 0x10000,
-        TEXTPLAYER_STARTED  = 0x20000,
+        TEXTPLAYER_INITIALIZED  = 0x20000,
 
         SLOW_DECODER_HACK   = 0x40000,
     };
index 4a3bfd3..c423ef0 100644 (file)
@@ -110,4 +110,8 @@ status_t TimedText3GPPSource::extractGlobalDescriptions(Parcel *parcel) {
     return OK;
 }
 
+sp<MetaData> TimedText3GPPSource::getFormat() {
+    return mSource->getFormat();
+}
+
 }  // namespace android
index dfc6418..4ec3d8a 100644 (file)
@@ -37,6 +37,7 @@ public:
             Parcel *parcel,
             const MediaSource::ReadOptions *options = NULL);
     virtual status_t extractGlobalDescriptions(Parcel *parcel);
+    virtual sp<MetaData> getFormat();
 
 protected:
     virtual ~TimedText3GPPSource();
index c70870e..ed83894 100644 (file)
 
 #include <binder/IPCThreadState.h>
 
+#include <media/mediaplayer.h>
 #include <media/MediaPlayerInterface.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaSource.h>
-#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MetaData.h>
 #include <media/stagefright/Utils.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/ALooper.h>
@@ -47,24 +50,22 @@ TimedTextDriver::TimedTextDriver(
 }
 
 TimedTextDriver::~TimedTextDriver() {
-    mTextInBandVector.clear();
-    mTextOutOfBandVector.clear();
+    mTextSourceVector.clear();
     mLooper->stop();
 }
 
-status_t TimedTextDriver::setTimedTextTrackIndex_l(int32_t index) {
-    if (index >=
-            (int)(mTextInBandVector.size() + mTextOutOfBandVector.size())) {
+status_t TimedTextDriver::selectTrack_l(int32_t index) {
+    if (index >= (int)(mTextSourceVector.size())) {
         return BAD_VALUE;
     }
 
     sp<TimedTextSource> source;
-    if (index < mTextInBandVector.size()) {
-        source = mTextInBandVector.itemAt(index);
-    } else {
-        source = mTextOutOfBandVector.itemAt(index - mTextInBandVector.size());
-    }
+    source = mTextSourceVector.itemAt(index);
     mPlayer->setDataSource(source);
+    if (mState == UNINITIALIZED) {
+        mState = PAUSED;
+    }
+    mCurrentTrackIndex = index;
     return OK;
 }
 
@@ -73,13 +74,10 @@ status_t TimedTextDriver::start() {
     switch (mState) {
         case UNINITIALIZED:
             return INVALID_OPERATION;
-        case STOPPED:
-            mPlayer->start();
-            break;
         case PLAYING:
             return OK;
         case PAUSED:
-            mPlayer->resume();
+            mPlayer->start();
             break;
         default:
             TRESPASS();
@@ -88,10 +86,6 @@ status_t TimedTextDriver::start() {
     return OK;
 }
 
-status_t TimedTextDriver::stop() {
-    return pause();
-}
-
 // TODO: Test if pause() works properly.
 // Scenario 1: start - pause - resume
 // Scenario 2: start - seek
@@ -101,8 +95,6 @@ status_t TimedTextDriver::pause() {
     switch (mState) {
         case UNINITIALIZED:
             return INVALID_OPERATION;
-        case STOPPED:
-            return OK;
         case PLAYING:
             mPlayer->pause();
             break;
@@ -115,45 +107,17 @@ status_t TimedTextDriver::pause() {
     return OK;
 }
 
-status_t TimedTextDriver::resume() {
-    return start();
-}
-
-status_t TimedTextDriver::seekToAsync(int64_t timeUs) {
-    mPlayer->seekToAsync(timeUs);
-    return OK;
-}
-
-status_t TimedTextDriver::setTimedTextTrackIndex(int32_t index) {
-    // TODO: This is current implementation for MediaPlayer::disableTimedText().
-    // Find better way for readability.
-    if (index < 0) {
-        mPlayer->pause();
-        return OK;
-    }
-
+status_t TimedTextDriver::selectTrack(int32_t index) {
     status_t ret = OK;
     Mutex::Autolock autoLock(mLock);
     switch (mState) {
         case UNINITIALIZED:
-            ret = INVALID_OPERATION;
-            break;
         case PAUSED:
-            ret = setTimedTextTrackIndex_l(index);
+            ret = selectTrack_l(index);
             break;
         case PLAYING:
             mPlayer->pause();
-            ret = setTimedTextTrackIndex_l(index);
-            if (ret != OK) {
-                break;
-            }
-            mPlayer->start();
-            break;
-        case STOPPED:
-            // TODO: The only difference between STOPPED and PAUSED is this
-            // part. Revise the flow from "MediaPlayer::enableTimedText()" and
-            // remove one of the status, PAUSED and STOPPED, if possible.
-            ret = setTimedTextTrackIndex_l(index);
+            ret = selectTrack_l(index);
             if (ret != OK) {
                 break;
             }
@@ -165,6 +129,24 @@ status_t TimedTextDriver::setTimedTextTrackIndex(int32_t index) {
     return ret;
 }
 
+status_t TimedTextDriver::unselectTrack(int32_t index) {
+    if (mCurrentTrackIndex != index) {
+        return INVALID_OPERATION;
+    }
+    status_t err = pause();
+    if (err != OK) {
+        return err;
+    }
+    Mutex::Autolock autoLock(mLock);
+    mState = UNINITIALIZED;
+    return OK;
+}
+
+status_t TimedTextDriver::seekToAsync(int64_t timeUs) {
+    mPlayer->seekToAsync(timeUs);
+    return OK;
+}
+
 status_t TimedTextDriver::addInBandTextSource(
         const sp<MediaSource>& mediaSource) {
     sp<TimedTextSource> source =
@@ -173,25 +155,17 @@ status_t TimedTextDriver::addInBandTextSource(
         return ERROR_UNSUPPORTED;
     }
     Mutex::Autolock autoLock(mLock);
-    mTextInBandVector.add(source);
-    if (mState == UNINITIALIZED) {
-        mState = STOPPED;
-    }
+    mTextSourceVector.add(source);
     return OK;
 }
 
 status_t TimedTextDriver::addOutOfBandTextSource(
-        const Parcel &request) {
+        const char *uri, const char *mimeType) {
     // TODO: Define "TimedTextSource::CreateFromURI(uri)"
     // and move below lines there..?
 
-    // String values written in Parcel are UTF-16 values.
-    const String16 uri16 = request.readString16();
-    String8 uri = String8(request.readString16());
-
-    uri.toLower();
     // To support local subtitle file only for now
-    if (strncasecmp("file://", uri.string(), 7)) {
+    if (strncasecmp("file://", uri, 7)) {
         return ERROR_UNSUPPORTED;
     }
     sp<DataSource> dataSource =
@@ -201,7 +175,7 @@ status_t TimedTextDriver::addOutOfBandTextSource(
     }
 
     sp<TimedTextSource> source;
-    if (uri.getPathExtension() == String8(".srt")) {
+    if (strcasecmp(mimeType, MEDIA_MIMETYPE_TEXT_SUBRIP)) {
         source = TimedTextSource::CreateTimedTextSource(
                 dataSource, TimedTextSource::OUT_OF_BAND_FILE_SRT);
     }
@@ -211,12 +185,38 @@ status_t TimedTextDriver::addOutOfBandTextSource(
     }
 
     Mutex::Autolock autoLock(mLock);
+    mTextSourceVector.add(source);
+    return OK;
+}
 
-    mTextOutOfBandVector.add(source);
-    if (mState == UNINITIALIZED) {
-        mState = STOPPED;
+status_t TimedTextDriver::addOutOfBandTextSource(
+        int fd, off64_t offset, size_t length, const char *mimeType) {
+    // Not supported yet. This requires DataSource::sniff to detect various text
+    // formats such as srt/smi/ttml.
+    return ERROR_UNSUPPORTED;
+}
+
+void TimedTextDriver::getTrackInfo(Parcel *parcel) {
+    Mutex::Autolock autoLock(mLock);
+    Vector<sp<TimedTextSource> >::const_iterator iter;
+    parcel->writeInt32(mTextSourceVector.size());
+    for (iter = mTextSourceVector.begin();
+         iter != mTextSourceVector.end(); ++iter) {
+        sp<MetaData> meta = (*iter)->getFormat();
+        if (meta != NULL) {
+            // There are two fields.
+            parcel->writeInt32(2);
+
+            // track type.
+            parcel->writeInt32(MEDIA_TRACK_TYPE_TIMEDTEXT);
+
+            const char *lang = "und";
+            meta->findCString(kKeyMediaLanguage, &lang);
+            parcel->writeString16(String16(lang));
+        } else {
+            parcel->writeInt32(0);
+        }
     }
-    return OK;
 }
 
 }  // namespace android
index bda7b46..8717914 100644 (file)
@@ -56,10 +56,6 @@ void TimedTextPlayer::pause() {
     (new AMessage(kWhatPause, id()))->post();
 }
 
-void TimedTextPlayer::resume() {
-    start();
-}
-
 void TimedTextPlayer::seekToAsync(int64_t timeUs) {
     sp<AMessage> msg = new AMessage(kWhatSeek, id());
     msg->setInt64("seekTimeUs", timeUs);
@@ -104,9 +100,9 @@ void TimedTextPlayer::onMessageReceived(const sp<AMessage> &msg) {
             if (obj != NULL) {
                 sp<ParcelEvent> parcelEvent;
                 parcelEvent = static_cast<ParcelEvent*>(obj.get());
-                notifyListener(MEDIA_TIMED_TEXT, &(parcelEvent->parcel));
+                notifyListener(&(parcelEvent->parcel));
             } else {
-                notifyListener(MEDIA_TIMED_TEXT);
+                notifyListener();
             }
             doRead();
             break;
@@ -119,14 +115,18 @@ void TimedTextPlayer::onMessageReceived(const sp<AMessage> &msg) {
                 mSource->stop();
             }
             mSource = static_cast<TimedTextSource*>(obj.get());
-            mSource->start();
+            status_t err = mSource->start();
+            if (err != OK) {
+                notifyError(err);
+                break;
+            }
             Parcel parcel;
-            if (mSource->extractGlobalDescriptions(&parcel) == OK &&
-                parcel.dataSize() > 0) {
-                notifyListener(MEDIA_TIMED_TEXT, &parcel);
-            } else {
-                notifyListener(MEDIA_TIMED_TEXT);
+            err = mSource->extractGlobalDescriptions(&parcel);
+            if (err != OK) {
+                notifyError(err);
+                break;
             }
+            notifyListener(&parcel);
             break;
         }
     }
@@ -141,8 +141,12 @@ void TimedTextPlayer::doSeekAndRead(int64_t seekTimeUs) {
 void TimedTextPlayer::doRead(MediaSource::ReadOptions* options) {
     int64_t timeUs = 0;
     sp<ParcelEvent> parcelEvent = new ParcelEvent();
-    mSource->read(&timeUs, &(parcelEvent->parcel), options);
-    postTextEvent(parcelEvent, timeUs);
+    status_t err = mSource->read(&timeUs, &(parcelEvent->parcel), options);
+    if (err != OK) {
+        notifyError(err);
+    } else {
+        postTextEvent(parcelEvent, timeUs);
+    }
 }
 
 void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeUs) {
@@ -151,7 +155,7 @@ void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeU
         int64_t positionUs, delayUs;
         int32_t positionMs = 0;
         listener->getCurrentPosition(&positionMs);
-        positionUs = positionMs * 1000;
+        positionUs = positionMs * 1000ll;
 
         if (timeUs <= positionUs + kAdjustmentProcessingTimeUs) {
             delayUs = 0;
@@ -167,13 +171,20 @@ void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeU
     }
 }
 
-void TimedTextPlayer::notifyListener(int msg, const Parcel *parcel) {
+void TimedTextPlayer::notifyError(int error) {
+    sp<MediaPlayerBase> listener = mListener.promote();
+    if (listener != NULL) {
+        listener->sendEvent(MEDIA_INFO, MEDIA_INFO_TIMED_TEXT_ERROR, error);
+    }
+}
+
+void TimedTextPlayer::notifyListener(const Parcel *parcel) {
     sp<MediaPlayerBase> listener = mListener.promote();
     if (listener != NULL) {
         if (parcel != NULL && (parcel->dataSize() > 0)) {
-            listener->sendEvent(msg, 0, 0, parcel);
+            listener->sendEvent(MEDIA_TIMED_TEXT, 0, 0, parcel);
         } else {  // send an empty timed text to clear the screen
-            listener->sendEvent(msg);
+            listener->sendEvent(MEDIA_TIMED_TEXT);
         }
     }
 }
index 837beeb..b869f18 100644 (file)
@@ -40,7 +40,6 @@ public:
 
     void start();
     void pause();
-    void resume();
     void seekToAsync(int64_t timeUs);
     void setDataSource(sp<TimedTextSource> source);
 
@@ -68,7 +67,8 @@ private:
     void doRead(MediaSource::ReadOptions* options = NULL);
     void onTextEvent();
     void postTextEvent(const sp<ParcelEvent>& parcel = NULL, int64_t timeUs = -1);
-    void notifyListener(int msg, const Parcel *parcel = NULL);
+    void notifyError(int error = 0);
+    void notifyListener(const Parcel *parcel = NULL);
 
     DISALLOW_EVIL_CONSTRUCTORS(TimedTextPlayer);
 };
index 3752d34..c44a99b 100644 (file)
 #include <binder/Parcel.h>
 #include <media/stagefright/foundation/AString.h>
 #include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDefs.h>  // for MEDIA_MIMETYPE_xxx
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
 
 #include "TimedTextSRTSource.h"
 #include "TextDescriptions.h"
@@ -31,6 +33,7 @@ namespace android {
 
 TimedTextSRTSource::TimedTextSRTSource(const sp<DataSource>& dataSource)
         : mSource(dataSource),
+          mMetaData(new MetaData),
           mIndex(0) {
 }
 
@@ -42,10 +45,14 @@ status_t TimedTextSRTSource::start() {
     if (err != OK) {
         reset();
     }
+    // TODO: Need to detect the language, because SRT doesn't give language
+    // information explicitly.
+    mMetaData->setCString(kKeyMediaLanguage, "");
     return err;
 }
 
 void TimedTextSRTSource::reset() {
+    mMetaData->clear();
     mTextVector.clear();
     mIndex = 0;
 }
@@ -272,4 +279,8 @@ status_t TimedTextSRTSource::extractAndAppendLocalDescriptions(
     return OK;
 }
 
+sp<MetaData> TimedTextSRTSource::getFormat() {
+    return mMetaData;
+}
+
 }  // namespace android
index acc01f9..62710a0 100644 (file)
@@ -39,12 +39,14 @@ public:
             int64_t *timeUs,
             Parcel *parcel,
             const MediaSource::ReadOptions *options = NULL);
+    virtual sp<MetaData> getFormat();
 
 protected:
     virtual ~TimedTextSRTSource();
 
 private:
     sp<DataSource> mSource;
+    sp<MetaData> mMetaData;
 
     struct TextInfo {
         int64_t endTimeUs;
index ffbe1c3..953f7b5 100644 (file)
@@ -59,4 +59,8 @@ sp<TimedTextSource> TimedTextSource::CreateTimedTextSource(
     return NULL;
 }
 
+sp<MetaData> TimedTextSource::getFormat() {
+    return NULL;
+}
+
 }  // namespace android
index 06bae71..9349342 100644 (file)
@@ -25,6 +25,7 @@
 namespace android {
 
 class DataSource;
+class MetaData;
 class Parcel;
 
 class TimedTextSource : public RefBase {
@@ -48,6 +49,7 @@ class TimedTextSource : public RefBase {
   virtual status_t extractGlobalDescriptions(Parcel *parcel) {
       return INVALID_OPERATION;
   }
+  virtual sp<MetaData> getFormat();
 
  protected:
   virtual ~TimedTextSource() { }