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);
     34         } else if (flags & LOCAL_DESCRIPTIONS) {
     35             return extract3GPPLocalDescriptions(data, size, timeMs, parcel);
     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) {
     73 
     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     if (size < 2) {
     81         return OK;
     82     }
     83     ssize_t textLen = (*data) << 8 | (*(data + 1));
     84 
     85     if (size < textLen + 2) {
     86         return OK;
     87     }
     88 
     89     // write text sample length and text sample itself
     90     parcel->writeInt32(KEY_STRUCT_TEXT);
     91     parcel->writeInt32(textLen);
     92     parcel->writeInt32(textLen);
     93     parcel->write(data + 2, textLen);
     94 
     95     if (size > textLen + 2) {
     96         data += (textLen + 2);
     97         size -= (textLen + 2);
     98     } else {
     99         return OK;
    100     }
    101 
    102     while (size >= 8) {
    103         const uint8_t *tmpData = data;
    104         ssize_t chunkSize = U32_AT(tmpData);      // size includes size and type
    105         uint32_t chunkType = U32_AT(tmpData + 4);
    106 
    107         if (chunkSize <= 8 || chunkSize > size) {
    108             return OK;
    109         }
    110 
    111         size_t remaining = chunkSize - 8;
    112 
    113         tmpData += 8;
    114 
    115         switch(chunkType) {
    116             // 'styl' box specifies the style of the text.
    117             case FOURCC('s', 't', 'y', 'l'):
    118             {
    119                 if (remaining < 2) {
    120                     return OK;
    121                 }
    122                 size_t dataPos = parcel->dataPosition();
    123                 uint16_t count = U16_AT(tmpData);
    124 
    125                 tmpData += 2;
    126                 remaining -= 2;
    127 
    128                 for (int i = 0; i < count; i++) {
    129                     if (remaining < 12) {
    130                         // roll back
    131                         parcel->setDataPosition(dataPos);
    132                         return OK;
    133                     }
    134                     parcel->writeInt32(KEY_STRUCT_STYLE_LIST);
    135                     parcel->writeInt32(KEY_START_CHAR);
    136                     parcel->writeInt32(U16_AT(tmpData));
    137 
    138                     parcel->writeInt32(KEY_END_CHAR);
    139                     parcel->writeInt32(U16_AT(tmpData + 2));
    140 
    141                     parcel->writeInt32(KEY_FONT_ID);
    142                     parcel->writeInt32(U16_AT(tmpData + 4));
    143 
    144                     parcel->writeInt32(KEY_STYLE_FLAGS);
    145                     parcel->writeInt32(*(tmpData + 6));
    146 
    147                     parcel->writeInt32(KEY_FONT_SIZE);
    148                     parcel->writeInt32(*(tmpData + 7));
    149 
    150                     parcel->writeInt32(KEY_TEXT_COLOR_RGBA);
    151                     uint32_t rgba = *(tmpData + 8) << 24 | *(tmpData + 9) << 16
    152                         | *(tmpData + 10) << 8 | *(tmpData + 11);
    153                     parcel->writeInt32(rgba);
    154 
    155                     tmpData += 12;
    156                     remaining -= 12;
    157                 }
    158 
    159                 break;
    160             }
    161             // 'krok' box. The number of highlight events is specified, and each
    162             // event is specified by a starting and ending char offset and an end
    163             // time for the event.
    164             case FOURCC('k', 'r', 'o', 'k'):
    165             {
    166                 if (remaining < 6) {
    167                     return OK;
    168                 }
    169                 size_t dataPos = parcel->dataPosition();
    170 
    171                 parcel->writeInt32(KEY_STRUCT_KARAOKE_LIST);
    172 
    173                 int startTime = U32_AT(tmpData);
    174                 uint16_t count = U16_AT(tmpData + 4);
    175                 parcel->writeInt32(count);
    176 
    177                 tmpData += 6;
    178                 remaining -= 6;
    179                 int lastEndTime = 0;
    180 
    181                 for (int i = 0; i < count; i++) {
    182                     if (remaining < 8) {
    183                         // roll back
    184                         parcel->setDataPosition(dataPos);
    185                         return OK;
    186                     }
    187                     parcel->writeInt32(startTime + lastEndTime);
    188 
    189                     lastEndTime = U32_AT(tmpData);
    190                     parcel->writeInt32(lastEndTime);
    191 
    192                     parcel->writeInt32(U16_AT(tmpData + 4));
    193                     parcel->writeInt32(U16_AT(tmpData + 6));
    194 
    195                     tmpData += 8;
    196                     remaining -= 8;
    197                 }
    198 
    199                 break;
    200             }
    201             // 'hlit' box specifies highlighted text
    202             case FOURCC('h', 'l', 'i', 't'):
    203             {
    204                 if (remaining < 4) {
    205                     return OK;
    206                 }
    207 
    208                 parcel->writeInt32(KEY_STRUCT_HIGHLIGHT_LIST);
    209 
    210                 // the start char offset to highlight
    211                 parcel->writeInt32(U16_AT(tmpData));
    212                 // the last char offset to highlight
    213                 parcel->writeInt32(U16_AT(tmpData + 2));
    214 
    215                 tmpData += 4;
    216                 remaining -= 4;
    217                 break;
    218             }
    219             // 'hclr' box specifies the RGBA color: 8 bits each of
    220             // red, green, blue, and an alpha(transparency) value
    221             case FOURCC('h', 'c', 'l', 'r'):
    222             {
    223                 if (remaining < 4) {
    224                     return OK;
    225                 }
    226                 parcel->writeInt32(KEY_HIGHLIGHT_COLOR_RGBA);
    227 
    228                 uint32_t rgba = *(tmpData) << 24 | *(tmpData + 1) << 16
    229                     | *(tmpData + 2) << 8 | *(tmpData + 3);
    230                 parcel->writeInt32(rgba);
    231 
    232                 tmpData += 4;
    233                 remaining -= 4;
    234                 break;
    235             }
    236             // 'dlay' box specifies a delay after a scroll in and/or
    237             // before scroll out.
    238             case FOURCC('d', 'l', 'a', 'y'):
    239             {
    240                 if (remaining < 4) {
    241                     return OK;
    242                 }
    243                 parcel->writeInt32(KEY_SCROLL_DELAY);
    244 
    245                 uint32_t delay = *(tmpData) << 24 | *(tmpData + 1) << 16
    246                     | *(tmpData + 2) << 8 | *(tmpData + 3);
    247                 parcel->writeInt32(delay);
    248 
    249                 tmpData += 4;
    250                 remaining -= 4;
    251                 break;
    252             }
    253             // 'href' box for hyper text link
    254             case FOURCC('h', 'r', 'e', 'f'):
    255             {
    256                 if (remaining < 5) {
    257                     return OK;
    258                 }
    259 
    260                 size_t dataPos = parcel->dataPosition();
    261 
    262                 parcel->writeInt32(KEY_STRUCT_HYPER_TEXT_LIST);
    263 
    264                 // the start offset of the text to be linked
    265                 parcel->writeInt32(U16_AT(tmpData));
    266                 // the end offset of the text
    267                 parcel->writeInt32(U16_AT(tmpData + 2));
    268 
    269                 // the number of bytes in the following URL
    270                 size_t len = *(tmpData + 4);
    271                 parcel->writeInt32(len);
    272 
    273                 remaining -= 5;
    274 
    275                 if (remaining  < len) {
    276                     parcel->setDataPosition(dataPos);
    277                     return OK;
    278                 }
    279                 // the linked-to URL
    280                 parcel->writeInt32(len);
    281                 parcel->write(tmpData + 5, len);
    282 
    283                 tmpData += (5 + len);
    284                 remaining -= len;
    285 
    286                 if (remaining  < 1) {
    287                     parcel->setDataPosition(dataPos);
    288                     return OK;
    289                 }
    290 
    291                 // the number of bytes in the following "alt" string
    292                 len = *tmpData;
    293                 parcel->writeInt32(len);
    294 
    295                 tmpData += 1;
    296                 remaining -= 1;
    297                 if (remaining  < len) {
    298                     parcel->setDataPosition(dataPos);
    299                     return OK;
    300                 }
    301 
    302                 // an "alt" string for user display
    303                 parcel->writeInt32(len);
    304                 parcel->write(tmpData, len);
    305 
    306                 tmpData += 1;
    307                 remaining -= 1;
    308                 break;
    309             }
    310             // 'tbox' box to indicate the position of the text with values
    311             // of top, left, bottom and right
    312             case FOURCC('t', 'b', 'o', 'x'):
    313             {
    314                 if (remaining < 8) {
    315                     return OK;
    316                 }
    317                 parcel->writeInt32(KEY_STRUCT_TEXT_POS);
    318                 parcel->writeInt32(U16_AT(tmpData));
    319                 parcel->writeInt32(U16_AT(tmpData + 2));
    320                 parcel->writeInt32(U16_AT(tmpData + 4));
    321                 parcel->writeInt32(U16_AT(tmpData + 6));
    322 
    323                 tmpData += 8;
    324                 remaining -= 8;
    325                 break;
    326             }
    327             // 'blnk' to specify the char range to be blinked
    328             case FOURCC('b', 'l', 'n', 'k'):
    329             {
    330                 if (remaining < 4) {
    331                     return OK;
    332                 }
    333 
    334                 parcel->writeInt32(KEY_STRUCT_BLINKING_TEXT_LIST);
    335 
    336                 // start char offset
    337                 parcel->writeInt32(U16_AT(tmpData));
    338                 // end char offset
    339                 parcel->writeInt32(U16_AT(tmpData + 2));
    340 
    341                 tmpData += 4;
    342                 remaining -= 4;
    343                 break;
    344             }
    345             // 'twrp' box specifies text wrap behavior. If the value if 0x00,
    346             // then no wrap. If it's 0x01, then automatic 'soft' wrap is enabled.
    347             // 0x02-0xff are reserved.
    348             case FOURCC('t', 'w', 'r', 'p'):
    349             {
    350                 if (remaining < 1) {
    351                     return OK;
    352                 }
    353                 parcel->writeInt32(KEY_WRAP_TEXT);
    354                 parcel->writeInt32(*tmpData);
    355 
    356                 tmpData += 1;
    357                 remaining -= 1;
    358                 break;
    359             }
    360             default:
    361             {
    362                 break;
    363             }
    364         }
    365 
    366         data += chunkSize;
    367         size -= chunkSize;
    368     }
    369 
    370     return OK;
    371 }
    372 
    373 // To extract box 'tx3g' defined in 3GPP TS 26.245, and store it in a Parcel
    374 status_t TextDescriptions::extract3GPPGlobalDescriptions(
    375         const uint8_t *data, ssize_t size, Parcel *parcel) {
    376 
    377     parcel->writeInt32(KEY_GLOBAL_SETTING);
    378 
    379     while (size >= 8) {
    380         ssize_t chunkSize = U32_AT(data);
    381         uint32_t chunkType = U32_AT(data + 4);
    382         const uint8_t *tmpData = data;
    383         tmpData += 8;
    384         size_t remaining = size - 8;
    385 
    386         if (size < chunkSize) {
    387             return OK;
    388         }
    389         switch(chunkType) {
    390             case FOURCC('t', 'x', '3', 'g'):
    391             {
    392                 if (remaining < 18) { // 8 just below, and another 10 a little further down
    393                     return OK;
    394                 }
    395                 tmpData += 8; // skip the first 8 bytes
    396                 remaining -=8;
    397                 parcel->writeInt32(KEY_DISPLAY_FLAGS);
    398                 parcel->writeInt32(U32_AT(tmpData));
    399 
    400                 parcel->writeInt32(KEY_STRUCT_JUSTIFICATION);
    401                 parcel->writeInt32(tmpData[4]);
    402                 parcel->writeInt32(tmpData[5]);
    403 
    404                 parcel->writeInt32(KEY_BACKGROUND_COLOR_RGBA);
    405                 uint32_t rgba = *(tmpData + 6) << 24 | *(tmpData + 7) << 16
    406                     | *(tmpData + 8) << 8 | *(tmpData + 9);
    407                 parcel->writeInt32(rgba);
    408 
    409                 tmpData += 10;
    410                 remaining -= 10;
    411 
    412                 if (remaining < 8) {
    413                     return OK;
    414                 }
    415                 parcel->writeInt32(KEY_STRUCT_TEXT_POS);
    416                 parcel->writeInt32(U16_AT(tmpData));
    417                 parcel->writeInt32(U16_AT(tmpData + 2));
    418                 parcel->writeInt32(U16_AT(tmpData + 4));
    419                 parcel->writeInt32(U16_AT(tmpData + 6));
    420 
    421                 tmpData += 8;
    422                 remaining -= 8;
    423 
    424                 if (remaining < 12) {
    425                     return OK;
    426                 }
    427                 parcel->writeInt32(KEY_STRUCT_STYLE_LIST);
    428                 parcel->writeInt32(KEY_START_CHAR);
    429                 parcel->writeInt32(U16_AT(tmpData));
    430 
    431                 parcel->writeInt32(KEY_END_CHAR);
    432                 parcel->writeInt32(U16_AT(tmpData + 2));
    433 
    434                 parcel->writeInt32(KEY_FONT_ID);
    435                 parcel->writeInt32(U16_AT(tmpData + 4));
    436 
    437                 parcel->writeInt32(KEY_STYLE_FLAGS);
    438                 parcel->writeInt32(*(tmpData + 6));
    439 
    440                 parcel->writeInt32(KEY_FONT_SIZE);
    441                 parcel->writeInt32(*(tmpData + 7));
    442 
    443                 parcel->writeInt32(KEY_TEXT_COLOR_RGBA);
    444                 rgba = *(tmpData + 8) << 24 | *(tmpData + 9) << 16
    445                     | *(tmpData + 10) << 8 | *(tmpData + 11);
    446                 parcel->writeInt32(rgba);
    447 
    448                 tmpData += 12;
    449                 remaining -= 12;
    450 
    451                 if (remaining < 2) {
    452                     return OK;
    453                 }
    454 
    455                 size_t dataPos = parcel->dataPosition();
    456 
    457                 parcel->writeInt32(KEY_STRUCT_FONT_LIST);
    458                 uint16_t count = U16_AT(tmpData);
    459                 parcel->writeInt32(count);
    460 
    461                 tmpData += 2;
    462                 remaining -= 2;
    463 
    464                 for (int i = 0; i < count; i++) {
    465                     if (remaining < 3) {
    466                         // roll back
    467                         parcel->setDataPosition(dataPos);
    468                         return OK;
    469                     }
    470                     // font ID
    471                     parcel->writeInt32(U16_AT(tmpData));
    472 
    473                     // font name length
    474                     parcel->writeInt32(*(tmpData + 2));
    475 
    476                     size_t len = *(tmpData + 2);
    477 
    478                     tmpData += 3;
    479                     remaining -= 3;
    480 
    481                     if (remaining < len) {
    482                         // roll back
    483                         parcel->setDataPosition(dataPos);
    484                         return OK;
    485                     }
    486 
    487                     parcel->write(tmpData, len);
    488                     tmpData += len;
    489                     remaining -= len;
    490                 }
    491 
    492                 // there is a "DisparityBox" after this according to the spec, but we ignore it
    493                 break;
    494             }
    495             default:
    496             {
    497                 break;
    498             }
    499         }
    500 
    501         data += chunkSize;
    502         size -= chunkSize;
    503     }
    504 
    505     return OK;
    506 }
    507 
    508 }  // namespace android
    509