GenericSource.cpp revision 42e8153cf7271b572e4a94ade332b68521977f36
1/* 2 * Copyright (C) 2012 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 "GenericSource" 19 20#include "GenericSource.h" 21 22#include "AnotherPacketSource.h" 23 24#include <media/IMediaHTTPService.h> 25#include <media/stagefright/foundation/ABuffer.h> 26#include <media/stagefright/foundation/ADebug.h> 27#include <media/stagefright/foundation/AMessage.h> 28#include <media/stagefright/DataSource.h> 29#include <media/stagefright/FileSource.h> 30#include <media/stagefright/MediaBuffer.h> 31#include <media/stagefright/MediaDefs.h> 32#include <media/stagefright/MediaExtractor.h> 33#include <media/stagefright/MediaSource.h> 34#include <media/stagefright/MetaData.h> 35#include <media/stagefright/Utils.h> 36#include "../../libstagefright/include/DRMExtractor.h" 37#include "../../libstagefright/include/NuCachedSource2.h" 38#include "../../libstagefright/include/WVMExtractor.h" 39#include "../../libstagefright/include/HTTPBase.h" 40 41namespace android { 42 43NuPlayer::GenericSource::GenericSource( 44 const sp<AMessage> ¬ify, 45 bool uidValid, 46 uid_t uid) 47 : Source(notify), 48 mAudioTimeUs(0), 49 mAudioLastDequeueTimeUs(0), 50 mVideoTimeUs(0), 51 mVideoLastDequeueTimeUs(0), 52 mFetchSubtitleDataGeneration(0), 53 mFetchTimedTextDataGeneration(0), 54 mDurationUs(0ll), 55 mAudioIsVorbis(false), 56 mIsWidevine(false), 57 mIsSecure(false), 58 mUIDValid(uidValid), 59 mUID(uid), 60 mFd(-1), 61 mDrmManagerClient(NULL), 62 mMetaDataSize(-1ll), 63 mBitrate(-1ll), 64 mPollBufferingGeneration(0), 65 mPendingReadBufferTypes(0) { 66 resetDataSource(); 67 DataSource::RegisterDefaultSniffers(); 68} 69 70void NuPlayer::GenericSource::resetDataSource() { 71 mHTTPService.clear(); 72 mHttpSource.clear(); 73 mUri.clear(); 74 mUriHeaders.clear(); 75 if (mFd >= 0) { 76 close(mFd); 77 mFd = -1; 78 } 79 mOffset = 0; 80 mLength = 0; 81 setDrmPlaybackStatusIfNeeded(Playback::STOP, 0); 82 mDecryptHandle = NULL; 83 mDrmManagerClient = NULL; 84 mStarted = false; 85 mStopRead = true; 86} 87 88status_t NuPlayer::GenericSource::setDataSource( 89 const sp<IMediaHTTPService> &httpService, 90 const char *url, 91 const KeyedVector<String8, String8> *headers) { 92 resetDataSource(); 93 94 mHTTPService = httpService; 95 mUri = url; 96 97 if (headers) { 98 mUriHeaders = *headers; 99 } 100 101 // delay data source creation to prepareAsync() to avoid blocking 102 // the calling thread in setDataSource for any significant time. 103 return OK; 104} 105 106status_t NuPlayer::GenericSource::setDataSource( 107 int fd, int64_t offset, int64_t length) { 108 resetDataSource(); 109 110 mFd = dup(fd); 111 mOffset = offset; 112 mLength = length; 113 114 // delay data source creation to prepareAsync() to avoid blocking 115 // the calling thread in setDataSource for any significant time. 116 return OK; 117} 118 119sp<MetaData> NuPlayer::GenericSource::getFileFormatMeta() const { 120 return mFileMeta; 121} 122 123status_t NuPlayer::GenericSource::initFromDataSource() { 124 sp<MediaExtractor> extractor; 125 126 CHECK(mDataSource != NULL); 127 128 if (mIsWidevine) { 129 String8 mimeType; 130 float confidence; 131 sp<AMessage> dummy; 132 bool success; 133 134 success = SniffWVM(mDataSource, &mimeType, &confidence, &dummy); 135 if (!success 136 || strcasecmp( 137 mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) { 138 ALOGE("unsupported widevine mime: %s", mimeType.string()); 139 return UNKNOWN_ERROR; 140 } 141 142 mWVMExtractor = new WVMExtractor(mDataSource); 143 mWVMExtractor->setAdaptiveStreamingMode(true); 144 if (mUIDValid) { 145 mWVMExtractor->setUID(mUID); 146 } 147 extractor = mWVMExtractor; 148 } else { 149 extractor = MediaExtractor::Create(mDataSource, 150 mSniffedMIME.empty() ? NULL: mSniffedMIME.c_str()); 151 } 152 153 if (extractor == NULL) { 154 return UNKNOWN_ERROR; 155 } 156 157 if (extractor->getDrmFlag()) { 158 checkDrmStatus(mDataSource); 159 } 160 161 mFileMeta = extractor->getMetaData(); 162 if (mFileMeta != NULL) { 163 int64_t duration; 164 if (mFileMeta->findInt64(kKeyDuration, &duration)) { 165 mDurationUs = duration; 166 } 167 168 if (!mIsWidevine) { 169 // Check mime to see if we actually have a widevine source. 170 // If the data source is not URL-type (eg. file source), we 171 // won't be able to tell until now. 172 const char *fileMime; 173 if (mFileMeta->findCString(kKeyMIMEType, &fileMime) 174 && !strncasecmp(fileMime, "video/wvm", 9)) { 175 mIsWidevine = true; 176 } 177 } 178 } 179 180 int32_t totalBitrate = 0; 181 182 size_t numtracks = extractor->countTracks(); 183 if (numtracks == 0) { 184 return UNKNOWN_ERROR; 185 } 186 187 for (size_t i = 0; i < numtracks; ++i) { 188 sp<MediaSource> track = extractor->getTrack(i); 189 190 sp<MetaData> meta = extractor->getTrackMetaData(i); 191 192 const char *mime; 193 CHECK(meta->findCString(kKeyMIMEType, &mime)); 194 195 // Do the string compare immediately with "mime", 196 // we can't assume "mime" would stay valid after another 197 // extractor operation, some extractors might modify meta 198 // during getTrack() and make it invalid. 199 if (!strncasecmp(mime, "audio/", 6)) { 200 if (mAudioTrack.mSource == NULL) { 201 mAudioTrack.mIndex = i; 202 mAudioTrack.mSource = track; 203 mAudioTrack.mPackets = 204 new AnotherPacketSource(mAudioTrack.mSource->getFormat()); 205 206 if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) { 207 mAudioIsVorbis = true; 208 } else { 209 mAudioIsVorbis = false; 210 } 211 } 212 } else if (!strncasecmp(mime, "video/", 6)) { 213 if (mVideoTrack.mSource == NULL) { 214 mVideoTrack.mIndex = i; 215 mVideoTrack.mSource = track; 216 mVideoTrack.mPackets = 217 new AnotherPacketSource(mVideoTrack.mSource->getFormat()); 218 219 // check if the source requires secure buffers 220 int32_t secure; 221 if (meta->findInt32(kKeyRequiresSecureBuffers, &secure) 222 && secure) { 223 mIsSecure = true; 224 if (mUIDValid) { 225 extractor->setUID(mUID); 226 } 227 } 228 } 229 } 230 231 if (track != NULL) { 232 mSources.push(track); 233 int64_t durationUs; 234 if (meta->findInt64(kKeyDuration, &durationUs)) { 235 if (durationUs > mDurationUs) { 236 mDurationUs = durationUs; 237 } 238 } 239 240 int32_t bitrate; 241 if (totalBitrate >= 0 && meta->findInt32(kKeyBitRate, &bitrate)) { 242 totalBitrate += bitrate; 243 } else { 244 totalBitrate = -1; 245 } 246 } 247 } 248 249 mBitrate = totalBitrate; 250 251 return OK; 252} 253 254void NuPlayer::GenericSource::checkDrmStatus(const sp<DataSource>& dataSource) { 255 dataSource->getDrmInfo(mDecryptHandle, &mDrmManagerClient); 256 if (mDecryptHandle != NULL) { 257 CHECK(mDrmManagerClient); 258 if (RightsStatus::RIGHTS_VALID != mDecryptHandle->status) { 259 sp<AMessage> msg = dupNotify(); 260 msg->setInt32("what", kWhatDrmNoLicense); 261 msg->post(); 262 } 263 } 264} 265 266int64_t NuPlayer::GenericSource::getLastReadPosition() { 267 if (mAudioTrack.mSource != NULL) { 268 return mAudioTimeUs; 269 } else if (mVideoTrack.mSource != NULL) { 270 return mVideoTimeUs; 271 } else { 272 return 0; 273 } 274} 275 276status_t NuPlayer::GenericSource::setBuffers( 277 bool audio, Vector<MediaBuffer *> &buffers) { 278 if (mIsSecure && !audio) { 279 return mVideoTrack.mSource->setBuffers(buffers); 280 } 281 return INVALID_OPERATION; 282} 283 284NuPlayer::GenericSource::~GenericSource() { 285 if (mLooper != NULL) { 286 mLooper->unregisterHandler(id()); 287 mLooper->stop(); 288 } 289 resetDataSource(); 290} 291 292void NuPlayer::GenericSource::prepareAsync() { 293 if (mLooper == NULL) { 294 mLooper = new ALooper; 295 mLooper->setName("generic"); 296 mLooper->start(); 297 298 mLooper->registerHandler(this); 299 } 300 301 sp<AMessage> msg = new AMessage(kWhatPrepareAsync, id()); 302 msg->post(); 303} 304 305void NuPlayer::GenericSource::onPrepareAsync() { 306 // delayed data source creation 307 if (mDataSource == NULL) { 308 // set to false first, if the extractor 309 // comes back as secure, set it to true then. 310 mIsSecure = false; 311 312 if (!mUri.empty()) { 313 const char* uri = mUri.c_str(); 314 mIsWidevine = !strncasecmp(uri, "widevine://", 11); 315 316 if (!strncasecmp("http://", uri, 7) 317 || !strncasecmp("https://", uri, 8) 318 || mIsWidevine) { 319 mHttpSource = DataSource::CreateMediaHTTP(mHTTPService); 320 if (mHttpSource == NULL) { 321 ALOGE("Failed to create http source!"); 322 notifyPreparedAndCleanup(UNKNOWN_ERROR); 323 return; 324 } 325 } 326 327 mDataSource = DataSource::CreateFromURI( 328 mHTTPService, uri, &mUriHeaders, &mContentType, 329 static_cast<HTTPBase *>(mHttpSource.get())); 330 } else { 331 mIsWidevine = false; 332 333 mDataSource = new FileSource(mFd, mOffset, mLength); 334 mFd = -1; 335 } 336 337 if (mDataSource == NULL) { 338 ALOGE("Failed to create data source!"); 339 notifyPreparedAndCleanup(UNKNOWN_ERROR); 340 return; 341 } 342 343 if (mDataSource->flags() & DataSource::kIsCachingDataSource) { 344 mCachedSource = static_cast<NuCachedSource2 *>(mDataSource.get()); 345 } 346 347 if (mIsWidevine || mCachedSource != NULL) { 348 schedulePollBuffering(); 349 } 350 } 351 352 // check initial caching status 353 status_t err = prefillCacheIfNecessary(); 354 if (err != OK) { 355 if (err == -EAGAIN) { 356 (new AMessage(kWhatPrepareAsync, id()))->post(200000); 357 } else { 358 ALOGE("Failed to prefill data cache!"); 359 notifyPreparedAndCleanup(UNKNOWN_ERROR); 360 } 361 return; 362 } 363 364 // init extrator from data source 365 err = initFromDataSource(); 366 367 if (err != OK) { 368 ALOGE("Failed to init from data source!"); 369 notifyPreparedAndCleanup(err); 370 return; 371 } 372 373 if (mVideoTrack.mSource != NULL) { 374 sp<MetaData> meta = doGetFormatMeta(false /* audio */); 375 sp<AMessage> msg = new AMessage; 376 err = convertMetaDataToMessage(meta, &msg); 377 if(err != OK) { 378 notifyPreparedAndCleanup(err); 379 return; 380 } 381 notifyVideoSizeChanged(msg); 382 } 383 384 notifyFlagsChanged( 385 (mIsSecure ? FLAG_SECURE : 0) 386 | FLAG_CAN_PAUSE 387 | FLAG_CAN_SEEK_BACKWARD 388 | FLAG_CAN_SEEK_FORWARD 389 | FLAG_CAN_SEEK); 390 391 notifyPrepared(); 392} 393 394void NuPlayer::GenericSource::notifyPreparedAndCleanup(status_t err) { 395 if (err != OK) { 396 mMetaDataSize = -1ll; 397 mContentType = ""; 398 mSniffedMIME = ""; 399 mDataSource.clear(); 400 mCachedSource.clear(); 401 mHttpSource.clear(); 402 403 cancelPollBuffering(); 404 } 405 notifyPrepared(err); 406} 407 408status_t NuPlayer::GenericSource::prefillCacheIfNecessary() { 409 CHECK(mDataSource != NULL); 410 411 if (mCachedSource == NULL) { 412 // no prefill if the data source is not cached 413 return OK; 414 } 415 416 // We're not doing this for streams that appear to be audio-only 417 // streams to ensure that even low bandwidth streams start 418 // playing back fairly instantly. 419 if (!strncasecmp(mContentType.string(), "audio/", 6)) { 420 return OK; 421 } 422 423 // We're going to prefill the cache before trying to instantiate 424 // the extractor below, as the latter is an operation that otherwise 425 // could block on the datasource for a significant amount of time. 426 // During that time we'd be unable to abort the preparation phase 427 // without this prefill. 428 429 // Initially make sure we have at least 192 KB for the sniff 430 // to complete without blocking. 431 static const size_t kMinBytesForSniffing = 192 * 1024; 432 static const size_t kDefaultMetaSize = 200000; 433 434 status_t finalStatus; 435 436 size_t cachedDataRemaining = 437 mCachedSource->approxDataRemaining(&finalStatus); 438 439 if (finalStatus != OK || (mMetaDataSize >= 0 440 && (off64_t)cachedDataRemaining >= mMetaDataSize)) { 441 ALOGV("stop caching, status %d, " 442 "metaDataSize %lld, cachedDataRemaining %zu", 443 finalStatus, mMetaDataSize, cachedDataRemaining); 444 return OK; 445 } 446 447 ALOGV("now cached %zu bytes of data", cachedDataRemaining); 448 449 if (mMetaDataSize < 0 450 && cachedDataRemaining >= kMinBytesForSniffing) { 451 String8 tmp; 452 float confidence; 453 sp<AMessage> meta; 454 if (!mCachedSource->sniff(&tmp, &confidence, &meta)) { 455 return UNKNOWN_ERROR; 456 } 457 458 // We successfully identified the file's extractor to 459 // be, remember this mime type so we don't have to 460 // sniff it again when we call MediaExtractor::Create() 461 mSniffedMIME = tmp.string(); 462 463 if (meta == NULL 464 || !meta->findInt64("meta-data-size", 465 reinterpret_cast<int64_t*>(&mMetaDataSize))) { 466 mMetaDataSize = kDefaultMetaSize; 467 } 468 469 if (mMetaDataSize < 0ll) { 470 ALOGE("invalid metaDataSize = %lld bytes", mMetaDataSize); 471 return UNKNOWN_ERROR; 472 } 473 } 474 475 return -EAGAIN; 476} 477 478void NuPlayer::GenericSource::start() { 479 ALOGI("start"); 480 481 mStopRead = false; 482 if (mAudioTrack.mSource != NULL) { 483 CHECK_EQ(mAudioTrack.mSource->start(), (status_t)OK); 484 485 postReadBuffer(MEDIA_TRACK_TYPE_AUDIO); 486 } 487 488 if (mVideoTrack.mSource != NULL) { 489 CHECK_EQ(mVideoTrack.mSource->start(), (status_t)OK); 490 491 postReadBuffer(MEDIA_TRACK_TYPE_VIDEO); 492 } 493 494 setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000); 495 mStarted = true; 496} 497 498void NuPlayer::GenericSource::stop() { 499 // nothing to do, just account for DRM playback status 500 setDrmPlaybackStatusIfNeeded(Playback::STOP, 0); 501 mStarted = false; 502 if (mIsWidevine || mIsSecure) { 503 // For widevine or secure sources we need to prevent any further reads. 504 sp<AMessage> msg = new AMessage(kWhatStopWidevine, id()); 505 sp<AMessage> response; 506 (void) msg->postAndAwaitResponse(&response); 507 } 508} 509 510void NuPlayer::GenericSource::pause() { 511 // nothing to do, just account for DRM playback status 512 setDrmPlaybackStatusIfNeeded(Playback::PAUSE, 0); 513 mStarted = false; 514} 515 516void NuPlayer::GenericSource::resume() { 517 // nothing to do, just account for DRM playback status 518 setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000); 519 mStarted = true; 520} 521 522void NuPlayer::GenericSource::disconnect() { 523 if (mDataSource != NULL) { 524 // disconnect data source 525 if (mDataSource->flags() & DataSource::kIsCachingDataSource) { 526 static_cast<NuCachedSource2 *>(mDataSource.get())->disconnect(); 527 } 528 } else if (mHttpSource != NULL) { 529 static_cast<HTTPBase *>(mHttpSource.get())->disconnect(); 530 } 531} 532 533void NuPlayer::GenericSource::setDrmPlaybackStatusIfNeeded(int playbackStatus, int64_t position) { 534 if (mDecryptHandle != NULL) { 535 mDrmManagerClient->setPlaybackStatus(mDecryptHandle, playbackStatus, position); 536 } 537 mSubtitleTrack.mPackets = new AnotherPacketSource(NULL); 538 mTimedTextTrack.mPackets = new AnotherPacketSource(NULL); 539} 540 541status_t NuPlayer::GenericSource::feedMoreTSData() { 542 return OK; 543} 544 545void NuPlayer::GenericSource::schedulePollBuffering() { 546 sp<AMessage> msg = new AMessage(kWhatPollBuffering, id()); 547 msg->setInt32("generation", mPollBufferingGeneration); 548 msg->post(1000000ll); 549} 550 551void NuPlayer::GenericSource::cancelPollBuffering() { 552 ++mPollBufferingGeneration; 553} 554 555void NuPlayer::GenericSource::notifyBufferingUpdate(int percentage) { 556 sp<AMessage> msg = dupNotify(); 557 msg->setInt32("what", kWhatBufferingUpdate); 558 msg->setInt32("percentage", percentage); 559 msg->post(); 560} 561 562void NuPlayer::GenericSource::onPollBuffering() { 563 status_t finalStatus = UNKNOWN_ERROR; 564 int64_t cachedDurationUs = 0ll; 565 566 if (mCachedSource != NULL) { 567 size_t cachedDataRemaining = 568 mCachedSource->approxDataRemaining(&finalStatus); 569 570 if (finalStatus == OK) { 571 off64_t size; 572 int64_t bitrate = 0ll; 573 if (mDurationUs > 0 && mCachedSource->getSize(&size) == OK) { 574 bitrate = size * 8000000ll / mDurationUs; 575 } else if (mBitrate > 0) { 576 bitrate = mBitrate; 577 } 578 if (bitrate > 0) { 579 cachedDurationUs = cachedDataRemaining * 8000000ll / bitrate; 580 } 581 } 582 } else if (mWVMExtractor != NULL) { 583 cachedDurationUs 584 = mWVMExtractor->getCachedDurationUs(&finalStatus); 585 } 586 587 if (finalStatus == ERROR_END_OF_STREAM) { 588 notifyBufferingUpdate(100); 589 cancelPollBuffering(); 590 return; 591 } else if (cachedDurationUs > 0ll && mDurationUs > 0ll) { 592 int percentage = 100.0 * cachedDurationUs / mDurationUs; 593 if (percentage > 100) { 594 percentage = 100; 595 } 596 597 notifyBufferingUpdate(percentage); 598 } 599 600 schedulePollBuffering(); 601} 602 603 604void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) { 605 switch (msg->what()) { 606 case kWhatPrepareAsync: 607 { 608 onPrepareAsync(); 609 break; 610 } 611 case kWhatFetchSubtitleData: 612 { 613 fetchTextData(kWhatSendSubtitleData, MEDIA_TRACK_TYPE_SUBTITLE, 614 mFetchSubtitleDataGeneration, mSubtitleTrack.mPackets, msg); 615 break; 616 } 617 618 case kWhatFetchTimedTextData: 619 { 620 fetchTextData(kWhatSendTimedTextData, MEDIA_TRACK_TYPE_TIMEDTEXT, 621 mFetchTimedTextDataGeneration, mTimedTextTrack.mPackets, msg); 622 break; 623 } 624 625 case kWhatSendSubtitleData: 626 { 627 sendTextData(kWhatSubtitleData, MEDIA_TRACK_TYPE_SUBTITLE, 628 mFetchSubtitleDataGeneration, mSubtitleTrack.mPackets, msg); 629 break; 630 } 631 632 case kWhatSendTimedTextData: 633 { 634 sendTextData(kWhatTimedTextData, MEDIA_TRACK_TYPE_TIMEDTEXT, 635 mFetchTimedTextDataGeneration, mTimedTextTrack.mPackets, msg); 636 break; 637 } 638 639 case kWhatChangeAVSource: 640 { 641 int32_t trackIndex; 642 CHECK(msg->findInt32("trackIndex", &trackIndex)); 643 const sp<MediaSource> source = mSources.itemAt(trackIndex); 644 645 Track* track; 646 const char *mime; 647 media_track_type trackType, counterpartType; 648 sp<MetaData> meta = source->getFormat(); 649 meta->findCString(kKeyMIMEType, &mime); 650 if (!strncasecmp(mime, "audio/", 6)) { 651 track = &mAudioTrack; 652 trackType = MEDIA_TRACK_TYPE_AUDIO; 653 counterpartType = MEDIA_TRACK_TYPE_VIDEO;; 654 } else { 655 CHECK(!strncasecmp(mime, "video/", 6)); 656 track = &mVideoTrack; 657 trackType = MEDIA_TRACK_TYPE_VIDEO; 658 counterpartType = MEDIA_TRACK_TYPE_AUDIO;; 659 } 660 661 662 if (track->mSource != NULL) { 663 track->mSource->stop(); 664 } 665 track->mSource = source; 666 track->mSource->start(); 667 track->mIndex = trackIndex; 668 669 int64_t timeUs, actualTimeUs; 670 const bool formatChange = true; 671 if (trackType == MEDIA_TRACK_TYPE_AUDIO) { 672 timeUs = mAudioLastDequeueTimeUs; 673 } else { 674 timeUs = mVideoLastDequeueTimeUs; 675 } 676 readBuffer(trackType, timeUs, &actualTimeUs, formatChange); 677 readBuffer(counterpartType, -1, NULL, formatChange); 678 ALOGV("timeUs %lld actualTimeUs %lld", timeUs, actualTimeUs); 679 680 break; 681 } 682 case kWhatPollBuffering: 683 { 684 int32_t generation; 685 CHECK(msg->findInt32("generation", &generation)); 686 if (generation == mPollBufferingGeneration) { 687 onPollBuffering(); 688 } 689 break; 690 } 691 692 case kWhatGetFormat: 693 { 694 onGetFormatMeta(msg); 695 break; 696 } 697 698 case kWhatGetSelectedTrack: 699 { 700 onGetSelectedTrack(msg); 701 break; 702 } 703 704 case kWhatSelectTrack: 705 { 706 onSelectTrack(msg); 707 break; 708 } 709 710 case kWhatSeek: 711 { 712 onSeek(msg); 713 break; 714 } 715 716 case kWhatReadBuffer: 717 { 718 onReadBuffer(msg); 719 break; 720 } 721 722 case kWhatStopWidevine: 723 { 724 // mStopRead is only used for Widevine to prevent the video source 725 // from being read while the associated video decoder is shutting down. 726 mStopRead = true; 727 if (mVideoTrack.mSource != NULL) { 728 mVideoTrack.mPackets->clear(); 729 } 730 sp<AMessage> response = new AMessage; 731 uint32_t replyID; 732 CHECK(msg->senderAwaitsResponse(&replyID)); 733 response->postReply(replyID); 734 break; 735 } 736 default: 737 Source::onMessageReceived(msg); 738 break; 739 } 740} 741 742void NuPlayer::GenericSource::fetchTextData( 743 uint32_t sendWhat, 744 media_track_type type, 745 int32_t curGen, 746 sp<AnotherPacketSource> packets, 747 sp<AMessage> msg) { 748 int32_t msgGeneration; 749 CHECK(msg->findInt32("generation", &msgGeneration)); 750 if (msgGeneration != curGen) { 751 // stale 752 return; 753 } 754 755 int32_t avail; 756 if (packets->hasBufferAvailable(&avail)) { 757 return; 758 } 759 760 int64_t timeUs; 761 CHECK(msg->findInt64("timeUs", &timeUs)); 762 763 int64_t subTimeUs; 764 readBuffer(type, timeUs, &subTimeUs); 765 766 int64_t delayUs = subTimeUs - timeUs; 767 if (msg->what() == kWhatFetchSubtitleData) { 768 const int64_t oneSecUs = 1000000ll; 769 delayUs -= oneSecUs; 770 } 771 sp<AMessage> msg2 = new AMessage(sendWhat, id()); 772 msg2->setInt32("generation", msgGeneration); 773 msg2->post(delayUs < 0 ? 0 : delayUs); 774} 775 776void NuPlayer::GenericSource::sendTextData( 777 uint32_t what, 778 media_track_type type, 779 int32_t curGen, 780 sp<AnotherPacketSource> packets, 781 sp<AMessage> msg) { 782 int32_t msgGeneration; 783 CHECK(msg->findInt32("generation", &msgGeneration)); 784 if (msgGeneration != curGen) { 785 // stale 786 return; 787 } 788 789 int64_t subTimeUs; 790 if (packets->nextBufferTime(&subTimeUs) != OK) { 791 return; 792 } 793 794 int64_t nextSubTimeUs; 795 readBuffer(type, -1, &nextSubTimeUs); 796 797 sp<ABuffer> buffer; 798 status_t dequeueStatus = packets->dequeueAccessUnit(&buffer); 799 if (dequeueStatus == OK) { 800 sp<AMessage> notify = dupNotify(); 801 notify->setInt32("what", what); 802 notify->setBuffer("buffer", buffer); 803 notify->post(); 804 805 const int64_t delayUs = nextSubTimeUs - subTimeUs; 806 msg->post(delayUs < 0 ? 0 : delayUs); 807 } 808} 809 810sp<MetaData> NuPlayer::GenericSource::getFormatMeta(bool audio) { 811 sp<AMessage> msg = new AMessage(kWhatGetFormat, id()); 812 msg->setInt32("audio", audio); 813 814 sp<AMessage> response; 815 void *format; 816 status_t err = msg->postAndAwaitResponse(&response); 817 if (err == OK && response != NULL) { 818 CHECK(response->findPointer("format", &format)); 819 return (MetaData *)format; 820 } else { 821 return NULL; 822 } 823} 824 825void NuPlayer::GenericSource::onGetFormatMeta(sp<AMessage> msg) const { 826 int32_t audio; 827 CHECK(msg->findInt32("audio", &audio)); 828 829 sp<AMessage> response = new AMessage; 830 sp<MetaData> format = doGetFormatMeta(audio); 831 response->setPointer("format", format.get()); 832 833 uint32_t replyID; 834 CHECK(msg->senderAwaitsResponse(&replyID)); 835 response->postReply(replyID); 836} 837 838sp<MetaData> NuPlayer::GenericSource::doGetFormatMeta(bool audio) const { 839 sp<MediaSource> source = audio ? mAudioTrack.mSource : mVideoTrack.mSource; 840 841 if (source == NULL) { 842 return NULL; 843 } 844 845 return source->getFormat(); 846} 847 848status_t NuPlayer::GenericSource::dequeueAccessUnit( 849 bool audio, sp<ABuffer> *accessUnit) { 850 Track *track = audio ? &mAudioTrack : &mVideoTrack; 851 852 if (track->mSource == NULL) { 853 return -EWOULDBLOCK; 854 } 855 856 if (mIsWidevine && !audio) { 857 // try to read a buffer as we may not have been able to the last time 858 postReadBuffer(MEDIA_TRACK_TYPE_VIDEO); 859 } 860 861 status_t finalResult; 862 if (!track->mPackets->hasBufferAvailable(&finalResult)) { 863 if (finalResult == OK) { 864 postReadBuffer( 865 audio ? MEDIA_TRACK_TYPE_AUDIO : MEDIA_TRACK_TYPE_VIDEO); 866 return -EWOULDBLOCK; 867 } 868 return finalResult; 869 } 870 871 status_t result = track->mPackets->dequeueAccessUnit(accessUnit); 872 873 if (!track->mPackets->hasBufferAvailable(&finalResult)) { 874 postReadBuffer(audio? MEDIA_TRACK_TYPE_AUDIO : MEDIA_TRACK_TYPE_VIDEO); 875 } 876 877 if (result != OK) { 878 if (mSubtitleTrack.mSource != NULL) { 879 mSubtitleTrack.mPackets->clear(); 880 mFetchSubtitleDataGeneration++; 881 } 882 if (mTimedTextTrack.mSource != NULL) { 883 mTimedTextTrack.mPackets->clear(); 884 mFetchTimedTextDataGeneration++; 885 } 886 return result; 887 } 888 889 int64_t timeUs; 890 status_t eosResult; // ignored 891 CHECK((*accessUnit)->meta()->findInt64("timeUs", &timeUs)); 892 if (audio) { 893 mAudioLastDequeueTimeUs = timeUs; 894 } else { 895 mVideoLastDequeueTimeUs = timeUs; 896 } 897 898 if (mSubtitleTrack.mSource != NULL 899 && !mSubtitleTrack.mPackets->hasBufferAvailable(&eosResult)) { 900 sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, id()); 901 msg->setInt64("timeUs", timeUs); 902 msg->setInt32("generation", mFetchSubtitleDataGeneration); 903 msg->post(); 904 } 905 906 if (mTimedTextTrack.mSource != NULL 907 && !mTimedTextTrack.mPackets->hasBufferAvailable(&eosResult)) { 908 sp<AMessage> msg = new AMessage(kWhatFetchTimedTextData, id()); 909 msg->setInt64("timeUs", timeUs); 910 msg->setInt32("generation", mFetchTimedTextDataGeneration); 911 msg->post(); 912 } 913 914 return result; 915} 916 917status_t NuPlayer::GenericSource::getDuration(int64_t *durationUs) { 918 *durationUs = mDurationUs; 919 return OK; 920} 921 922size_t NuPlayer::GenericSource::getTrackCount() const { 923 return mSources.size(); 924} 925 926sp<AMessage> NuPlayer::GenericSource::getTrackInfo(size_t trackIndex) const { 927 size_t trackCount = mSources.size(); 928 if (trackIndex >= trackCount) { 929 return NULL; 930 } 931 932 sp<AMessage> format = new AMessage(); 933 sp<MetaData> meta = mSources.itemAt(trackIndex)->getFormat(); 934 935 const char *mime; 936 CHECK(meta->findCString(kKeyMIMEType, &mime)); 937 938 int32_t trackType; 939 if (!strncasecmp(mime, "video/", 6)) { 940 trackType = MEDIA_TRACK_TYPE_VIDEO; 941 } else if (!strncasecmp(mime, "audio/", 6)) { 942 trackType = MEDIA_TRACK_TYPE_AUDIO; 943 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) { 944 trackType = MEDIA_TRACK_TYPE_TIMEDTEXT; 945 } else { 946 trackType = MEDIA_TRACK_TYPE_UNKNOWN; 947 } 948 format->setInt32("type", trackType); 949 950 const char *lang; 951 if (!meta->findCString(kKeyMediaLanguage, &lang)) { 952 lang = "und"; 953 } 954 format->setString("language", lang); 955 956 if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) { 957 format->setString("mime", mime); 958 959 int32_t isAutoselect = 1, isDefault = 0, isForced = 0; 960 meta->findInt32(kKeyTrackIsAutoselect, &isAutoselect); 961 meta->findInt32(kKeyTrackIsDefault, &isDefault); 962 meta->findInt32(kKeyTrackIsForced, &isForced); 963 964 format->setInt32("auto", !!isAutoselect); 965 format->setInt32("default", !!isDefault); 966 format->setInt32("forced", !!isForced); 967 } 968 969 return format; 970} 971 972ssize_t NuPlayer::GenericSource::getSelectedTrack(media_track_type type) const { 973 sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, id()); 974 msg->setInt32("type", type); 975 976 sp<AMessage> response; 977 int32_t index; 978 status_t err = msg->postAndAwaitResponse(&response); 979 if (err == OK && response != NULL) { 980 CHECK(response->findInt32("index", &index)); 981 return index; 982 } else { 983 return -1; 984 } 985} 986 987void NuPlayer::GenericSource::onGetSelectedTrack(sp<AMessage> msg) const { 988 int32_t tmpType; 989 CHECK(msg->findInt32("type", &tmpType)); 990 media_track_type type = (media_track_type)tmpType; 991 992 sp<AMessage> response = new AMessage; 993 ssize_t index = doGetSelectedTrack(type); 994 response->setInt32("index", index); 995 996 uint32_t replyID; 997 CHECK(msg->senderAwaitsResponse(&replyID)); 998 response->postReply(replyID); 999} 1000 1001ssize_t NuPlayer::GenericSource::doGetSelectedTrack(media_track_type type) const { 1002 const Track *track = NULL; 1003 switch (type) { 1004 case MEDIA_TRACK_TYPE_VIDEO: 1005 track = &mVideoTrack; 1006 break; 1007 case MEDIA_TRACK_TYPE_AUDIO: 1008 track = &mAudioTrack; 1009 break; 1010 case MEDIA_TRACK_TYPE_TIMEDTEXT: 1011 track = &mTimedTextTrack; 1012 break; 1013 case MEDIA_TRACK_TYPE_SUBTITLE: 1014 track = &mSubtitleTrack; 1015 break; 1016 default: 1017 break; 1018 } 1019 1020 if (track != NULL && track->mSource != NULL) { 1021 return track->mIndex; 1022 } 1023 1024 return -1; 1025} 1026 1027status_t NuPlayer::GenericSource::selectTrack(size_t trackIndex, bool select, int64_t timeUs) { 1028 ALOGV("%s track: %zu", select ? "select" : "deselect", trackIndex); 1029 sp<AMessage> msg = new AMessage(kWhatSelectTrack, id()); 1030 msg->setInt32("trackIndex", trackIndex); 1031 msg->setInt32("select", select); 1032 msg->setInt64("timeUs", timeUs); 1033 1034 sp<AMessage> response; 1035 status_t err = msg->postAndAwaitResponse(&response); 1036 if (err == OK && response != NULL) { 1037 CHECK(response->findInt32("err", &err)); 1038 } 1039 1040 return err; 1041} 1042 1043void NuPlayer::GenericSource::onSelectTrack(sp<AMessage> msg) { 1044 int32_t trackIndex, select; 1045 int64_t timeUs; 1046 CHECK(msg->findInt32("trackIndex", &trackIndex)); 1047 CHECK(msg->findInt32("select", &select)); 1048 CHECK(msg->findInt64("timeUs", &timeUs)); 1049 1050 sp<AMessage> response = new AMessage; 1051 status_t err = doSelectTrack(trackIndex, select, timeUs); 1052 response->setInt32("err", err); 1053 1054 uint32_t replyID; 1055 CHECK(msg->senderAwaitsResponse(&replyID)); 1056 response->postReply(replyID); 1057} 1058 1059status_t NuPlayer::GenericSource::doSelectTrack(size_t trackIndex, bool select, int64_t timeUs) { 1060 if (trackIndex >= mSources.size()) { 1061 return BAD_INDEX; 1062 } 1063 1064 if (!select) { 1065 Track* track = NULL; 1066 if (mSubtitleTrack.mSource != NULL && trackIndex == mSubtitleTrack.mIndex) { 1067 track = &mSubtitleTrack; 1068 mFetchSubtitleDataGeneration++; 1069 } else if (mTimedTextTrack.mSource != NULL && trackIndex == mTimedTextTrack.mIndex) { 1070 track = &mTimedTextTrack; 1071 mFetchTimedTextDataGeneration++; 1072 } 1073 if (track == NULL) { 1074 return INVALID_OPERATION; 1075 } 1076 track->mSource->stop(); 1077 track->mSource = NULL; 1078 track->mPackets->clear(); 1079 return OK; 1080 } 1081 1082 const sp<MediaSource> source = mSources.itemAt(trackIndex); 1083 sp<MetaData> meta = source->getFormat(); 1084 const char *mime; 1085 CHECK(meta->findCString(kKeyMIMEType, &mime)); 1086 if (!strncasecmp(mime, "text/", 5)) { 1087 bool isSubtitle = strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP); 1088 Track *track = isSubtitle ? &mSubtitleTrack : &mTimedTextTrack; 1089 if (track->mSource != NULL && track->mIndex == trackIndex) { 1090 return OK; 1091 } 1092 track->mIndex = trackIndex; 1093 if (track->mSource != NULL) { 1094 track->mSource->stop(); 1095 } 1096 track->mSource = mSources.itemAt(trackIndex); 1097 track->mSource->start(); 1098 if (track->mPackets == NULL) { 1099 track->mPackets = new AnotherPacketSource(track->mSource->getFormat()); 1100 } else { 1101 track->mPackets->clear(); 1102 track->mPackets->setFormat(track->mSource->getFormat()); 1103 1104 } 1105 1106 if (isSubtitle) { 1107 mFetchSubtitleDataGeneration++; 1108 } else { 1109 mFetchTimedTextDataGeneration++; 1110 } 1111 1112 status_t eosResult; // ignored 1113 if (mSubtitleTrack.mSource != NULL 1114 && !mSubtitleTrack.mPackets->hasBufferAvailable(&eosResult)) { 1115 sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, id()); 1116 msg->setInt64("timeUs", timeUs); 1117 msg->setInt32("generation", mFetchSubtitleDataGeneration); 1118 msg->post(); 1119 } 1120 1121 if (mTimedTextTrack.mSource != NULL 1122 && !mTimedTextTrack.mPackets->hasBufferAvailable(&eosResult)) { 1123 sp<AMessage> msg = new AMessage(kWhatFetchTimedTextData, id()); 1124 msg->setInt64("timeUs", timeUs); 1125 msg->setInt32("generation", mFetchTimedTextDataGeneration); 1126 msg->post(); 1127 } 1128 1129 return OK; 1130 } else if (!strncasecmp(mime, "audio/", 6) || !strncasecmp(mime, "video/", 6)) { 1131 bool audio = !strncasecmp(mime, "audio/", 6); 1132 Track *track = audio ? &mAudioTrack : &mVideoTrack; 1133 if (track->mSource != NULL && track->mIndex == trackIndex) { 1134 return OK; 1135 } 1136 1137 sp<AMessage> msg = new AMessage(kWhatChangeAVSource, id()); 1138 msg->setInt32("trackIndex", trackIndex); 1139 msg->post(); 1140 return OK; 1141 } 1142 1143 return INVALID_OPERATION; 1144} 1145 1146status_t NuPlayer::GenericSource::seekTo(int64_t seekTimeUs) { 1147 sp<AMessage> msg = new AMessage(kWhatSeek, id()); 1148 msg->setInt64("seekTimeUs", seekTimeUs); 1149 1150 sp<AMessage> response; 1151 status_t err = msg->postAndAwaitResponse(&response); 1152 if (err == OK && response != NULL) { 1153 CHECK(response->findInt32("err", &err)); 1154 } 1155 1156 return err; 1157} 1158 1159void NuPlayer::GenericSource::onSeek(sp<AMessage> msg) { 1160 int64_t seekTimeUs; 1161 CHECK(msg->findInt64("seekTimeUs", &seekTimeUs)); 1162 1163 sp<AMessage> response = new AMessage; 1164 status_t err = doSeek(seekTimeUs); 1165 response->setInt32("err", err); 1166 1167 uint32_t replyID; 1168 CHECK(msg->senderAwaitsResponse(&replyID)); 1169 response->postReply(replyID); 1170} 1171 1172status_t NuPlayer::GenericSource::doSeek(int64_t seekTimeUs) { 1173 // If the Widevine source is stopped, do not attempt to read any 1174 // more buffers. 1175 if (mStopRead) { 1176 return INVALID_OPERATION; 1177 } 1178 if (mVideoTrack.mSource != NULL) { 1179 int64_t actualTimeUs; 1180 readBuffer(MEDIA_TRACK_TYPE_VIDEO, seekTimeUs, &actualTimeUs); 1181 1182 seekTimeUs = actualTimeUs; 1183 mVideoLastDequeueTimeUs = seekTimeUs; 1184 } 1185 1186 if (mAudioTrack.mSource != NULL) { 1187 readBuffer(MEDIA_TRACK_TYPE_AUDIO, seekTimeUs); 1188 mAudioLastDequeueTimeUs = seekTimeUs; 1189 } 1190 1191 setDrmPlaybackStatusIfNeeded(Playback::START, seekTimeUs / 1000); 1192 if (!mStarted) { 1193 setDrmPlaybackStatusIfNeeded(Playback::PAUSE, 0); 1194 } 1195 return OK; 1196} 1197 1198sp<ABuffer> NuPlayer::GenericSource::mediaBufferToABuffer( 1199 MediaBuffer* mb, 1200 media_track_type trackType, 1201 int64_t *actualTimeUs) { 1202 bool audio = trackType == MEDIA_TRACK_TYPE_AUDIO; 1203 size_t outLength = mb->range_length(); 1204 1205 if (audio && mAudioIsVorbis) { 1206 outLength += sizeof(int32_t); 1207 } 1208 1209 sp<ABuffer> ab; 1210 if (mIsSecure && !audio) { 1211 // data is already provided in the buffer 1212 ab = new ABuffer(NULL, mb->range_length()); 1213 mb->add_ref(); 1214 ab->setMediaBufferBase(mb); 1215 } else { 1216 ab = new ABuffer(outLength); 1217 memcpy(ab->data(), 1218 (const uint8_t *)mb->data() + mb->range_offset(), 1219 mb->range_length()); 1220 } 1221 1222 if (audio && mAudioIsVorbis) { 1223 int32_t numPageSamples; 1224 if (!mb->meta_data()->findInt32(kKeyValidSamples, &numPageSamples)) { 1225 numPageSamples = -1; 1226 } 1227 1228 uint8_t* abEnd = ab->data() + mb->range_length(); 1229 memcpy(abEnd, &numPageSamples, sizeof(numPageSamples)); 1230 } 1231 1232 sp<AMessage> meta = ab->meta(); 1233 1234 int64_t timeUs; 1235 CHECK(mb->meta_data()->findInt64(kKeyTime, &timeUs)); 1236 meta->setInt64("timeUs", timeUs); 1237 1238 if (trackType == MEDIA_TRACK_TYPE_TIMEDTEXT) { 1239 const char *mime; 1240 CHECK(mTimedTextTrack.mSource != NULL 1241 && mTimedTextTrack.mSource->getFormat()->findCString(kKeyMIMEType, &mime)); 1242 meta->setString("mime", mime); 1243 } 1244 1245 int64_t durationUs; 1246 if (mb->meta_data()->findInt64(kKeyDuration, &durationUs)) { 1247 meta->setInt64("durationUs", durationUs); 1248 } 1249 1250 if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) { 1251 meta->setInt32("trackIndex", mSubtitleTrack.mIndex); 1252 } 1253 1254 if (actualTimeUs) { 1255 *actualTimeUs = timeUs; 1256 } 1257 1258 mb->release(); 1259 mb = NULL; 1260 1261 return ab; 1262} 1263 1264void NuPlayer::GenericSource::postReadBuffer(media_track_type trackType) { 1265 Mutex::Autolock _l(mReadBufferLock); 1266 1267 if ((mPendingReadBufferTypes & (1 << trackType)) == 0) { 1268 mPendingReadBufferTypes |= (1 << trackType); 1269 sp<AMessage> msg = new AMessage(kWhatReadBuffer, id()); 1270 msg->setInt32("trackType", trackType); 1271 msg->post(); 1272 } 1273} 1274 1275void NuPlayer::GenericSource::onReadBuffer(sp<AMessage> msg) { 1276 int32_t tmpType; 1277 CHECK(msg->findInt32("trackType", &tmpType)); 1278 media_track_type trackType = (media_track_type)tmpType; 1279 readBuffer(trackType); 1280 { 1281 // only protect the variable change, as readBuffer may 1282 // take considerable time. 1283 Mutex::Autolock _l(mReadBufferLock); 1284 mPendingReadBufferTypes &= ~(1 << trackType); 1285 } 1286} 1287 1288void NuPlayer::GenericSource::readBuffer( 1289 media_track_type trackType, int64_t seekTimeUs, int64_t *actualTimeUs, bool formatChange) { 1290 // Do not read data if Widevine source is stopped 1291 if (mStopRead) { 1292 return; 1293 } 1294 Track *track; 1295 size_t maxBuffers = 1; 1296 switch (trackType) { 1297 case MEDIA_TRACK_TYPE_VIDEO: 1298 track = &mVideoTrack; 1299 if (mIsWidevine) { 1300 maxBuffers = 2; 1301 } 1302 break; 1303 case MEDIA_TRACK_TYPE_AUDIO: 1304 track = &mAudioTrack; 1305 if (mIsWidevine) { 1306 maxBuffers = 8; 1307 } else { 1308 maxBuffers = 64; 1309 } 1310 break; 1311 case MEDIA_TRACK_TYPE_SUBTITLE: 1312 track = &mSubtitleTrack; 1313 break; 1314 case MEDIA_TRACK_TYPE_TIMEDTEXT: 1315 track = &mTimedTextTrack; 1316 break; 1317 default: 1318 TRESPASS(); 1319 } 1320 1321 if (track->mSource == NULL) { 1322 return; 1323 } 1324 1325 if (actualTimeUs) { 1326 *actualTimeUs = seekTimeUs; 1327 } 1328 1329 MediaSource::ReadOptions options; 1330 1331 bool seeking = false; 1332 1333 if (seekTimeUs >= 0) { 1334 options.setSeekTo(seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); 1335 seeking = true; 1336 } 1337 1338 if (mIsWidevine) { 1339 options.setNonBlocking(); 1340 } 1341 1342 for (size_t numBuffers = 0; numBuffers < maxBuffers; ) { 1343 MediaBuffer *mbuf; 1344 status_t err = track->mSource->read(&mbuf, &options); 1345 1346 options.clearSeekTo(); 1347 1348 if (err == OK) { 1349 int64_t timeUs; 1350 CHECK(mbuf->meta_data()->findInt64(kKeyTime, &timeUs)); 1351 if (trackType == MEDIA_TRACK_TYPE_AUDIO) { 1352 mAudioTimeUs = timeUs; 1353 } else if (trackType == MEDIA_TRACK_TYPE_VIDEO) { 1354 mVideoTimeUs = timeUs; 1355 } 1356 1357 // formatChange && seeking: track whose source is changed during selection 1358 // formatChange && !seeking: track whose source is not changed during selection 1359 // !formatChange: normal seek 1360 if ((seeking || formatChange) 1361 && (trackType == MEDIA_TRACK_TYPE_AUDIO 1362 || trackType == MEDIA_TRACK_TYPE_VIDEO)) { 1363 ATSParser::DiscontinuityType type = (formatChange && seeking) 1364 ? ATSParser::DISCONTINUITY_FORMATCHANGE 1365 : ATSParser::DISCONTINUITY_NONE; 1366 track->mPackets->queueDiscontinuity( type, NULL, true /* discard */); 1367 } 1368 1369 sp<ABuffer> buffer = mediaBufferToABuffer(mbuf, trackType, actualTimeUs); 1370 track->mPackets->queueAccessUnit(buffer); 1371 formatChange = false; 1372 seeking = false; 1373 ++numBuffers; 1374 } else if (err == WOULD_BLOCK) { 1375 break; 1376 } else if (err == INFO_FORMAT_CHANGED) { 1377#if 0 1378 track->mPackets->queueDiscontinuity( 1379 ATSParser::DISCONTINUITY_FORMATCHANGE, 1380 NULL, 1381 false /* discard */); 1382#endif 1383 } else { 1384 track->mPackets->signalEOS(err); 1385 break; 1386 } 1387 } 1388} 1389 1390} // namespace android 1391