1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "TextDescriptions2.h"
18 #include <media/stagefright/foundation/ByteUtils.h>
19 #include <media/stagefright/MediaErrors.h>
20
21 namespace android {
22
TextDescriptions2()23 TextDescriptions2::TextDescriptions2() {
24 }
25
getPlayerMessageOfDescriptions(const uint8_t * data,ssize_t size,uint32_t flags,int timeMs,PlayerMessage * playerMsg)26 status_t TextDescriptions2::getPlayerMessageOfDescriptions(
27 const uint8_t *data, ssize_t size,
28 uint32_t flags, int timeMs, PlayerMessage *playerMsg) {
29 if (flags & IN_BAND_TEXT_3GPP) {
30 if (flags & GLOBAL_DESCRIPTIONS) {
31 return extract3GPPGlobalDescriptions(data, size, playerMsg);
32 } else if (flags & LOCAL_DESCRIPTIONS) {
33 return extract3GPPLocalDescriptions(data, size, timeMs, playerMsg);
34 }
35 } else if (flags & OUT_OF_BAND_TEXT_SRT) {
36 if (flags & LOCAL_DESCRIPTIONS) {
37 return extractSRTLocalDescriptions(data, size, timeMs, playerMsg);
38 }
39 }
40
41 return ERROR_UNSUPPORTED;
42 }
43
44 // Parse the SRT text sample, and store the timing and text sample in a PlayerMessage.
45 // The PlayerMessage will be sent to MediaPlayer2.java through event, and will be
46 // parsed in TimedText.java.
extractSRTLocalDescriptions(const uint8_t * data,ssize_t size,int timeMs,PlayerMessage * playerMsg)47 status_t TextDescriptions2::extractSRTLocalDescriptions(
48 const uint8_t *data, ssize_t size, int timeMs, PlayerMessage *playerMsg) {
49 playerMsg->add_values()->set_int32_value(KEY_LOCAL_SETTING);
50 playerMsg->add_values()->set_int32_value(KEY_START_TIME);
51 playerMsg->add_values()->set_int32_value(timeMs);
52
53 playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT);
54 playerMsg->add_values()->set_bytes_value(data, size);
55
56 return OK;
57 }
58
59 // Extract the local 3GPP display descriptions. 3GPP local descriptions
60 // are appended to the text sample if any.
extract3GPPLocalDescriptions(const uint8_t * data,ssize_t size,int timeMs,PlayerMessage * playerMsg)61 status_t TextDescriptions2::extract3GPPLocalDescriptions(
62 const uint8_t *data, ssize_t size,
63 int timeMs, PlayerMessage *playerMsg) {
64
65 playerMsg->add_values()->set_int32_value(KEY_LOCAL_SETTING);
66
67 // write start time to display this text sample
68 playerMsg->add_values()->set_int32_value(KEY_START_TIME);
69 playerMsg->add_values()->set_int32_value(timeMs);
70
71 if (size < 2) {
72 return OK;
73 }
74 ssize_t textLen = (*data) << 8 | (*(data + 1));
75
76 if (size < textLen + 2) {
77 return OK;
78 }
79
80 // write text sample length and text sample itself
81 playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT);
82 playerMsg->add_values()->set_bytes_value(data + 2, textLen);
83
84 if (size > textLen + 2) {
85 data += (textLen + 2);
86 size -= (textLen + 2);
87 } else {
88 return OK;
89 }
90
91 while (size >= 8) {
92 const uint8_t *tmpData = data;
93 ssize_t chunkSize = U32_AT(tmpData); // size includes size and type
94 uint32_t chunkType = U32_AT(tmpData + 4);
95
96 if (chunkSize <= 8 || chunkSize > size) {
97 return OK;
98 }
99
100 size_t remaining = chunkSize - 8;
101
102 tmpData += 8;
103
104 switch(chunkType) {
105 // 'tbox' box to indicate the position of the text with values
106 // of top, left, bottom and right
107 case FOURCC('t', 'b', 'o', 'x'):
108 {
109 if (remaining < 8) {
110 return OK;
111 }
112 playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT_POS);
113 playerMsg->add_values()->set_int32_value(U16_AT(tmpData));
114 playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 2));
115 playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 4));
116 playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 6));
117
118 tmpData += 8;
119 remaining -= 8;
120 break;
121 }
122 default:
123 {
124 break;
125 }
126 }
127
128 data += chunkSize;
129 size -= chunkSize;
130 }
131
132 return OK;
133 }
134
135 // To extract box 'tx3g' defined in 3GPP TS 26.245, and store it in a PlayerMessage
extract3GPPGlobalDescriptions(const uint8_t * data,ssize_t size,PlayerMessage * playerMsg)136 status_t TextDescriptions2::extract3GPPGlobalDescriptions(
137 const uint8_t *data, ssize_t size, PlayerMessage *playerMsg) {
138
139 playerMsg->add_values()->set_int32_value(KEY_GLOBAL_SETTING);
140
141 while (size >= 8) {
142 ssize_t chunkSize = U32_AT(data);
143 uint32_t chunkType = U32_AT(data + 4);
144 const uint8_t *tmpData = data;
145 tmpData += 8;
146 size_t remaining = size - 8;
147
148 if (chunkSize <= 8 || size < chunkSize) {
149 return OK;
150 }
151 switch(chunkType) {
152 case FOURCC('t', 'x', '3', 'g'):
153 {
154 if (remaining < 18) {
155 return OK;
156 }
157 // Skip DISPLAY_FLAGS, STRUCT_JUSTIFICATION, and BACKGROUND_COLOR_RGBA
158 tmpData += 18;
159 remaining -= 18;
160
161 if (remaining < 8) {
162 return OK;
163 }
164 playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT_POS);
165 playerMsg->add_values()->set_int32_value(U16_AT(tmpData));
166 playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 2));
167 playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 4));
168 playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 6));
169
170 tmpData += 8;
171 remaining -= 18;
172 // Ignore remaining data.
173 break;
174 }
175 default:
176 {
177 break;
178 }
179 }
180
181 data += chunkSize;
182 size -= chunkSize;
183 }
184
185 return OK;
186 }
187
188 } // namespace android
189