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
21namespace android {
22
23TextDescriptions::TextDescriptions() {
24}
25
26status_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.
49status_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.
70status_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
287status_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