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