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 "RTSPSource" 19 #include <utils/Log.h> 20 21 #include "RTSPSource.h" 22 23 #include "AnotherPacketSource.h" 24 #include "MyHandler.h" 25 #include "SDPLoader.h" 26 27 #include <media/IMediaHTTPService.h> 28 #include <media/stagefright/MediaDefs.h> 29 #include <media/stagefright/MetaData.h> 30 31 namespace android { 32 33 const int64_t kNearEOSTimeoutUs = 2000000ll; // 2 secs 34 35 // Buffer Underflow/Prepare/StartServer/Overflow Marks 36 const int64_t NuPlayer::RTSPSource::kUnderflowMarkUs = 1000000ll; 37 const int64_t NuPlayer::RTSPSource::kPrepareMarkUs = 3000000ll; 38 const int64_t NuPlayer::RTSPSource::kStartServerMarkUs = 5000000ll; 39 const int64_t NuPlayer::RTSPSource::kOverflowMarkUs = 10000000ll; 40 41 NuPlayer::RTSPSource::RTSPSource( 42 const sp<AMessage> ¬ify, 43 const sp<IMediaHTTPService> &httpService, 44 const char *url, 45 const KeyedVector<String8, String8> *headers, 46 bool uidValid, 47 uid_t uid, 48 bool isSDP) 49 : Source(notify), 50 mHTTPService(httpService), 51 mURL(url), 52 mUIDValid(uidValid), 53 mUID(uid), 54 mFlags(0), 55 mIsSDP(isSDP), 56 mState(DISCONNECTED), 57 mFinalResult(OK), 58 mDisconnectReplyID(0), 59 mBuffering(false), 60 mInPreparationPhase(true), 61 mSeekGeneration(0), 62 mEOSTimeoutAudio(0), 63 mEOSTimeoutVideo(0) { 64 if (headers) { 65 mExtraHeaders = *headers; 66 67 ssize_t index = 68 mExtraHeaders.indexOfKey(String8("x-hide-urls-from-log")); 69 70 if (index >= 0) { 71 mFlags |= kFlagIncognito; 72 73 mExtraHeaders.removeItemsAt(index); 74 } 75 } 76 } 77 78 NuPlayer::RTSPSource::~RTSPSource() { 79 if (mLooper != NULL) { 80 mLooper->unregisterHandler(id()); 81 mLooper->stop(); 82 } 83 } 84 85 void NuPlayer::RTSPSource::prepareAsync() { 86 if (mIsSDP && mHTTPService == NULL) { 87 notifyPrepared(BAD_VALUE); 88 return; 89 } 90 91 if (mLooper == NULL) { 92 mLooper = new ALooper; 93 mLooper->setName("rtsp"); 94 mLooper->start(); 95 96 mLooper->registerHandler(this); 97 } 98 99 CHECK(mHandler == NULL); 100 CHECK(mSDPLoader == NULL); 101 102 sp<AMessage> notify = new AMessage(kWhatNotify, this); 103 104 CHECK_EQ(mState, (int)DISCONNECTED); 105 mState = CONNECTING; 106 107 if (mIsSDP) { 108 mSDPLoader = new SDPLoader(notify, 109 (mFlags & kFlagIncognito) ? SDPLoader::kFlagIncognito : 0, 110 mHTTPService); 111 112 mSDPLoader->load( 113 mURL.c_str(), mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders); 114 } else { 115 mHandler = new MyHandler(mURL.c_str(), notify, mUIDValid, mUID); 116 mLooper->registerHandler(mHandler); 117 118 mHandler->connect(); 119 } 120 121 startBufferingIfNecessary(); 122 } 123 124 void NuPlayer::RTSPSource::start() { 125 } 126 127 void NuPlayer::RTSPSource::stop() { 128 if (mLooper == NULL) { 129 return; 130 } 131 sp<AMessage> msg = new AMessage(kWhatDisconnect, this); 132 133 sp<AMessage> dummy; 134 msg->postAndAwaitResponse(&dummy); 135 } 136 137 status_t NuPlayer::RTSPSource::feedMoreTSData() { 138 Mutex::Autolock _l(mBufferingLock); 139 return mFinalResult; 140 } 141 142 sp<MetaData> NuPlayer::RTSPSource::getFormatMeta(bool audio) { 143 sp<AnotherPacketSource> source = getSource(audio); 144 145 if (source == NULL) { 146 return NULL; 147 } 148 149 return source->getFormat(); 150 } 151 152 bool NuPlayer::RTSPSource::haveSufficientDataOnAllTracks() { 153 // We're going to buffer at least 2 secs worth data on all tracks before 154 // starting playback (both at startup and after a seek). 155 156 static const int64_t kMinDurationUs = 2000000ll; 157 158 int64_t mediaDurationUs = 0; 159 getDuration(&mediaDurationUs); 160 if ((mAudioTrack != NULL && mAudioTrack->isFinished(mediaDurationUs)) 161 || (mVideoTrack != NULL && mVideoTrack->isFinished(mediaDurationUs))) { 162 return true; 163 } 164 165 status_t err; 166 int64_t durationUs; 167 if (mAudioTrack != NULL 168 && (durationUs = mAudioTrack->getBufferedDurationUs(&err)) 169 < kMinDurationUs 170 && err == OK) { 171 ALOGV("audio track doesn't have enough data yet. (%.2f secs buffered)", 172 durationUs / 1E6); 173 return false; 174 } 175 176 if (mVideoTrack != NULL 177 && (durationUs = mVideoTrack->getBufferedDurationUs(&err)) 178 < kMinDurationUs 179 && err == OK) { 180 ALOGV("video track doesn't have enough data yet. (%.2f secs buffered)", 181 durationUs / 1E6); 182 return false; 183 } 184 185 return true; 186 } 187 188 status_t NuPlayer::RTSPSource::dequeueAccessUnit( 189 bool audio, sp<ABuffer> *accessUnit) { 190 if (!stopBufferingIfNecessary()) { 191 return -EWOULDBLOCK; 192 } 193 194 sp<AnotherPacketSource> source = getSource(audio); 195 196 if (source == NULL) { 197 return -EWOULDBLOCK; 198 } 199 200 status_t finalResult; 201 if (!source->hasBufferAvailable(&finalResult)) { 202 if (finalResult == OK) { 203 int64_t mediaDurationUs = 0; 204 getDuration(&mediaDurationUs); 205 sp<AnotherPacketSource> otherSource = getSource(!audio); 206 status_t otherFinalResult; 207 208 // If other source already signaled EOS, this source should also signal EOS 209 if (otherSource != NULL && 210 !otherSource->hasBufferAvailable(&otherFinalResult) && 211 otherFinalResult == ERROR_END_OF_STREAM) { 212 source->signalEOS(ERROR_END_OF_STREAM); 213 return ERROR_END_OF_STREAM; 214 } 215 216 // If this source has detected near end, give it some time to retrieve more 217 // data before signaling EOS 218 if (source->isFinished(mediaDurationUs)) { 219 int64_t eosTimeout = audio ? mEOSTimeoutAudio : mEOSTimeoutVideo; 220 if (eosTimeout == 0) { 221 setEOSTimeout(audio, ALooper::GetNowUs()); 222 } else if ((ALooper::GetNowUs() - eosTimeout) > kNearEOSTimeoutUs) { 223 setEOSTimeout(audio, 0); 224 source->signalEOS(ERROR_END_OF_STREAM); 225 return ERROR_END_OF_STREAM; 226 } 227 return -EWOULDBLOCK; 228 } 229 230 if (!(otherSource != NULL && otherSource->isFinished(mediaDurationUs))) { 231 // We should not enter buffering mode 232 // if any of the sources already have detected EOS. 233 startBufferingIfNecessary(); 234 } 235 236 return -EWOULDBLOCK; 237 } 238 return finalResult; 239 } 240 241 setEOSTimeout(audio, 0); 242 243 return source->dequeueAccessUnit(accessUnit); 244 } 245 246 sp<AnotherPacketSource> NuPlayer::RTSPSource::getSource(bool audio) { 247 if (mTSParser != NULL) { 248 sp<MediaSource> source = mTSParser->getSource( 249 audio ? ATSParser::AUDIO : ATSParser::VIDEO); 250 251 return static_cast<AnotherPacketSource *>(source.get()); 252 } 253 254 return audio ? mAudioTrack : mVideoTrack; 255 } 256 257 void NuPlayer::RTSPSource::setEOSTimeout(bool audio, int64_t timeout) { 258 if (audio) { 259 mEOSTimeoutAudio = timeout; 260 } else { 261 mEOSTimeoutVideo = timeout; 262 } 263 } 264 265 status_t NuPlayer::RTSPSource::getDuration(int64_t *durationUs) { 266 *durationUs = 0ll; 267 268 int64_t audioDurationUs; 269 if (mAudioTrack != NULL 270 && mAudioTrack->getFormat()->findInt64( 271 kKeyDuration, &audioDurationUs) 272 && audioDurationUs > *durationUs) { 273 *durationUs = audioDurationUs; 274 } 275 276 int64_t videoDurationUs; 277 if (mVideoTrack != NULL 278 && mVideoTrack->getFormat()->findInt64( 279 kKeyDuration, &videoDurationUs) 280 && videoDurationUs > *durationUs) { 281 *durationUs = videoDurationUs; 282 } 283 284 return OK; 285 } 286 287 status_t NuPlayer::RTSPSource::seekTo(int64_t seekTimeUs) { 288 sp<AMessage> msg = new AMessage(kWhatPerformSeek, this); 289 msg->setInt32("generation", ++mSeekGeneration); 290 msg->setInt64("timeUs", seekTimeUs); 291 292 sp<AMessage> response; 293 status_t err = msg->postAndAwaitResponse(&response); 294 if (err == OK && response != NULL) { 295 CHECK(response->findInt32("err", &err)); 296 } 297 298 return err; 299 } 300 301 void NuPlayer::RTSPSource::performSeek(int64_t seekTimeUs) { 302 if (mState != CONNECTED) { 303 finishSeek(INVALID_OPERATION); 304 return; 305 } 306 307 mState = SEEKING; 308 mHandler->seek(seekTimeUs); 309 } 310 311 void NuPlayer::RTSPSource::schedulePollBuffering() { 312 sp<AMessage> msg = new AMessage(kWhatPollBuffering, this); 313 msg->post(1000000ll); // 1 second intervals 314 } 315 316 void NuPlayer::RTSPSource::checkBuffering( 317 bool *prepared, bool *underflow, bool *overflow, bool *startServer) { 318 size_t numTracks = mTracks.size(); 319 size_t preparedCount, underflowCount, overflowCount, startCount; 320 preparedCount = underflowCount = overflowCount = startCount = 0; 321 for (size_t i = 0; i < numTracks; ++i) { 322 status_t finalResult; 323 TrackInfo *info = &mTracks.editItemAt(i); 324 sp<AnotherPacketSource> src = info->mSource; 325 int64_t bufferedDurationUs = src->getBufferedDurationUs(&finalResult); 326 327 // isFinished when duration is 0 checks for EOS result only 328 if (bufferedDurationUs > kPrepareMarkUs || src->isFinished(/* duration */ 0)) { 329 ++preparedCount; 330 } 331 332 if (src->isFinished(/* duration */ 0)) { 333 ++overflowCount; 334 } else { 335 if (bufferedDurationUs < kUnderflowMarkUs) { 336 ++underflowCount; 337 } 338 if (bufferedDurationUs > kOverflowMarkUs) { 339 ++overflowCount; 340 } 341 if (bufferedDurationUs < kStartServerMarkUs) { 342 ++startCount; 343 } 344 } 345 } 346 347 *prepared = (preparedCount == numTracks); 348 *underflow = (underflowCount > 0); 349 *overflow = (overflowCount == numTracks); 350 *startServer = (startCount > 0); 351 } 352 353 void NuPlayer::RTSPSource::onPollBuffering() { 354 bool prepared, underflow, overflow, startServer; 355 checkBuffering(&prepared, &underflow, &overflow, &startServer); 356 357 if (prepared && mInPreparationPhase) { 358 mInPreparationPhase = false; 359 notifyPrepared(); 360 } 361 362 if (!mInPreparationPhase && underflow) { 363 startBufferingIfNecessary(); 364 } 365 366 if (overflow && mHandler != NULL) { 367 stopBufferingIfNecessary(); 368 mHandler->pause(); 369 } 370 371 if (startServer && mHandler != NULL) { 372 mHandler->resume(); 373 } 374 375 schedulePollBuffering(); 376 } 377 378 void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) { 379 if (msg->what() == kWhatDisconnect) { 380 sp<AReplyToken> replyID; 381 CHECK(msg->senderAwaitsResponse(&replyID)); 382 383 mDisconnectReplyID = replyID; 384 finishDisconnectIfPossible(); 385 return; 386 } else if (msg->what() == kWhatPerformSeek) { 387 int32_t generation; 388 CHECK(msg->findInt32("generation", &generation)); 389 CHECK(msg->senderAwaitsResponse(&mSeekReplyID)); 390 391 if (generation != mSeekGeneration) { 392 // obsolete. 393 finishSeek(OK); 394 return; 395 } 396 397 int64_t seekTimeUs; 398 CHECK(msg->findInt64("timeUs", &seekTimeUs)); 399 400 performSeek(seekTimeUs); 401 return; 402 } else if (msg->what() == kWhatPollBuffering) { 403 onPollBuffering(); 404 return; 405 } 406 407 CHECK_EQ(msg->what(), (int)kWhatNotify); 408 409 int32_t what; 410 CHECK(msg->findInt32("what", &what)); 411 412 switch (what) { 413 case MyHandler::kWhatConnected: 414 { 415 onConnected(); 416 417 notifyVideoSizeChanged(); 418 419 uint32_t flags = 0; 420 421 if (mHandler->isSeekable()) { 422 flags = FLAG_CAN_PAUSE 423 | FLAG_CAN_SEEK 424 | FLAG_CAN_SEEK_BACKWARD 425 | FLAG_CAN_SEEK_FORWARD; 426 } 427 428 notifyFlagsChanged(flags); 429 schedulePollBuffering(); 430 break; 431 } 432 433 case MyHandler::kWhatDisconnected: 434 { 435 onDisconnected(msg); 436 break; 437 } 438 439 case MyHandler::kWhatSeekDone: 440 { 441 mState = CONNECTED; 442 // Unblock seekTo here in case we attempted to seek in a live stream 443 finishSeek(OK); 444 break; 445 } 446 447 case MyHandler::kWhatSeekPaused: 448 { 449 sp<AnotherPacketSource> source = getSource(true /* audio */); 450 if (source != NULL) { 451 source->queueDiscontinuity(ATSParser::DISCONTINUITY_NONE, 452 /* extra */ NULL, 453 /* discard */ true); 454 } 455 source = getSource(false /* video */); 456 if (source != NULL) { 457 source->queueDiscontinuity(ATSParser::DISCONTINUITY_NONE, 458 /* extra */ NULL, 459 /* discard */ true); 460 }; 461 462 status_t err = OK; 463 msg->findInt32("err", &err); 464 465 if (err == OK) { 466 int64_t timeUs; 467 CHECK(msg->findInt64("time", &timeUs)); 468 mHandler->continueSeekAfterPause(timeUs); 469 } else { 470 finishSeek(err); 471 } 472 break; 473 } 474 475 case MyHandler::kWhatAccessUnit: 476 { 477 size_t trackIndex; 478 CHECK(msg->findSize("trackIndex", &trackIndex)); 479 480 if (mTSParser == NULL) { 481 CHECK_LT(trackIndex, mTracks.size()); 482 } else { 483 CHECK_EQ(trackIndex, 0u); 484 } 485 486 sp<ABuffer> accessUnit; 487 CHECK(msg->findBuffer("accessUnit", &accessUnit)); 488 489 int32_t damaged; 490 if (accessUnit->meta()->findInt32("damaged", &damaged) 491 && damaged) { 492 ALOGI("dropping damaged access unit."); 493 break; 494 } 495 496 if (mTSParser != NULL) { 497 size_t offset = 0; 498 status_t err = OK; 499 while (offset + 188 <= accessUnit->size()) { 500 err = mTSParser->feedTSPacket( 501 accessUnit->data() + offset, 188); 502 if (err != OK) { 503 break; 504 } 505 506 offset += 188; 507 } 508 509 if (offset < accessUnit->size()) { 510 err = ERROR_MALFORMED; 511 } 512 513 if (err != OK) { 514 sp<AnotherPacketSource> source = getSource(false /* audio */); 515 if (source != NULL) { 516 source->signalEOS(err); 517 } 518 519 source = getSource(true /* audio */); 520 if (source != NULL) { 521 source->signalEOS(err); 522 } 523 } 524 break; 525 } 526 527 TrackInfo *info = &mTracks.editItemAt(trackIndex); 528 529 sp<AnotherPacketSource> source = info->mSource; 530 if (source != NULL) { 531 uint32_t rtpTime; 532 CHECK(accessUnit->meta()->findInt32("rtp-time", (int32_t *)&rtpTime)); 533 534 if (!info->mNPTMappingValid) { 535 // This is a live stream, we didn't receive any normal 536 // playtime mapping. We won't map to npt time. 537 source->queueAccessUnit(accessUnit); 538 break; 539 } 540 541 int64_t nptUs = 542 ((double)rtpTime - (double)info->mRTPTime) 543 / info->mTimeScale 544 * 1000000ll 545 + info->mNormalPlaytimeUs; 546 547 accessUnit->meta()->setInt64("timeUs", nptUs); 548 549 source->queueAccessUnit(accessUnit); 550 } 551 break; 552 } 553 554 case MyHandler::kWhatEOS: 555 { 556 int32_t finalResult; 557 CHECK(msg->findInt32("finalResult", &finalResult)); 558 CHECK_NE(finalResult, (status_t)OK); 559 560 if (mTSParser != NULL) { 561 sp<AnotherPacketSource> source = getSource(false /* audio */); 562 if (source != NULL) { 563 source->signalEOS(finalResult); 564 } 565 566 source = getSource(true /* audio */); 567 if (source != NULL) { 568 source->signalEOS(finalResult); 569 } 570 571 return; 572 } 573 574 size_t trackIndex; 575 CHECK(msg->findSize("trackIndex", &trackIndex)); 576 CHECK_LT(trackIndex, mTracks.size()); 577 578 TrackInfo *info = &mTracks.editItemAt(trackIndex); 579 sp<AnotherPacketSource> source = info->mSource; 580 if (source != NULL) { 581 source->signalEOS(finalResult); 582 } 583 584 break; 585 } 586 587 case MyHandler::kWhatSeekDiscontinuity: 588 { 589 size_t trackIndex; 590 CHECK(msg->findSize("trackIndex", &trackIndex)); 591 CHECK_LT(trackIndex, mTracks.size()); 592 593 TrackInfo *info = &mTracks.editItemAt(trackIndex); 594 sp<AnotherPacketSource> source = info->mSource; 595 if (source != NULL) { 596 source->queueDiscontinuity( 597 ATSParser::DISCONTINUITY_TIME, 598 NULL, 599 true /* discard */); 600 } 601 602 break; 603 } 604 605 case MyHandler::kWhatNormalPlayTimeMapping: 606 { 607 size_t trackIndex; 608 CHECK(msg->findSize("trackIndex", &trackIndex)); 609 CHECK_LT(trackIndex, mTracks.size()); 610 611 uint32_t rtpTime; 612 CHECK(msg->findInt32("rtpTime", (int32_t *)&rtpTime)); 613 614 int64_t nptUs; 615 CHECK(msg->findInt64("nptUs", &nptUs)); 616 617 TrackInfo *info = &mTracks.editItemAt(trackIndex); 618 info->mRTPTime = rtpTime; 619 info->mNormalPlaytimeUs = nptUs; 620 info->mNPTMappingValid = true; 621 break; 622 } 623 624 case SDPLoader::kWhatSDPLoaded: 625 { 626 onSDPLoaded(msg); 627 break; 628 } 629 630 default: 631 TRESPASS(); 632 } 633 } 634 635 void NuPlayer::RTSPSource::onConnected() { 636 CHECK(mAudioTrack == NULL); 637 CHECK(mVideoTrack == NULL); 638 639 size_t numTracks = mHandler->countTracks(); 640 for (size_t i = 0; i < numTracks; ++i) { 641 int32_t timeScale; 642 sp<MetaData> format = mHandler->getTrackFormat(i, &timeScale); 643 644 const char *mime; 645 CHECK(format->findCString(kKeyMIMEType, &mime)); 646 647 if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) { 648 // Very special case for MPEG2 Transport Streams. 649 CHECK_EQ(numTracks, 1u); 650 651 mTSParser = new ATSParser; 652 return; 653 } 654 655 bool isAudio = !strncasecmp(mime, "audio/", 6); 656 bool isVideo = !strncasecmp(mime, "video/", 6); 657 658 TrackInfo info; 659 info.mTimeScale = timeScale; 660 info.mRTPTime = 0; 661 info.mNormalPlaytimeUs = 0ll; 662 info.mNPTMappingValid = false; 663 664 if ((isAudio && mAudioTrack == NULL) 665 || (isVideo && mVideoTrack == NULL)) { 666 sp<AnotherPacketSource> source = new AnotherPacketSource(format); 667 668 if (isAudio) { 669 mAudioTrack = source; 670 } else { 671 mVideoTrack = source; 672 } 673 674 info.mSource = source; 675 } 676 677 mTracks.push(info); 678 } 679 680 mState = CONNECTED; 681 } 682 683 void NuPlayer::RTSPSource::onSDPLoaded(const sp<AMessage> &msg) { 684 status_t err; 685 CHECK(msg->findInt32("result", &err)); 686 687 mSDPLoader.clear(); 688 689 if (mDisconnectReplyID != 0) { 690 err = UNKNOWN_ERROR; 691 } 692 693 if (err == OK) { 694 sp<ASessionDescription> desc; 695 sp<RefBase> obj; 696 CHECK(msg->findObject("description", &obj)); 697 desc = static_cast<ASessionDescription *>(obj.get()); 698 699 AString rtspUri; 700 if (!desc->findAttribute(0, "a=control", &rtspUri)) { 701 ALOGE("Unable to find url in SDP"); 702 err = UNKNOWN_ERROR; 703 } else { 704 sp<AMessage> notify = new AMessage(kWhatNotify, this); 705 706 mHandler = new MyHandler(rtspUri.c_str(), notify, mUIDValid, mUID); 707 mLooper->registerHandler(mHandler); 708 709 mHandler->loadSDP(desc); 710 } 711 } 712 713 if (err != OK) { 714 if (mState == CONNECTING) { 715 // We're still in the preparation phase, signal that it 716 // failed. 717 notifyPrepared(err); 718 } 719 720 mState = DISCONNECTED; 721 setError(err); 722 723 if (mDisconnectReplyID != 0) { 724 finishDisconnectIfPossible(); 725 } 726 } 727 } 728 729 void NuPlayer::RTSPSource::onDisconnected(const sp<AMessage> &msg) { 730 if (mState == DISCONNECTED) { 731 return; 732 } 733 734 status_t err; 735 CHECK(msg->findInt32("result", &err)); 736 CHECK_NE(err, (status_t)OK); 737 738 mLooper->unregisterHandler(mHandler->id()); 739 mHandler.clear(); 740 741 if (mState == CONNECTING) { 742 // We're still in the preparation phase, signal that it 743 // failed. 744 notifyPrepared(err); 745 } 746 747 mState = DISCONNECTED; 748 setError(err); 749 750 if (mDisconnectReplyID != 0) { 751 finishDisconnectIfPossible(); 752 } 753 } 754 755 void NuPlayer::RTSPSource::finishDisconnectIfPossible() { 756 if (mState != DISCONNECTED) { 757 if (mHandler != NULL) { 758 mHandler->disconnect(); 759 } else if (mSDPLoader != NULL) { 760 mSDPLoader->cancel(); 761 } 762 return; 763 } 764 765 (new AMessage)->postReply(mDisconnectReplyID); 766 mDisconnectReplyID = 0; 767 } 768 769 void NuPlayer::RTSPSource::setError(status_t err) { 770 Mutex::Autolock _l(mBufferingLock); 771 mFinalResult = err; 772 } 773 774 void NuPlayer::RTSPSource::startBufferingIfNecessary() { 775 Mutex::Autolock _l(mBufferingLock); 776 777 if (!mBuffering) { 778 mBuffering = true; 779 780 sp<AMessage> notify = dupNotify(); 781 notify->setInt32("what", kWhatPauseOnBufferingStart); 782 notify->post(); 783 } 784 } 785 786 bool NuPlayer::RTSPSource::stopBufferingIfNecessary() { 787 Mutex::Autolock _l(mBufferingLock); 788 789 if (mBuffering) { 790 if (!haveSufficientDataOnAllTracks()) { 791 return false; 792 } 793 794 mBuffering = false; 795 796 sp<AMessage> notify = dupNotify(); 797 notify->setInt32("what", kWhatResumeOnBufferingEnd); 798 notify->post(); 799 } 800 801 return true; 802 } 803 804 void NuPlayer::RTSPSource::finishSeek(status_t err) { 805 if (mSeekReplyID == NULL) { 806 return; 807 } 808 sp<AMessage> seekReply = new AMessage; 809 seekReply->setInt32("err", err); 810 seekReply->postReply(mSeekReplyID); 811 mSeekReplyID = NULL; 812 } 813 814 } // namespace android 815