Initial CL for the timed text support:
- Add support for MP4 timed text
- Add API for app to turn on/off a text track
- Add timed text metadata(language) in the MediaMetadataRetriever

Change-Id: I0055beba38ac761627dbcc6d581ae9582d68bb94
diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h
index 3e343e0..28f305d 100644
--- a/include/media/mediametadataretriever.h
+++ b/include/media/mediametadataretriever.h
@@ -52,6 +52,7 @@
     METADATA_KEY_VIDEO_WIDTH     = 18,
     METADATA_KEY_VIDEO_HEIGHT    = 19,
     METADATA_KEY_BITRATE         = 20,
+    METADATA_KEY_TIMED_TEXT_LANGUAGES      = 21,
 
     // Add more here...
 };
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 241626c..cfa4cfd 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -125,6 +125,9 @@
     MEDIA_PLAYER_PLAYBACK_COMPLETE  = 1 << 7
 };
 
+enum media_set_parameter_keys {
+    KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX = 1000,
+};
 // ----------------------------------------------------------------------------
 // ref-counted object for callbacks
 class MediaPlayerListener: virtual public RefBase
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index 6a21627..5e471c1 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -49,6 +49,8 @@
 
 extern const char *MEDIA_MIMETYPE_CONTAINER_WVM;
 
+extern const char *MEDIA_MIMETYPE_TEXT_3GPP;
+
 }  // namespace android
 
 #endif  // MEDIA_DEFS_H_
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index f7f2235..4044c5d 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -114,6 +114,9 @@
 
     // An indication that a video buffer has been rendered.
     kKeyRendered          = 'rend',  // bool (int32_t)
+
+    // The language code for this media
+    kKeyMediaLanguage     = 'lang',  // cstring
 };
 
 enum {
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 60085b5..5f7f36f 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -432,5 +432,12 @@
      * This key retrieves the average bitrate (in bits/sec), if available.
      */
     public static final int METADATA_KEY_BITRATE         = 20;
+    /**
+     * This key retrieves the language code of text tracks, if available.
+     * If multiple text tracks present, the return value will look like:
+     * "eng:chi"
+     * @hide
+     */
+    public static final int METADATA_KEY_TIMED_TEXT_LANGUAGES      = 21;
     // Add more here...
 }
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index b914169..3f799cf 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1227,6 +1227,15 @@
      */
     public native void attachAuxEffect(int effectId);
 
+    /* Do not change these values without updating their counterparts
+     * in include/media/mediaplayer.h!
+     */
+    /**
+     * Key used in setParameter method.
+     * Indicates the index of the timed text track to be enabled/disabled
+     */
+    private static final int KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX = 1000;
+
     /**
      * Sets the parameter indicated by key.
      * @param key key indicates the parameter to be set.
@@ -1360,6 +1369,36 @@
     private native final void native_finalize();
 
     /**
+     * @param index The index of the text track to be turned on.
+     * @return true if the text track is enabled successfully.
+     * {@hide}
+     */
+    public boolean enableTimedTextTrackIndex(int index) {
+        if (index < 0) {
+            return false;
+        }
+        return setParameter(KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX, index);
+    }
+
+    /**
+     * Enables the first timed text track if any.
+     * @return true if the text track is enabled successfully
+     * {@hide}
+     */
+    public boolean enableTimedText() {
+        return enableTimedTextTrackIndex(0);
+    }
+
+    /**
+     * Disables timed text display.
+     * @return true if the text track is disabled successfully.
+     * {@hide}
+     */
+    public boolean disableTimedText() {
+        return setParameter(KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX, -1);
+    }
+
+    /**
      * @param reply Parcel with audio/video duration info for battery
                     tracking usage
      * @return The status code.
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 2f3e1410..6c7176c 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -48,6 +48,7 @@
         ThrottledSource.cpp               \
         TimeSource.cpp                    \
         TimedEventQueue.cpp               \
+        TimedTextPlayer.cpp               \
         Utils.cpp                         \
         VBRISeeker.cpp                    \
         WAVExtractor.cpp                  \
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 974efa7..cccd0b7 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -28,6 +28,7 @@
 #include "include/NuCachedSource2.h"
 #include "include/ThrottledSource.h"
 #include "include/MPEG2TSExtractor.h"
+#include "include/TimedTextPlayer.h"
 
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
@@ -185,7 +186,8 @@
       mExtractorFlags(0),
       mVideoBuffer(NULL),
       mDecryptHandle(NULL),
-      mLastVideoTimeUs(-1) {
+      mLastVideoTimeUs(-1),
+      mTextPlayer(NULL) {
     CHECK_EQ(mClient.connect(), (status_t)OK);
 
     DataSource::RegisterDefaultSniffers();
@@ -381,10 +383,8 @@
                     mFlags |= AUTO_LOOPING;
                 }
             }
-        }
-
-        if (haveAudio && haveVideo) {
-            break;
+        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
+            addTextSource(extractor->getTrack(i));
         }
     }
 
@@ -469,6 +469,11 @@
     delete mAudioPlayer;
     mAudioPlayer = NULL;
 
+    if (mTextPlayer != NULL) {
+        delete mTextPlayer;
+        mTextPlayer = NULL;
+    }
+
     mVideoRenderer.clear();
 
     if (mRTSPController != NULL) {
@@ -971,6 +976,11 @@
         mFlags &= ~AUDIO_RUNNING;
     }
 
+    if (mFlags & TEXTPLAYER_STARTED) {
+        mTextPlayer->pause();
+        mFlags &= ~TEXT_RUNNING;
+    }
+
     mFlags &= ~PLAYING;
 
     if (mDecryptHandle != NULL) {
@@ -1119,6 +1129,32 @@
     return OK;
 }
 
+status_t AwesomePlayer::setTimedTextTrackIndex(int32_t index) {
+    if (mTextPlayer != NULL) {
+        if (index >= 0) { // to turn on a text track
+            status_t err = mTextPlayer->setTimedTextTrackIndex(index);
+            if (err != OK) {
+                return err;
+            }
+
+            mFlags |= TEXT_RUNNING;
+            mFlags |= TEXTPLAYER_STARTED;
+            return OK;
+        } else { // to turn off the text track display
+            if (mFlags  & TEXT_RUNNING) {
+                mFlags &= ~TEXT_RUNNING;
+            }
+            if (mFlags  & TEXTPLAYER_STARTED) {
+                mFlags &= ~TEXTPLAYER_STARTED;
+            }
+
+            return mTextPlayer->setTimedTextTrackIndex(index);
+        }
+    } else {
+        return INVALID_OPERATION;
+    }
+}
+
 // static
 void AwesomePlayer::OnRTSPSeekDoneWrapper(void *cookie) {
     static_cast<AwesomePlayer *>(cookie)->onRTSPSeekDone();
@@ -1155,6 +1191,10 @@
 
     seekAudioIfNecessary_l();
 
+    if (mFlags & TEXTPLAYER_STARTED) {
+        mTextPlayer->seekTo(mSeekTimeUs);
+    }
+
     if (!(mFlags & PLAYING)) {
         LOGV("seeking while paused, sending SEEK_COMPLETE notification"
              " immediately.");
@@ -1193,6 +1233,16 @@
     mAudioTrack = source;
 }
 
+void AwesomePlayer::addTextSource(sp<MediaSource> source) {
+    CHECK(source != NULL);
+
+    if (mTextPlayer == NULL) {
+        mTextPlayer = new TimedTextPlayer(this, mListener, &mQueue);
+    }
+
+    mTextPlayer->addTextSource(source);
+}
+
 status_t AwesomePlayer::initAudioDecoder() {
     sp<MetaData> meta = mAudioTrack->getFormat();
 
@@ -1472,6 +1522,11 @@
         }
     }
 
+    if ((mFlags & TEXTPLAYER_STARTED) && !(mFlags & (TEXT_RUNNING | SEEK_PREVIEW))) {
+        mTextPlayer->resume();
+        mFlags |= TEXT_RUNNING;
+    }
+
     TimeSource *ts = (mFlags & AUDIO_AT_EOS) ? &mSystemTimeSource : mTimeSource;
 
     if (mFlags & FIRST_FRAME) {
@@ -1906,7 +1961,10 @@
 }
 
 status_t AwesomePlayer::setParameter(int key, const Parcel &request) {
-    return OK;
+    if (key == KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX) {
+        return setTimedTextTrackIndex(request.readInt32());
+    }
+    return ERROR_UNSUPPORTED;
 }
 
 status_t AwesomePlayer::getParameter(int key, Parcel *reply) {
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 8787214..c79d02e 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -19,6 +19,8 @@
 
 #include "include/MPEG4Extractor.h"
 #include "include/SampleTable.h"
+#include "include/ESDS.h"
+#include "include/TimedTextPlayer.h"
 
 #include <arpa/inet.h>
 
@@ -29,7 +31,6 @@
 
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/DataSource.h>
-#include "include/ESDS.h"
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaBufferGroup.h>
 #include <media/stagefright/MediaDefs.h>
@@ -832,6 +833,33 @@
             mLastTrack->meta->setInt64(
                     kKeyDuration, (duration * 1000000) / mLastTrack->timescale);
 
+            uint8_t lang[2];
+            off64_t lang_offset;
+            if (version == 1) {
+                lang_offset = timescale_offset + 4 + 8;
+            } else if (version == 0) {
+                lang_offset = timescale_offset + 4 + 4;
+            } else {
+                return ERROR_IO;
+            }
+
+            if (mDataSource->readAt(lang_offset, &lang, sizeof(lang))
+                    < (ssize_t)sizeof(lang)) {
+                return ERROR_IO;
+            }
+
+            // To get the ISO-639-2/T three character language code
+            // 1 bit pad followed by 3 5-bits characters. Each character
+            // is packed as the difference between its ASCII value and 0x60.
+            char lang_code[4];
+            lang_code[0] = ((lang[0] >> 2) & 0x1f) + 0x60;
+            lang_code[1] = ((lang[0] & 0x3) << 3 | (lang[1] >> 5)) + 0x60;
+            lang_code[2] = (lang[1] & 0x1f) + 0x60;
+            lang_code[3] = '\0';
+
+            mLastTrack->meta->setCString(
+                    kKeyMediaLanguage, lang_code);
+
             *offset += chunk_size;
             break;
         }
@@ -1295,6 +1323,14 @@
             return parseDrmSINF(offset, data_offset);
         }
 
+        case FOURCC('t', 'x', '3', 'g'):
+        {
+            mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP);
+
+            *offset += chunk_size;
+            break;
+        }
+
         default:
         {
             *offset += chunk_size;
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index 8ca6ee8..8cd08bc 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -47,4 +47,6 @@
 
 const char *MEDIA_MIMETYPE_CONTAINER_WVM = "video/wvm";
 
+const char *MEDIA_MIMETYPE_TEXT_3GPP = "text/3gpp-tt";
+
 }  // namespace android
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 7621f2c..4c3dc47 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -27,6 +27,7 @@
 #include <media/stagefright/MediaExtractor.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/MediaDefs.h>
 
 namespace android {
 
@@ -429,6 +430,7 @@
 
     // The overall duration is the duration of the longest track.
     int64_t maxDurationUs = 0;
+    String8 timedTextLang;
     for (size_t i = 0; i < numTracks; ++i) {
         sp<MetaData> trackMeta = mExtractor->getTrackMetaData(i);
 
@@ -452,10 +454,22 @@
 
                 CHECK(trackMeta->findInt32(kKeyWidth, &videoWidth));
                 CHECK(trackMeta->findInt32(kKeyHeight, &videoHeight));
+            } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
+                const char *lang;
+                trackMeta->findCString(kKeyMediaLanguage, &lang);
+                timedTextLang.append(String8(lang));
+                timedTextLang.append(String8(":"));
             }
         }
     }
 
+    // To save the language codes for all timed text tracks
+    // If multiple text tracks present, the format will look
+    // like "eng:chi"
+    if (!timedTextLang.isEmpty()) {
+        mMetaData.add(METADATA_KEY_TIMED_TEXT_LANGUAGES, timedTextLang);
+    }
+
     // The duration value is a string representing the duration in ms.
     sprintf(tmp, "%lld", (maxDurationUs + 500) / 1000);
     mMetaData.add(METADATA_KEY_DURATION, String8(tmp));
diff --git a/media/libstagefright/TimedTextPlayer.cpp b/media/libstagefright/TimedTextPlayer.cpp
new file mode 100644
index 0000000..1ac22cb
--- /dev/null
+++ b/media/libstagefright/TimedTextPlayer.cpp
@@ -0,0 +1,252 @@
+ /*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TimedTextPlayer"
+#include <utils/Log.h>
+
+#include <binder/IPCThreadState.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/Utils.h>
+#include "include/AwesomePlayer.h"
+#include "include/TimedTextPlayer.h"
+
+namespace android {
+
+struct TimedTextEvent : public TimedEventQueue::Event {
+    TimedTextEvent(
+            TimedTextPlayer *player,
+            void (TimedTextPlayer::*method)())
+        : mPlayer(player),
+          mMethod(method) {
+    }
+
+protected:
+    virtual ~TimedTextEvent() {}
+
+    virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
+        (mPlayer->*mMethod)();
+    }
+
+private:
+    TimedTextPlayer *mPlayer;
+    void (TimedTextPlayer::*mMethod)();
+
+    TimedTextEvent(const TimedTextEvent &);
+    TimedTextEvent &operator=(const TimedTextEvent &);
+};
+
+TimedTextPlayer::TimedTextPlayer(
+        AwesomePlayer *observer,
+        const wp<MediaPlayerBase> &listener,
+        TimedEventQueue *queue)
+    : mSource(NULL),
+      mSeekTimeUs(0),
+      mStarted(false),
+      mTextEventPending(false),
+      mQueue(queue),
+      mListener(listener),
+      mObserver(observer),
+      mTextBuffer(NULL) {
+    mTextEvent = new TimedTextEvent(this, &TimedTextPlayer::onTextEvent);
+}
+
+TimedTextPlayer::~TimedTextPlayer() {
+    if (mStarted) {
+        reset();
+    }
+
+    mTextTrackVector.clear();
+}
+
+status_t TimedTextPlayer::start(uint8_t index) {
+    CHECK(!mStarted);
+
+    if (index >= mTextTrackVector.size()) {
+        LOGE("Incorrect text track index");
+        return BAD_VALUE;
+    }
+
+    mSource = mTextTrackVector.itemAt(index);
+
+    status_t err = mSource->start();
+
+    if (err != OK) {
+        return err;
+    }
+
+    int64_t positionUs;
+    mObserver->getPosition(&positionUs);
+    seekTo(positionUs);
+
+    postTextEvent();
+
+    mStarted = true;
+
+    return OK;
+}
+
+void TimedTextPlayer::pause() {
+    CHECK(mStarted);
+
+    cancelTextEvent();
+}
+
+void TimedTextPlayer::resume() {
+    CHECK(mStarted);
+
+    postTextEvent();
+}
+
+void TimedTextPlayer::reset() {
+    CHECK(mStarted);
+
+    // send an empty text to clear the screen
+    notifyListener(MEDIA_TIMED_TEXT);
+
+    cancelTextEvent();
+
+    mSeeking = false;
+    mStarted = false;
+
+    if (mTextBuffer != NULL) {
+        mTextBuffer->release();
+        mTextBuffer = NULL;
+    }
+
+    if (mSource != NULL) {
+        mSource->stop();
+        mSource.clear();
+        mSource = NULL;
+    }
+}
+
+status_t TimedTextPlayer::seekTo(int64_t time_us) {
+    Mutex::Autolock autoLock(mLock);
+
+    mSeeking = true;
+    mSeekTimeUs = time_us;
+
+    return OK;
+}
+
+status_t TimedTextPlayer::setTimedTextTrackIndex(int32_t index) {
+    if (index >= (int)(mTextTrackVector.size())) {
+        return BAD_VALUE;
+    }
+
+    if (mStarted) {
+        reset();
+    }
+
+    if (index >= 0) {
+        return start(index);
+    }
+    return OK;
+}
+
+void TimedTextPlayer::onTextEvent() {
+    Mutex::Autolock autoLock(mLock);
+
+    if (!mTextEventPending) {
+        return;
+    }
+    mTextEventPending = false;
+
+    MediaSource::ReadOptions options;
+    if (mSeeking) {
+        options.setSeekTo(mSeekTimeUs,
+                MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
+        mSeeking = false;
+
+        if (mTextBuffer != NULL) {
+            mTextBuffer->release();
+            mTextBuffer = NULL;
+        }
+
+        notifyListener(MEDIA_TIMED_TEXT); //empty text to clear the screen
+    }
+
+    if (mTextBuffer != NULL) {
+        uint8_t *tmp = (uint8_t *)(mTextBuffer->data());
+        size_t len = (*tmp) << 8 | (*(tmp + 1));
+
+        notifyListener(MEDIA_TIMED_TEXT,
+                       tmp + 2,
+                       len);
+
+        mTextBuffer->release();
+        mTextBuffer = NULL;
+
+    }
+
+    if (mSource->read(&mTextBuffer, &options) != OK) {
+        return;
+    }
+
+    int64_t positionUs, timeUs;
+    mObserver->getPosition(&positionUs);
+    mTextBuffer->meta_data()->findInt64(kKeyTime, &timeUs);
+
+    //send the text now
+    if (timeUs <= positionUs + 100000ll) {
+        postTextEvent();
+    } else {
+        postTextEvent(timeUs - positionUs - 100000ll);
+    }
+}
+
+void TimedTextPlayer::postTextEvent(int64_t delayUs) {
+    if (mTextEventPending) {
+        return;
+    }
+
+    mTextEventPending = true;
+    mQueue->postEventWithDelay(mTextEvent, delayUs < 0 ? 10000 : delayUs);
+}
+
+void TimedTextPlayer::cancelTextEvent() {
+    mQueue->cancelEvent(mTextEvent->eventID());
+    mTextEventPending = false;
+}
+
+void TimedTextPlayer::addTextSource(sp<MediaSource> source) {
+    mTextTrackVector.add(source);
+}
+
+void TimedTextPlayer::notifyListener(
+        int msg, const void *data, size_t size) {
+    if (mListener != NULL) {
+        sp<MediaPlayerBase> listener = mListener.promote();
+
+        if (listener != NULL) {
+            if (size > 0) {
+                mData.freeData();
+                mData.write(data, size);
+
+                listener->sendEvent(msg, 0, 0, &mData);
+            } else { // send an empty timed text to clear the screen
+                listener->sendEvent(msg);
+            }
+        }
+    }
+}
+}
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 2c17d92..fd3ddf7 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -44,6 +44,8 @@
 class DrmManagerClinet;
 class DecryptHandle;
 
+class TimedTextPlayer;
+
 struct AwesomeRenderer : public RefBase {
     AwesomeRenderer() {}
 
@@ -99,36 +101,41 @@
     void postAudioEOS(int64_t delayUs = 0ll);
     void postAudioSeekComplete();
 
+    status_t setTimedTextTrackIndex(int32_t index);
+
 private:
     friend struct AwesomeEvent;
     friend struct PreviewPlayer;
 
     enum {
-        PLAYING             = 1,
-        LOOPING             = 2,
-        FIRST_FRAME         = 4,
-        PREPARING           = 8,
-        PREPARED            = 16,
-        AT_EOS              = 32,
-        PREPARE_CANCELLED   = 64,
-        CACHE_UNDERRUN      = 128,
-        AUDIO_AT_EOS        = 256,
-        VIDEO_AT_EOS        = 512,
-        AUTO_LOOPING        = 1024,
+        PLAYING             = 0x01,
+        LOOPING             = 0x02,
+        FIRST_FRAME         = 0x04,
+        PREPARING           = 0x08,
+        PREPARED            = 0x10,
+        AT_EOS              = 0x20,
+        PREPARE_CANCELLED   = 0x40,
+        CACHE_UNDERRUN      = 0x80,
+        AUDIO_AT_EOS        = 0x0100,
+        VIDEO_AT_EOS        = 0x0200,
+        AUTO_LOOPING        = 0x0400,
 
         // We are basically done preparing but are currently buffering
         // sufficient data to begin playback and finish the preparation phase
         // for good.
-        PREPARING_CONNECTED = 2048,
+        PREPARING_CONNECTED = 0x0800,
 
         // We're triggering a single video event to display the first frame
         // after the seekpoint.
-        SEEK_PREVIEW        = 4096,
+        SEEK_PREVIEW        = 0x1000,
 
-        AUDIO_RUNNING       = 8192,
-        AUDIOPLAYER_STARTED = 16384,
+        AUDIO_RUNNING       = 0x2000,
+        AUDIOPLAYER_STARTED = 0x4000,
 
-        INCOGNITO           = 32768,
+        INCOGNITO           = 0x8000,
+
+        TEXT_RUNNING        = 0x10000,
+        TEXTPLAYER_STARTED  = 0x20000,
     };
 
     mutable Mutex mLock;
@@ -222,6 +229,7 @@
     sp<DecryptHandle> mDecryptHandle;
 
     int64_t mLastVideoTimeUs;
+    TimedTextPlayer *mTextPlayer;
 
     status_t setDataSource_l(
             const char *uri,
@@ -244,6 +252,8 @@
     void setVideoSource(sp<MediaSource> source);
     status_t initVideoDecoder(uint32_t flags = 0);
 
+    void addTextSource(sp<MediaSource> source);
+
     void onStreamDone();
 
     void notifyListener_l(int msg, int ext1 = 0, int ext2 = 0);
diff --git a/media/libstagefright/include/TimedTextPlayer.h b/media/libstagefright/include/TimedTextPlayer.h
new file mode 100644
index 0000000..ac41b4f
--- /dev/null
+++ b/media/libstagefright/include/TimedTextPlayer.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TIMEDTEXT_PLAYER_H_
+
+#define TIMEDTEXT_PLAYER_H_
+
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/foundation/ABase.h>
+
+#include "include/TimedEventQueue.h"
+
+namespace android {
+
+class MediaSource;
+class AwesomePlayer;
+class MediaBuffer;
+
+class TimedTextPlayer {
+public:
+    TimedTextPlayer(AwesomePlayer *observer,
+                    const wp<MediaPlayerBase> &listener,
+                    TimedEventQueue *queue);
+
+    virtual ~TimedTextPlayer();
+
+    // index: the index of the text track which will
+    // be turned on
+    status_t start(uint8_t index);
+
+    void pause();
+
+    void resume();
+
+    status_t seekTo(int64_t time_us);
+
+    void addTextSource(sp<MediaSource> source);
+
+    status_t setTimedTextTrackIndex(int32_t index);
+
+private:
+    Mutex mLock;
+
+    sp<MediaSource> mSource;
+
+    bool mSeeking;
+    int64_t mSeekTimeUs;
+
+    bool mStarted;
+
+    sp<TimedEventQueue::Event> mTextEvent;
+    bool mTextEventPending;
+
+    TimedEventQueue *mQueue;
+
+    wp<MediaPlayerBase> mListener;
+    AwesomePlayer *mObserver;
+
+    MediaBuffer *mTextBuffer;
+    Parcel mData;
+
+    Vector<sp<MediaSource> > mTextTrackVector;
+
+    void reset();
+
+    void onTextEvent();
+    void postTextEvent(int64_t delayUs = -1);
+    void cancelTextEvent();
+
+    void notifyListener(
+            int msg, const void *data = NULL, size_t size = 0);
+
+    DISALLOW_EVIL_CONSTRUCTORS(TimedTextPlayer);
+};
+
+}  // namespace android
+
+#endif  // TIMEDTEXT_PLAYER_H_