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