NuPlayerDecoder.cpp revision a7fa1d9530b6870f2c7850e3025d7db963661803
1/* 2 * Copyright (C) 2010 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//#define LOG_NDEBUG 0 18#define LOG_TAG "NuPlayerDecoder" 19#include <utils/Log.h> 20#include <inttypes.h> 21 22#include "NuPlayerDecoder.h" 23 24#include <media/ICrypto.h> 25#include <media/stagefright/foundation/ABitReader.h> 26#include <media/stagefright/foundation/ABuffer.h> 27#include <media/stagefright/foundation/ADebug.h> 28#include <media/stagefright/foundation/AMessage.h> 29#include <media/stagefright/MediaCodec.h> 30#include <media/stagefright/MediaDefs.h> 31#include <media/stagefright/MediaErrors.h> 32 33namespace android { 34 35NuPlayer::Decoder::Decoder( 36 const sp<AMessage> ¬ify, 37 const sp<NativeWindowWrapper> &nativeWindow) 38 : mNotify(notify), 39 mNativeWindow(nativeWindow), 40 mBufferGeneration(0), 41 mPaused(true), 42 mComponentName("decoder") { 43 // Every decoder has its own looper because MediaCodec operations 44 // are blocking, but NuPlayer needs asynchronous operations. 45 mDecoderLooper = new ALooper; 46 mDecoderLooper->setName("NuPlayerDecoder"); 47 mDecoderLooper->start(false, false, ANDROID_PRIORITY_AUDIO); 48 49 mCodecLooper = new ALooper; 50 mCodecLooper->setName("NuPlayerDecoder-MC"); 51 mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO); 52} 53 54NuPlayer::Decoder::~Decoder() { 55} 56 57void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { 58 CHECK(mCodec == NULL); 59 60 ++mBufferGeneration; 61 62 AString mime; 63 CHECK(format->findString("mime", &mime)); 64 65 sp<Surface> surface = NULL; 66 if (mNativeWindow != NULL) { 67 surface = mNativeWindow->getSurfaceTextureClient(); 68 } 69 70 mComponentName = mime; 71 mComponentName.append(" decoder"); 72 ALOGV("[%s] onConfigure (surface=%p)", mComponentName.c_str(), surface.get()); 73 74 mCodec = MediaCodec::CreateByType(mCodecLooper, mime.c_str(), false /* encoder */); 75 if (mCodec == NULL) { 76 ALOGE("Failed to create %s decoder", mime.c_str()); 77 handleError(UNKNOWN_ERROR); 78 return; 79 } 80 81 mCodec->getName(&mComponentName); 82 83 if (mNativeWindow != NULL) { 84 // disconnect from surface as MediaCodec will reconnect 85 CHECK_EQ((int)NO_ERROR, 86 native_window_api_disconnect( 87 surface.get(), 88 NATIVE_WINDOW_API_MEDIA)); 89 } 90 status_t err = mCodec->configure( 91 format, surface, NULL /* crypto */, 0 /* flags */); 92 if (err != OK) { 93 ALOGE("Failed to configure %s decoder (err=%d)", mComponentName.c_str(), err); 94 handleError(err); 95 return; 96 } 97 // the following should work in configured state 98 CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat)); 99 CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat)); 100 101 err = mCodec->start(); 102 if (err != OK) { 103 ALOGE("Failed to start %s decoder (err=%d)", mComponentName.c_str(), err); 104 handleError(err); 105 return; 106 } 107 108 // the following should work after start 109 CHECK_EQ((status_t)OK, mCodec->getInputBuffers(&mInputBuffers)); 110 CHECK_EQ((status_t)OK, mCodec->getOutputBuffers(&mOutputBuffers)); 111 ALOGV("[%s] got %zu input and %zu output buffers", 112 mComponentName.c_str(), 113 mInputBuffers.size(), 114 mOutputBuffers.size()); 115 116 requestCodecNotification(); 117 mPaused = false; 118} 119 120void NuPlayer::Decoder::requestCodecNotification() { 121 if (mCodec != NULL) { 122 sp<AMessage> reply = new AMessage(kWhatCodecNotify, id()); 123 reply->setInt32("generation", mBufferGeneration); 124 mCodec->requestActivityNotification(reply); 125 } 126} 127 128bool NuPlayer::Decoder::isStaleReply(const sp<AMessage> &msg) { 129 int32_t generation; 130 CHECK(msg->findInt32("generation", &generation)); 131 return generation != mBufferGeneration; 132} 133 134void NuPlayer::Decoder::init() { 135 mDecoderLooper->registerHandler(this); 136} 137 138void NuPlayer::Decoder::configure(const sp<AMessage> &format) { 139 sp<AMessage> msg = new AMessage(kWhatConfigure, id()); 140 msg->setMessage("format", format); 141 msg->post(); 142} 143 144void NuPlayer::Decoder::handleError(int32_t err) 145{ 146 sp<AMessage> notify = mNotify->dup(); 147 notify->setInt32("what", kWhatError); 148 notify->setInt32("err", err); 149 notify->post(); 150} 151 152bool NuPlayer::Decoder::handleAnInputBuffer() { 153 size_t bufferIx = -1; 154 status_t res = mCodec->dequeueInputBuffer(&bufferIx); 155 ALOGV("[%s] dequeued input: %d", 156 mComponentName.c_str(), res == OK ? (int)bufferIx : res); 157 if (res != OK) { 158 if (res != -EAGAIN) { 159 handleError(res); 160 } 161 return false; 162 } 163 164 CHECK_LT(bufferIx, mInputBuffers.size()); 165 166 sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id()); 167 reply->setSize("buffer-ix", bufferIx); 168 reply->setInt32("generation", mBufferGeneration); 169 170 sp<AMessage> notify = mNotify->dup(); 171 notify->setInt32("what", kWhatFillThisBuffer); 172 notify->setBuffer("buffer", mInputBuffers[bufferIx]); 173 notify->setMessage("reply", reply); 174 notify->post(); 175 return true; 176} 177 178void android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) { 179 size_t bufferIx; 180 CHECK(msg->findSize("buffer-ix", &bufferIx)); 181 CHECK_LT(bufferIx, mInputBuffers.size()); 182 sp<ABuffer> codecBuffer = mInputBuffers[bufferIx]; 183 184 sp<ABuffer> buffer; 185 bool hasBuffer = msg->findBuffer("buffer", &buffer); 186 if (buffer == NULL /* includes !hasBuffer */) { 187 int32_t streamErr = ERROR_END_OF_STREAM; 188 CHECK(msg->findInt32("err", &streamErr) || !hasBuffer); 189 190 if (streamErr == OK) { 191 /* buffers are returned to hold on to */ 192 return; 193 } 194 195 // attempt to queue EOS 196 status_t err = mCodec->queueInputBuffer( 197 bufferIx, 198 0, 199 0, 200 0, 201 MediaCodec::BUFFER_FLAG_EOS); 202 if (streamErr == ERROR_END_OF_STREAM && err != OK) { 203 streamErr = err; 204 // err will not be ERROR_END_OF_STREAM 205 } 206 207 if (streamErr != ERROR_END_OF_STREAM) { 208 handleError(streamErr); 209 } 210 } else { 211 int64_t timeUs = 0; 212 uint32_t flags = 0; 213 CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); 214 215 int32_t eos; 216 // we do not expect CODECCONFIG or SYNCFRAME for decoder 217 if (buffer->meta()->findInt32("eos", &eos) && eos) { 218 flags |= MediaCodec::BUFFER_FLAG_EOS; 219 } 220 221 // copy into codec buffer 222 if (buffer != codecBuffer) { 223 CHECK_LE(buffer->size(), codecBuffer->capacity()); 224 codecBuffer->setRange(0, buffer->size()); 225 memcpy(codecBuffer->data(), buffer->data(), buffer->size()); 226 } 227 228 status_t err = mCodec->queueInputBuffer( 229 bufferIx, 230 codecBuffer->offset(), 231 codecBuffer->size(), 232 timeUs, 233 flags); 234 if (err != OK) { 235 ALOGE("Failed to queue input buffer for %s (err=%d)", 236 mComponentName.c_str(), err); 237 handleError(err); 238 } 239 } 240} 241 242bool NuPlayer::Decoder::handleAnOutputBuffer() { 243 size_t bufferIx = -1; 244 size_t offset; 245 size_t size; 246 int64_t timeUs; 247 uint32_t flags; 248 status_t res = mCodec->dequeueOutputBuffer( 249 &bufferIx, &offset, &size, &timeUs, &flags); 250 251 if (res != OK) { 252 ALOGV("[%s] dequeued output: %d", mComponentName.c_str(), res); 253 } else { 254 ALOGV("[%s] dequeued output: %d (time=%lld flags=%" PRIu32 ")", 255 mComponentName.c_str(), (int)bufferIx, timeUs, flags); 256 } 257 258 if (res == INFO_OUTPUT_BUFFERS_CHANGED) { 259 res = mCodec->getOutputBuffers(&mOutputBuffers); 260 if (res != OK) { 261 ALOGE("Failed to get output buffers for %s after INFO event (err=%d)", 262 mComponentName.c_str(), res); 263 handleError(res); 264 return false; 265 } 266 // NuPlayer ignores this 267 return true; 268 } else if (res == INFO_FORMAT_CHANGED) { 269 sp<AMessage> format = new AMessage(); 270 res = mCodec->getOutputFormat(&format); 271 if (res != OK) { 272 ALOGE("Failed to get output format for %s after INFO event (err=%d)", 273 mComponentName.c_str(), res); 274 handleError(res); 275 return false; 276 } 277 278 sp<AMessage> notify = mNotify->dup(); 279 notify->setInt32("what", kWhatOutputFormatChanged); 280 notify->setMessage("format", format); 281 notify->post(); 282 return true; 283 } else if (res == INFO_DISCONTINUITY) { 284 // nothing to do 285 return true; 286 } else if (res != OK) { 287 if (res != -EAGAIN) { 288 handleError(res); 289 } 290 return false; 291 } 292 293 CHECK_LT(bufferIx, mOutputBuffers.size()); 294 sp<ABuffer> buffer = mOutputBuffers[bufferIx]; 295 buffer->setRange(offset, size); 296 buffer->meta()->clear(); 297 buffer->meta()->setInt64("timeUs", timeUs); 298 if (flags & MediaCodec::BUFFER_FLAG_EOS) { 299 buffer->meta()->setInt32("eos", true); 300 } 301 // we do not expect CODECCONFIG or SYNCFRAME for decoder 302 303 sp<AMessage> reply = new AMessage(kWhatRenderBuffer, id()); 304 reply->setSize("buffer-ix", bufferIx); 305 reply->setInt32("generation", mBufferGeneration); 306 307 sp<AMessage> notify = mNotify->dup(); 308 notify->setInt32("what", kWhatDrainThisBuffer); 309 notify->setBuffer("buffer", buffer); 310 notify->setMessage("reply", reply); 311 notify->post(); 312 313 // FIXME: This should be handled after rendering is complete, 314 // but Renderer needs it now 315 if (flags & MediaCodec::BUFFER_FLAG_EOS) { 316 ALOGV("queueing eos [%s]", mComponentName.c_str()); 317 sp<AMessage> notify = mNotify->dup(); 318 notify->setInt32("what", kWhatEOS); 319 notify->setInt32("err", ERROR_END_OF_STREAM); 320 notify->post(); 321 } 322 return true; 323} 324 325void NuPlayer::Decoder::onRenderBuffer(const sp<AMessage> &msg) { 326 status_t err; 327 int32_t render; 328 size_t bufferIx; 329 CHECK(msg->findSize("buffer-ix", &bufferIx)); 330 if (msg->findInt32("render", &render) && render) { 331 err = mCodec->renderOutputBufferAndRelease(bufferIx); 332 } else { 333 err = mCodec->releaseOutputBuffer(bufferIx); 334 } 335 if (err != OK) { 336 ALOGE("failed to release output buffer for %s (err=%d)", 337 mComponentName.c_str(), err); 338 handleError(err); 339 } 340} 341 342void NuPlayer::Decoder::onFlush() { 343 status_t err = OK; 344 if (mCodec != NULL) { 345 err = mCodec->flush(); 346 ++mBufferGeneration; 347 } 348 349 if (err != OK) { 350 ALOGE("failed to flush %s (err=%d)", mComponentName.c_str(), err); 351 handleError(err); 352 return; 353 } 354 355 sp<AMessage> notify = mNotify->dup(); 356 notify->setInt32("what", kWhatFlushCompleted); 357 notify->post(); 358 mPaused = true; 359} 360 361void NuPlayer::Decoder::onResume() { 362 mPaused = false; 363} 364 365void NuPlayer::Decoder::onShutdown() { 366 status_t err = OK; 367 if (mCodec != NULL) { 368 err = mCodec->release(); 369 mCodec = NULL; 370 ++mBufferGeneration; 371 372 if (mNativeWindow != NULL) { 373 // reconnect to surface as MediaCodec disconnected from it 374 CHECK_EQ((int)NO_ERROR, 375 native_window_api_connect( 376 mNativeWindow->getNativeWindow().get(), 377 NATIVE_WINDOW_API_MEDIA)); 378 } 379 mComponentName = "decoder"; 380 } 381 382 if (err != OK) { 383 ALOGE("failed to release %s (err=%d)", mComponentName.c_str(), err); 384 handleError(err); 385 return; 386 } 387 388 sp<AMessage> notify = mNotify->dup(); 389 notify->setInt32("what", kWhatShutdownCompleted); 390 notify->post(); 391 mPaused = true; 392} 393 394void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { 395 ALOGV("[%s] onMessage: %s", mComponentName.c_str(), msg->debugString().c_str()); 396 397 switch (msg->what()) { 398 case kWhatConfigure: 399 { 400 sp<AMessage> format; 401 CHECK(msg->findMessage("format", &format)); 402 onConfigure(format); 403 break; 404 } 405 406 case kWhatCodecNotify: 407 { 408 if (!isStaleReply(msg)) { 409 if (!mPaused) { 410 while (handleAnInputBuffer()) { 411 } 412 } 413 414 while (handleAnOutputBuffer()) { 415 } 416 } 417 418 requestCodecNotification(); 419 break; 420 } 421 422 case kWhatInputBufferFilled: 423 { 424 if (!isStaleReply(msg)) { 425 onInputBufferFilled(msg); 426 } 427 break; 428 } 429 430 case kWhatRenderBuffer: 431 { 432 if (!isStaleReply(msg)) { 433 onRenderBuffer(msg); 434 } 435 break; 436 } 437 438 case kWhatFlush: 439 { 440 onFlush(); 441 break; 442 } 443 444 case kWhatResume: 445 { 446 onResume(); 447 break; 448 } 449 450 case kWhatShutdown: 451 { 452 onShutdown(); 453 break; 454 } 455 456 default: 457 TRESPASS(); 458 break; 459 } 460} 461 462void NuPlayer::Decoder::signalFlush() { 463 (new AMessage(kWhatFlush, id()))->post(); 464} 465 466void NuPlayer::Decoder::signalResume() { 467 (new AMessage(kWhatResume, id()))->post(); 468} 469 470void NuPlayer::Decoder::initiateShutdown() { 471 (new AMessage(kWhatShutdown, id()))->post(); 472} 473 474bool NuPlayer::Decoder::supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const { 475 if (targetFormat == NULL) { 476 return true; 477 } 478 479 AString mime; 480 if (!targetFormat->findString("mime", &mime)) { 481 return false; 482 } 483 484 if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) { 485 // field-by-field comparison 486 const char * keys[] = { "channel-count", "sample-rate", "is-adts" }; 487 for (unsigned int i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) { 488 int32_t oldVal, newVal; 489 if (!mOutputFormat->findInt32(keys[i], &oldVal) || 490 !targetFormat->findInt32(keys[i], &newVal) || 491 oldVal != newVal) { 492 return false; 493 } 494 } 495 496 sp<ABuffer> oldBuf, newBuf; 497 if (mOutputFormat->findBuffer("csd-0", &oldBuf) && 498 targetFormat->findBuffer("csd-0", &newBuf)) { 499 if (oldBuf->size() != newBuf->size()) { 500 return false; 501 } 502 return !memcmp(oldBuf->data(), newBuf->data(), oldBuf->size()); 503 } 504 } 505 return false; 506} 507 508bool NuPlayer::Decoder::supportsSeamlessFormatChange(const sp<AMessage> &targetFormat) const { 509 if (mOutputFormat == NULL) { 510 return false; 511 } 512 513 if (targetFormat == NULL) { 514 return true; 515 } 516 517 AString oldMime, newMime; 518 if (!mOutputFormat->findString("mime", &oldMime) 519 || !targetFormat->findString("mime", &newMime) 520 || !(oldMime == newMime)) { 521 return false; 522 } 523 524 bool audio = !strncasecmp(oldMime.c_str(), "audio/", strlen("audio/")); 525 bool seamless; 526 if (audio) { 527 seamless = supportsSeamlessAudioFormatChange(targetFormat); 528 } else { 529 int32_t isAdaptive; 530 seamless = (mCodec != NULL && 531 mInputFormat->findInt32("adaptive-playback", &isAdaptive) && 532 isAdaptive); 533 } 534 535 ALOGV("%s seamless support for %s", seamless ? "yes" : "no", oldMime.c_str()); 536 return seamless; 537} 538 539struct NuPlayer::CCDecoder::CCData { 540 CCData(uint8_t type, uint8_t data1, uint8_t data2) 541 : mType(type), mData1(data1), mData2(data2) { 542 } 543 544 uint8_t mType; 545 uint8_t mData1; 546 uint8_t mData2; 547}; 548 549NuPlayer::CCDecoder::CCDecoder(const sp<AMessage> ¬ify) 550 : mNotify(notify), 551 mTrackCount(0), 552 mSelectedTrack(-1) { 553} 554 555size_t NuPlayer::CCDecoder::getTrackCount() const { 556 return mTrackCount; 557} 558 559sp<AMessage> NuPlayer::CCDecoder::getTrackInfo(size_t index) const { 560 CHECK(index == 0); 561 562 sp<AMessage> format = new AMessage(); 563 564 format->setInt32("type", MEDIA_TRACK_TYPE_SUBTITLE); 565 format->setString("language", "und"); 566 format->setString("mime", MEDIA_MIMETYPE_TEXT_CEA_608); 567 format->setInt32("auto", 1); 568 format->setInt32("default", 1); 569 format->setInt32("forced", 0); 570 571 return format; 572} 573 574status_t NuPlayer::CCDecoder::selectTrack(size_t index, bool select) { 575 CHECK(index < mTrackCount); 576 577 if (select) { 578 if (mSelectedTrack == (ssize_t)index) { 579 ALOGE("track %zu already selected", index); 580 return BAD_VALUE; 581 } 582 ALOGV("selected track %zu", index); 583 mSelectedTrack = index; 584 } else { 585 if (mSelectedTrack != (ssize_t)index) { 586 ALOGE("track %zu is not selected", index); 587 return BAD_VALUE; 588 } 589 ALOGV("unselected track %zu", index); 590 mSelectedTrack = -1; 591 } 592 593 return OK; 594} 595 596bool NuPlayer::CCDecoder::isSelected() const { 597 return mSelectedTrack >= 0 && mSelectedTrack < (int32_t)mTrackCount; 598} 599 600bool NuPlayer::CCDecoder::isNullPad(CCData *cc) const { 601 return cc->mData1 < 0x10 && cc->mData2 < 0x10; 602} 603 604void NuPlayer::CCDecoder::dumpBytePair(const sp<ABuffer> &ccBuf) const { 605 size_t offset = 0; 606 AString out; 607 608 while (offset < ccBuf->size()) { 609 char tmp[128]; 610 611 CCData *cc = (CCData *) (ccBuf->data() + offset); 612 613 if (isNullPad(cc)) { 614 // 1 null pad or XDS metadata, ignore 615 offset += sizeof(CCData); 616 continue; 617 } 618 619 if (cc->mData1 >= 0x20 && cc->mData1 <= 0x7f) { 620 // 2 basic chars 621 sprintf(tmp, "[%d]Basic: %c %c", cc->mType, cc->mData1, cc->mData2); 622 } else if ((cc->mData1 == 0x11 || cc->mData1 == 0x19) 623 && cc->mData2 >= 0x30 && cc->mData2 <= 0x3f) { 624 // 1 special char 625 sprintf(tmp, "[%d]Special: %02x %02x", cc->mType, cc->mData1, cc->mData2); 626 } else if ((cc->mData1 == 0x12 || cc->mData1 == 0x1A) 627 && cc->mData2 >= 0x20 && cc->mData2 <= 0x3f){ 628 // 1 Spanish/French char 629 sprintf(tmp, "[%d]Spanish: %02x %02x", cc->mType, cc->mData1, cc->mData2); 630 } else if ((cc->mData1 == 0x13 || cc->mData1 == 0x1B) 631 && cc->mData2 >= 0x20 && cc->mData2 <= 0x3f){ 632 // 1 Portuguese/German/Danish char 633 sprintf(tmp, "[%d]German: %02x %02x", cc->mType, cc->mData1, cc->mData2); 634 } else if ((cc->mData1 == 0x11 || cc->mData1 == 0x19) 635 && cc->mData2 >= 0x20 && cc->mData2 <= 0x2f){ 636 // Mid-Row Codes (Table 69) 637 sprintf(tmp, "[%d]Mid-row: %02x %02x", cc->mType, cc->mData1, cc->mData2); 638 } else if (((cc->mData1 == 0x14 || cc->mData1 == 0x1c) 639 && cc->mData2 >= 0x20 && cc->mData2 <= 0x2f) 640 || 641 ((cc->mData1 == 0x17 || cc->mData1 == 0x1f) 642 && cc->mData2 >= 0x21 && cc->mData2 <= 0x23)){ 643 // Misc Control Codes (Table 70) 644 sprintf(tmp, "[%d]Ctrl: %02x %02x", cc->mType, cc->mData1, cc->mData2); 645 } else if ((cc->mData1 & 0x70) == 0x10 646 && (cc->mData2 & 0x40) == 0x40 647 && ((cc->mData1 & 0x07) || !(cc->mData2 & 0x20)) ) { 648 // Preamble Address Codes (Table 71) 649 sprintf(tmp, "[%d]PAC: %02x %02x", cc->mType, cc->mData1, cc->mData2); 650 } else { 651 sprintf(tmp, "[%d]Invalid: %02x %02x", cc->mType, cc->mData1, cc->mData2); 652 } 653 654 if (out.size() > 0) { 655 out.append(", "); 656 } 657 658 out.append(tmp); 659 660 offset += sizeof(CCData); 661 } 662 663 ALOGI("%s", out.c_str()); 664} 665 666bool NuPlayer::CCDecoder::extractFromSEI(const sp<ABuffer> &accessUnit) { 667 int64_t timeUs; 668 CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); 669 670 sp<ABuffer> sei; 671 if (!accessUnit->meta()->findBuffer("sei", &sei) || sei == NULL) { 672 return false; 673 } 674 675 bool hasCC = false; 676 677 ABitReader br(sei->data() + 1, sei->size() - 1); 678 // sei_message() 679 while (br.numBitsLeft() >= 16) { // at least 16-bit for sei_message() 680 uint32_t payload_type = 0; 681 size_t payload_size = 0; 682 uint8_t last_byte; 683 684 do { 685 last_byte = br.getBits(8); 686 payload_type += last_byte; 687 } while (last_byte == 0xFF); 688 689 do { 690 last_byte = br.getBits(8); 691 payload_size += last_byte; 692 } while (last_byte == 0xFF); 693 694 // sei_payload() 695 if (payload_type == 4) { 696 // user_data_registered_itu_t_t35() 697 698 // ATSC A/72: 6.4.2 699 uint8_t itu_t_t35_country_code = br.getBits(8); 700 uint16_t itu_t_t35_provider_code = br.getBits(16); 701 uint32_t user_identifier = br.getBits(32); 702 uint8_t user_data_type_code = br.getBits(8); 703 704 payload_size -= 1 + 2 + 4 + 1; 705 706 if (itu_t_t35_country_code == 0xB5 707 && itu_t_t35_provider_code == 0x0031 708 && user_identifier == 'GA94' 709 && user_data_type_code == 0x3) { 710 hasCC = true; 711 712 // MPEG_cc_data() 713 // ATSC A/53 Part 4: 6.2.3.1 714 br.skipBits(1); //process_em_data_flag 715 bool process_cc_data_flag = br.getBits(1); 716 br.skipBits(1); //additional_data_flag 717 size_t cc_count = br.getBits(5); 718 br.skipBits(8); // em_data; 719 payload_size -= 2; 720 721 if (process_cc_data_flag) { 722 AString out; 723 724 sp<ABuffer> ccBuf = new ABuffer(cc_count * sizeof(CCData)); 725 ccBuf->setRange(0, 0); 726 727 for (size_t i = 0; i < cc_count; i++) { 728 uint8_t marker = br.getBits(5); 729 CHECK_EQ(marker, 0x1f); 730 731 bool cc_valid = br.getBits(1); 732 uint8_t cc_type = br.getBits(2); 733 // remove odd parity bit 734 uint8_t cc_data_1 = br.getBits(8) & 0x7f; 735 uint8_t cc_data_2 = br.getBits(8) & 0x7f; 736 737 if (cc_valid 738 && (cc_type == 0 || cc_type == 1)) { 739 CCData cc(cc_type, cc_data_1, cc_data_2); 740 if (!isNullPad(&cc)) { 741 memcpy(ccBuf->data() + ccBuf->size(), 742 (void *)&cc, sizeof(cc)); 743 ccBuf->setRange(0, ccBuf->size() + sizeof(CCData)); 744 } 745 } 746 } 747 payload_size -= cc_count * 3; 748 749 mCCMap.add(timeUs, ccBuf); 750 break; 751 } 752 } else { 753 ALOGV("Malformed SEI payload type 4"); 754 } 755 } else { 756 ALOGV("Unsupported SEI payload type %d", payload_type); 757 } 758 759 // skipping remaining bits of this payload 760 br.skipBits(payload_size * 8); 761 } 762 763 return hasCC; 764} 765 766void NuPlayer::CCDecoder::decode(const sp<ABuffer> &accessUnit) { 767 if (extractFromSEI(accessUnit) && mTrackCount == 0) { 768 mTrackCount++; 769 770 ALOGI("Found CEA-608 track"); 771 sp<AMessage> msg = mNotify->dup(); 772 msg->setInt32("what", kWhatTrackAdded); 773 msg->post(); 774 } 775 // TODO: extract CC from other sources 776} 777 778void NuPlayer::CCDecoder::display(int64_t timeUs) { 779 ssize_t index = mCCMap.indexOfKey(timeUs); 780 if (index < 0) { 781 ALOGV("cc for timestamp %" PRId64 " not found", timeUs); 782 return; 783 } 784 785 sp<ABuffer> &ccBuf = mCCMap.editValueAt(index); 786 787 if (ccBuf->size() > 0) { 788#if 0 789 dumpBytePair(ccBuf); 790#endif 791 792 ccBuf->meta()->setInt32("trackIndex", mSelectedTrack); 793 ccBuf->meta()->setInt64("timeUs", timeUs); 794 ccBuf->meta()->setInt64("durationUs", 0ll); 795 796 sp<AMessage> msg = mNotify->dup(); 797 msg->setInt32("what", kWhatClosedCaptionData); 798 msg->setBuffer("buffer", ccBuf); 799 msg->post(); 800 } 801 802 // remove all entries before timeUs 803 mCCMap.removeItemsAt(0, index + 1); 804} 805 806} // namespace android 807 808