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