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