1 /*
2 * Copyright (C) 2011 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 "TextDescriptions.h"
18 #include <media/stagefright/Utils.h>
19 #include <media/stagefright/MediaErrors.h>
20
21 namespace android {
22
TextDescriptions()23 TextDescriptions::TextDescriptions() {
24 }
25
getParcelOfDescriptions(const uint8_t * data,ssize_t size,uint32_t flags,int timeMs,Parcel * parcel)26 status_t TextDescriptions::getParcelOfDescriptions(
27 const uint8_t *data, ssize_t size,
28 uint32_t flags, int timeMs, Parcel *parcel) {
29 parcel->freeData();
30
31 if (flags & IN_BAND_TEXT_3GPP) {
32 if (flags & GLOBAL_DESCRIPTIONS) {
33 return extract3GPPGlobalDescriptions(data, size, parcel, 0);
34 } else if (flags & LOCAL_DESCRIPTIONS) {
35 return extract3GPPLocalDescriptions(data, size, timeMs, parcel, 0);
36 }
37 } else if (flags & OUT_OF_BAND_TEXT_SRT) {
38 if (flags & LOCAL_DESCRIPTIONS) {
39 return extractSRTLocalDescriptions(data, size, timeMs, parcel);
40 }
41 }
42
43 return ERROR_UNSUPPORTED;
44 }
45
46 // Parse the SRT text sample, and store the timing and text sample in a Parcel.
47 // The Parcel will be sent to MediaPlayer.java through event, and will be
48 // parsed in TimedText.java.
extractSRTLocalDescriptions(const uint8_t * data,ssize_t size,int timeMs,Parcel * parcel)49 status_t TextDescriptions::extractSRTLocalDescriptions(
50 const uint8_t *data, ssize_t size, int timeMs, Parcel *parcel) {
51 parcel->writeInt32(KEY_LOCAL_SETTING);
52 parcel->writeInt32(KEY_START_TIME);
53 parcel->writeInt32(timeMs);
54
55 parcel->writeInt32(KEY_STRUCT_TEXT);
56 // write the size of the text sample
57 parcel->writeInt32(size);
58 // write the text sample as a byte array
59 parcel->writeInt32(size);
60 parcel->write(data, size);
61
62 return OK;
63 }
64
65 // Extract the local 3GPP display descriptions. 3GPP local descriptions
66 // are appended to the text sample if any. The descriptions could include
67 // information such as text styles, highlights, karaoke and so on. They
68 // are contained in different boxes, such as 'styl' box contains text
69 // styles, and 'krok' box contains karaoke timing and positions.
extract3GPPLocalDescriptions(const uint8_t * data,ssize_t size,int timeMs,Parcel * parcel,int depth)70 status_t TextDescriptions::extract3GPPLocalDescriptions(
71 const uint8_t *data, ssize_t size,
72 int timeMs, Parcel *parcel, int depth) {
73 if (depth == 0) {
74 parcel->writeInt32(KEY_LOCAL_SETTING);
75
76 // write start time to display this text sample
77 parcel->writeInt32(KEY_START_TIME);
78 parcel->writeInt32(timeMs);
79
80 ssize_t textLen = (*data) << 8 | (*(data + 1));
81
82 // write text sample length and text sample itself
83 parcel->writeInt32(KEY_STRUCT_TEXT);
84 parcel->writeInt32(textLen);
85 parcel->writeInt32(textLen);
86 parcel->write(data + 2, textLen);
87
88 if (size > textLen) {
89 data += (textLen + 2);
90 size -= (textLen + 2);
91 } else {
92 return OK;
93 }
94 }
95
96 const uint8_t *tmpData = data;
97 ssize_t chunkSize = U32_AT(tmpData);
98 uint32_t chunkType = U32_AT(tmpData + 4);
99
100 if (chunkSize <= 0) {
101 return OK;
102 }
103
104 tmpData += 8;
105
106 switch(chunkType) {
107 // 'styl' box specifies the style of the text.
108 case FOURCC('s', 't', 'y', 'l'):
109 {
110 uint16_t count = U16_AT(tmpData);
111
112 tmpData += 2;
113
114 for (int i = 0; i < count; i++) {
115 parcel->writeInt32(KEY_STRUCT_STYLE_LIST);
116 parcel->writeInt32(KEY_START_CHAR);
117 parcel->writeInt32(U16_AT(tmpData));
118
119 parcel->writeInt32(KEY_END_CHAR);
120 parcel->writeInt32(U16_AT(tmpData + 2));
121
122 parcel->writeInt32(KEY_FONT_ID);
123 parcel->writeInt32(U16_AT(tmpData + 4));
124
125 parcel->writeInt32(KEY_STYLE_FLAGS);
126 parcel->writeInt32(*(tmpData + 6));
127
128 parcel->writeInt32(KEY_FONT_SIZE);
129 parcel->writeInt32(*(tmpData + 7));
130
131 parcel->writeInt32(KEY_TEXT_COLOR_RGBA);
132 uint32_t rgba = *(tmpData + 8) << 24 | *(tmpData + 9) << 16
133 | *(tmpData + 10) << 8 | *(tmpData + 11);
134 parcel->writeInt32(rgba);
135
136 tmpData += 12;
137 }
138
139 break;
140 }
141 // 'krok' box. The number of highlight events is specified, and each
142 // event is specified by a starting and ending char offset and an end
143 // time for the event.
144 case FOURCC('k', 'r', 'o', 'k'):
145 {
146
147 parcel->writeInt32(KEY_STRUCT_KARAOKE_LIST);
148
149 int startTime = U32_AT(tmpData);
150 uint16_t count = U16_AT(tmpData + 4);
151 parcel->writeInt32(count);
152
153 tmpData += 6;
154 int lastEndTime = 0;
155
156 for (int i = 0; i < count; i++) {
157 parcel->writeInt32(startTime + lastEndTime);
158
159 lastEndTime = U32_AT(tmpData);
160 parcel->writeInt32(lastEndTime);
161
162 parcel->writeInt32(U16_AT(tmpData + 4));
163 parcel->writeInt32(U16_AT(tmpData + 6));
164
165 tmpData += 8;
166 }
167
168 break;
169 }
170 // 'hlit' box specifies highlighted text
171 case FOURCC('h', 'l', 'i', 't'):
172 {
173 parcel->writeInt32(KEY_STRUCT_HIGHLIGHT_LIST);
174
175 // the start char offset to highlight
176 parcel->writeInt32(U16_AT(tmpData));
177 // the last char offset to highlight
178 parcel->writeInt32(U16_AT(tmpData + 2));
179
180 break;
181 }
182 // 'hclr' box specifies the RGBA color: 8 bits each of
183 // red, green, blue, and an alpha(transparency) value
184 case FOURCC('h', 'c', 'l', 'r'):
185 {
186 parcel->writeInt32(KEY_HIGHLIGHT_COLOR_RGBA);
187
188 uint32_t rgba = *(tmpData) << 24 | *(tmpData + 1) << 16
189 | *(tmpData + 2) << 8 | *(tmpData + 3);
190 parcel->writeInt32(rgba);
191
192 break;
193 }
194 // 'dlay' box specifies a delay after a scroll in and/or
195 // before scroll out.
196 case FOURCC('d', 'l', 'a', 'y'):
197 {
198 parcel->writeInt32(KEY_SCROLL_DELAY);
199
200 uint32_t delay = *(tmpData) << 24 | *(tmpData + 1) << 16
201 | *(tmpData + 2) << 8 | *(tmpData + 3);
202 parcel->writeInt32(delay);
203
204 break;
205 }
206 // 'href' box for hyper text link
207 case FOURCC('h', 'r', 'e', 'f'):
208 {
209 parcel->writeInt32(KEY_STRUCT_HYPER_TEXT_LIST);
210
211 // the start offset of the text to be linked
212 parcel->writeInt32(U16_AT(tmpData));
213 // the end offset of the text
214 parcel->writeInt32(U16_AT(tmpData + 2));
215
216 // the number of bytes in the following URL
217 int len = *(tmpData + 4);
218 parcel->writeInt32(len);
219
220 // the linked-to URL
221 parcel->writeInt32(len);
222 parcel->write(tmpData + 5, len);
223
224 tmpData += (5 + len);
225
226 // the number of bytes in the following "alt" string
227 len = *tmpData;
228 parcel->writeInt32(len);
229
230 // an "alt" string for user display
231 parcel->writeInt32(len);
232 parcel->write(tmpData + 1, len);
233
234 break;
235 }
236 // 'tbox' box to indicate the position of the text with values
237 // of top, left, bottom and right
238 case FOURCC('t', 'b', 'o', 'x'):
239 {
240 parcel->writeInt32(KEY_STRUCT_TEXT_POS);
241 parcel->writeInt32(U16_AT(tmpData));
242 parcel->writeInt32(U16_AT(tmpData + 2));
243 parcel->writeInt32(U16_AT(tmpData + 4));
244 parcel->writeInt32(U16_AT(tmpData + 6));
245
246 break;
247 }
248 // 'blnk' to specify the char range to be blinked
249 case FOURCC('b', 'l', 'n', 'k'):
250 {
251 parcel->writeInt32(KEY_STRUCT_BLINKING_TEXT_LIST);
252
253 // start char offset
254 parcel->writeInt32(U16_AT(tmpData));
255 // end char offset
256 parcel->writeInt32(U16_AT(tmpData + 2));
257
258 break;
259 }
260 // 'twrp' box specifies text wrap behavior. If the value if 0x00,
261 // then no wrap. If it's 0x01, then automatic 'soft' wrap is enabled.
262 // 0x02-0xff are reserved.
263 case FOURCC('t', 'w', 'r', 'p'):
264 {
265 parcel->writeInt32(KEY_WRAP_TEXT);
266 parcel->writeInt32(*tmpData);
267
268 break;
269 }
270 default:
271 {
272 break;
273 }
274 }
275
276 if (size > chunkSize) {
277 data += chunkSize;
278 size -= chunkSize;
279 // continue to parse next box
280 return extract3GPPLocalDescriptions(data, size, 0, parcel, 1);
281 }
282
283 return OK;
284 }
285
286 // To extract box 'tx3g' defined in 3GPP TS 26.245, and store it in a Parcel
extract3GPPGlobalDescriptions(const uint8_t * data,ssize_t size,Parcel * parcel,int depth)287 status_t TextDescriptions::extract3GPPGlobalDescriptions(
288 const uint8_t *data, ssize_t size, Parcel *parcel, int depth) {
289
290 ssize_t chunkSize = U32_AT(data);
291 uint32_t chunkType = U32_AT(data + 4);
292 const uint8_t *tmpData = data;
293 tmpData += 8;
294
295 if (size < chunkSize) {
296 return OK;
297 }
298
299 if (depth == 0) {
300 parcel->writeInt32(KEY_GLOBAL_SETTING);
301 }
302 switch(chunkType) {
303 case FOURCC('t', 'x', '3', 'g'):
304 {
305 tmpData += 8; // skip the first 8 bytes
306 parcel->writeInt32(KEY_DISPLAY_FLAGS);
307 parcel->writeInt32(U32_AT(tmpData));
308
309 parcel->writeInt32(KEY_STRUCT_JUSTIFICATION);
310 parcel->writeInt32(tmpData[4]);
311 parcel->writeInt32(tmpData[5]);
312
313 parcel->writeInt32(KEY_BACKGROUND_COLOR_RGBA);
314 uint32_t rgba = *(tmpData + 6) << 24 | *(tmpData + 7) << 16
315 | *(tmpData + 8) << 8 | *(tmpData + 9);
316 parcel->writeInt32(rgba);
317
318 tmpData += 10;
319 parcel->writeInt32(KEY_STRUCT_TEXT_POS);
320 parcel->writeInt32(U16_AT(tmpData));
321 parcel->writeInt32(U16_AT(tmpData + 2));
322 parcel->writeInt32(U16_AT(tmpData + 4));
323 parcel->writeInt32(U16_AT(tmpData + 6));
324
325 tmpData += 8;
326 parcel->writeInt32(KEY_STRUCT_STYLE_LIST);
327 parcel->writeInt32(KEY_START_CHAR);
328 parcel->writeInt32(U16_AT(tmpData));
329
330 parcel->writeInt32(KEY_END_CHAR);
331 parcel->writeInt32(U16_AT(tmpData + 2));
332
333 parcel->writeInt32(KEY_FONT_ID);
334 parcel->writeInt32(U16_AT(tmpData + 4));
335
336 parcel->writeInt32(KEY_STYLE_FLAGS);
337 parcel->writeInt32(*(tmpData + 6));
338
339 parcel->writeInt32(KEY_FONT_SIZE);
340 parcel->writeInt32(*(tmpData + 7));
341
342 parcel->writeInt32(KEY_TEXT_COLOR_RGBA);
343 rgba = *(tmpData + 8) << 24 | *(tmpData + 9) << 16
344 | *(tmpData + 10) << 8 | *(tmpData + 11);
345 parcel->writeInt32(rgba);
346
347 tmpData += 12;
348 parcel->writeInt32(KEY_STRUCT_FONT_LIST);
349 uint16_t count = U16_AT(tmpData);
350 parcel->writeInt32(count);
351
352 tmpData += 2;
353 for (int i = 0; i < count; i++) {
354 // font ID
355 parcel->writeInt32(U16_AT(tmpData));
356
357 // font name length
358 parcel->writeInt32(*(tmpData + 2));
359
360 int len = *(tmpData + 2);
361
362 parcel->write(tmpData + 3, len);
363 tmpData += 3 + len;
364 }
365
366 break;
367 }
368 default:
369 {
370 break;
371 }
372 }
373
374 data += chunkSize;
375 size -= chunkSize;
376
377 if (size > 0) {
378 // continue to extract next 'tx3g'
379 return extract3GPPGlobalDescriptions(data, size, parcel, 1);
380 }
381
382 return OK;
383 }
384
385 } // namespace android
386