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