diff options
Diffstat (limited to 'media/libstagefright/httplive/LiveSession.cpp')
-rw-r--r-- | media/libstagefright/httplive/LiveSession.cpp | 214 |
1 files changed, 140 insertions, 74 deletions
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 2d93152..74f58e9 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -141,6 +141,21 @@ const char *LiveSession::getKeyForStream(StreamType type) { return NULL; } +//static +const char *LiveSession::getNameForStream(StreamType type) { + switch (type) { + case STREAMTYPE_VIDEO: + return "video"; + case STREAMTYPE_AUDIO: + return "audio"; + case STREAMTYPE_SUBTITLES: + return "subs"; + default: + break; + } + return "unknown"; +} + LiveSession::LiveSession( const sp<AMessage> ¬ify, uint32_t flags, const sp<IMediaHTTPService> &httpService) @@ -192,7 +207,11 @@ status_t LiveSession::dequeueAccessUnit( status_t finalResult = OK; sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(stream); - ssize_t idx = typeToIndex(stream); + ssize_t streamIdx = typeToIndex(stream); + if (streamIdx < 0) { + return INVALID_VALUE; + } + const char *streamStr = getNameForStream(stream); // Do not let client pull data if we don't have data packets yet. // We might only have a format discontinuity queued without data. // When NuPlayerDecoder dequeues the format discontinuity, it will @@ -200,6 +219,9 @@ status_t LiveSession::dequeueAccessUnit( // thinks it can do seamless change, so will not shutdown decoder. // When the actual format arrives, it can't handle it and get stuck. if (!packetSource->hasDataBufferAvailable(&finalResult)) { + ALOGV("[%s] dequeueAccessUnit: no buffer available (finalResult=%d)", + streamStr, finalResult); + if (finalResult == OK) { return -EAGAIN; } else { @@ -212,25 +234,6 @@ status_t LiveSession::dequeueAccessUnit( status_t err = packetSource->dequeueAccessUnit(accessUnit); - size_t streamIdx; - const char *streamStr; - switch (stream) { - case STREAMTYPE_AUDIO: - streamIdx = kAudioIndex; - streamStr = "audio"; - break; - case STREAMTYPE_VIDEO: - streamIdx = kVideoIndex; - streamStr = "video"; - break; - case STREAMTYPE_SUBTITLES: - streamIdx = kSubtitleIndex; - streamStr = "subs"; - break; - default: - TRESPASS(); - } - StreamItem& strm = mStreams[streamIdx]; if (err == INFO_DISCONTINUITY) { // adaptive streaming, discontinuities in the playlist @@ -249,9 +252,10 @@ status_t LiveSession::dequeueAccessUnit( } else if (err == OK) { if (stream == STREAMTYPE_AUDIO || stream == STREAMTYPE_VIDEO) { - int64_t timeUs; + int64_t timeUs, originalTimeUs; int32_t discontinuitySeq = 0; CHECK((*accessUnit)->meta()->findInt64("timeUs", &timeUs)); + originalTimeUs = timeUs; (*accessUnit)->meta()->findInt32("discontinuitySeq", &discontinuitySeq); if (discontinuitySeq > (int32_t) strm.mCurDiscontinuitySeq) { int64_t offsetTimeUs; @@ -303,7 +307,8 @@ status_t LiveSession::dequeueAccessUnit( timeUs += mDiscontinuityOffsetTimesUs.valueFor(discontinuitySeq); } - ALOGV("[%s] read buffer at time %" PRId64 " us", streamStr, timeUs); + ALOGV("[%s] dequeueAccessUnit: time %lld us, original %lld us", + streamStr, (long long)timeUs, (long long)originalTimeUs); (*accessUnit)->meta()->setInt64("timeUs", timeUs); mLastDequeuedTimeUs = timeUs; mRealTimeBaseUs = ALooper::GetNowUs() - timeUs; @@ -409,7 +414,7 @@ bool LiveSession::checkSwitchProgress( if (lastDequeueMeta == NULL) { // this means we don't have enough cushion, try again later ALOGV("[%s] up switching failed due to insufficient buffer", - stream == STREAMTYPE_AUDIO ? "audio" : "video"); + getNameForStream(stream)); return false; } } else { @@ -428,7 +433,7 @@ bool LiveSession::checkSwitchProgress( if (firstNewMeta[i] == NULL) { HLSTime dequeueTime(lastDequeueMeta); ALOGV("[%s] dequeue time (%d, %lld) past start time", - stream == STREAMTYPE_AUDIO ? "audio" : "video", + getNameForStream(stream), dequeueTime.mSeq, (long long) dequeueTime.mTimeUs); return false; } @@ -493,16 +498,15 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { case kWhatSeek: { - sp<AReplyToken> seekReplyID; - CHECK(msg->senderAwaitsResponse(&seekReplyID)); - mSeekReplyID = seekReplyID; - mSeekReply = new AMessage; - - status_t err = onSeek(msg); - - if (err != OK) { + if (mReconfigurationInProgress) { msg->post(50000); + break; } + + CHECK(msg->senderAwaitsResponse(&mSeekReplyID)); + mSeekReply = new AMessage; + + onSeek(msg); break; } @@ -525,6 +529,11 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { break; } + ALOGV("fetcher-%d %s", + mFetcherInfos[index].mFetcher->getFetcherID(), + what == PlaylistFetcher::kWhatPaused ? + "paused" : "stopped"); + if (what == PlaylistFetcher::kWhatStopped) { mFetcherLooper->unregisterHandler( mFetcherInfos[index].mFetcher->id()); @@ -544,6 +553,7 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { if (--mContinuationCounter == 0) { mContinuation->post(); } + ALOGV("%zu fetcher(s) left", mContinuationCounter); } break; } @@ -636,6 +646,9 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { int32_t switchGeneration; CHECK(msg->findInt32("switchGeneration", &switchGeneration)); + ALOGV("kWhatStartedAt: switchGen=%d, mSwitchGen=%d", + switchGeneration, mSwitchGeneration); + if (switchGeneration != mSwitchGeneration) { break; } @@ -667,6 +680,7 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { if (checkSwitchProgress(stopParams, delayUs, &needResumeUntil)) { // playback time hasn't passed startAt time if (!needResumeUntil) { + ALOGV("finish switch"); for (size_t i = 0; i < kMaxStreams; ++i) { if ((mSwapMask & indexToType(i)) && uri == mStreams[i].mNewUri) { @@ -682,6 +696,7 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { // Resume fetcher for the original variant; the resumed fetcher should // continue until the timestamps found in msg, which is stored by the // new fetcher to indicate where the new variant has started buffering. + ALOGV("finish switch with resumeUntilAsync"); for (size_t i = 0; i < mFetcherInfos.size(); i++) { const FetcherInfo &info = mFetcherInfos.valueAt(i); if (info.mToBeRemoved) { @@ -693,8 +708,10 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) { // playback time passed startAt time if (switchUp) { // if switching up, cancel and retry if condition satisfies again + ALOGV("cancel up switch because we're too late"); cancelBandwidthSwitch(true /* resume */); } else { + ALOGV("retry down switch at next sample"); resumeFetcher(uri, mSwapMask, -1, true /* newUri */); } } @@ -933,7 +950,8 @@ sp<PlaylistFetcher> LiveSession::addFetcher(const char *uri) { notify->setInt32("switchGeneration", mSwitchGeneration); FetcherInfo info; - info.mFetcher = new PlaylistFetcher(notify, this, uri, mSubtitleGeneration); + info.mFetcher = new PlaylistFetcher( + notify, this, uri, mCurBandwidthIndex, mSubtitleGeneration); info.mDurationUs = -1ll; info.mToBeRemoved = false; info.mToBeResumed = false; @@ -1167,9 +1185,13 @@ bool LiveSession::resumeFetcher( } if (resume) { - ALOGV("resuming fetcher %s, timeUs %lld", uri.c_str(), (long long)timeUs); + sp<PlaylistFetcher> &fetcher = mFetcherInfos.editValueAt(index).mFetcher; SeekMode seekMode = newUri ? kSeekModeNextSample : kSeekModeExactPosition; - mFetcherInfos.editValueAt(index).mFetcher->startAsync( + + ALOGV("resuming fetcher-%d, timeUs=%lld, seekMode=%d", + fetcher->getFetcherID(), (long long)timeUs, seekMode); + + fetcher->startAsync( sources[kAudioIndex], sources[kVideoIndex], sources[kSubtitleIndex], @@ -1349,16 +1371,10 @@ HLSTime LiveSession::latestMediaSegmentStartTime() const { return audioTime < videoTime ? videoTime : audioTime; } -status_t LiveSession::onSeek(const sp<AMessage> &msg) { +void LiveSession::onSeek(const sp<AMessage> &msg) { int64_t timeUs; CHECK(msg->findInt64("timeUs", &timeUs)); - - if (!mReconfigurationInProgress) { - changeConfiguration(timeUs); - return OK; - } else { - return -EWOULDBLOCK; - } + changeConfiguration(timeUs); } status_t LiveSession::getDuration(int64_t *durationUs) const { @@ -1406,6 +1422,9 @@ status_t LiveSession::selectTrack(size_t index, bool select) { return INVALID_OPERATION; } + ALOGV("selectTrack: index=%zu, select=%d, mSubtitleGen=%d++", + index, select, mSubtitleGeneration); + ++mSubtitleGeneration; status_t err = mPlaylist->selectTrack(index, select); if (err == OK) { @@ -1426,6 +1445,9 @@ ssize_t LiveSession::getSelectedTrack(media_track_type type) const { void LiveSession::changeConfiguration( int64_t timeUs, ssize_t bandwidthIndex, bool pickTrack) { + ALOGV("changeConfiguration: timeUs=%lld us, bwIndex=%zd, pickTrack=%d", + (long long)timeUs, bandwidthIndex, pickTrack); + cancelBandwidthSwitch(); CHECK(!mReconfigurationInProgress); @@ -1433,6 +1455,10 @@ void LiveSession::changeConfiguration( if (bandwidthIndex >= 0) { mOrigBandwidthIndex = mCurBandwidthIndex; mCurBandwidthIndex = bandwidthIndex; + if (mOrigBandwidthIndex != mCurBandwidthIndex) { + ALOGI("#### Starting Bandwidth Switch: %zd => %zd", + mOrigBandwidthIndex, mCurBandwidthIndex); + } } CHECK_LT(mCurBandwidthIndex, mBandwidthItems.size()); const BandwidthItem &item = mBandwidthItems.itemAt(mCurBandwidthIndex); @@ -1478,6 +1504,7 @@ void LiveSession::changeConfiguration( } if (discardFetcher) { + ALOGV("discarding fetcher-%d", fetcher->getFetcherID()); fetcher->stopAsync(); } else { float threshold = -1.0f; // always finish fetching by default @@ -1490,8 +1517,8 @@ void LiveSession::changeConfiguration( mOrigBandwidthIndex, mCurBandwidthIndex); } - ALOGV("Pausing with threshold %.3f", threshold); - + ALOGV("pausing fetcher-%d, threshold=%.2f", + fetcher->getFetcherID(), threshold); fetcher->pauseAsync(threshold); } } @@ -1526,6 +1553,8 @@ void LiveSession::changeConfiguration( } void LiveSession::onChangeConfiguration(const sp<AMessage> &msg) { + ALOGV("onChangeConfiguration"); + if (!mReconfigurationInProgress) { int32_t pickTrack = 0; msg->findInt32("pickTrack", &pickTrack); @@ -1536,6 +1565,8 @@ void LiveSession::onChangeConfiguration(const sp<AMessage> &msg) { } void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) { + ALOGV("onChangeConfiguration2"); + mContinuation.clear(); // All fetchers are either suspended or have been removed now. @@ -1547,6 +1578,7 @@ void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) { if (timeUs >= 0) { mLastSeekTimeUs = timeUs; + mLastDequeuedTimeUs = timeUs; for (size_t i = 0; i < mPacketSources.size(); i++) { mPacketSources.editValueAt(i)->clear(); @@ -1599,8 +1631,10 @@ void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) { ALOGV("stream %zu changed: oldURI %s, newURI %s", i, mStreams[i].mUri.c_str(), URIs[i].c_str()); sp<AnotherPacketSource> source = mPacketSources.valueFor(indexToType(i)); - source->queueDiscontinuity( - ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true); + if (source->getLatestDequeuedMeta() != NULL) { + source->queueDiscontinuity( + ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true); + } } // Determine which decoders to shutdown on the player side, // a decoder has to be shutdown if its streamtype was active @@ -1660,16 +1694,17 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { // and resume audio. mSwapMask = mNewStreamMask & mStreamMask & ~resumeMask; switching = (mSwapMask != 0); - if (!switching) { - ALOGV("#### Finishing Bandwidth Switch Early: %zd => %zd", - mOrigBandwidthIndex, mCurBandwidthIndex); - } } mRealTimeBaseUs = ALooper::GetNowUs() - mLastDequeuedTimeUs; } else { mRealTimeBaseUs = ALooper::GetNowUs() - timeUs; } + ALOGV("onChangeConfiguration3: timeUs=%lld, switching=%d, pickTrack=%d, " + "mStreamMask=0x%x, mNewStreamMask=0x%x, mSwapMask=0x%x", + (long long)timeUs, switching, pickTrack, + mStreamMask, mNewStreamMask, mSwapMask); + for (size_t i = 0; i < kMaxStreams; ++i) { if (streamMask & indexToType(i)) { if (switching) { @@ -1687,6 +1722,9 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { for (size_t i = 0; i < mFetcherInfos.size(); ++i) { const AString &uri = mFetcherInfos.keyAt(i); if (!resumeFetcher(uri, resumeMask, timeUs)) { + ALOGV("marking fetcher-%d to be removed", + mFetcherInfos[i].mFetcher->getFetcherID()); + mFetcherInfos.editValueAt(i).mToBeRemoved = true; } } @@ -1776,6 +1814,14 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { } } + ALOGV("[fetcher-%d] startAsync: startTimeUs %lld mLastSeekTimeUs %lld " + "segmentStartTimeUs %lld seekMode %d", + fetcher->getFetcherID(), + (long long)startTime.mTimeUs, + (long long)mLastSeekTimeUs, + (long long)startTime.getSegmentTimeUs(true /* midpoint */), + seekMode); + // Set the target segment start time to the middle point of the // segment where the last sample was. // This gives a better guess if segments of the two variants are not @@ -1795,22 +1841,28 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { // All fetchers have now been started, the configuration change // has completed. - ALOGV("XXX configuration change completed."); mReconfigurationInProgress = false; if (switching) { mSwitchInProgress = true; } else { mStreamMask = mNewStreamMask; - mOrigBandwidthIndex = mCurBandwidthIndex; + if (mOrigBandwidthIndex != mCurBandwidthIndex) { + ALOGV("#### Finished Bandwidth Switch Early: %zd => %zd", + mOrigBandwidthIndex, mCurBandwidthIndex); + mOrigBandwidthIndex = mCurBandwidthIndex; + } } + ALOGV("onChangeConfiguration3: mSwitchInProgress %d, mStreamMask 0x%x", + mSwitchInProgress, mStreamMask); + if (mDisconnectReplyID != NULL) { finishDisconnect(); } } void LiveSession::swapPacketSource(StreamType stream) { - ALOGV("swapPacketSource: stream = %d", stream); + ALOGV("[%s] swapPacketSource", getNameForStream(stream)); // transfer packets from source2 to source sp<AnotherPacketSource> &aps = mPacketSources.editValueFor(stream); @@ -1858,7 +1910,7 @@ void LiveSession::tryToFinishBandwidthSwitch(const AString &oldUri) { mFetcherInfos.editValueAt(index).mFetcher->stopAsync(false /* clear */); - ALOGV("tryToFinishBandwidthSwitch: mSwapMask=%x", mSwapMask); + ALOGV("tryToFinishBandwidthSwitch: mSwapMask=0x%x", mSwapMask); if (mSwapMask != 0) { return; } @@ -1925,11 +1977,19 @@ void LiveSession::onPollBuffering() { bool underflow, ready, down, up; if (checkBuffering(underflow, ready, down, up)) { - if (mInPreparationPhase && ready) { - postPrepared(OK); + if (mInPreparationPhase) { + // Allow down switch even if we're still preparing. + // + // Some streams have a high bandwidth index as default, + // when bandwidth is low, it takes a long time to buffer + // to ready mark, then it immediately pauses after start + // as we have to do a down switch. It's better experience + // to restart from a lower index, if we detect low bw. + if (!switchBandwidthIfNeeded(false /* up */, down) && ready) { + postPrepared(OK); + } } - // don't switch before we report prepared if (!mInPreparationPhase) { if (ready) { stopBufferingIfNecessary(); @@ -1937,8 +1997,7 @@ void LiveSession::onPollBuffering() { startBufferingIfNecessary(); } switchBandwidthIfNeeded(up, down); - } - + } } schedulePollBuffering(); @@ -1983,7 +2042,7 @@ void LiveSession::cancelBandwidthSwitch(bool resume) { } ALOGI("#### Canceled Bandwidth Switch: %zd => %zd", - mCurBandwidthIndex, mOrigBandwidthIndex); + mOrigBandwidthIndex, mCurBandwidthIndex); mSwitchGeneration++; mSwitchInProgress = false; @@ -2022,13 +2081,16 @@ bool LiveSession::checkBuffering( int64_t bufferedDurationUs = mPacketSources[i]->getEstimatedDurationUs(); - ALOGV("source[%zu]: buffered %lld us", i, (long long)bufferedDurationUs); + ALOGV("[%s] buffered %lld us", + getNameForStream(mPacketSources.keyAt(i)), + (long long)bufferedDurationUs); if (durationUs >= 0) { int32_t percent; if (mPacketSources[i]->isFinished(0 /* duration */)) { percent = 100; } else { - percent = (int32_t)(100.0 * (mLastDequeuedTimeUs + bufferedDurationUs) / durationUs); + percent = (int32_t)(100.0 * + (mLastDequeuedTimeUs + bufferedDurationUs) / durationUs); } if (minBufferPercent < 0 || percent < minBufferPercent) { minBufferPercent = percent; @@ -2111,10 +2173,14 @@ void LiveSession::notifyBufferingUpdate(int32_t percentage) { notify->post(); } -void LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { +/* + * returns true if a bandwidth switch is actually needed (and started), + * returns false otherwise + */ +bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { // no need to check bandwidth if we only have 1 bandwidth settings if (mSwitchInProgress || mBandwidthItems.size() < 2) { - return; + return false; } int32_t bandwidthBps; @@ -2123,7 +2189,7 @@ void LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { mLastBandwidthBps = bandwidthBps; } else { ALOGV("no bandwidth estimate."); - return; + return false; } int32_t curBandwidth = mBandwidthItems.itemAt(mCurBandwidthIndex).mBandwidth; @@ -2142,16 +2208,16 @@ void LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { // bandwidthIndex is < mCurBandwidthIndex, as getBandwidthIndex() only uses 70% // of measured bw. In that case we don't want to do anything, since we have // both enough buffer and enough bw. - if (bandwidthIndex == mCurBandwidthIndex - || (canSwitchUp && bandwidthIndex < mCurBandwidthIndex) - || (canSwithDown && bandwidthIndex > mCurBandwidthIndex)) { - return; + if ((canSwitchUp && bandwidthIndex > mCurBandwidthIndex) + || (canSwithDown && bandwidthIndex < mCurBandwidthIndex)) { + // if not yet prepared, just restart again with new bw index. + // this is faster and playback experience is cleaner. + changeConfiguration( + mInPreparationPhase ? 0 : -1ll, bandwidthIndex); + return true; } - - ALOGI("#### Starting Bandwidth Switch: %zd => %zd", - mCurBandwidthIndex, bandwidthIndex); - changeConfiguration(-1, bandwidthIndex, false); } + return false; } void LiveSession::postError(status_t err) { |