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