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_