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 362 | FLAG_CAN_SEEK 363 | FLAG_CAN_SEEK_BACKWARD 364 | FLAG_CAN_SEEK_FORWARD; 365 } 366 367 notifyFlagsChanged(flags); 368 notifyPrepared(); 369 break; 370 } 371 372 case MyHandler::kWhatDisconnected: 373 { 374 onDisconnected(msg); 375 break; 376 } 377 378 case MyHandler::kWhatSeekDone: 379 { 380 mState = CONNECTED; 381 break; 382 } 383 384 case MyHandler::kWhatAccessUnit: 385 { 386 size_t trackIndex; 387 CHECK(msg->findSize("trackIndex", &trackIndex)); 388 389 if (mTSParser == NULL) { 390 CHECK_LT(trackIndex, mTracks.size()); 391 } else { 392 CHECK_EQ(trackIndex, 0u); 393 } 394 395 sp<ABuffer> accessUnit; 396 CHECK(msg->findBuffer("accessUnit", &accessUnit)); 397 398 int32_t damaged; 399 if (accessUnit->meta()->findInt32("damaged", &damaged) 400 && damaged) { 401 ALOGI("dropping damaged access unit."); 402 break; 403 } 404 405 if (mTSParser != NULL) { 406 size_t offset = 0; 407 status_t err = OK; 408 while (offset + 188 <= accessUnit->size()) { 409 err = mTSParser->feedTSPacket( 410 accessUnit->data() + offset, 188); 411 if (err != OK) { 412 break; 413 } 414 415 offset += 188; 416 } 417 418 if (offset < accessUnit->size()) { 419 err = ERROR_MALFORMED; 420 } 421 422 if (err != OK) { 423 sp<AnotherPacketSource> source = getSource(false /* audio */); 424 if (source != NULL) { 425 source->signalEOS(err); 426 } 427 428 source = getSource(true /* audio */); 429 if (source != NULL) { 430 source->signalEOS(err); 431 } 432 } 433 break; 434 } 435 436 TrackInfo *info = &mTracks.editItemAt(trackIndex); 437 438 sp<AnotherPacketSource> source = info->mSource; 439 if (source != NULL) { 440 uint32_t rtpTime; 441 CHECK(accessUnit->meta()->findInt32("rtp-time", (int32_t *)&rtpTime)); 442 443 if (!info->mNPTMappingValid) { 444 // This is a live stream, we didn't receive any normal 445 // playtime mapping. We won't map to npt time. 446 source->queueAccessUnit(accessUnit); 447 break; 448 } 449 450 int64_t nptUs = 451 ((double)rtpTime - (double)info->mRTPTime) 452 / info->mTimeScale 453 * 1000000ll 454 + info->mNormalPlaytimeUs; 455 456 accessUnit->meta()->setInt64("timeUs", nptUs); 457 458 source->queueAccessUnit(accessUnit); 459 } 460 break; 461 } 462 463 case MyHandler::kWhatEOS: 464 { 465 int32_t finalResult; 466 CHECK(msg->findInt32("finalResult", &finalResult)); 467 CHECK_NE(finalResult, (status_t)OK); 468 469 if (mTSParser != NULL) { 470 sp<AnotherPacketSource> source = getSource(false /* audio */); 471 if (source != NULL) { 472 source->signalEOS(finalResult); 473 } 474 475 source = getSource(true /* audio */); 476 if (source != NULL) { 477 source->signalEOS(finalResult); 478 } 479 480 return; 481 } 482 483 size_t trackIndex; 484 CHECK(msg->findSize("trackIndex", &trackIndex)); 485 CHECK_LT(trackIndex, mTracks.size()); 486 487 TrackInfo *info = &mTracks.editItemAt(trackIndex); 488 sp<AnotherPacketSource> source = info->mSource; 489 if (source != NULL) { 490 source->signalEOS(finalResult); 491 } 492 493 break; 494 } 495 496 case MyHandler::kWhatSeekDiscontinuity: 497 { 498 size_t trackIndex; 499 CHECK(msg->findSize("trackIndex", &trackIndex)); 500 CHECK_LT(trackIndex, mTracks.size()); 501 502 TrackInfo *info = &mTracks.editItemAt(trackIndex); 503 sp<AnotherPacketSource> source = info->mSource; 504 if (source != NULL) { 505 source->queueDiscontinuity(ATSParser::DISCONTINUITY_SEEK, NULL); 506 } 507 508 break; 509 } 510 511 case MyHandler::kWhatNormalPlayTimeMapping: 512 { 513 size_t trackIndex; 514 CHECK(msg->findSize("trackIndex", &trackIndex)); 515 CHECK_LT(trackIndex, mTracks.size()); 516 517 uint32_t rtpTime; 518 CHECK(msg->findInt32("rtpTime", (int32_t *)&rtpTime)); 519 520 int64_t nptUs; 521 CHECK(msg->findInt64("nptUs", &nptUs)); 522 523 TrackInfo *info = &mTracks.editItemAt(trackIndex); 524 info->mRTPTime = rtpTime; 525 info->mNormalPlaytimeUs = nptUs; 526 info->mNPTMappingValid = true; 527 break; 528 } 529 530 case SDPLoader::kWhatSDPLoaded: 531 { 532 onSDPLoaded(msg); 533 break; 534 } 535 536 default: 537 TRESPASS(); 538 } 539 } 540 541 void NuPlayer::RTSPSource::onConnected() { 542 CHECK(mAudioTrack == NULL); 543 CHECK(mVideoTrack == NULL); 544 545 size_t numTracks = mHandler->countTracks(); 546 for (size_t i = 0; i < numTracks; ++i) { 547 int32_t timeScale; 548 sp<MetaData> format = mHandler->getTrackFormat(i, &timeScale); 549 550 const char *mime; 551 CHECK(format->findCString(kKeyMIMEType, &mime)); 552 553 if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) { 554 // Very special case for MPEG2 Transport Streams. 555 CHECK_EQ(numTracks, 1u); 556 557 mTSParser = new ATSParser; 558 return; 559 } 560 561 bool isAudio = !strncasecmp(mime, "audio/", 6); 562 bool isVideo = !strncasecmp(mime, "video/", 6); 563 564 TrackInfo info; 565 info.mTimeScale = timeScale; 566 info.mRTPTime = 0; 567 info.mNormalPlaytimeUs = 0ll; 568 info.mNPTMappingValid = false; 569 570 if ((isAudio && mAudioTrack == NULL) 571 || (isVideo && mVideoTrack == NULL)) { 572 sp<AnotherPacketSource> source = new AnotherPacketSource(format); 573 574 if (isAudio) { 575 mAudioTrack = source; 576 } else { 577 mVideoTrack = source; 578 } 579 580 info.mSource = source; 581 } 582 583 mTracks.push(info); 584 } 585 586 mState = CONNECTED; 587 } 588 589 void NuPlayer::RTSPSource::onSDPLoaded(const sp<AMessage> &msg) { 590 status_t err; 591 CHECK(msg->findInt32("result", &err)); 592 593 mSDPLoader.clear(); 594 595 if (mDisconnectReplyID != 0) { 596 err = UNKNOWN_ERROR; 597 } 598 599 if (err == OK) { 600 sp<ASessionDescription> desc; 601 sp<RefBase> obj; 602 CHECK(msg->findObject("description", &obj)); 603 desc = static_cast<ASessionDescription *>(obj.get()); 604 605 AString rtspUri; 606 if (!desc->findAttribute(0, "a=control", &rtspUri)) { 607 ALOGE("Unable to find url in SDP"); 608 err = UNKNOWN_ERROR; 609 } else { 610 sp<AMessage> notify = new AMessage(kWhatNotify, mReflector->id()); 611 612 mHandler = new MyHandler(rtspUri.c_str(), notify, mUIDValid, mUID); 613 mLooper->registerHandler(mHandler); 614 615 mHandler->loadSDP(desc); 616 } 617 } 618 619 if (err != OK) { 620 if (mState == CONNECTING) { 621 // We're still in the preparation phase, signal that it 622 // failed. 623 notifyPrepared(err); 624 } 625 626 mState = DISCONNECTED; 627 mFinalResult = err; 628 629 if (mDisconnectReplyID != 0) { 630 finishDisconnectIfPossible(); 631 } 632 } 633 } 634 635 void NuPlayer::RTSPSource::onDisconnected(const sp<AMessage> &msg) { 636 if (mState == DISCONNECTED) { 637 return; 638 } 639 640 status_t err; 641 CHECK(msg->findInt32("result", &err)); 642 CHECK_NE(err, (status_t)OK); 643 644 mLooper->unregisterHandler(mHandler->id()); 645 mHandler.clear(); 646 647 if (mState == CONNECTING) { 648 // We're still in the preparation phase, signal that it 649 // failed. 650 notifyPrepared(err); 651 } 652 653 mState = DISCONNECTED; 654 mFinalResult = err; 655 656 if (mDisconnectReplyID != 0) { 657 finishDisconnectIfPossible(); 658 } 659 } 660 661 void NuPlayer::RTSPSource::finishDisconnectIfPossible() { 662 if (mState != DISCONNECTED) { 663 if (mHandler != NULL) { 664 mHandler->disconnect(); 665 } else if (mSDPLoader != NULL) { 666 mSDPLoader->cancel(); 667 } 668 return; 669 } 670 671 (new AMessage)->postReply(mDisconnectReplyID); 672 mDisconnectReplyID = 0; 673 } 674 675 } // namespace android 676