RTSPSource.cpp revision 91ea571836401ba77854d7b3aefb3ccee9c20eb3
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 31namespace android { 32 33const int64_t kNearEOSTimeoutUs = 2000000ll; // 2 secs 34 35// Buffer Underflow/Prepare/StartServer/Overflow Marks 36const int64_t NuPlayer::RTSPSource::kUnderflowMarkUs = 1000000ll; 37const int64_t NuPlayer::RTSPSource::kPrepareMarkUs = 3000000ll; 38const int64_t NuPlayer::RTSPSource::kStartServerMarkUs = 5000000ll; 39const int64_t NuPlayer::RTSPSource::kOverflowMarkUs = 10000000ll; 40 41NuPlayer::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 78NuPlayer::RTSPSource::~RTSPSource() { 79 if (mLooper != NULL) { 80 mLooper->unregisterHandler(id()); 81 mLooper->stop(); 82 } 83} 84 85void 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 124void NuPlayer::RTSPSource::start() { 125} 126 127void 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 137status_t NuPlayer::RTSPSource::feedMoreTSData() { 138 Mutex::Autolock _l(mBufferingLock); 139 return mFinalResult; 140} 141 142sp<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 152bool 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 188status_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 246sp<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 257void NuPlayer::RTSPSource::setEOSTimeout(bool audio, int64_t timeout) { 258 if (audio) { 259 mEOSTimeoutAudio = timeout; 260 } else { 261 mEOSTimeoutVideo = timeout; 262 } 263} 264 265status_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 287status_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 301void 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 311void NuPlayer::RTSPSource::schedulePollBuffering() { 312 sp<AMessage> msg = new AMessage(kWhatPollBuffering, this); 313 msg->post(1000000ll); // 1 second intervals 314} 315 316void 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 322 size_t count = numTracks; 323 for (size_t i = 0; i < count; ++i) { 324 status_t finalResult; 325 TrackInfo *info = &mTracks.editItemAt(i); 326 sp<AnotherPacketSource> src = info->mSource; 327 if (src == NULL) { 328 --numTracks; 329 continue; 330 } 331 int64_t bufferedDurationUs = src->getBufferedDurationUs(&finalResult); 332 333 // isFinished when duration is 0 checks for EOS result only 334 if (bufferedDurationUs > kPrepareMarkUs || src->isFinished(/* duration */ 0)) { 335 ++preparedCount; 336 } 337 338 if (src->isFinished(/* duration */ 0)) { 339 ++overflowCount; 340 } else { 341 if (bufferedDurationUs < kUnderflowMarkUs) { 342 ++underflowCount; 343 } 344 if (bufferedDurationUs > kOverflowMarkUs) { 345 ++overflowCount; 346 } 347 if (bufferedDurationUs < kStartServerMarkUs) { 348 ++startCount; 349 } 350 } 351 } 352 353 *prepared = (preparedCount == numTracks); 354 *underflow = (underflowCount > 0); 355 *overflow = (overflowCount == numTracks); 356 *startServer = (startCount > 0); 357} 358 359void NuPlayer::RTSPSource::onPollBuffering() { 360 bool prepared, underflow, overflow, startServer; 361 checkBuffering(&prepared, &underflow, &overflow, &startServer); 362 363 if (prepared && mInPreparationPhase) { 364 mInPreparationPhase = false; 365 notifyPrepared(); 366 } 367 368 if (!mInPreparationPhase && underflow) { 369 startBufferingIfNecessary(); 370 } 371 372 if (haveSufficientDataOnAllTracks()) { 373 stopBufferingIfNecessary(); 374 } 375 376 if (overflow && mHandler != NULL) { 377 mHandler->pause(); 378 } 379 380 if (startServer && mHandler != NULL) { 381 mHandler->resume(); 382 } 383 384 schedulePollBuffering(); 385} 386 387void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) { 388 if (msg->what() == kWhatDisconnect) { 389 sp<AReplyToken> replyID; 390 CHECK(msg->senderAwaitsResponse(&replyID)); 391 392 mDisconnectReplyID = replyID; 393 finishDisconnectIfPossible(); 394 return; 395 } else if (msg->what() == kWhatPerformSeek) { 396 int32_t generation; 397 CHECK(msg->findInt32("generation", &generation)); 398 CHECK(msg->senderAwaitsResponse(&mSeekReplyID)); 399 400 if (generation != mSeekGeneration) { 401 // obsolete. 402 finishSeek(OK); 403 return; 404 } 405 406 int64_t seekTimeUs; 407 CHECK(msg->findInt64("timeUs", &seekTimeUs)); 408 409 performSeek(seekTimeUs); 410 return; 411 } else if (msg->what() == kWhatPollBuffering) { 412 onPollBuffering(); 413 return; 414 } 415 416 CHECK_EQ(msg->what(), (int)kWhatNotify); 417 418 int32_t what; 419 CHECK(msg->findInt32("what", &what)); 420 421 switch (what) { 422 case MyHandler::kWhatConnected: 423 { 424 onConnected(); 425 426 notifyVideoSizeChanged(); 427 428 uint32_t flags = 0; 429 430 if (mHandler->isSeekable()) { 431 flags = FLAG_CAN_PAUSE 432 | FLAG_CAN_SEEK 433 | FLAG_CAN_SEEK_BACKWARD 434 | FLAG_CAN_SEEK_FORWARD; 435 } 436 437 notifyFlagsChanged(flags); 438 schedulePollBuffering(); 439 break; 440 } 441 442 case MyHandler::kWhatDisconnected: 443 { 444 onDisconnected(msg); 445 break; 446 } 447 448 case MyHandler::kWhatSeekDone: 449 { 450 mState = CONNECTED; 451 // Unblock seekTo here in case we attempted to seek in a live stream 452 finishSeek(OK); 453 break; 454 } 455 456 case MyHandler::kWhatSeekPaused: 457 { 458 sp<AnotherPacketSource> source = getSource(true /* audio */); 459 if (source != NULL) { 460 source->queueDiscontinuity(ATSParser::DISCONTINUITY_NONE, 461 /* extra */ NULL, 462 /* discard */ true); 463 } 464 source = getSource(false /* video */); 465 if (source != NULL) { 466 source->queueDiscontinuity(ATSParser::DISCONTINUITY_NONE, 467 /* extra */ NULL, 468 /* discard */ true); 469 }; 470 471 status_t err = OK; 472 msg->findInt32("err", &err); 473 474 if (err == OK) { 475 int64_t timeUs; 476 CHECK(msg->findInt64("time", &timeUs)); 477 mHandler->continueSeekAfterPause(timeUs); 478 } else { 479 finishSeek(err); 480 } 481 break; 482 } 483 484 case MyHandler::kWhatAccessUnit: 485 { 486 size_t trackIndex; 487 CHECK(msg->findSize("trackIndex", &trackIndex)); 488 489 if (mTSParser == NULL) { 490 CHECK_LT(trackIndex, mTracks.size()); 491 } else { 492 CHECK_EQ(trackIndex, 0u); 493 } 494 495 sp<ABuffer> accessUnit; 496 CHECK(msg->findBuffer("accessUnit", &accessUnit)); 497 498 int32_t damaged; 499 if (accessUnit->meta()->findInt32("damaged", &damaged) 500 && damaged) { 501 ALOGI("dropping damaged access unit."); 502 break; 503 } 504 505 if (mTSParser != NULL) { 506 size_t offset = 0; 507 status_t err = OK; 508 while (offset + 188 <= accessUnit->size()) { 509 err = mTSParser->feedTSPacket( 510 accessUnit->data() + offset, 188); 511 if (err != OK) { 512 break; 513 } 514 515 offset += 188; 516 } 517 518 if (offset < accessUnit->size()) { 519 err = ERROR_MALFORMED; 520 } 521 522 if (err != OK) { 523 sp<AnotherPacketSource> source = getSource(false /* audio */); 524 if (source != NULL) { 525 source->signalEOS(err); 526 } 527 528 source = getSource(true /* audio */); 529 if (source != NULL) { 530 source->signalEOS(err); 531 } 532 } 533 break; 534 } 535 536 TrackInfo *info = &mTracks.editItemAt(trackIndex); 537 538 sp<AnotherPacketSource> source = info->mSource; 539 if (source != NULL) { 540 uint32_t rtpTime; 541 CHECK(accessUnit->meta()->findInt32("rtp-time", (int32_t *)&rtpTime)); 542 543 if (!info->mNPTMappingValid) { 544 // This is a live stream, we didn't receive any normal 545 // playtime mapping. We won't map to npt time. 546 source->queueAccessUnit(accessUnit); 547 break; 548 } 549 550 int64_t nptUs = 551 ((double)rtpTime - (double)info->mRTPTime) 552 / info->mTimeScale 553 * 1000000ll 554 + info->mNormalPlaytimeUs; 555 556 accessUnit->meta()->setInt64("timeUs", nptUs); 557 558 source->queueAccessUnit(accessUnit); 559 } 560 break; 561 } 562 563 case MyHandler::kWhatEOS: 564 { 565 int32_t finalResult; 566 CHECK(msg->findInt32("finalResult", &finalResult)); 567 CHECK_NE(finalResult, (status_t)OK); 568 569 if (mTSParser != NULL) { 570 sp<AnotherPacketSource> source = getSource(false /* audio */); 571 if (source != NULL) { 572 source->signalEOS(finalResult); 573 } 574 575 source = getSource(true /* audio */); 576 if (source != NULL) { 577 source->signalEOS(finalResult); 578 } 579 580 return; 581 } 582 583 size_t trackIndex; 584 CHECK(msg->findSize("trackIndex", &trackIndex)); 585 CHECK_LT(trackIndex, mTracks.size()); 586 587 TrackInfo *info = &mTracks.editItemAt(trackIndex); 588 sp<AnotherPacketSource> source = info->mSource; 589 if (source != NULL) { 590 source->signalEOS(finalResult); 591 } 592 593 break; 594 } 595 596 case MyHandler::kWhatSeekDiscontinuity: 597 { 598 size_t trackIndex; 599 CHECK(msg->findSize("trackIndex", &trackIndex)); 600 CHECK_LT(trackIndex, mTracks.size()); 601 602 TrackInfo *info = &mTracks.editItemAt(trackIndex); 603 sp<AnotherPacketSource> source = info->mSource; 604 if (source != NULL) { 605 source->queueDiscontinuity( 606 ATSParser::DISCONTINUITY_TIME, 607 NULL, 608 true /* discard */); 609 } 610 611 break; 612 } 613 614 case MyHandler::kWhatNormalPlayTimeMapping: 615 { 616 size_t trackIndex; 617 CHECK(msg->findSize("trackIndex", &trackIndex)); 618 CHECK_LT(trackIndex, mTracks.size()); 619 620 uint32_t rtpTime; 621 CHECK(msg->findInt32("rtpTime", (int32_t *)&rtpTime)); 622 623 int64_t nptUs; 624 CHECK(msg->findInt64("nptUs", &nptUs)); 625 626 TrackInfo *info = &mTracks.editItemAt(trackIndex); 627 info->mRTPTime = rtpTime; 628 info->mNormalPlaytimeUs = nptUs; 629 info->mNPTMappingValid = true; 630 break; 631 } 632 633 case SDPLoader::kWhatSDPLoaded: 634 { 635 onSDPLoaded(msg); 636 break; 637 } 638 639 default: 640 TRESPASS(); 641 } 642} 643 644void NuPlayer::RTSPSource::onConnected() { 645 CHECK(mAudioTrack == NULL); 646 CHECK(mVideoTrack == NULL); 647 648 size_t numTracks = mHandler->countTracks(); 649 for (size_t i = 0; i < numTracks; ++i) { 650 int32_t timeScale; 651 sp<MetaData> format = mHandler->getTrackFormat(i, &timeScale); 652 653 const char *mime; 654 CHECK(format->findCString(kKeyMIMEType, &mime)); 655 656 if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) { 657 // Very special case for MPEG2 Transport Streams. 658 CHECK_EQ(numTracks, 1u); 659 660 mTSParser = new ATSParser; 661 return; 662 } 663 664 bool isAudio = !strncasecmp(mime, "audio/", 6); 665 bool isVideo = !strncasecmp(mime, "video/", 6); 666 667 TrackInfo info; 668 info.mTimeScale = timeScale; 669 info.mRTPTime = 0; 670 info.mNormalPlaytimeUs = 0ll; 671 info.mNPTMappingValid = false; 672 673 if ((isAudio && mAudioTrack == NULL) 674 || (isVideo && mVideoTrack == NULL)) { 675 sp<AnotherPacketSource> source = new AnotherPacketSource(format); 676 677 if (isAudio) { 678 mAudioTrack = source; 679 } else { 680 mVideoTrack = source; 681 } 682 683 info.mSource = source; 684 } 685 686 mTracks.push(info); 687 } 688 689 mState = CONNECTED; 690} 691 692void NuPlayer::RTSPSource::onSDPLoaded(const sp<AMessage> &msg) { 693 status_t err; 694 CHECK(msg->findInt32("result", &err)); 695 696 mSDPLoader.clear(); 697 698 if (mDisconnectReplyID != 0) { 699 err = UNKNOWN_ERROR; 700 } 701 702 if (err == OK) { 703 sp<ASessionDescription> desc; 704 sp<RefBase> obj; 705 CHECK(msg->findObject("description", &obj)); 706 desc = static_cast<ASessionDescription *>(obj.get()); 707 708 AString rtspUri; 709 if (!desc->findAttribute(0, "a=control", &rtspUri)) { 710 ALOGE("Unable to find url in SDP"); 711 err = UNKNOWN_ERROR; 712 } else { 713 sp<AMessage> notify = new AMessage(kWhatNotify, this); 714 715 mHandler = new MyHandler(rtspUri.c_str(), notify, mUIDValid, mUID); 716 mLooper->registerHandler(mHandler); 717 718 mHandler->loadSDP(desc); 719 } 720 } 721 722 if (err != OK) { 723 if (mState == CONNECTING) { 724 // We're still in the preparation phase, signal that it 725 // failed. 726 notifyPrepared(err); 727 } 728 729 mState = DISCONNECTED; 730 setError(err); 731 732 if (mDisconnectReplyID != 0) { 733 finishDisconnectIfPossible(); 734 } 735 } 736} 737 738void NuPlayer::RTSPSource::onDisconnected(const sp<AMessage> &msg) { 739 if (mState == DISCONNECTED) { 740 return; 741 } 742 743 status_t err; 744 CHECK(msg->findInt32("result", &err)); 745 CHECK_NE(err, (status_t)OK); 746 747 mLooper->unregisterHandler(mHandler->id()); 748 mHandler.clear(); 749 750 if (mState == CONNECTING) { 751 // We're still in the preparation phase, signal that it 752 // failed. 753 notifyPrepared(err); 754 } 755 756 mState = DISCONNECTED; 757 setError(err); 758 759 if (mDisconnectReplyID != 0) { 760 finishDisconnectIfPossible(); 761 } 762} 763 764void NuPlayer::RTSPSource::finishDisconnectIfPossible() { 765 if (mState != DISCONNECTED) { 766 if (mHandler != NULL) { 767 mHandler->disconnect(); 768 } else if (mSDPLoader != NULL) { 769 mSDPLoader->cancel(); 770 } 771 return; 772 } 773 774 (new AMessage)->postReply(mDisconnectReplyID); 775 mDisconnectReplyID = 0; 776} 777 778void NuPlayer::RTSPSource::setError(status_t err) { 779 Mutex::Autolock _l(mBufferingLock); 780 mFinalResult = err; 781} 782 783void NuPlayer::RTSPSource::startBufferingIfNecessary() { 784 Mutex::Autolock _l(mBufferingLock); 785 786 if (!mBuffering) { 787 mBuffering = true; 788 789 sp<AMessage> notify = dupNotify(); 790 notify->setInt32("what", kWhatPauseOnBufferingStart); 791 notify->post(); 792 } 793} 794 795bool NuPlayer::RTSPSource::stopBufferingIfNecessary() { 796 Mutex::Autolock _l(mBufferingLock); 797 798 if (mBuffering) { 799 if (!haveSufficientDataOnAllTracks()) { 800 return false; 801 } 802 803 mBuffering = false; 804 805 sp<AMessage> notify = dupNotify(); 806 notify->setInt32("what", kWhatResumeOnBufferingEnd); 807 notify->post(); 808 } 809 810 return true; 811} 812 813void NuPlayer::RTSPSource::finishSeek(status_t err) { 814 if (mSeekReplyID == NULL) { 815 return; 816 } 817 sp<AMessage> seekReply = new AMessage; 818 seekReply->setInt32("err", err); 819 seekReply->postReply(mSeekReplyID); 820 mSeekReplyID = NULL; 821} 822 823} // namespace android 824