MediaPlayer2: use protobuf instead of parcel for TimedText

Test: MediaPlayer2Test
Bug: 112767225
Change-Id: I3f6b06d49b2a245e8350d792c01bccb9fd44037f
diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp
index 300adec..0672ca9 100644
--- a/media/libmediaplayer2/Android.bp
+++ b/media/libmediaplayer2/Android.bp
@@ -60,7 +60,7 @@
         "libprotobuf-cpp-lite",
         "libstagefright_nuplayer2",
         "libstagefright_rtsp",
-        "libstagefright_timedtext",
+        "libstagefright_timedtext2",
     ],
 
     export_include_dirs: [
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
index 2ec7bc0..2f90825 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
@@ -33,7 +33,7 @@
 #include "NuPlayer2Source.h"
 #include "RTSPSource2.h"
 #include "GenericSource2.h"
-#include "TextDescriptions.h"
+#include "TextDescriptions2.h"
 
 #include "ATSParser.h"
 
@@ -2870,7 +2870,7 @@
     const void *data;
     size_t size = 0;
     int64_t timeUs;
-    int32_t flag = TextDescriptions::IN_BAND_TEXT_3GPP;
+    int32_t flag = TextDescriptions2::IN_BAND_TEXT_3GPP;
 
     AString mime;
     CHECK(buffer->meta()->findString("mime", &mime));
@@ -2879,22 +2879,21 @@
     data = buffer->data();
     size = buffer->size();
 
-    Parcel parcel;
+    PlayerMessage playerMsg;
     if (size > 0) {
         CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
         int32_t global = 0;
         if (buffer->meta()->findInt32("global", &global) && global) {
-            flag |= TextDescriptions::GLOBAL_DESCRIPTIONS;
+            flag |= TextDescriptions2::GLOBAL_DESCRIPTIONS;
         } else {
-            flag |= TextDescriptions::LOCAL_DESCRIPTIONS;
+            flag |= TextDescriptions2::LOCAL_DESCRIPTIONS;
         }
-        TextDescriptions::getParcelOfDescriptions(
-                (const uint8_t *)data, size, flag, timeUs / 1000, &parcel);
+        TextDescriptions2::getPlayerMessageOfDescriptions(
+                (const uint8_t *)data, size, flag, timeUs / 1000, &playerMsg);
     }
 
-    if ((parcel.dataSize() > 0)) {
-        // TODO: convert text data to PlayerMessage
-        // notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0, &parcel);
+    if (playerMsg.values_size() > 0) {
+        notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0, &playerMsg);
     } else {  // send an empty timed text
         notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0);
     }
diff --git a/media/libstagefright/timedtext/Android.bp b/media/libstagefright/timedtext/Android.bp
index a5ad6c6..7c51333 100644
--- a/media/libstagefright/timedtext/Android.bp
+++ b/media/libstagefright/timedtext/Android.bp
@@ -25,3 +25,36 @@
 
     shared_libs: ["libmedia"],
 }
+
+cc_library_static {
+    name: "libstagefright_timedtext2",
+
+    srcs: ["TextDescriptions2.cpp"],
+
+    static_libs: [
+        "libmediaplayer2-protos",
+        "libprotobuf-cpp-lite",
+    ],
+
+    cflags: [
+        "-Wno-multichar",
+        "-Werror",
+        "-Wall",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    include_dirs: [
+        "frameworks/av/media/libstagefright",
+    ],
+
+    shared_libs: ["libmedia"],
+}
diff --git a/media/libstagefright/timedtext/TextDescriptions2.cpp b/media/libstagefright/timedtext/TextDescriptions2.cpp
new file mode 100644
index 0000000..f48eacc
--- /dev/null
+++ b/media/libstagefright/timedtext/TextDescriptions2.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2018 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 "TextDescriptions2.h"
+#include <media/stagefright/foundation/ByteUtils.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+TextDescriptions2::TextDescriptions2() {
+}
+
+status_t TextDescriptions2::getPlayerMessageOfDescriptions(
+        const uint8_t *data, ssize_t size,
+        uint32_t flags, int timeMs, PlayerMessage *playerMsg) {
+    if (flags & IN_BAND_TEXT_3GPP) {
+        if (flags & GLOBAL_DESCRIPTIONS) {
+            return extract3GPPGlobalDescriptions(data, size, playerMsg);
+        } else if (flags & LOCAL_DESCRIPTIONS) {
+            return extract3GPPLocalDescriptions(data, size, timeMs, playerMsg);
+        }
+    } else if (flags & OUT_OF_BAND_TEXT_SRT) {
+        if (flags & LOCAL_DESCRIPTIONS) {
+            return extractSRTLocalDescriptions(data, size, timeMs, playerMsg);
+        }
+    }
+
+    return ERROR_UNSUPPORTED;
+}
+
+// Parse the SRT text sample, and store the timing and text sample in a PlayerMessage.
+// The PlayerMessage will be sent to MediaPlayer2.java through event, and will be
+// parsed in TimedText.java.
+status_t TextDescriptions2::extractSRTLocalDescriptions(
+        const uint8_t *data, ssize_t size, int timeMs, PlayerMessage *playerMsg) {
+    playerMsg->add_values()->set_int32_value(KEY_LOCAL_SETTING);
+    playerMsg->add_values()->set_int32_value(KEY_START_TIME);
+    playerMsg->add_values()->set_int32_value(timeMs);
+
+    playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT);
+    playerMsg->add_values()->set_bytes_value(data, size);
+
+    return OK;
+}
+
+// Extract the local 3GPP display descriptions. 3GPP local descriptions
+// are appended to the text sample if any.
+status_t TextDescriptions2::extract3GPPLocalDescriptions(
+        const uint8_t *data, ssize_t size,
+        int timeMs, PlayerMessage *playerMsg) {
+
+    playerMsg->add_values()->set_int32_value(KEY_LOCAL_SETTING);
+
+    // write start time to display this text sample
+    playerMsg->add_values()->set_int32_value(KEY_START_TIME);
+    playerMsg->add_values()->set_int32_value(timeMs);
+
+    if (size < 2) {
+        return OK;
+    }
+    ssize_t textLen = (*data) << 8 | (*(data + 1));
+
+    if (size < textLen + 2) {
+        return OK;
+    }
+
+    // write text sample length and text sample itself
+    playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT);
+    playerMsg->add_values()->set_bytes_value(data + 2, textLen);
+
+    if (size > textLen + 2) {
+        data += (textLen + 2);
+        size -= (textLen + 2);
+    } else {
+        return OK;
+    }
+
+    while (size >= 8) {
+        const uint8_t *tmpData = data;
+        ssize_t chunkSize = U32_AT(tmpData);      // size includes size and type
+        uint32_t chunkType = U32_AT(tmpData + 4);
+
+        if (chunkSize <= 8 || chunkSize > size) {
+            return OK;
+        }
+
+        size_t remaining = chunkSize - 8;
+
+        tmpData += 8;
+
+        switch(chunkType) {
+            // 'tbox' box to indicate the position of the text with values
+            // of top, left, bottom and right
+            case FOURCC('t', 'b', 'o', 'x'):
+            {
+                if (remaining < 8) {
+                    return OK;
+                }
+                playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT_POS);
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData));
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 2));
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 4));
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 6));
+
+                tmpData += 8;
+                remaining -= 8;
+                break;
+            }
+            default:
+            {
+                break;
+            }
+        }
+
+        data += chunkSize;
+        size -= chunkSize;
+    }
+
+    return OK;
+}
+
+// To extract box 'tx3g' defined in 3GPP TS 26.245, and store it in a PlayerMessage
+status_t TextDescriptions2::extract3GPPGlobalDescriptions(
+        const uint8_t *data, ssize_t size, PlayerMessage *playerMsg) {
+
+    playerMsg->add_values()->set_int32_value(KEY_GLOBAL_SETTING);
+
+    while (size >= 8) {
+        ssize_t chunkSize = U32_AT(data);
+        uint32_t chunkType = U32_AT(data + 4);
+        const uint8_t *tmpData = data;
+        tmpData += 8;
+        size_t remaining = size - 8;
+
+        if (size < chunkSize) {
+            return OK;
+        }
+        switch(chunkType) {
+            case FOURCC('t', 'x', '3', 'g'):
+            {
+                if (remaining < 18) {
+                    return OK;
+                }
+                // Skip DISPLAY_FLAGS, STRUCT_JUSTIFICATION, and BACKGROUND_COLOR_RGBA
+                tmpData += 18;
+                remaining -= 18;
+
+                if (remaining < 8) {
+                    return OK;
+                }
+                playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT_POS);
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData));
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 2));
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 4));
+                playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 6));
+
+                tmpData += 8;
+                remaining -= 18;
+                // Ignore remaining data.
+                break;
+            }
+            default:
+            {
+                break;
+            }
+        }
+
+        data += chunkSize;
+        size -= chunkSize;
+    }
+
+    return OK;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/timedtext/TextDescriptions2.h b/media/libstagefright/timedtext/TextDescriptions2.h
new file mode 100644
index 0000000..7c7d2d0
--- /dev/null
+++ b/media/libstagefright/timedtext/TextDescriptions2.h
@@ -0,0 +1,88 @@
+ /*
+ * Copyright (C) 2018 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_DESCRIPTIONS2_H_
+
+#define TEXT_DESCRIPTIONS2_H_
+
+#include <binder/Parcel.h>
+#include <media/stagefright/foundation/ABase.h>
+
+#include "mediaplayer2.pb.h"
+
+using android::media::MediaPlayer2Proto::PlayerMessage;
+
+namespace android {
+
+class TextDescriptions2 {
+public:
+    enum {
+        IN_BAND_TEXT_3GPP             = 0x01,
+        OUT_OF_BAND_TEXT_SRT          = 0x02,
+
+        GLOBAL_DESCRIPTIONS           = 0x100,
+        LOCAL_DESCRIPTIONS            = 0x200,
+    };
+
+    static status_t getPlayerMessageOfDescriptions(
+            const uint8_t *data, ssize_t size,
+            uint32_t flags, int timeMs, PlayerMessage *playerMsg);
+private:
+    TextDescriptions2();
+
+    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, PlayerMessage *playerMsg);
+    static status_t extract3GPPGlobalDescriptions(
+            const uint8_t *data, ssize_t size,
+            PlayerMessage *playerMsg);
+    static status_t extract3GPPLocalDescriptions(
+            const uint8_t *data, ssize_t size,
+            int timeMs, PlayerMessage *playerMsg);
+
+    DISALLOW_EVIL_CONSTRUCTORS(TextDescriptions2);
+};
+
+}  // namespace android
+#endif  // TEXT_DESCRIPTIONS2_H_