Home | History | Annotate | Download | only in timedtext
      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 
     23 TextDescriptions::TextDescriptions() {
     24 }
     25 
     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.
     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.
     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
    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