summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
Diffstat (limited to 'media')
-rw-r--r--media/libmedia/AudioRecord.cpp7
-rw-r--r--media/libmedia/AudioTrackShared.cpp1
-rw-r--r--media/libmediaplayerservice/DrmSessionManager.cpp33
-rw-r--r--media/libmediaplayerservice/ProcessInfoInterface.h33
-rw-r--r--media/libmediaplayerservice/tests/DrmSessionManager_test.cpp2
-rw-r--r--media/libstagefright/Android.mk1
-rw-r--r--media/libstagefright/ProcessInfo.cpp53
-rw-r--r--media/libstagefright/httplive/LiveSession.cpp91
-rw-r--r--media/libstagefright/httplive/M3UParser.cpp6
-rw-r--r--media/libstagefright/httplive/M3UParser.h1
-rw-r--r--media/libstagefright/httplive/PlaylistFetcher.cpp160
-rw-r--r--media/libstagefright/httplive/PlaylistFetcher.h13
-rw-r--r--media/libstagefright/mpeg2ts/AnotherPacketSource.cpp12
13 files changed, 237 insertions, 176 deletions
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 07ca14f..48abb96 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -863,8 +863,11 @@ nsecs_t AudioRecord::processAudioBuffer()
if (!markerReached && position < markerPosition) {
minFrames = markerPosition - position;
}
- if (updatePeriod > 0 && updatePeriod < minFrames) {
- minFrames = updatePeriod;
+ if (updatePeriod > 0) {
+ uint32_t remaining = newPosition - position;
+ if (remaining < minFrames) {
+ minFrames = remaining;
+ }
}
// If > 0, poll periodically to recover from a stuck server. A good value is 2.
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 08241e2..6d5f1af 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -423,7 +423,6 @@ status_t AudioTrackClientProxy::waitStreamEndDone(const struct timespec *request
goto end;
}
// check for obtainBuffer interrupted by client
- // check for obtainBuffer interrupted by client
if (flags & CBLK_INTERRUPT) {
ALOGV("waitStreamEndDone() interrupted by client");
status = -EINTR;
diff --git a/media/libmediaplayerservice/DrmSessionManager.cpp b/media/libmediaplayerservice/DrmSessionManager.cpp
index 6e17eb1..641f881 100644
--- a/media/libmediaplayerservice/DrmSessionManager.cpp
+++ b/media/libmediaplayerservice/DrmSessionManager.cpp
@@ -21,10 +21,10 @@
#include "DrmSessionManager.h"
#include "DrmSessionClientInterface.h"
-#include "ProcessInfoInterface.h"
#include <binder/IPCThreadState.h>
#include <binder/IProcessInfoService.h>
#include <binder/IServiceManager.h>
+#include <media/stagefright/ProcessInfo.h>
#include <unistd.h>
#include <utils/String8.h>
@@ -38,37 +38,6 @@ static String8 GetSessionIdString(const Vector<uint8_t> &sessionId) {
return sessionIdStr;
}
-struct ProcessInfo : public ProcessInfoInterface {
- ProcessInfo() {}
-
- virtual bool getPriority(int pid, int* priority) {
- sp<IBinder> binder = defaultServiceManager()->getService(String16("processinfo"));
- sp<IProcessInfoService> service = interface_cast<IProcessInfoService>(binder);
-
- size_t length = 1;
- int32_t states;
- status_t err = service->getProcessStatesFromPids(length, &pid, &states);
- if (err != OK) {
- ALOGE("getProcessStatesFromPids failed");
- return false;
- }
- ALOGV("pid %d states %d", pid, states);
- if (states < 0) {
- return false;
- }
-
- // Use process state as the priority. Lower the value, higher the priority.
- *priority = states;
- return true;
- }
-
-protected:
- virtual ~ProcessInfo() {}
-
-private:
- DISALLOW_EVIL_CONSTRUCTORS(ProcessInfo);
-};
-
bool isEqualSessionId(const Vector<uint8_t> &sessionId1, const Vector<uint8_t> &sessionId2) {
if (sessionId1.size() != sessionId2.size()) {
return false;
diff --git a/media/libmediaplayerservice/ProcessInfoInterface.h b/media/libmediaplayerservice/ProcessInfoInterface.h
deleted file mode 100644
index 222f92d..0000000
--- a/media/libmediaplayerservice/ProcessInfoInterface.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#ifndef PROCESS_INFO_INTERFACE_H_
-#define PROCESS_INFO_INTERFACE_H_
-
-#include <utils/RefBase.h>
-
-namespace android {
-
-struct ProcessInfoInterface : public RefBase {
- virtual bool getPriority(int pid, int* priority) = 0;
-
-protected:
- virtual ~ProcessInfoInterface() {}
-};
-
-} // namespace android
-
-#endif // PROCESS_INFO_INTERFACE_H_
diff --git a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
index 27b482b..d3e760b 100644
--- a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
+++ b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
@@ -23,8 +23,8 @@
#include "Drm.h"
#include "DrmSessionClientInterface.h"
#include "DrmSessionManager.h"
-#include "ProcessInfoInterface.h"
#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/ProcessInfoInterface.h>
namespace android {
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 38f2e34..177293d 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -47,6 +47,7 @@ LOCAL_SRC_FILES:= \
OMXClient.cpp \
OMXCodec.cpp \
OggExtractor.cpp \
+ ProcessInfo.cpp \
SampleIterator.cpp \
SampleTable.cpp \
SkipCutBuffer.cpp \
diff --git a/media/libstagefright/ProcessInfo.cpp b/media/libstagefright/ProcessInfo.cpp
new file mode 100644
index 0000000..b4172b3
--- /dev/null
+++ b/media/libstagefright/ProcessInfo.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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 "ProcessInfo"
+#include <utils/Log.h>
+
+#include <media/stagefright/ProcessInfo.h>
+
+#include <binder/IProcessInfoService.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+ProcessInfo::ProcessInfo() {}
+
+bool ProcessInfo::getPriority(int pid, int* priority) {
+ sp<IBinder> binder = defaultServiceManager()->getService(String16("processinfo"));
+ sp<IProcessInfoService> service = interface_cast<IProcessInfoService>(binder);
+
+ size_t length = 1;
+ int32_t states;
+ status_t err = service->getProcessStatesFromPids(length, &pid, &states);
+ if (err != OK) {
+ ALOGE("getProcessStatesFromPids failed");
+ return false;
+ }
+ ALOGV("pid %d states %d", pid, states);
+ if (states < 0) {
+ return false;
+ }
+
+ // Use process state as the priority. Lower the value, higher the priority.
+ *priority = states;
+ return true;
+}
+
+ProcessInfo::~ProcessInfo() {}
+
+} // namespace android
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index a8f60a8..a94754b 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -76,8 +76,6 @@ LiveSession::LiveSession(
mRealTimeBaseUs(0ll),
mReconfigurationInProgress(false),
mSwitchInProgress(false),
- mDisconnectReplyID(0),
- mSeekReplyID(0),
mFirstTimeUsValid(false),
mFirstTimeUs(0),
mLastSeekTimeUs(0),
@@ -90,7 +88,6 @@ LiveSession::LiveSession(
for (size_t i = 0; i < kMaxStreams; ++i) {
mPacketSources.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
mPacketSources2.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
- mBuffering[i] = false;
}
size_t numHistoryItems = kBandwidthHistoryBytes /
@@ -139,13 +136,24 @@ status_t LiveSession::dequeueAccessUnit(
ssize_t idx = typeToIndex(stream);
if (!packetSource->hasBufferAvailable(&finalResult)) {
if (finalResult == OK) {
- mBuffering[idx] = true;
return -EAGAIN;
} else {
return finalResult;
}
}
+ // Do not let client pull data if we don't have format yet.
+ // We might only have a format discontinuity queued without actual data.
+ // When NuPlayerDecoder dequeues the format discontinuity, it will
+ // immediately try to getFormat. If we return NULL, NuPlayerDecoder
+ // thinks it can do seamless change, so will not shutdown decoder.
+ // When the actual format arrives, it can't handle it and get stuck.
+ // TODO: We need a method to check if the packet source has any
+ // data packets available, dequeuing should only start then.
+ sp<MetaData> format = packetSource->getFormat();
+ if (format == NULL) {
+ return -EAGAIN;
+ }
int32_t targetDuration = 0;
sp<AMessage> meta = packetSource->getLatestEnqueuedMeta();
if (meta != NULL) {
@@ -160,18 +168,6 @@ status_t LiveSession::dequeueAccessUnit(
targetDurationUs = PlaylistFetcher::kMinBufferedDurationUs;
}
- if (mBuffering[idx]) {
- if (mSwitchInProgress
- || packetSource->isFinished(0)
- || packetSource->hasBufferAvailable(&finalResult)) {
- mBuffering[idx] = false;
- }
- }
-
- if (mBuffering[idx]) {
- return -EAGAIN;
- }
-
// wait for counterpart
sp<AnotherPacketSource> otherSource;
uint32_t mask = mNewStreamMask & mStreamMask;
@@ -737,7 +733,7 @@ void LiveSession::onFinishDisconnect2() {
response->setInt32("err", OK);
response->postReply(mDisconnectReplyID);
- mDisconnectReplyID = 0;
+ mDisconnectReplyID.clear();
}
sp<PlaylistFetcher> LiveSession::addFetcher(const char *uri) {
@@ -1192,25 +1188,27 @@ void LiveSession::changeConfiguration(
bool discardFetcher = true;
- // If we're seeking all current fetchers are discarded.
if (timeUs < 0ll) {
// delay fetcher removal if not picking tracks
discardFetcher = pickTrack;
- for (size_t j = 0; j < kMaxStreams; ++j) {
- StreamType type = indexToType(j);
- if ((streamMask & type) && uri == URIs[j]) {
- resumeMask |= type;
- streamMask &= ~type;
- discardFetcher = false;
- }
+ }
+
+ for (size_t j = 0; j < kMaxStreams; ++j) {
+ StreamType type = indexToType(j);
+ if ((streamMask & type) && uri == URIs[j]) {
+ resumeMask |= type;
+ streamMask &= ~type;
+ discardFetcher = false;
}
}
if (discardFetcher) {
mFetcherInfos.valueAt(i).mFetcher->stopAsync();
} else {
- mFetcherInfos.valueAt(i).mFetcher->pauseAsync();
+ // if we're seeking, pause immediately (no need to finish the segment)
+ bool immediate = (timeUs >= 0ll);
+ mFetcherInfos.valueAt(i).mFetcher->pauseAsync(immediate);
}
}
@@ -1274,11 +1272,11 @@ void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) {
mDiscontinuityOffsetTimesUs.clear();
mDiscontinuityAbsStartTimesUs.clear();
- if (mSeekReplyID != 0) {
+ if (mSeekReplyID != NULL) {
CHECK(mSeekReply != NULL);
mSeekReply->setInt32("err", OK);
mSeekReply->postReply(mSeekReplyID);
- mSeekReplyID = 0;
+ mSeekReplyID.clear();
mSeekReply.clear();
}
}
@@ -1287,9 +1285,6 @@ void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) {
CHECK(msg->findInt32("streamMask", (int32_t *)&streamMask));
CHECK(msg->findInt32("resumeMask", (int32_t *)&resumeMask));
- // currently onChangeConfiguration2 is only called for seeking;
- // remove the following CHECK if using it else where.
- CHECK_EQ(resumeMask, 0);
streamMask |= resumeMask;
AString URIs[kMaxStreams];
@@ -1301,17 +1296,25 @@ void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) {
}
}
- // Determine which decoders to shutdown on the player side,
- // a decoder has to be shutdown if either
- // 1) its streamtype was active before but now longer isn't.
- // or
- // 2) its streamtype was already active and still is but the URI
- // has changed.
uint32_t changedMask = 0;
for (size_t i = 0; i < kMaxStreams && i != kSubtitleIndex; ++i) {
- if (((mStreamMask & streamMask & indexToType(i))
- && !(URIs[i] == mStreams[i].mUri))
- || (mStreamMask & ~streamMask & indexToType(i))) {
+ // stream URI could change even if onChangeConfiguration2 is only
+ // used for seek. Seek could happen during a bw switch, in this
+ // case bw switch will be cancelled, but the seekTo position will
+ // fetch from the new URI.
+ if ((mStreamMask & streamMask & indexToType(i))
+ && !mStreams[i].mUri.empty()
+ && !(URIs[i] == mStreams[i].mUri)) {
+ 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);
+ }
+ // Determine which decoders to shutdown on the player side,
+ // a decoder has to be shutdown if its streamtype was active
+ // before but now longer isn't.
+ if ((mStreamMask & ~streamMask & indexToType(i))) {
changedMask |= indexToType(i);
}
}
@@ -1394,7 +1397,7 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) {
if (sources[kAudioIndex] != NULL || sources[kVideoIndex] != NULL
|| sources[kSubtitleIndex] != NULL) {
info.mFetcher->startAsync(
- sources[kAudioIndex], sources[kVideoIndex], sources[kSubtitleIndex]);
+ sources[kAudioIndex], sources[kVideoIndex], sources[kSubtitleIndex], timeUs);
} else {
info.mToBeRemoved = true;
}
@@ -1474,7 +1477,7 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) {
break;
}
- ALOGV("stream[%d]: queue format change", j);
+ ALOGV("stream[%zu]: queue format change", j);
sources[j]->queueDiscontinuity(
ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true);
@@ -1514,7 +1517,7 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) {
mStreamMask = mNewStreamMask;
}
- if (mDisconnectReplyID != 0) {
+ if (mDisconnectReplyID != NULL) {
finishDisconnect();
}
}
@@ -1679,7 +1682,7 @@ bool LiveSession::checkBuffering(bool &low, bool &mid, bool &high) {
++activeCount;
int64_t bufferedDurationUs =
mPacketSources[i]->getEstimatedDurationUs();
- ALOGV("source[%d]: buffered %lld us", i, bufferedDurationUs);
+ ALOGV("source[%zu]: buffered %lld us", i, (long long)bufferedDurationUs);
if (bufferedDurationUs < kLowWaterMark) {
++lowCount;
break;
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 997b694..3c5d7cf 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -251,6 +251,7 @@ M3UParser::M3UParser(
mIsComplete(false),
mIsEvent(false),
mDiscontinuitySeq(0),
+ mDiscontinuityCount(0),
mSelectedIndex(-1) {
mInitCheck = parse(data, size);
}
@@ -582,6 +583,7 @@ status_t M3UParser::parse(const void *_data, size_t size) {
itemMeta = new AMessage;
}
itemMeta->setInt32("discontinuity", true);
+ ++mDiscontinuityCount;
} else if (line.startsWith("#EXT-X-STREAM-INF")) {
if (mMeta != NULL) {
return ERROR_MALFORMED;
@@ -609,6 +611,9 @@ status_t M3UParser::parse(const void *_data, size_t size) {
} else if (line.startsWith("#EXT-X-MEDIA")) {
err = parseMedia(line);
} else if (line.startsWith("#EXT-X-DISCONTINUITY-SEQUENCE")) {
+ if (mIsVariantPlaylist) {
+ return ERROR_MALFORMED;
+ }
size_t seq;
err = parseDiscontinuitySequence(line, &seq);
if (err == OK) {
@@ -628,6 +633,7 @@ status_t M3UParser::parse(const void *_data, size_t size) {
|| !itemMeta->findInt64("durationUs", &durationUs)) {
return ERROR_MALFORMED;
}
+ itemMeta->setInt32("discontinuity-sequence", mDiscontinuitySeq + mDiscontinuityCount);
}
mItems.push();
diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h
index 1cad060..d475683 100644
--- a/media/libstagefright/httplive/M3UParser.h
+++ b/media/libstagefright/httplive/M3UParser.h
@@ -70,6 +70,7 @@ private:
bool mIsComplete;
bool mIsEvent;
size_t mDiscontinuitySeq;
+ int32_t mDiscontinuityCount;
sp<AMessage> mMeta;
Vector<Item> mItems;
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 3710686..7f818a8 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -60,7 +60,6 @@ PlaylistFetcher::PlaylistFetcher(
const char *uri,
int32_t subtitleGeneration)
: mNotify(notify),
- mStartTimeUsNotify(notify->dup()),
mSession(session),
mURI(uri),
mStreamTypeMask(0),
@@ -74,16 +73,16 @@ PlaylistFetcher::PlaylistFetcher(
mStartup(true),
mAdaptive(false),
mPrepared(false),
+ mTimeChangeSignaled(false),
mNextPTSTimeUs(-1ll),
mMonitorQueueGeneration(0),
mSubtitleGeneration(subtitleGeneration),
+ mLastDiscontinuitySeq(-1ll),
+ mStopping(false),
mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY),
mFirstPTSValid(false),
- mAbsoluteTimeAnchorUs(0ll),
mVideoBuffer(new AnotherPacketSource(NULL)) {
memset(mPlaylistHash, 0, sizeof(mPlaylistHash));
- mStartTimeUsNotify->setInt32("what", kWhatStartedAt);
- mStartTimeUsNotify->setInt32("streamMask", 0);
}
PlaylistFetcher::~PlaylistFetcher() {
@@ -335,6 +334,11 @@ void PlaylistFetcher::cancelMonitorQueue() {
++mMonitorQueueGeneration;
}
+void PlaylistFetcher::setStopping(bool stopping) {
+ AutoMutex _l(mStoppingLock);
+ mStopping = stopping;
+}
+
void PlaylistFetcher::startAsync(
const sp<AnotherPacketSource> &audioSource,
const sp<AnotherPacketSource> &videoSource,
@@ -370,11 +374,16 @@ void PlaylistFetcher::startAsync(
msg->post();
}
-void PlaylistFetcher::pauseAsync() {
+void PlaylistFetcher::pauseAsync(bool immediate) {
+ if (immediate) {
+ setStopping(true);
+ }
(new AMessage(kWhatPause, this))->post();
}
void PlaylistFetcher::stopAsync(bool clear) {
+ setStopping(true);
+
sp<AMessage> msg = new AMessage(kWhatStop, this);
msg->setInt32("clear", clear);
msg->post();
@@ -451,6 +460,10 @@ void PlaylistFetcher::onMessageReceived(const sp<AMessage> &msg) {
status_t PlaylistFetcher::onStart(const sp<AMessage> &msg) {
mPacketSources.clear();
+ mStopParams.clear();
+ mStartTimeUsNotify = mNotify->dup();
+ mStartTimeUsNotify->setInt32("what", kWhatStartedAt);
+ mStartTimeUsNotify->setInt32("streamMask", 0);
uint32_t streamTypeMask;
CHECK(msg->findInt32("streamTypeMask", (int32_t *)&streamTypeMask));
@@ -496,12 +509,18 @@ status_t PlaylistFetcher::onStart(const sp<AMessage> &msg) {
mSegmentStartTimeUs = segmentStartTimeUs;
mDiscontinuitySeq = startDiscontinuitySeq;
+ mRefreshState = INITIAL_MINIMUM_RELOAD_DELAY;
+
if (startTimeUs >= 0) {
mStartTimeUs = startTimeUs;
+ mFirstPTSValid = false;
mSeqNumber = -1;
mStartup = true;
mPrepared = false;
+ mIDRFound = false;
+ mTimeChangeSignaled = false;
mAdaptive = adaptive;
+ mVideoBuffer->clear();
}
postMonitorQueue();
@@ -511,6 +530,9 @@ status_t PlaylistFetcher::onStart(const sp<AMessage> &msg) {
void PlaylistFetcher::onPause() {
cancelMonitorQueue();
+ mLastDiscontinuitySeq = mDiscontinuitySeq;
+
+ setStopping(false);
}
void PlaylistFetcher::onStop(const sp<AMessage> &msg) {
@@ -527,6 +549,8 @@ void PlaylistFetcher::onStop(const sp<AMessage> &msg) {
mPacketSources.clear();
mStreamTypeMask = 0;
+
+ setStopping(false);
}
// Resume until we have reached the boundary timestamps listed in `msg`; when
@@ -624,11 +648,7 @@ void PlaylistFetcher::onMonitorQueue() {
targetDurationUs = targetDurationSecs * 1000000ll;
}
- // buffer at least 3 times the target duration, or up to 10 seconds
- int64_t durationToBufferUs = targetDurationUs * 3;
- if (durationToBufferUs > kMinBufferedDurationUs) {
- durationToBufferUs = kMinBufferedDurationUs;
- }
+ int64_t durationToBufferUs = kMinBufferedDurationUs;
int64_t bufferedDurationUs = 0ll;
status_t finalResult = NOT_ENOUGH_DATA;
@@ -874,11 +894,22 @@ void PlaylistFetcher::onDownloadNext() {
&uri,
&itemMeta));
+ CHECK(itemMeta->findInt32("discontinuity-sequence", &mDiscontinuitySeq));
+
int32_t val;
if (itemMeta->findInt32("discontinuity", &val) && val != 0) {
- mDiscontinuitySeq++;
+ discontinuity = true;
+ } else if (mLastDiscontinuitySeq >= 0
+ && mDiscontinuitySeq != mLastDiscontinuitySeq) {
+ // Seek jumped to a new discontinuity sequence. We need to signal
+ // a format change to decoder. Decoder needs to shutdown and be
+ // created again if seamless format change is unsupported.
+ ALOGV("saw discontinuity: mStartup %d, mLastDiscontinuitySeq %d, "
+ "mDiscontinuitySeq %d, mStartTimeUs %lld",
+ mStartup, mLastDiscontinuitySeq, mDiscontinuitySeq, (long long)mStartTimeUs);
discontinuity = true;
}
+ mLastDiscontinuitySeq = -1;
int64_t range_offset, range_length;
if (!itemMeta->findInt64("range-offset", &range_offset)
@@ -907,8 +938,52 @@ void PlaylistFetcher::onDownloadNext() {
}
}
+ if ((mStartup && !mTimeChangeSignaled) || discontinuity) {
+ // We need to signal a time discontinuity to ATSParser on the
+ // first segment after start, or on a discontinuity segment.
+ // Setting mNextPTSTimeUs informs extractAndQueueAccessUnitsXX()
+ // to send the time discontinuity.
+ if (mPlaylist->isComplete() || mPlaylist->isEvent()) {
+ // If this was a live event this made no sense since
+ // we don't have access to all the segment before the current
+ // one.
+ mNextPTSTimeUs = getSegmentStartTimeUs(mSeqNumber);
+ }
+
+ // Setting mTimeChangeSignaled to true, so that if start time
+ // searching goes into 2nd segment (without a discontinuity),
+ // we don't reset time again. It causes corruption when pending
+ // data in ATSParser is cleared.
+ mTimeChangeSignaled = true;
+ }
+
+ if (discontinuity) {
+ ALOGI("queueing discontinuity (explicit=%d)", discontinuity);
+
+ // Signal a format discontinuity to ATSParser to clear partial data
+ // from previous streams. Not doing this causes bitstream corruption.
+ mTSParser->signalDiscontinuity(
+ ATSParser::DISCONTINUITY_FORMATCHANGE, NULL /* extra */);
+
+ queueDiscontinuity(
+ ATSParser::DISCONTINUITY_FORMATCHANGE,
+ NULL /* extra */);
+
+ if (mStartup && mStartTimeUsRelative && mFirstPTSValid) {
+ // This means we guessed mStartTimeUs to be in the previous
+ // segment (likely very close to the end), but either video or
+ // audio has not found start by the end of that segment.
+ //
+ // If this new segment is not a discontinuity, keep searching.
+ //
+ // If this new segment even got a discontinuity marker, just
+ // set mStartTimeUs=0, and take all samples from now on.
+ mStartTimeUs = 0;
+ mFirstPTSValid = false;
+ }
+ }
+
// block-wise download
- bool startup = mStartup;
ssize_t bytesRead;
do {
bytesRead = mSession->fetchFile(
@@ -938,29 +1013,6 @@ void PlaylistFetcher::onDownloadNext() {
return;
}
- if (startup || discontinuity) {
- // Signal discontinuity.
-
- if (mPlaylist->isComplete() || mPlaylist->isEvent()) {
- // If this was a live event this made no sense since
- // we don't have access to all the segment before the current
- // one.
- mNextPTSTimeUs = getSegmentStartTimeUs(mSeqNumber);
- }
-
- if (discontinuity) {
- ALOGI("queueing discontinuity (explicit=%d)", discontinuity);
-
- queueDiscontinuity(
- ATSParser::DISCONTINUITY_FORMATCHANGE,
- NULL /* extra */);
-
- discontinuity = false;
- }
-
- startup = false;
- }
-
err = OK;
if (bufferStartsWithTsSyncByte(buffer)) {
// Incremental extraction is only supported for MPEG2 transport streams.
@@ -995,7 +1047,7 @@ void PlaylistFetcher::onDownloadNext() {
return;
}
- } while (bytesRead != 0);
+ } while (bytesRead != 0 && !mStopping);
if (bufferStartsWithTsSyncByte(buffer)) {
// If we don't see a stream in the program table after fetching a full ts segment
@@ -1197,9 +1249,7 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu
mTSParser->signalDiscontinuity(
ATSParser::DISCONTINUITY_TIME, extra);
- mAbsoluteTimeAnchorUs = mNextPTSTimeUs;
mNextPTSTimeUs = -1ll;
- mFirstPTSValid = false;
}
size_t offset = 0;
@@ -1252,12 +1302,17 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu
continue;
}
- int64_t timeUs;
+ const char *mime;
+ sp<MetaData> format = source->getFormat();
+ bool isAvc = format != NULL && format->findCString(kKeyMIMEType, &mime)
+ && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
+
sp<ABuffer> accessUnit;
status_t finalResult;
while (source->hasBufferAvailable(&finalResult)
&& source->dequeueAccessUnit(&accessUnit) == OK) {
+ int64_t timeUs;
CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
if (mStartup) {
@@ -1272,30 +1327,25 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu
}
}
- if (timeUs < mStartTimeUs) {
+ if (timeUs < mStartTimeUs || (isAvc && !mIDRFound)) {
// buffer up to the closest preceding IDR frame
ALOGV("timeUs %" PRId64 " us < mStartTimeUs %" PRId64 " us",
timeUs, mStartTimeUs);
- const char *mime;
- sp<MetaData> format = source->getFormat();
- bool isAvc = false;
- if (format != NULL && format->findCString(kKeyMIMEType, &mime)
- && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
- isAvc = true;
- }
- if (isAvc && IsIDR(accessUnit)) {
- mVideoBuffer->clear();
- }
if (isAvc) {
- mVideoBuffer->queueAccessUnit(accessUnit);
+ if (IsIDR(accessUnit)) {
+ mVideoBuffer->clear();
+ mIDRFound = true;
+ }
+ if (mIDRFound) {
+ mVideoBuffer->queueAccessUnit(accessUnit);
+ }
}
continue;
}
}
- CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
- if (mStartTimeUsNotify != NULL && timeUs > mStartTimeUs) {
+ if (mStartTimeUsNotify != NULL) {
int32_t firstSeqNumberInPlaylist;
if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
"media-sequence", &firstSeqNumberInPlaylist)) {
@@ -1464,8 +1514,6 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits(
}
if (mNextPTSTimeUs >= 0ll) {
- mFirstPTSValid = false;
- mAbsoluteTimeAnchorUs = mNextPTSTimeUs;
mNextPTSTimeUs = -1ll;
}
@@ -1566,7 +1614,7 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits(
CHECK(packetSource->getFormat()->findInt32(kKeySampleRate, &sampleRate));
int64_t timeUs = (PTS * 100ll) / 9ll;
- if (!mFirstPTSValid) {
+ if (mStartup && !mFirstPTSValid) {
mFirstPTSValid = true;
mFirstTimeUs = timeUs;
}
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index 2f11949..b82e50d 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -67,7 +67,7 @@ struct PlaylistFetcher : public AHandler {
int32_t startDiscontinuitySeq = 0,
bool adaptive = false);
- void pauseAsync();
+ void pauseAsync(bool immediate = false);
void stopAsync(bool clear = true);
@@ -116,7 +116,7 @@ private:
// adapting or switching tracks.
int64_t mSegmentStartTimeUs;
- ssize_t mDiscontinuitySeq;
+ int32_t mDiscontinuitySeq;
bool mStartTimeUsRelative;
sp<AMessage> mStopParams; // message containing the latest timestamps we should fetch.
@@ -130,13 +130,20 @@ private:
int32_t mSeqNumber;
int32_t mNumRetries;
bool mStartup;
+ bool mIDRFound;
bool mAdaptive;
bool mPrepared;
+ bool mTimeChangeSignaled;
int64_t mNextPTSTimeUs;
int32_t mMonitorQueueGeneration;
const int32_t mSubtitleGeneration;
+ int32_t mLastDiscontinuitySeq;
+
+ Mutex mStoppingLock;
+ bool mStopping;
+
enum RefreshState {
INITIAL_MINIMUM_RELOAD_DELAY,
FIRST_UNCHANGED_RELOAD_ATTEMPT,
@@ -152,7 +159,6 @@ private:
bool mFirstPTSValid;
uint64_t mFirstPTS;
int64_t mFirstTimeUs;
- int64_t mAbsoluteTimeAnchorUs;
sp<AnotherPacketSource> mVideoBuffer;
// Stores the initialization vector to decrypt the next block of cipher text, which can
@@ -175,6 +181,7 @@ private:
void postMonitorQueue(int64_t delayUs = 0, int64_t minDelayUs = 0);
void cancelMonitorQueue();
+ void setStopping(bool stopping);
int64_t delayUsToRefreshPlaylist() const;
status_t refreshPlaylist();
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index bb05417..79a9b04 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -48,7 +48,10 @@ AnotherPacketSource::AnotherPacketSource(const sp<MetaData> &meta)
}
void AnotherPacketSource::setFormat(const sp<MetaData> &meta) {
- CHECK(mFormat == NULL);
+ if (mFormat != NULL) {
+ // Only allowed to be set once. Requires explicit clear to reset.
+ return;
+ }
mIsAudio = false;
mIsVideo = false;
@@ -94,7 +97,8 @@ sp<MetaData> AnotherPacketSource::getFormat() {
if (!buffer->meta()->findInt32("discontinuity", &discontinuity)) {
sp<RefBase> object;
if (buffer->meta()->findObject("format", &object)) {
- return mFormat = static_cast<MetaData*>(object.get());
+ setFormat(static_cast<MetaData*>(object.get()));
+ return mFormat;
}
}
@@ -129,7 +133,7 @@ status_t AnotherPacketSource::dequeueAccessUnit(sp<ABuffer> *buffer) {
sp<RefBase> object;
if ((*buffer)->meta()->findObject("format", &object)) {
- mFormat = static_cast<MetaData*>(object.get());
+ setFormat(static_cast<MetaData*>(object.get()));
}
return OK;
@@ -164,7 +168,7 @@ status_t AnotherPacketSource::read(
sp<RefBase> object;
if (buffer->meta()->findObject("format", &object)) {
- mFormat = static_cast<MetaData*>(object.get());
+ setFormat(static_cast<MetaData*>(object.get()));
}
int64_t timeUs;