summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorChong Zhang <chz@google.com>2015-04-18 00:20:11 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2015-04-18 00:20:11 +0000
commit7184bab920699d49718e8fc3cf8a23d3c9d8d54a (patch)
tree06913515dd08a743e9e092171c91666f011cee18 /media
parent5030647ab3860575312532655ccf2ea3cc96088f (diff)
parent978449984366946a2e5c9f7cf350746f4306caf8 (diff)
downloadframeworks_av-7184bab920699d49718e8fc3cf8a23d3c9d8d54a.zip
frameworks_av-7184bab920699d49718e8fc3cf8a23d3c9d8d54a.tar.gz
frameworks_av-7184bab920699d49718e8fc3cf8a23d3c9d8d54a.tar.bz2
Merge "HLS: reduce number of guessed wrong seq numbers"
Diffstat (limited to 'media')
-rw-r--r--media/libstagefright/Utils.cpp24
-rw-r--r--media/libstagefright/httplive/LiveSession.cpp58
-rw-r--r--media/libstagefright/httplive/M3UParser.cpp37
-rw-r--r--media/libstagefright/httplive/M3UParser.h6
-rw-r--r--media/libstagefright/httplive/PlaylistFetcher.cpp327
-rw-r--r--media/libstagefright/httplive/PlaylistFetcher.h4
6 files changed, 266 insertions, 190 deletions
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index dfe8ad1..0d8e64a 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -852,14 +852,32 @@ HLSTime::HLSTime(const sp<AMessage>& meta) :
}
}
-int64_t HLSTime::getSegmentTimeUs(bool midpoint) const {
+int64_t HLSTime::getSegmentTimeUs() const {
int64_t segmentStartTimeUs = -1ll;
if (mMeta != NULL) {
CHECK(mMeta->findInt64("segmentStartTimeUs", &segmentStartTimeUs));
- if (midpoint) {
+
+ int64_t segmentFirstTimeUs;
+ if (mMeta->findInt64("segmentFirstTimeUs", &segmentFirstTimeUs)) {
+ segmentStartTimeUs += mTimeUs - segmentFirstTimeUs;
+ }
+
+ // adjust segment time by playlist age (for live streaming)
+ int64_t playlistTimeUs;
+ if (mMeta->findInt64("playlistTimeUs", &playlistTimeUs)) {
+ int64_t playlistAgeUs = ALooper::GetNowUs() - playlistTimeUs;
+
int64_t durationUs;
CHECK(mMeta->findInt64("segmentDurationUs", &durationUs));
- segmentStartTimeUs += durationUs / 2;
+
+ // round to nearest whole segment
+ playlistAgeUs = (playlistAgeUs + durationUs / 2)
+ / durationUs * durationUs;
+
+ segmentStartTimeUs -= playlistAgeUs;
+ if (segmentStartTimeUs < 0) {
+ segmentStartTimeUs = 0;
+ }
}
}
return segmentStartTimeUs;
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 203444a..764ff82 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -67,7 +67,7 @@ struct LiveSession::BandwidthEstimator : public RefBase {
BandwidthEstimator();
void addBandwidthMeasurement(size_t numBytes, int64_t delayUs);
- bool estimateBandwidth(int32_t *bandwidth);
+ bool estimateBandwidth(int32_t *bandwidth, bool *isStable = NULL);
private:
// Bandwidth estimation parameters
@@ -81,6 +81,9 @@ private:
Mutex mLock;
List<BandwidthEntry> mBandwidthHistory;
+ List<int32_t> mPrevEstimates;
+ bool mHasNewSample;
+ bool mIsStable;
int64_t mTotalTransferTimeUs;
size_t mTotalTransferBytes;
@@ -88,6 +91,8 @@ private:
};
LiveSession::BandwidthEstimator::BandwidthEstimator() :
+ mHasNewSample(false),
+ mIsStable(true),
mTotalTransferTimeUs(0),
mTotalTransferBytes(0) {
}
@@ -102,6 +107,7 @@ void LiveSession::BandwidthEstimator::addBandwidthMeasurement(
mTotalTransferTimeUs += delayUs;
mTotalTransferBytes += numBytes;
mBandwidthHistory.push_back(entry);
+ mHasNewSample = true;
// trim old samples, keeping at least kMaxBandwidthHistoryItems samples,
// and total transfer time at least kMaxBandwidthHistoryWindowUs.
@@ -116,14 +122,43 @@ void LiveSession::BandwidthEstimator::addBandwidthMeasurement(
}
}
-bool LiveSession::BandwidthEstimator::estimateBandwidth(int32_t *bandwidthBps) {
+bool LiveSession::BandwidthEstimator::estimateBandwidth(int32_t *bandwidthBps, bool *isStable) {
AutoMutex autoLock(mLock);
if (mBandwidthHistory.size() < 2) {
return false;
}
+ if (!mHasNewSample) {
+ *bandwidthBps = *(--mPrevEstimates.end());
+ if (isStable) {
+ *isStable = mIsStable;
+ }
+ return true;
+ }
+
*bandwidthBps = ((double)mTotalTransferBytes * 8E6 / mTotalTransferTimeUs);
+ mPrevEstimates.push_back(*bandwidthBps);
+ while (mPrevEstimates.size() > 3) {
+ mPrevEstimates.erase(mPrevEstimates.begin());
+ }
+ mHasNewSample = false;
+
+ int32_t minEstimate = -1, maxEstimate = -1;
+ List<int32_t>::iterator it;
+ for (it = mPrevEstimates.begin(); it != mPrevEstimates.end(); it++) {
+ int32_t estimate = *it;
+ if (minEstimate < 0 || minEstimate > estimate) {
+ minEstimate = estimate;
+ }
+ if (maxEstimate < 0 || maxEstimate < estimate) {
+ maxEstimate = estimate;
+ }
+ }
+ mIsStable = (maxEstimate <= minEstimate * 4 / 3);
+ if (isStable) {
+ *isStable = mIsStable;
+ }
return true;
}
@@ -1930,7 +1965,7 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) {
fetcher->getFetcherID(),
(long long)startTime.mTimeUs,
(long long)mLastSeekTimeUs,
- (long long)startTime.getSegmentTimeUs(true /* midpoint */),
+ (long long)startTime.getSegmentTimeUs(),
seekMode);
// Set the target segment start time to the middle point of the
@@ -1945,7 +1980,7 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) {
sources[kSubtitleIndex],
getMetadataSource(sources, mNewStreamMask, switching),
startTime.mTimeUs < 0 ? mLastSeekTimeUs : startTime.mTimeUs,
- startTime.getSegmentTimeUs(true /* midpoint */),
+ startTime.getSegmentTimeUs(),
startTime.mSeq,
seekMode);
}
@@ -2296,7 +2331,8 @@ bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) {
}
int32_t bandwidthBps;
- if (mBandwidthEstimator->estimateBandwidth(&bandwidthBps)) {
+ bool isStable;
+ if (mBandwidthEstimator->estimateBandwidth(&bandwidthBps, &isStable)) {
ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f);
mLastBandwidthBps = bandwidthBps;
} else {
@@ -2308,12 +2344,18 @@ bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) {
// canSwithDown and canSwitchUp can't both be true.
// we only want to switch up when measured bw is 120% higher than current variant,
// and we only want to switch down when measured bw is below current variant.
- bool canSwithDown = bufferLow
+ bool canSwitchDown = bufferLow
&& (bandwidthBps < (int32_t)curBandwidth);
bool canSwitchUp = bufferHigh
&& (bandwidthBps > (int32_t)curBandwidth * 12 / 10);
- if (canSwithDown || canSwitchUp) {
+ if (canSwitchDown || canSwitchUp) {
+ // bandwidth estimating has some delay, if we have to downswitch when
+ // it hasn't stabilized, be very conservative on bandwidth.
+ if (!isStable && canSwitchDown) {
+ bandwidthBps /= 2;
+ }
+
ssize_t bandwidthIndex = getBandwidthIndex(bandwidthBps);
// it's possible that we're checking for canSwitchUp case, but the returned
@@ -2321,7 +2363,7 @@ bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) {
// of measured bw. In that case we don't want to do anything, since we have
// both enough buffer and enough bw.
if ((canSwitchUp && bandwidthIndex > mCurBandwidthIndex)
- || (canSwithDown && bandwidthIndex < mCurBandwidthIndex)) {
+ || (canSwitchDown && bandwidthIndex < mCurBandwidthIndex)) {
// if not yet prepared, just restart again with new bw index.
// this is faster and playback experience is cleaner.
changeConfiguration(
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index ef9145c..ff2bb27 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -250,6 +250,9 @@ M3UParser::M3UParser(
mIsVariantPlaylist(false),
mIsComplete(false),
mIsEvent(false),
+ mFirstSeqNumber(-1),
+ mLastSeqNumber(-1),
+ mTargetDurationUs(-1ll),
mDiscontinuitySeq(0),
mDiscontinuityCount(0),
mSelectedIndex(-1) {
@@ -283,6 +286,19 @@ size_t M3UParser::getDiscontinuitySeq() const {
return mDiscontinuitySeq;
}
+int64_t M3UParser::getTargetDuration() const {
+ return mTargetDurationUs;
+}
+
+int32_t M3UParser::getFirstSeqNumber() const {
+ return mFirstSeqNumber;
+}
+
+void M3UParser::getSeqNumberRange(int32_t *firstSeq, int32_t *lastSeq) const {
+ *firstSeq = mFirstSeqNumber;
+ *lastSeq = mLastSeqNumber;
+}
+
sp<AMessage> M3UParser::meta() {
return mMeta;
}
@@ -664,11 +680,22 @@ status_t M3UParser::parse(const void *_data, size_t size) {
}
// error checking of all fields that's required to appear once
- // (currently only checking "target-duration")
- int32_t targetDurationSecs;
- if (!mIsVariantPlaylist && (mMeta == NULL || !mMeta->findInt32(
- "target-duration", &targetDurationSecs))) {
- return ERROR_MALFORMED;
+ // (currently only checking "target-duration"), and
+ // initialization of playlist properties (eg. mTargetDurationUs)
+ if (!mIsVariantPlaylist) {
+ int32_t targetDurationSecs;
+ if (mMeta == NULL || !mMeta->findInt32(
+ "target-duration", &targetDurationSecs)) {
+ ALOGE("Media playlist missing #EXT-X-TARGETDURATION");
+ return ERROR_MALFORMED;
+ }
+ mTargetDurationUs = targetDurationSecs * 1000000ll;
+
+ mFirstSeqNumber = 0;
+ if (mMeta != NULL) {
+ mMeta->findInt32("media-sequence", &mFirstSeqNumber);
+ }
+ mLastSeqNumber = mFirstSeqNumber + mItems.size() - 1;
}
return OK;
diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h
index fef361f..fa648ed 100644
--- a/media/libstagefright/httplive/M3UParser.h
+++ b/media/libstagefright/httplive/M3UParser.h
@@ -36,6 +36,9 @@ struct M3UParser : public RefBase {
bool isComplete() const;
bool isEvent() const;
size_t getDiscontinuitySeq() const;
+ int64_t getTargetDuration() const;
+ int32_t getFirstSeqNumber() const;
+ void getSeqNumberRange(int32_t *firstSeq, int32_t *lastSeq) const;
sp<AMessage> meta();
@@ -70,6 +73,9 @@ private:
bool mIsVariantPlaylist;
bool mIsComplete;
bool mIsEvent;
+ int32_t mFirstSeqNumber;
+ int32_t mLastSeqNumber;
+ int64_t mTargetDurationUs;
size_t mDiscontinuitySeq;
int32_t mDiscontinuityCount;
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index e44b0d9..8350c1b 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -160,6 +160,7 @@ PlaylistFetcher::PlaylistFetcher(
mDiscontinuitySeq(-1ll),
mStartTimeUsRelative(false),
mLastPlaylistFetchTimeUs(-1ll),
+ mPlaylistTimeUs(-1ll),
mSeqNumber(-1),
mNumRetries(0),
mStartup(true),
@@ -191,14 +192,9 @@ int32_t PlaylistFetcher::getFetcherID() const {
int64_t PlaylistFetcher::getSegmentStartTimeUs(int32_t seqNumber) const {
CHECK(mPlaylist != NULL);
- int32_t firstSeqNumberInPlaylist;
- if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
- "media-sequence", &firstSeqNumberInPlaylist)) {
- firstSeqNumberInPlaylist = 0;
- }
-
- int32_t lastSeqNumberInPlaylist =
- firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
+ int32_t firstSeqNumberInPlaylist, lastSeqNumberInPlaylist;
+ mPlaylist->getSeqNumberRange(
+ &firstSeqNumberInPlaylist, &lastSeqNumberInPlaylist);
CHECK_GE(seqNumber, firstSeqNumberInPlaylist);
CHECK_LE(seqNumber, lastSeqNumberInPlaylist);
@@ -222,14 +218,9 @@ int64_t PlaylistFetcher::getSegmentStartTimeUs(int32_t seqNumber) const {
int64_t PlaylistFetcher::getSegmentDurationUs(int32_t seqNumber) const {
CHECK(mPlaylist != NULL);
- int32_t firstSeqNumberInPlaylist;
- if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
- "media-sequence", &firstSeqNumberInPlaylist)) {
- firstSeqNumberInPlaylist = 0;
- }
-
- int32_t lastSeqNumberInPlaylist =
- firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
+ int32_t firstSeqNumberInPlaylist, lastSeqNumberInPlaylist;
+ mPlaylist->getSeqNumberRange(
+ &firstSeqNumberInPlaylist, &lastSeqNumberInPlaylist);
CHECK_GE(seqNumber, firstSeqNumberInPlaylist);
CHECK_LE(seqNumber, lastSeqNumberInPlaylist);
@@ -257,10 +248,7 @@ int64_t PlaylistFetcher::delayUsToRefreshPlaylist() const {
return (~0llu >> 1);
}
- int32_t targetDurationSecs;
- CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
-
- int64_t targetDurationUs = targetDurationSecs * 1000000ll;
+ int64_t targetDurationUs = mPlaylist->getTargetDuration();
int64_t minPlaylistAgeUs;
@@ -756,16 +744,9 @@ void PlaylistFetcher::onMonitorQueue() {
refreshPlaylist();
}
- int32_t targetDurationSecs;
int64_t targetDurationUs = kMinBufferedDurationUs;
if (mPlaylist != NULL) {
- if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
- "target-duration", &targetDurationSecs)) {
- ALOGE("Playlist is missing required EXT-X-TARGETDURATION tag");
- notifyError(ERROR_MALFORMED);
- return;
- }
- targetDurationUs = targetDurationSecs * 1000000ll;
+ targetDurationUs = mPlaylist->getTargetDuration();
}
int64_t bufferedDurationUs = 0ll;
@@ -863,6 +844,7 @@ status_t PlaylistFetcher::refreshPlaylist() {
if (!mPlaylist->isComplete()) {
updateTargetDuration();
}
+ mPlaylistTimeUs = ALooper::GetNowUs();
}
mLastPlaylistFetchTimeUs = ALooper::GetNowUs();
@@ -882,9 +864,7 @@ bool PlaylistFetcher::shouldPauseDownload() {
}
// Calculate threshold to abort current download
- int32_t targetDurationSecs;
- CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
- int64_t targetDurationUs = targetDurationSecs * 1000000ll;
+ int64_t targetDurationUs = mPlaylist->getTargetDuration();
int64_t thresholdUs = -1;
{
AutoMutex _l(mThresholdLock);
@@ -947,12 +927,8 @@ bool PlaylistFetcher::initDownloadState(
bool discontinuity = false;
if (mPlaylist != NULL) {
- if (mPlaylist->meta() != NULL) {
- mPlaylist->meta()->findInt32("media-sequence", &firstSeqNumberInPlaylist);
- }
-
- lastSeqNumberInPlaylist =
- firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
+ mPlaylist->getSeqNumberRange(
+ &firstSeqNumberInPlaylist, &lastSeqNumberInPlaylist);
if (mDiscontinuitySeq < 0) {
mDiscontinuitySeq = mPlaylist->getDiscontinuitySeq();
@@ -987,11 +963,18 @@ bool PlaylistFetcher::initDownloadState(
// to media time 0) is used to determine the start segment; mStartTimeUs (absolute
// timestamps coming from the media container) is used to determine the position
// inside a segments.
- mSeqNumber = getSeqNumberForTime(mSegmentStartTimeUs);
if (mStreamTypeMask != LiveSession::STREAMTYPE_SUBTITLES
&& mSeekMode != LiveSession::kSeekModeNextSample) {
// avoid double fetch/decode
- mSeqNumber += 1;
+ // Use (mSegmentStartTimeUs + 1/2 * targetDurationUs) to search
+ // for the starting segment in new variant.
+ // If the two variants' segments are aligned, this gives the
+ // next segment. If they're not aligned, this gives the segment
+ // that overlaps no more than 1/2 * targetDurationUs.
+ mSeqNumber = getSeqNumberForTime(mSegmentStartTimeUs
+ + mPlaylist->getTargetDuration() / 2);
+ } else {
+ mSeqNumber = getSeqNumberForTime(mSegmentStartTimeUs);
}
ssize_t minSeq = getSeqNumberForDiscontinuity(mDiscontinuitySeq);
if (mSeqNumber < minSeq) {
@@ -1025,11 +1008,9 @@ bool PlaylistFetcher::initDownloadState(
// refresh in increasing fraction (1/2, 1/3, ...) of the
// playlist's target duration or 3 seconds, whichever is less
int64_t delayUs = kMaxMonitorDelayUs;
- if (mPlaylist != NULL && mPlaylist->meta() != NULL) {
- int32_t targetDurationSecs;
- CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
- delayUs = mPlaylist->size() * targetDurationSecs *
- 1000000ll / (1 + mNumRetries);
+ if (mPlaylist != NULL) {
+ delayUs = mPlaylist->size() * mPlaylist->getTargetDuration()
+ / (1 + mNumRetries);
}
if (delayUs > kMaxMonitorDelayUs) {
delayUs = kMaxMonitorDelayUs;
@@ -1156,7 +1137,7 @@ bool PlaylistFetcher::initDownloadState(
}
queueDiscontinuity(
- ATSParser::DISCONTINUITY_FORMATCHANGE,
+ ATSParser::DISCONTINUITY_FORMAT_ONLY,
NULL /* extra */);
if (mStartup && mStartTimeUsRelative && mFirstPTSValid) {
@@ -1170,6 +1151,8 @@ bool PlaylistFetcher::initDownloadState(
// set mStartTimeUs=0, and take all samples from now on.
mStartTimeUs = 0;
mFirstPTSValid = false;
+ mIDRFound = false;
+ mVideoBuffer->clear();
}
}
@@ -1406,42 +1389,67 @@ void PlaylistFetcher::onDownloadNext() {
}
}
-int32_t PlaylistFetcher::getSeqNumberWithAnchorTime(
- int64_t anchorTimeUs, int64_t targetDiffUs) const {
- int32_t firstSeqNumberInPlaylist, lastSeqNumberInPlaylist;
- if (mPlaylist->meta() == NULL
- || !mPlaylist->meta()->findInt32("media-sequence", &firstSeqNumberInPlaylist)) {
- firstSeqNumberInPlaylist = 0;
+/*
+ * returns true if we need to adjust mSeqNumber
+ */
+bool PlaylistFetcher::adjustSeqNumberWithAnchorTime(int64_t anchorTimeUs) {
+ int32_t firstSeqNumberInPlaylist = mPlaylist->getFirstSeqNumber();
+
+ int64_t minDiffUs, maxDiffUs;
+ if (mSeekMode == LiveSession::kSeekModeNextSample) {
+ minDiffUs = -mPlaylist->getTargetDuration();
+ maxDiffUs = 0ll;
+ } else {
+ minDiffUs = -mPlaylist->getTargetDuration() / 2;
+ maxDiffUs = mPlaylist->getTargetDuration();
}
- lastSeqNumberInPlaylist = firstSeqNumberInPlaylist + mPlaylist->size() - 1;
- int32_t index = mSeqNumber - firstSeqNumberInPlaylist - 1;
- // adjust anchorTimeUs to within targetDiffUs from mStartTimeUs
- while (index >= 0 && anchorTimeUs - mStartTimeUs > targetDiffUs) {
- sp<AMessage> itemMeta;
- CHECK(mPlaylist->itemAt(index, NULL /* uri */, &itemMeta));
+ int32_t oldSeqNumber = mSeqNumber;
+ ssize_t index = mSeqNumber - firstSeqNumberInPlaylist;
- int64_t itemDurationUs;
- CHECK(itemMeta->findInt64("durationUs", &itemDurationUs));
+ // adjust anchorTimeUs to within (minDiffUs, maxDiffUs) from mStartTimeUs
+ int64_t diffUs = anchorTimeUs - mStartTimeUs;
+ if (diffUs > maxDiffUs) {
+ while (index > 0 && diffUs > maxDiffUs) {
+ --index;
+
+ sp<AMessage> itemMeta;
+ CHECK(mPlaylist->itemAt(index, NULL /* uri */, &itemMeta));
+
+ int64_t itemDurationUs;
+ CHECK(itemMeta->findInt64("durationUs", &itemDurationUs));
+
+ diffUs -= itemDurationUs;
+ }
+ } else if (diffUs < minDiffUs) {
+ while (index + 1 < (ssize_t) mPlaylist->size()
+ && diffUs < minDiffUs) {
+ ++index;
+
+ sp<AMessage> itemMeta;
+ CHECK(mPlaylist->itemAt(index, NULL /* uri */, &itemMeta));
+
+ int64_t itemDurationUs;
+ CHECK(itemMeta->findInt64("durationUs", &itemDurationUs));
- anchorTimeUs -= itemDurationUs;
- --index;
+ diffUs += itemDurationUs;
+ }
}
- int32_t newSeqNumber = firstSeqNumberInPlaylist + index + 1;
- if (newSeqNumber <= lastSeqNumberInPlaylist) {
- return newSeqNumber;
- } else {
- return lastSeqNumberInPlaylist;
+ mSeqNumber = firstSeqNumberInPlaylist + index;
+
+ if (mSeqNumber != oldSeqNumber) {
+ FLOGV("guessed wrong seg number: diff %lld out of [%lld, %lld]",
+ (long long) anchorTimeUs - mStartTimeUs,
+ (long long) minDiffUs,
+ (long long) maxDiffUs);
+ return true;
}
+ return false;
}
int32_t PlaylistFetcher::getSeqNumberForDiscontinuity(size_t discontinuitySeq) const {
- int32_t firstSeqNumberInPlaylist;
- if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
- "media-sequence", &firstSeqNumberInPlaylist)) {
- firstSeqNumberInPlaylist = 0;
- }
+ int32_t firstSeqNumberInPlaylist = mPlaylist->getFirstSeqNumber();
size_t index = 0;
while (index < mPlaylist->size()) {
@@ -1463,12 +1471,6 @@ int32_t PlaylistFetcher::getSeqNumberForDiscontinuity(size_t discontinuitySeq) c
}
int32_t PlaylistFetcher::getSeqNumberForTime(int64_t timeUs) const {
- int32_t firstSeqNumberInPlaylist;
- if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
- "media-sequence", &firstSeqNumberInPlaylist)) {
- firstSeqNumberInPlaylist = 0;
- }
-
size_t index = 0;
int64_t segmentStartUs = 0;
while (index < mPlaylist->size()) {
@@ -1491,7 +1493,7 @@ int32_t PlaylistFetcher::getSeqNumberForTime(int64_t timeUs) const {
index = mPlaylist->size() - 1;
}
- return firstSeqNumberInPlaylist + index;
+ return mPlaylist->getFirstSeqNumber() + index;
}
const sp<ABuffer> &PlaylistFetcher::setAccessUnitProperties(
@@ -1506,14 +1508,13 @@ const sp<ABuffer> &PlaylistFetcher::setAccessUnitProperties(
accessUnit->meta()->setInt32("discard", discard);
}
- int32_t targetDurationSecs;
- if (mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)) {
- accessUnit->meta()->setInt32("targetDuration", targetDurationSecs);
- }
-
accessUnit->meta()->setInt32("discontinuitySeq", mDiscontinuitySeq);
accessUnit->meta()->setInt64("segmentStartTimeUs", getSegmentStartTimeUs(mSeqNumber));
+ accessUnit->meta()->setInt64("segmentFirstTimeUs", mSegmentFirstPTS);
accessUnit->meta()->setInt64("segmentDurationUs", getSegmentDurationUs(mSeqNumber));
+ if (!mPlaylist->isComplete() && !mPlaylist->isEvent()) {
+ accessUnit->meta()->setInt64("playlistTimeUs", mPlaylistTimeUs);
+ }
return accessUnit;
}
@@ -1575,6 +1576,59 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu
// setRange to indicate consumed bytes.
buffer->setRange(buffer->offset() + offset, buffer->size() - offset);
+ if (mSegmentFirstPTS < 0ll) {
+ // get the smallest first PTS from all streams present in this parser
+ for (size_t i = mPacketSources.size(); i-- > 0;) {
+ const LiveSession::StreamType stream = mPacketSources.keyAt(i);
+ if (stream == LiveSession::STREAMTYPE_SUBTITLES) {
+ ALOGE("MPEG2 Transport streams do not contain subtitles.");
+ return ERROR_MALFORMED;
+ }
+ ATSParser::SourceType type =LiveSession::getSourceTypeForStream(stream);
+ sp<AnotherPacketSource> source =
+ static_cast<AnotherPacketSource *>(
+ mTSParser->getSource(type).get());
+
+ if (source == NULL) {
+ continue;
+ }
+ sp<AMessage> meta = source->getMetaAfterLastDequeued(0);
+ if (meta != NULL) {
+ int64_t timeUs;
+ CHECK(meta->findInt64("timeUs", &timeUs));
+ if (mSegmentFirstPTS < 0ll || timeUs < mSegmentFirstPTS) {
+ mSegmentFirstPTS = timeUs;
+ }
+ }
+ }
+ if (mSegmentFirstPTS < 0ll) {
+ // didn't find any TS packet, can return early
+ return OK;
+ }
+ if (!mStartTimeUsRelative) {
+ // mStartup
+ // mStartup is true until we have queued a packet for all the streams
+ // we are fetching. We queue packets whose timestamps are greater than
+ // mStartTimeUs.
+ // mSegmentStartTimeUs >= 0
+ // mSegmentStartTimeUs is non-negative when adapting or switching tracks
+ // adjustSeqNumberWithAnchorTime(timeUs) == true
+ // we guessed a seq number that's either too large or too small.
+ // If this happens, we'll adjust mSeqNumber and restart fetching from new
+ // location. Note that we only want to adjust once, so set mSegmentStartTimeUs
+ // to -1 so that we don't enter this chunk next time.
+ if (mStartup && mSegmentStartTimeUs >= 0
+ && adjustSeqNumberWithAnchorTime(mSegmentFirstPTS)) {
+ mStartTimeUsNotify = mNotify->dup();
+ mStartTimeUsNotify->setInt32("what", kWhatStartedAt);
+ mStartTimeUsNotify->setString("uri", mURI);
+ mIDRFound = false;
+ mSegmentStartTimeUs = -1;
+ return -EAGAIN;
+ }
+ }
+ }
+
status_t err = OK;
for (size_t i = mPacketSources.size(); i-- > 0;) {
sp<AnotherPacketSource> packetSource = mPacketSources.valueAt(i);
@@ -1609,76 +1663,17 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu
int64_t timeUs;
CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
- if (mSegmentFirstPTS < 0ll) {
- mSegmentFirstPTS = timeUs;
- if (!mStartTimeUsRelative) {
- int32_t firstSeqNumberInPlaylist;
- if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
- "media-sequence", &firstSeqNumberInPlaylist)) {
- firstSeqNumberInPlaylist = 0;
- }
-
- int32_t targetDurationSecs;
- CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
- int64_t targetDurationUs = targetDurationSecs * 1000000ll;
- // mStartup
- // mStartup is true until we have queued a packet for all the streams
- // we are fetching. We queue packets whose timestamps are greater than
- // mStartTimeUs.
- // mSegmentStartTimeUs >= 0
- // mSegmentStartTimeUs is non-negative when adapting or switching tracks
- // mSeqNumber > firstSeqNumberInPlaylist
- // don't decrement mSeqNumber if it already points to the 1st segment
- // timeUs - mStartTimeUs > targetDurationUs:
- // This and the 2 above conditions should only happen when adapting in a live
- // stream; the old fetcher has already fetched to mStartTimeUs; the new fetcher
- // would start fetching after timeUs, which should be greater than mStartTimeUs;
- // the old fetcher would then continue fetching data until timeUs. We don't want
- // timeUs to be too far ahead of mStartTimeUs because we want the old fetcher to
- // stop as early as possible. The definition of being "too far ahead" is
- // arbitrary; here we use targetDurationUs as threshold.
- int64_t targetDiffUs = (mSeekMode == LiveSession::kSeekModeNextSample
- ? 0 : targetDurationUs);
- if (mStartup && mSegmentStartTimeUs >= 0
- && mSeqNumber > firstSeqNumberInPlaylist
- && timeUs - mStartTimeUs > targetDiffUs) {
- // we just guessed a starting timestamp that is too high when adapting in a
- // live stream; re-adjust based on the actual timestamp extracted from the
- // media segment; if we didn't move backward after the re-adjustment
- // (newSeqNumber), start at least 1 segment prior.
- int32_t newSeqNumber = getSeqNumberWithAnchorTime(
- timeUs, targetDiffUs);
-
- FLOGV("guessed wrong seq number: timeUs=%lld, mStartTimeUs=%lld, "
- "targetDurationUs=%lld, mSeqNumber=%d, newSeq=%d, firstSeq=%d",
- (long long)timeUs,
- (long long)mStartTimeUs,
- (long long)targetDurationUs,
- mSeqNumber,
- newSeqNumber,
- firstSeqNumberInPlaylist);
-
- if (newSeqNumber >= mSeqNumber) {
- --mSeqNumber;
- } else {
- mSeqNumber = newSeqNumber;
- }
- mStartTimeUsNotify = mNotify->dup();
- mStartTimeUsNotify->setInt32("what", kWhatStartedAt);
- mStartTimeUsNotify->setString("uri", mURI);
- mIDRFound = false;
- return -EAGAIN;
- }
- }
- }
if (mStartup) {
bool startTimeReached = isStartTimeReached(timeUs);
if (!startTimeReached || (isAvc && !mIDRFound)) {
// buffer up to the closest preceding IDR frame in the next segement,
// or the closest succeeding IDR frame after the exact position
- FSLOGV(stream, "timeUs=%lld, mStartTimeUs=%lld, mIDRFound=%d",
- (long long)timeUs, (long long)mStartTimeUs, mIDRFound);
+ FSLOGV(stream, "timeUs(%lld)-mStartTimeUs(%lld)=%lld, mIDRFound=%d",
+ (long long)timeUs,
+ (long long)mStartTimeUs,
+ (long long)timeUs - mStartTimeUs,
+ mIDRFound);
if (isAvc) {
if (IsIDR(accessUnit)) {
mVideoBuffer->clear();
@@ -1819,7 +1814,6 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits(
buffer->meta()->setInt64("segmentStartTimeUs", getSegmentStartTimeUs(mSeqNumber));
buffer->meta()->setInt32("discontinuitySeq", mDiscontinuitySeq);
buffer->meta()->setInt32("subtitleGeneration", mSubtitleGeneration);
-
packetSource->queueAccessUnit(buffer);
return OK;
}
@@ -1930,6 +1924,18 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits(
mFirstTimeUs = timeUs;
}
+ if (mSegmentFirstPTS < 0ll) {
+ mSegmentFirstPTS = timeUs;
+ if (!mStartTimeUsRelative) {
+ // Duplicated logic from how we handle .ts playlists.
+ if (mStartup && mSegmentStartTimeUs >= 0
+ && adjustSeqNumberWithAnchorTime(timeUs)) {
+ mSegmentStartTimeUs = -1;
+ return -EAGAIN;
+ }
+ }
+ }
+
size_t offset = 0;
while (offset < buffer->size()) {
const uint8_t *adtsHeader = buffer->data() + offset;
@@ -1973,25 +1979,6 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits(
}
if (mStartTimeUsNotify != NULL) {
- int32_t targetDurationSecs;
- CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
- int64_t targetDurationUs = targetDurationSecs * 1000000ll;
-
- int64_t targetDiffUs =(mSeekMode == LiveSession::kSeekModeNextSample
- ? 0 : targetDurationUs);
- // Duplicated logic from how we handle .ts playlists.
- if (mStartup && mSegmentStartTimeUs >= 0
- && timeUs - mStartTimeUs > targetDiffUs) {
- int32_t newSeqNumber = getSeqNumberWithAnchorTime(
- timeUs, targetDiffUs);
- if (newSeqNumber >= mSeqNumber) {
- --mSeqNumber;
- } else {
- mSeqNumber = newSeqNumber;
- }
- return -EAGAIN;
- }
-
mStartTimeUsNotify->setInt32("streamMask", LiveSession::STREAMTYPE_AUDIO);
mStartup = false;
}
@@ -2041,13 +2028,9 @@ void PlaylistFetcher::updateDuration() {
}
void PlaylistFetcher::updateTargetDuration() {
- int32_t targetDurationSecs;
- CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
- int64_t targetDurationUs = targetDurationSecs * 1000000ll;
-
sp<AMessage> msg = mNotify->dup();
msg->setInt32("what", kWhatTargetDurationUpdate);
- msg->setInt64("targetDurationUs", targetDurationUs);
+ msg->setInt64("targetDurationUs", mPlaylist->getTargetDuration());
msg->post();
}
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index 95de9c3..1f5e9b0 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -138,6 +138,7 @@ private:
KeyedVector<AString, sp<ABuffer> > mAESKeyForURI;
int64_t mLastPlaylistFetchTimeUs;
+ int64_t mPlaylistTimeUs;
sp<M3UParser> mPlaylist;
int32_t mSeqNumber;
int32_t mNumRetries;
@@ -238,8 +239,7 @@ private:
void queueDiscontinuity(
ATSParser::DiscontinuityType type, const sp<AMessage> &extra);
- int32_t getSeqNumberWithAnchorTime(
- int64_t anchorTimeUs, int64_t targetDurationUs) const;
+ bool adjustSeqNumberWithAnchorTime(int64_t anchorTimeUs);
int32_t getSeqNumberForDiscontinuity(size_t discontinuitySeq) const;
int32_t getSeqNumberForTime(int64_t timeUs) const;