RTSPSource.cpp revision bfd4d0d9fe0033abf3f55b94f30f6a58846a875e
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 26#include <media/stagefright/MetaData.h> 27 28namespace android { 29 30NuPlayer::RTSPSource::RTSPSource( 31 const char *url, 32 const KeyedVector<String8, String8> *headers, 33 bool uidValid, 34 uid_t uid) 35 : mURL(url), 36 mUIDValid(uidValid), 37 mUID(uid), 38 mFlags(0), 39 mState(DISCONNECTED), 40 mFinalResult(OK), 41 mDisconnectReplyID(0), 42 mStartingUp(true), 43 mSeekGeneration(0) { 44 if (headers) { 45 mExtraHeaders = *headers; 46 47 ssize_t index = 48 mExtraHeaders.indexOfKey(String8("x-hide-urls-from-log")); 49 50 if (index >= 0) { 51 mFlags |= kFlagIncognito; 52 53 mExtraHeaders.removeItemsAt(index); 54 } 55 } 56} 57 58NuPlayer::RTSPSource::~RTSPSource() { 59 if (mLooper != NULL) { 60 mLooper->stop(); 61 } 62} 63 64void NuPlayer::RTSPSource::start() { 65 if (mLooper == NULL) { 66 mLooper = new ALooper; 67 mLooper->setName("rtsp"); 68 mLooper->start(); 69 70 mReflector = new AHandlerReflector<RTSPSource>(this); 71 mLooper->registerHandler(mReflector); 72 } 73 74 CHECK(mHandler == NULL); 75 76 sp<AMessage> notify = new AMessage(kWhatNotify, mReflector->id()); 77 78 mHandler = new MyHandler(mURL.c_str(), notify, mUIDValid, mUID); 79 mLooper->registerHandler(mHandler); 80 81 CHECK_EQ(mState, (int)DISCONNECTED); 82 mState = CONNECTING; 83 84 mHandler->connect(); 85} 86 87void NuPlayer::RTSPSource::stop() { 88 sp<AMessage> msg = new AMessage(kWhatDisconnect, mReflector->id()); 89 90 sp<AMessage> dummy; 91 msg->postAndAwaitResponse(&dummy); 92} 93 94status_t NuPlayer::RTSPSource::feedMoreTSData() { 95 return mFinalResult; 96} 97 98sp<MetaData> NuPlayer::RTSPSource::getFormat(bool audio) { 99 sp<AnotherPacketSource> source = getSource(audio); 100 101 if (source == NULL) { 102 return NULL; 103 } 104 105 return source->getFormat(); 106} 107 108bool NuPlayer::RTSPSource::haveSufficientDataOnAllTracks() { 109 // We're going to buffer at least 2 secs worth data on all tracks before 110 // starting playback (both at startup and after a seek). 111 112 static const int64_t kMinDurationUs = 2000000ll; 113 114 status_t err; 115 int64_t durationUs; 116 if (mAudioTrack != NULL 117 && (durationUs = mAudioTrack->getBufferedDurationUs(&err)) 118 < kMinDurationUs 119 && err == OK) { 120 ALOGV("audio track doesn't have enough data yet. (%.2f secs buffered)", 121 durationUs / 1E6); 122 return false; 123 } 124 125 if (mVideoTrack != NULL 126 && (durationUs = mVideoTrack->getBufferedDurationUs(&err)) 127 < kMinDurationUs 128 && err == OK) { 129 ALOGV("video track doesn't have enough data yet. (%.2f secs buffered)", 130 durationUs / 1E6); 131 return false; 132 } 133 134 return true; 135} 136 137status_t NuPlayer::RTSPSource::dequeueAccessUnit( 138 bool audio, sp<ABuffer> *accessUnit) { 139 if (mStartingUp) { 140 if (!haveSufficientDataOnAllTracks()) { 141 return -EWOULDBLOCK; 142 } 143 144 mStartingUp = false; 145 } 146 147 sp<AnotherPacketSource> source = getSource(audio); 148 149 if (source == NULL) { 150 return -EWOULDBLOCK; 151 } 152 153 status_t finalResult; 154 if (!source->hasBufferAvailable(&finalResult)) { 155 return finalResult == OK ? -EWOULDBLOCK : finalResult; 156 } 157 158 return source->dequeueAccessUnit(accessUnit); 159} 160 161sp<AnotherPacketSource> NuPlayer::RTSPSource::getSource(bool audio) { 162 return audio ? mAudioTrack : mVideoTrack; 163} 164 165status_t NuPlayer::RTSPSource::getDuration(int64_t *durationUs) { 166 *durationUs = 0ll; 167 168 int64_t audioDurationUs; 169 if (mAudioTrack != NULL 170 && mAudioTrack->getFormat()->findInt64( 171 kKeyDuration, &audioDurationUs) 172 && audioDurationUs > *durationUs) { 173 *durationUs = audioDurationUs; 174 } 175 176 int64_t videoDurationUs; 177 if (mVideoTrack != NULL 178 && mVideoTrack->getFormat()->findInt64( 179 kKeyDuration, &videoDurationUs) 180 && videoDurationUs > *durationUs) { 181 *durationUs = videoDurationUs; 182 } 183 184 return OK; 185} 186 187status_t NuPlayer::RTSPSource::seekTo(int64_t seekTimeUs) { 188 sp<AMessage> msg = new AMessage(kWhatPerformSeek, mReflector->id()); 189 msg->setInt32("generation", ++mSeekGeneration); 190 msg->setInt64("timeUs", seekTimeUs); 191 msg->post(200000ll); 192 193 return OK; 194} 195 196void NuPlayer::RTSPSource::performSeek(int64_t seekTimeUs) { 197 if (mState != CONNECTED) { 198 return; 199 } 200 201 mState = SEEKING; 202 mHandler->seek(seekTimeUs); 203} 204 205bool NuPlayer::RTSPSource::isSeekable() { 206 return true; 207} 208 209void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) { 210 if (msg->what() == kWhatDisconnect) { 211 uint32_t replyID; 212 CHECK(msg->senderAwaitsResponse(&replyID)); 213 214 mDisconnectReplyID = replyID; 215 finishDisconnectIfPossible(); 216 return; 217 } else if (msg->what() == kWhatPerformSeek) { 218 int32_t generation; 219 CHECK(msg->findInt32("generation", &generation)); 220 221 if (generation != mSeekGeneration) { 222 // obsolete. 223 return; 224 } 225 226 int64_t seekTimeUs; 227 CHECK(msg->findInt64("timeUs", &seekTimeUs)); 228 229 performSeek(seekTimeUs); 230 return; 231 } 232 233 CHECK_EQ(msg->what(), (int)kWhatNotify); 234 235 int32_t what; 236 CHECK(msg->findInt32("what", &what)); 237 238 switch (what) { 239 case MyHandler::kWhatConnected: 240 onConnected(); 241 break; 242 243 case MyHandler::kWhatDisconnected: 244 onDisconnected(msg); 245 break; 246 247 case MyHandler::kWhatSeekDone: 248 { 249 mState = CONNECTED; 250 mStartingUp = true; 251 break; 252 } 253 254 case MyHandler::kWhatAccessUnit: 255 { 256 size_t trackIndex; 257 CHECK(msg->findSize("trackIndex", &trackIndex)); 258 CHECK_LT(trackIndex, mTracks.size()); 259 260 sp<ABuffer> accessUnit; 261 CHECK(msg->findBuffer("accessUnit", &accessUnit)); 262 263 int32_t damaged; 264 if (accessUnit->meta()->findInt32("damaged", &damaged) 265 && damaged) { 266 ALOGI("dropping damaged access unit."); 267 break; 268 } 269 270 TrackInfo *info = &mTracks.editItemAt(trackIndex); 271 272 sp<AnotherPacketSource> source = info->mSource; 273 if (source != NULL) { 274 uint32_t rtpTime; 275 CHECK(accessUnit->meta()->findInt32("rtp-time", (int32_t *)&rtpTime)); 276 277 if (!info->mNPTMappingValid) { 278 // This is a live stream, we didn't receive any normal 279 // playtime mapping. Assume the first packets correspond 280 // to time 0. 281 282 ALOGV("This is a live stream, assuming time = 0"); 283 284 info->mRTPTime = rtpTime; 285 info->mNormalPlaytimeUs = 0ll; 286 info->mNPTMappingValid = true; 287 } 288 289 int64_t nptUs = 290 ((double)rtpTime - (double)info->mRTPTime) 291 / info->mTimeScale 292 * 1000000ll 293 + info->mNormalPlaytimeUs; 294 295 accessUnit->meta()->setInt64("timeUs", nptUs); 296 297 source->queueAccessUnit(accessUnit); 298 } 299 break; 300 } 301 302 case MyHandler::kWhatEOS: 303 { 304 size_t trackIndex; 305 CHECK(msg->findSize("trackIndex", &trackIndex)); 306 CHECK_LT(trackIndex, mTracks.size()); 307 308 int32_t finalResult; 309 CHECK(msg->findInt32("finalResult", &finalResult)); 310 CHECK_NE(finalResult, (status_t)OK); 311 312 TrackInfo *info = &mTracks.editItemAt(trackIndex); 313 sp<AnotherPacketSource> source = info->mSource; 314 if (source != NULL) { 315 source->signalEOS(finalResult); 316 } 317 318 break; 319 } 320 321 case MyHandler::kWhatSeekDiscontinuity: 322 { 323 size_t trackIndex; 324 CHECK(msg->findSize("trackIndex", &trackIndex)); 325 CHECK_LT(trackIndex, mTracks.size()); 326 327 TrackInfo *info = &mTracks.editItemAt(trackIndex); 328 sp<AnotherPacketSource> source = info->mSource; 329 if (source != NULL) { 330 source->queueDiscontinuity(ATSParser::DISCONTINUITY_SEEK, NULL); 331 } 332 333 break; 334 } 335 336 case MyHandler::kWhatNormalPlayTimeMapping: 337 { 338 size_t trackIndex; 339 CHECK(msg->findSize("trackIndex", &trackIndex)); 340 CHECK_LT(trackIndex, mTracks.size()); 341 342 uint32_t rtpTime; 343 CHECK(msg->findInt32("rtpTime", (int32_t *)&rtpTime)); 344 345 int64_t nptUs; 346 CHECK(msg->findInt64("nptUs", &nptUs)); 347 348 TrackInfo *info = &mTracks.editItemAt(trackIndex); 349 info->mRTPTime = rtpTime; 350 info->mNormalPlaytimeUs = nptUs; 351 info->mNPTMappingValid = true; 352 break; 353 } 354 355 default: 356 TRESPASS(); 357 } 358} 359 360void NuPlayer::RTSPSource::onConnected() { 361 CHECK(mAudioTrack == NULL); 362 CHECK(mVideoTrack == NULL); 363 364 size_t numTracks = mHandler->countTracks(); 365 for (size_t i = 0; i < numTracks; ++i) { 366 int32_t timeScale; 367 sp<MetaData> format = mHandler->getTrackFormat(i, &timeScale); 368 369 const char *mime; 370 CHECK(format->findCString(kKeyMIMEType, &mime)); 371 372 bool isAudio = !strncasecmp(mime, "audio/", 6); 373 bool isVideo = !strncasecmp(mime, "video/", 6); 374 375 TrackInfo info; 376 info.mTimeScale = timeScale; 377 info.mRTPTime = 0; 378 info.mNormalPlaytimeUs = 0ll; 379 info.mNPTMappingValid = false; 380 381 if ((isAudio && mAudioTrack == NULL) 382 || (isVideo && mVideoTrack == NULL)) { 383 sp<AnotherPacketSource> source = new AnotherPacketSource(format); 384 385 if (isAudio) { 386 mAudioTrack = source; 387 } else { 388 mVideoTrack = source; 389 } 390 391 info.mSource = source; 392 } 393 394 mTracks.push(info); 395 } 396 397 mState = CONNECTED; 398} 399 400void NuPlayer::RTSPSource::onDisconnected(const sp<AMessage> &msg) { 401 status_t err; 402 CHECK(msg->findInt32("result", &err)); 403 CHECK_NE(err, (status_t)OK); 404 405 mLooper->unregisterHandler(mHandler->id()); 406 mHandler.clear(); 407 408 mState = DISCONNECTED; 409 mFinalResult = err; 410 411 if (mDisconnectReplyID != 0) { 412 finishDisconnectIfPossible(); 413 } 414} 415 416void NuPlayer::RTSPSource::finishDisconnectIfPossible() { 417 if (mState != DISCONNECTED) { 418 mHandler->disconnect(); 419 return; 420 } 421 422 (new AMessage)->postReply(mDisconnectReplyID); 423 mDisconnectReplyID = 0; 424} 425 426} // namespace android 427