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