Timed text display format support:
1. Extract 3GPP global format descriptions
2. Extract 3GPP local format descriptions
3. Define data structure (TimedText) for applications to
retrieve the format metadata

Change-Id: I6eac2a78df29ee15beee456656331fdd83b24e8e
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index deade5e..99b72ad 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -118,6 +118,9 @@
 
     // The language code for this media
     kKeyMediaLanguage     = 'lang',  // cstring
+
+    // To store the timed text format data
+    kKeyTextFormatData    = 'text',  // raw data
 };
 
 enum {
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 5fe511f..5582f92 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -889,11 +889,17 @@
             uint32_t entry_count = U32_AT(&buffer[4]);
 
             if (entry_count > 1) {
-                // For now we only support a single type of media per track.
-
-                mLastTrack->skipTrack = true;
-                *offset += chunk_size;
-                break;
+                // For 3GPP timed text, there could be multiple tx3g boxes contain
+                // multiple text display formats. These formats will be used to
+                // display the timed text.
+                const char *mime;
+                CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
+                if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
+                    // For now we only support a single type of media per track.
+                    mLastTrack->skipTrack = true;
+                    *offset += chunk_size;
+                    break;
+                }
             }
 
             off64_t stop_offset = *offset + chunk_size;
@@ -1324,9 +1330,53 @@
             return parseDrmSINF(offset, data_offset);
         }
 
+        case FOURCC('h', 'd', 'l', 'r'):
+        {
+            uint32_t buffer;
+            if (mDataSource->readAt(
+                        data_offset + 8, &buffer, 4) < 4) {
+                return ERROR_IO;
+            }
+
+            uint32_t type = ntohl(buffer);
+            // For the 3GPP file format, the handler-type within the 'hdlr' box
+            // shall be 'text'
+            if (type == FOURCC('t', 'e', 'x', 't')) {
+                mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP);
+            }
+
+            *offset += chunk_size;
+            break;
+        }
+
         case FOURCC('t', 'x', '3', 'g'):
         {
-            mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP);
+            uint32_t type;
+            const void *data;
+            size_t size = 0;
+            if (!mLastTrack->meta->findData(
+                    kKeyTextFormatData, &type, &data, &size)) {
+                size = 0;
+            }
+
+            uint8_t *buffer = new uint8_t[size + chunk_size];
+
+            if (size > 0) {
+                memcpy(buffer, data, size);
+            }
+
+            if ((size_t)(mDataSource->readAt(*offset, buffer + size, chunk_size))
+                    < chunk_size) {
+                delete[] buffer;
+                buffer = NULL;
+
+                return ERROR_IO;
+            }
+
+            mLastTrack->meta->setData(
+                    kKeyTextFormatData, 0, buffer, size + chunk_size);
+
+            delete[] buffer;
 
             *offset += chunk_size;
             break;
diff --git a/media/libstagefright/timedtext/Android.mk b/media/libstagefright/timedtext/Android.mk
index 9a6062c..59d0e15 100644
--- a/media/libstagefright/timedtext/Android.mk
+++ b/media/libstagefright/timedtext/Android.mk
@@ -2,6 +2,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=                 \
+        TextDescriptions.cpp      \
         TimedTextParser.cpp       \
         TimedTextPlayer.cpp
 
diff --git a/media/libstagefright/timedtext/TextDescriptions.cpp b/media/libstagefright/timedtext/TextDescriptions.cpp
new file mode 100644
index 0000000..f9c1fe0
--- /dev/null
+++ b/media/libstagefright/timedtext/TextDescriptions.cpp
@@ -0,0 +1,385 @@
+/*
+ * 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.
+ */
+
+#include "TextDescriptions.h"
+#include <media/stagefright/Utils.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+TextDescriptions::TextDescriptions() {
+}
+
+status_t TextDescriptions::getParcelOfDescriptions(
+        const uint8_t *data, ssize_t size,
+        uint32_t flags, int timeMs, Parcel *parcel) {
+    parcel->freeData();
+
+    if (flags & IN_BAND_TEXT_3GPP) {
+        if (flags & GLOBAL_DESCRIPTIONS) {
+            return extract3GPPGlobalDescriptions(data, size, parcel, 0);
+        } else if (flags & LOCAL_DESCRIPTIONS) {
+            return extract3GPPLocalDescriptions(data, size, timeMs, parcel, 0);
+        }
+    } else if (flags & OUT_OF_BAND_TEXT_SRT) {
+        if (flags & LOCAL_DESCRIPTIONS) {
+            return extractSRTLocalDescriptions(data, size, timeMs, parcel);
+        }
+    }
+
+    return ERROR_UNSUPPORTED;
+}
+
+// Parse the SRT text sample, and store the timing and text sample in a Parcel.
+// The Parcel will be sent to MediaPlayer.java through event, and will be
+// parsed in TimedText.java.
+status_t TextDescriptions::extractSRTLocalDescriptions(
+        const uint8_t *data, ssize_t size, int timeMs, Parcel *parcel) {
+    parcel->writeInt32(KEY_LOCAL_SETTING);
+    parcel->writeInt32(KEY_START_TIME);
+    parcel->writeInt32(timeMs);
+
+    parcel->writeInt32(KEY_STRUCT_TEXT);
+    // write the size of the text sample
+    parcel->writeInt32(size);
+    // write the text sample as a byte array
+    parcel->writeInt32(size);
+    parcel->write(data, size);
+
+    return OK;
+}
+
+// Extract the local 3GPP display descriptions. 3GPP local descriptions
+// are appended to the text sample if any. The descriptions could include
+// information such as text styles, highlights, karaoke and so on. They
+// are contained in different boxes, such as 'styl' box contains text
+// styles, and 'krok' box contains karaoke timing and positions.
+status_t TextDescriptions::extract3GPPLocalDescriptions(
+        const uint8_t *data, ssize_t size,
+        int timeMs, Parcel *parcel, int depth) {
+    if (depth == 0) {
+        parcel->writeInt32(KEY_LOCAL_SETTING);
+
+        // write start time to display this text sample
+        parcel->writeInt32(KEY_START_TIME);
+        parcel->writeInt32(timeMs);
+
+        ssize_t textLen = (*data) << 8 | (*(data + 1));
+
+        // write text sample length and text sample itself
+        parcel->writeInt32(KEY_STRUCT_TEXT);
+        parcel->writeInt32(textLen);
+        parcel->writeInt32(textLen);
+        parcel->write(data + 2, textLen);
+
+        if (size > textLen) {
+            data += (textLen + 2);
+            size -= (textLen + 2);
+        } else {
+            return OK;
+        }
+    }
+
+    const uint8_t *tmpData = data;
+    ssize_t chunkSize = U32_AT(tmpData);
+    uint32_t chunkType = U32_AT(tmpData + 4);
+
+    if (chunkSize <= 0) {
+        return OK;
+    }
+
+    tmpData += 8;
+
+    switch(chunkType) {
+        // 'styl' box specifies the style of the text.
+        case FOURCC('s', 't', 'y', 'l'):
+        {
+            uint16_t count = U16_AT(tmpData);
+
+            tmpData += 2;
+
+            for (int i = 0; i < count; i++) {
+                parcel->writeInt32(KEY_STRUCT_STYLE_LIST);
+                parcel->writeInt32(KEY_START_CHAR);
+                parcel->writeInt32(U16_AT(tmpData));
+
+                parcel->writeInt32(KEY_END_CHAR);
+                parcel->writeInt32(U16_AT(tmpData + 2));
+
+                parcel->writeInt32(KEY_FONT_ID);
+                parcel->writeInt32(U16_AT(tmpData + 4));
+
+                parcel->writeInt32(KEY_STYLE_FLAGS);
+                parcel->writeInt32(*(tmpData + 6));
+
+                parcel->writeInt32(KEY_FONT_SIZE);
+                parcel->writeInt32(*(tmpData + 7));
+
+                parcel->writeInt32(KEY_TEXT_COLOR_RGBA);
+                uint32_t rgba = *(tmpData + 8) << 24 | *(tmpData + 9) << 16
+                    | *(tmpData + 10) << 8 | *(tmpData + 11);
+                parcel->writeInt32(rgba);
+
+                tmpData += 12;
+            }
+
+            break;
+        }
+        // 'krok' box. The number of highlight events is specified, and each
+        // event is specified by a starting and ending char offset and an end
+        // time for the event.
+        case FOURCC('k', 'r', 'o', 'k'):
+        {
+
+            parcel->writeInt32(KEY_STRUCT_KARAOKE_LIST);
+
+            int startTime = U32_AT(tmpData);
+            uint16_t count = U16_AT(tmpData + 4);
+            parcel->writeInt32(count);
+
+            tmpData += 6;
+            int lastEndTime = 0;
+
+            for (int i = 0; i < count; i++) {
+                parcel->writeInt32(startTime + lastEndTime);
+
+                lastEndTime = U32_AT(tmpData);
+                parcel->writeInt32(lastEndTime);
+
+                parcel->writeInt32(U16_AT(tmpData + 4));
+                parcel->writeInt32(U16_AT(tmpData + 6));
+
+                tmpData += 8;
+            }
+
+            break;
+        }
+        // 'hlit' box specifies highlighted text
+        case FOURCC('h', 'l', 'i', 't'):
+        {
+            parcel->writeInt32(KEY_STRUCT_HIGHLIGHT_LIST);
+
+            // the start char offset to highlight
+            parcel->writeInt32(U16_AT(tmpData));
+            // the last char offset to highlight
+            parcel->writeInt32(U16_AT(tmpData + 2));
+
+            break;
+        }
+        // 'hclr' box specifies the RGBA color: 8 bits each of
+        // red, green, blue, and an alpha(transparency) value
+        case FOURCC('h', 'c', 'l', 'r'):
+        {
+            parcel->writeInt32(KEY_HIGHLIGHT_COLOR_RGBA);
+
+            uint32_t rgba = *(tmpData) << 24 | *(tmpData + 1) << 16
+                | *(tmpData + 2) << 8 | *(tmpData + 3);
+            parcel->writeInt32(rgba);
+
+            break;
+        }
+        // 'dlay' box specifies a delay after a scroll in and/or
+        // before scroll out.
+        case FOURCC('d', 'l', 'a', 'y'):
+        {
+            parcel->writeInt32(KEY_SCROLL_DELAY);
+
+            uint32_t delay = *(tmpData) << 24 | *(tmpData + 1) << 16
+                | *(tmpData + 2) << 8 | *(tmpData + 3);
+            parcel->writeInt32(delay);
+
+            break;
+        }
+        // 'href' box for hyper text link
+        case FOURCC('h', 'r', 'e', 'f'):
+        {
+            parcel->writeInt32(KEY_STRUCT_HYPER_TEXT_LIST);
+
+            // the start offset of the text to be linked
+            parcel->writeInt32(U16_AT(tmpData));
+            // the end offset of the text
+            parcel->writeInt32(U16_AT(tmpData + 2));
+
+            // the number of bytes in the following URL
+            int len = *(tmpData + 4);
+            parcel->writeInt32(len);
+
+            // the linked-to URL
+            parcel->writeInt32(len);
+            parcel->write(tmpData + 5, len);
+
+            tmpData += (5 + len);
+
+            // the number of bytes in the following "alt" string
+            len = *tmpData;
+            parcel->writeInt32(len);
+
+            // an "alt" string for user display
+            parcel->writeInt32(len);
+            parcel->write(tmpData + 1, len);
+
+            break;
+        }
+        // 'tbox' box to indicate the position of the text with values
+        // of top, left, bottom and right
+        case FOURCC('t', 'b', 'o', 'x'):
+        {
+            parcel->writeInt32(KEY_STRUCT_TEXT_POS);
+            parcel->writeInt32(U16_AT(tmpData));
+            parcel->writeInt32(U16_AT(tmpData + 2));
+            parcel->writeInt32(U16_AT(tmpData + 4));
+            parcel->writeInt32(U16_AT(tmpData + 6));
+
+            break;
+        }
+        // 'blnk' to specify the char range to be blinked
+        case FOURCC('b', 'l', 'n', 'k'):
+        {
+            parcel->writeInt32(KEY_STRUCT_BLINKING_TEXT_LIST);
+
+            // start char offset
+            parcel->writeInt32(U16_AT(tmpData));
+            // end char offset
+            parcel->writeInt32(U16_AT(tmpData + 2));
+
+            break;
+        }
+        // 'twrp' box specifies text wrap behavior. If the value if 0x00,
+        // then no wrap. If it's 0x01, then automatic 'soft' wrap is enabled.
+        // 0x02-0xff are reserved.
+        case FOURCC('t', 'w', 'r', 'p'):
+        {
+            parcel->writeInt32(KEY_WRAP_TEXT);
+            parcel->writeInt32(*tmpData);
+
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+
+    if (size > chunkSize) {
+        data += chunkSize;
+        size -= chunkSize;
+        // continue to parse next box
+        return extract3GPPLocalDescriptions(data, size, 0, parcel, 1);
+    }
+
+    return OK;
+}
+
+// To extract box 'tx3g' defined in 3GPP TS 26.245, and store it in a Parcel
+status_t TextDescriptions::extract3GPPGlobalDescriptions(
+        const uint8_t *data, ssize_t size, Parcel *parcel, int depth) {
+
+    ssize_t chunkSize = U32_AT(data);
+    uint32_t chunkType = U32_AT(data + 4);
+    const uint8_t *tmpData = data;
+    tmpData += 8;
+
+    if (size < chunkSize) {
+        return OK;
+    }
+
+    if (depth == 0) {
+        parcel->writeInt32(KEY_GLOBAL_SETTING);
+    }
+    switch(chunkType) {
+        case FOURCC('t', 'x', '3', 'g'):
+        {
+            tmpData += 8; // skip the first 8 bytes
+            parcel->writeInt32(KEY_DISPLAY_FLAGS);
+            parcel->writeInt32(U32_AT(tmpData));
+
+            parcel->writeInt32(KEY_STRUCT_JUSTIFICATION);
+            parcel->writeInt32(tmpData[4]);
+            parcel->writeInt32(tmpData[5]);
+
+            parcel->writeInt32(KEY_BACKGROUND_COLOR_RGBA);
+            uint32_t rgba = *(tmpData + 6) << 24 | *(tmpData + 7) << 16
+                | *(tmpData + 8) << 8 | *(tmpData + 9);
+            parcel->writeInt32(rgba);
+
+            tmpData += 10;
+            parcel->writeInt32(KEY_STRUCT_TEXT_POS);
+            parcel->writeInt32(U16_AT(tmpData));
+            parcel->writeInt32(U16_AT(tmpData + 2));
+            parcel->writeInt32(U16_AT(tmpData + 4));
+            parcel->writeInt32(U16_AT(tmpData + 6));
+
+            tmpData += 8;
+            parcel->writeInt32(KEY_STRUCT_STYLE_LIST);
+            parcel->writeInt32(KEY_START_CHAR);
+            parcel->writeInt32(U16_AT(tmpData));
+
+            parcel->writeInt32(KEY_END_CHAR);
+            parcel->writeInt32(U16_AT(tmpData + 2));
+
+            parcel->writeInt32(KEY_FONT_ID);
+            parcel->writeInt32(U16_AT(tmpData + 4));
+
+            parcel->writeInt32(KEY_STYLE_FLAGS);
+            parcel->writeInt32(*(tmpData + 6));
+
+            parcel->writeInt32(KEY_FONT_SIZE);
+            parcel->writeInt32(*(tmpData + 7));
+
+            parcel->writeInt32(KEY_TEXT_COLOR_RGBA);
+            rgba = *(tmpData + 8) << 24 | *(tmpData + 9) << 16
+                | *(tmpData + 10) << 8 | *(tmpData + 11);
+            parcel->writeInt32(rgba);
+
+            tmpData += 12;
+            parcel->writeInt32(KEY_STRUCT_FONT_LIST);
+            uint16_t count = U16_AT(tmpData);
+            parcel->writeInt32(count);
+
+            tmpData += 2;
+            for (int i = 0; i < count; i++) {
+                // font ID
+                parcel->writeInt32(U16_AT(tmpData));
+
+                // font name length
+                parcel->writeInt32(*(tmpData + 2));
+
+                int len = *(tmpData + 2);
+
+                parcel->write(tmpData + 3, len);
+                tmpData += 3 + len;
+            }
+
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+
+    data += chunkSize;
+    size -= chunkSize;
+
+    if (size > 0) {
+        // continue to extract next 'tx3g'
+        return extract3GPPGlobalDescriptions(data, size, parcel, 1);
+    }
+
+    return OK;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/timedtext/TextDescriptions.h b/media/libstagefright/timedtext/TextDescriptions.h
new file mode 100644
index 0000000..0144917
--- /dev/null
+++ b/media/libstagefright/timedtext/TextDescriptions.h
@@ -0,0 +1,84 @@
+ /*
+ * 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 TEXT_DESCRIPTIONS_H_
+
+#define TEXT_DESCRIPTIONS_H_
+
+#include <binder/Parcel.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+class TextDescriptions {
+public:
+    enum {
+        IN_BAND_TEXT_3GPP             = 0x01,
+        OUT_OF_BAND_TEXT_SRT          = 0x02,
+
+        GLOBAL_DESCRIPTIONS           = 0x100,
+        LOCAL_DESCRIPTIONS            = 0x200,
+    };
+
+    static status_t getParcelOfDescriptions(
+            const uint8_t *data, ssize_t size,
+            uint32_t flags, int timeMs, Parcel *parcel);
+private:
+    TextDescriptions();
+
+    enum {
+        // These keys must be in sync with the keys in TimedText.java
+        KEY_DISPLAY_FLAGS                 = 1, // int
+        KEY_STYLE_FLAGS                   = 2, // int
+        KEY_BACKGROUND_COLOR_RGBA         = 3, // int
+        KEY_HIGHLIGHT_COLOR_RGBA          = 4, // int
+        KEY_SCROLL_DELAY                  = 5, // int
+        KEY_WRAP_TEXT                     = 6, // int
+        KEY_START_TIME                    = 7, // int
+        KEY_STRUCT_BLINKING_TEXT_LIST     = 8, // List<CharPos>
+        KEY_STRUCT_FONT_LIST              = 9, // List<Font>
+        KEY_STRUCT_HIGHLIGHT_LIST         = 10, // List<CharPos>
+        KEY_STRUCT_HYPER_TEXT_LIST        = 11, // List<HyperText>
+        KEY_STRUCT_KARAOKE_LIST           = 12, // List<Karaoke>
+        KEY_STRUCT_STYLE_LIST             = 13, // List<Style>
+        KEY_STRUCT_TEXT_POS               = 14, // TextPos
+        KEY_STRUCT_JUSTIFICATION          = 15, // Justification
+        KEY_STRUCT_TEXT                   = 16, // Text
+
+        KEY_GLOBAL_SETTING                = 101,
+        KEY_LOCAL_SETTING                 = 102,
+        KEY_START_CHAR                    = 103,
+        KEY_END_CHAR                      = 104,
+        KEY_FONT_ID                       = 105,
+        KEY_FONT_SIZE                     = 106,
+        KEY_TEXT_COLOR_RGBA               = 107,
+    };
+
+    static status_t extractSRTLocalDescriptions(
+            const uint8_t *data, ssize_t size,
+            int timeMs, Parcel *parcel);
+    static status_t extract3GPPGlobalDescriptions(
+            const uint8_t *data, ssize_t size,
+            Parcel *parcel, int depth);
+    static status_t extract3GPPLocalDescriptions(
+            const uint8_t *data, ssize_t size,
+            int timeMs, Parcel *parcel, int depth);
+
+    DISALLOW_EVIL_CONSTRUCTORS(TextDescriptions);
+};
+
+}  // namespace android
+#endif  // TEXT_DESCRIPTIONS_H_
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp
index 50bb16d..7c8a747 100644
--- a/media/libstagefright/timedtext/TimedTextPlayer.cpp
+++ b/media/libstagefright/timedtext/TimedTextPlayer.cpp
@@ -19,6 +19,7 @@
 #include <utils/Log.h>
 
 #include <binder/IPCThreadState.h>
+
 #include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
@@ -27,9 +28,11 @@
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/FileSource.h>
 #include <media/stagefright/Utils.h>
+
 #include "include/AwesomePlayer.h"
 #include "TimedTextPlayer.h"
 #include "TimedTextParser.h"
+#include "TextDescriptions.h"
 
 namespace android {
 
@@ -92,10 +95,11 @@
         return BAD_VALUE;
     }
 
+    status_t err;
     if (index < mTextTrackVector.size()) { // start an in-band text
         mSource = mTextTrackVector.itemAt(index);
 
-        status_t err = mSource->start();
+        err = mSource->start();
 
         if (err != OK) {
             return err;
@@ -112,13 +116,17 @@
             mTextParser = new TimedTextParser();
         }
 
-        status_t err;
         if ((err = mTextParser->init(mOutOfBandSource, fileType)) != OK) {
             return err;
         }
         mTextType = kOutOfBandText;
     }
 
+    // send sample description format
+    if ((err = extractAndSendGlobalDescriptions()) != OK) {
+        return err;
+    }
+
     int64_t positionUs;
     mObserver->getPosition(&positionUs);
     seekTo(positionUs);
@@ -211,21 +219,17 @@
     }
     mTextEventPending = false;
 
+    if (mData.dataSize() > 0) {
+        notifyListener(MEDIA_TIMED_TEXT, &mData);
+        mData.freeData();
+    }
+
     MediaSource::ReadOptions options;
     if (mSeeking) {
         options.setSeekTo(mSeekTimeUs,
                 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
         mSeeking = false;
 
-        if (mTextType == kInBandText) {
-            if (mTextBuffer != NULL) {
-                mTextBuffer->release();
-                mTextBuffer = NULL;
-            }
-        } else {
-            mText.clear();
-        }
-
         notifyListener(MEDIA_TIMED_TEXT); //empty text to clear the screen
     }
 
@@ -233,32 +237,12 @@
     mObserver->getPosition(&positionUs);
 
     if (mTextType == kInBandText) {
-        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;
         }
 
         mTextBuffer->meta_data()->findInt64(kKeyTime, &timeUs);
     } else {
-        if (mText.size() > 0) {
-            notifyListener(MEDIA_TIMED_TEXT,
-                           mText.c_str(),
-                           mText.size());
-            mText.clear();
-        }
-
         int64_t endTimeUs;
         if (mTextParser->getText(
                     &mText, &timeUs, &endTimeUs, &options) != OK) {
@@ -266,6 +250,19 @@
         }
     }
 
+    if (timeUs > 0) {
+        extractAndAppendLocalDescriptions(timeUs);
+    }
+
+    if (mTextType == kInBandText) {
+        if (mTextBuffer != NULL) {
+            mTextBuffer->release();
+            mTextBuffer = NULL;
+        }
+    } else {
+        mText.clear();
+    }
+
     //send the text now
     if (timeUs <= positionUs + 100000ll) {
         postTextEvent();
@@ -297,7 +294,8 @@
     Mutex::Autolock autoLock(mLock);
 
     if (key == KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE) {
-        String8 uri = request.readString8();
+        const String16 uri16 = request.readString16();
+        String8 uri = String8(uri16);
         KeyedVector<String8, String8> headers;
 
         // To support local subtitle file only for now
@@ -327,21 +325,92 @@
     return INVALID_OPERATION;
 }
 
-void TimedTextPlayer::notifyListener(
-        int msg, const void *data, size_t size) {
+void TimedTextPlayer::notifyListener(int msg, const Parcel *parcel) {
     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);
+            if (parcel && (parcel->dataSize() > 0)) {
+                listener->sendEvent(msg, 0, 0, parcel);
             } else { // send an empty timed text to clear the screen
                 listener->sendEvent(msg);
             }
         }
     }
 }
+
+// Each text sample consists of a string of text, optionally with sample
+// modifier description. The modifier description could specify a new
+// text style for the string of text. These descriptions are present only
+// if they are needed. This method is used to extract the modifier
+// description and append it at the end of the text.
+status_t TimedTextPlayer::extractAndAppendLocalDescriptions(int64_t timeUs) {
+    const void *data;
+    size_t size = 0;
+    int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS;
+
+    if (mTextType == kInBandText) {
+        const char *mime;
+        CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
+
+        if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
+            flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
+            data = mTextBuffer->data();
+            size = mTextBuffer->size();
+        } else {
+            // support 3GPP only for now
+            return ERROR_UNSUPPORTED;
+        }
+    } else {
+        data = mText.c_str();
+        size = mText.size();
+        flag |= TextDescriptions::OUT_OF_BAND_TEXT_SRT;
+    }
+
+    if ((size > 0) && (flag != TextDescriptions::LOCAL_DESCRIPTIONS)) {
+        mData.freeData();
+        return TextDescriptions::getParcelOfDescriptions(
+                (const uint8_t *)data, size, flag, timeUs / 1000, &mData);
+    }
+
+    return OK;
+}
+
+// To extract and send the global text descriptions for all the text samples
+// in the text track or text file.
+status_t TimedTextPlayer::extractAndSendGlobalDescriptions() {
+    const void *data;
+    size_t size = 0;
+    int32_t flag = TextDescriptions::GLOBAL_DESCRIPTIONS;
+
+    if (mTextType == kInBandText) {
+        const char *mime;
+        CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
+
+        // support 3GPP only for now
+        if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
+            uint32_t type;
+            // get the 'tx3g' box content. This box contains the text descriptions
+            // used to render the text track
+            if (!mSource->getFormat()->findData(
+                        kKeyTextFormatData, &type, &data, &size)) {
+                return ERROR_MALFORMED;
+            }
+
+            flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
+        }
+    }
+
+    if ((size > 0) && (flag != TextDescriptions::GLOBAL_DESCRIPTIONS)) {
+        Parcel parcel;
+        if (TextDescriptions::getParcelOfDescriptions(
+                (const uint8_t *)data, size, flag, 0, &parcel) == OK) {
+            if (parcel.dataSize() > 0) {
+                notifyListener(MEDIA_TIMED_TEXT, &parcel);
+            }
+        }
+    }
+
+    return OK;
+}
 }
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.h b/media/libstagefright/timedtext/TimedTextPlayer.h
index 590760b..a744db5 100644
--- a/media/libstagefright/timedtext/TimedTextPlayer.h
+++ b/media/libstagefright/timedtext/TimedTextPlayer.h
@@ -103,8 +103,10 @@
     void postTextEvent(int64_t delayUs = -1);
     void cancelTextEvent();
 
-    void notifyListener(
-            int msg, const void *data = NULL, size_t size = 0);
+    void notifyListener(int msg, const Parcel *parcel = NULL);
+
+    status_t extractAndAppendLocalDescriptions(int64_t timeUs);
+    status_t extractAndSendGlobalDescriptions();
 
     DISALLOW_EVIL_CONSTRUCTORS(TimedTextPlayer);
 };