/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //#define LOG_NDEBUG 0 #define LOG_TAG "RTSPSource" #include #include "RTSPSource.h" #include "AnotherPacketSource.h" #include "MyHandler.h" #include namespace android { NuPlayer::RTSPSource::RTSPSource( const char *url, const KeyedVector *headers, bool uidValid, uid_t uid) : mURL(url), mUIDValid(uidValid), mUID(uid), mFlags(0), mState(DISCONNECTED), mFinalResult(OK), mDisconnectReplyID(0), mSeekGeneration(0) { if (headers) { mExtraHeaders = *headers; ssize_t index = mExtraHeaders.indexOfKey(String8("x-hide-urls-from-log")); if (index >= 0) { mFlags |= kFlagIncognito; mExtraHeaders.removeItemsAt(index); } } } NuPlayer::RTSPSource::~RTSPSource() { if (mLooper != NULL) { mLooper->stop(); } } void NuPlayer::RTSPSource::start() { if (mLooper == NULL) { mLooper = new ALooper; mLooper->setName("rtsp"); mLooper->start(); mReflector = new AHandlerReflector(this); mLooper->registerHandler(mReflector); } CHECK(mHandler == NULL); sp notify = new AMessage(kWhatNotify, mReflector->id()); mHandler = new MyHandler(mURL.c_str(), notify, mUIDValid, mUID); mLooper->registerHandler(mHandler); CHECK_EQ(mState, (int)DISCONNECTED); mState = CONNECTING; mHandler->connect(); } void NuPlayer::RTSPSource::stop() { sp msg = new AMessage(kWhatDisconnect, mReflector->id()); sp dummy; msg->postAndAwaitResponse(&dummy); } status_t NuPlayer::RTSPSource::feedMoreTSData() { return mFinalResult; } sp NuPlayer::RTSPSource::getFormat(bool audio) { sp source = getSource(audio); if (source == NULL) { return NULL; } return source->getFormat(); } status_t NuPlayer::RTSPSource::dequeueAccessUnit( bool audio, sp *accessUnit) { sp source = getSource(audio); if (source == NULL) { return -EWOULDBLOCK; } status_t finalResult; if (!source->hasBufferAvailable(&finalResult)) { return finalResult == OK ? -EWOULDBLOCK : finalResult; } return source->dequeueAccessUnit(accessUnit); } sp NuPlayer::RTSPSource::getSource(bool audio) { return audio ? mAudioTrack : mVideoTrack; } status_t NuPlayer::RTSPSource::getDuration(int64_t *durationUs) { *durationUs = 0ll; int64_t audioDurationUs; if (mAudioTrack != NULL && mAudioTrack->getFormat()->findInt64( kKeyDuration, &audioDurationUs) && audioDurationUs > *durationUs) { *durationUs = audioDurationUs; } int64_t videoDurationUs; if (mVideoTrack != NULL && mVideoTrack->getFormat()->findInt64( kKeyDuration, &videoDurationUs) && videoDurationUs > *durationUs) { *durationUs = videoDurationUs; } return OK; } status_t NuPlayer::RTSPSource::seekTo(int64_t seekTimeUs) { sp msg = new AMessage(kWhatPerformSeek, mReflector->id()); msg->setInt32("generation", ++mSeekGeneration); msg->setInt64("timeUs", seekTimeUs); msg->post(200000ll); return OK; } void NuPlayer::RTSPSource::performSeek(int64_t seekTimeUs) { if (mState != CONNECTED) { return; } mState = SEEKING; mHandler->seek(seekTimeUs); } bool NuPlayer::RTSPSource::isSeekable() { return true; } void NuPlayer::RTSPSource::onMessageReceived(const sp &msg) { if (msg->what() == kWhatDisconnect) { uint32_t replyID; CHECK(msg->senderAwaitsResponse(&replyID)); mDisconnectReplyID = replyID; finishDisconnectIfPossible(); return; } else if (msg->what() == kWhatPerformSeek) { int32_t generation; CHECK(msg->findInt32("generation", &generation)); if (generation != mSeekGeneration) { // obsolete. return; } int64_t seekTimeUs; CHECK(msg->findInt64("timeUs", &seekTimeUs)); performSeek(seekTimeUs); return; } CHECK_EQ(msg->what(), (int)kWhatNotify); int32_t what; CHECK(msg->findInt32("what", &what)); switch (what) { case MyHandler::kWhatConnected: onConnected(); break; case MyHandler::kWhatDisconnected: onDisconnected(msg); break; case MyHandler::kWhatSeekDone: { mState = CONNECTED; break; } case MyHandler::kWhatAccessUnit: { size_t trackIndex; CHECK(msg->findSize("trackIndex", &trackIndex)); CHECK_LT(trackIndex, mTracks.size()); sp obj; CHECK(msg->findObject("accessUnit", &obj)); sp accessUnit = static_cast(obj.get()); int32_t damaged; if (accessUnit->meta()->findInt32("damaged", &damaged) && damaged) { ALOGI("dropping damaged access unit."); break; } TrackInfo *info = &mTracks.editItemAt(trackIndex); sp source = info->mSource; if (source != NULL) { uint32_t rtpTime; CHECK(accessUnit->meta()->findInt32("rtp-time", (int32_t *)&rtpTime)); if (!info->mNPTMappingValid) { // This is a live stream, we didn't receive any normal // playtime mapping. Assume the first packets correspond // to time 0. ALOGV("This is a live stream, assuming time = 0"); info->mRTPTime = rtpTime; info->mNormalPlaytimeUs = 0ll; info->mNPTMappingValid = true; } int64_t nptUs = ((double)rtpTime - (double)info->mRTPTime) / info->mTimeScale * 1000000ll + info->mNormalPlaytimeUs; accessUnit->meta()->setInt64("timeUs", nptUs); source->queueAccessUnit(accessUnit); } break; } case MyHandler::kWhatEOS: { size_t trackIndex; CHECK(msg->findSize("trackIndex", &trackIndex)); CHECK_LT(trackIndex, mTracks.size()); int32_t finalResult; CHECK(msg->findInt32("finalResult", &finalResult)); CHECK_NE(finalResult, (status_t)OK); TrackInfo *info = &mTracks.editItemAt(trackIndex); sp source = info->mSource; if (source != NULL) { source->signalEOS(finalResult); } break; } case MyHandler::kWhatSeekDiscontinuity: { size_t trackIndex; CHECK(msg->findSize("trackIndex", &trackIndex)); CHECK_LT(trackIndex, mTracks.size()); TrackInfo *info = &mTracks.editItemAt(trackIndex); sp source = info->mSource; if (source != NULL) { source->queueDiscontinuity(ATSParser::DISCONTINUITY_SEEK, NULL); } break; } case MyHandler::kWhatNormalPlayTimeMapping: { size_t trackIndex; CHECK(msg->findSize("trackIndex", &trackIndex)); CHECK_LT(trackIndex, mTracks.size()); uint32_t rtpTime; CHECK(msg->findInt32("rtpTime", (int32_t *)&rtpTime)); int64_t nptUs; CHECK(msg->findInt64("nptUs", &nptUs)); TrackInfo *info = &mTracks.editItemAt(trackIndex); info->mRTPTime = rtpTime; info->mNormalPlaytimeUs = nptUs; info->mNPTMappingValid = true; break; } default: TRESPASS(); } } void NuPlayer::RTSPSource::onConnected() { CHECK(mAudioTrack == NULL); CHECK(mVideoTrack == NULL); size_t numTracks = mHandler->countTracks(); for (size_t i = 0; i < numTracks; ++i) { int32_t timeScale; sp format = mHandler->getTrackFormat(i, &timeScale); const char *mime; CHECK(format->findCString(kKeyMIMEType, &mime)); bool isAudio = !strncasecmp(mime, "audio/", 6); bool isVideo = !strncasecmp(mime, "video/", 6); TrackInfo info; info.mTimeScale = timeScale; info.mRTPTime = 0; info.mNormalPlaytimeUs = 0ll; info.mNPTMappingValid = false; if ((isAudio && mAudioTrack == NULL) || (isVideo && mVideoTrack == NULL)) { sp source = new AnotherPacketSource(format); if (isAudio) { mAudioTrack = source; } else { mVideoTrack = source; } info.mSource = source; } mTracks.push(info); } mState = CONNECTED; } void NuPlayer::RTSPSource::onDisconnected(const sp &msg) { status_t err; CHECK(msg->findInt32("result", &err)); CHECK_NE(err, (status_t)OK); mLooper->unregisterHandler(mHandler->id()); mHandler.clear(); mState = DISCONNECTED; mFinalResult = err; if (mDisconnectReplyID != 0) { finishDisconnectIfPossible(); } } void NuPlayer::RTSPSource::finishDisconnectIfPossible() { if (mState != DISCONNECTED) { mHandler->disconnect(); return; } (new AMessage)->postReply(mDisconnectReplyID); mDisconnectReplyID = 0; } } // namespace android