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