зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1245463: [MSE] P3. When abort() is called, wait until the current appendBuffer completes. r=gerald
The W3C spec indicates that while everything in MSE is asynchronous, the abort() command is to interrupt the current segment parser loop and have the reset parser loop synchronously completes the frames present in the input buffer. This causes a fundamental issue that abort() will never result in a deterministic outcome as the segment parser loop may be in different condition. We used to really attempt to abort the current operation, however there could have been a race in the order in which tasks were queued. As such, we now simply wait for the current appendBuffer to complete. This also simplifies the code greatly, as we don't need to worry about pending concurrent appendBuffer. The actually happens to be similar to the Chromium behavior. Similar to bug 1239983, we strongly assert should a segment parser loop be running when it must have completed. MozReview-Commit-ID: 9772PLQEozf
This commit is contained in:
Родитель
9d45d60388
Коммит
30db7c749e
|
@ -39,27 +39,6 @@ using media::TimeUnit;
|
|||
|
||||
namespace dom {
|
||||
|
||||
class BufferAppendRunnable : public nsRunnable {
|
||||
public:
|
||||
BufferAppendRunnable(SourceBuffer* aSourceBuffer,
|
||||
uint32_t aUpdateID)
|
||||
: mSourceBuffer(aSourceBuffer)
|
||||
, mUpdateID(aUpdateID)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override final {
|
||||
|
||||
mSourceBuffer->BufferAppend(mUpdateID);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<SourceBuffer> mSourceBuffer;
|
||||
uint32_t mUpdateID;
|
||||
};
|
||||
|
||||
void
|
||||
SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
|
||||
{
|
||||
|
@ -226,10 +205,13 @@ void
|
|||
SourceBuffer::AbortBufferAppend()
|
||||
{
|
||||
if (mUpdating) {
|
||||
mPendingAppend.DisconnectIfExists();
|
||||
// TODO: Abort stream append loop algorithms.
|
||||
// cancel any pending buffer append.
|
||||
mContentManager->AbortAppendData();
|
||||
if (mPendingAppend.Exists()) {
|
||||
mPendingAppend.Disconnect();
|
||||
mContentManager->AbortAppendData();
|
||||
// Some data may have been added by the Segment Parser Loop.
|
||||
// Check if we need to update the duration.
|
||||
CheckEndTime();
|
||||
}
|
||||
AbortUpdating();
|
||||
}
|
||||
}
|
||||
|
@ -309,7 +291,6 @@ SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
|
|||
, mMediaSource(aMediaSource)
|
||||
, mUpdating(false)
|
||||
, mActive(false)
|
||||
, mUpdateID(0)
|
||||
, mType(aType)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
@ -381,7 +362,6 @@ SourceBuffer::StartUpdating()
|
|||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mUpdating);
|
||||
mUpdating = true;
|
||||
mUpdateID++;
|
||||
QueueAsyncSimpleEvent("updatestart");
|
||||
}
|
||||
|
||||
|
@ -390,12 +370,8 @@ SourceBuffer::StopUpdating()
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mUpdating) {
|
||||
// The buffer append algorithm has been interrupted by abort().
|
||||
//
|
||||
// If the sequence appendBuffer(), abort(), appendBuffer() occurs before
|
||||
// the first StopUpdating() runnable runs, then a second StopUpdating()
|
||||
// runnable will be scheduled, but still only one (the first) will queue
|
||||
// events.
|
||||
// The buffer append or range removal algorithm has been interrupted by
|
||||
// abort().
|
||||
return;
|
||||
}
|
||||
mUpdating = false;
|
||||
|
@ -407,7 +383,6 @@ void
|
|||
SourceBuffer::AbortUpdating()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mUpdating);
|
||||
mUpdating = false;
|
||||
QueueAsyncSimpleEvent("abort");
|
||||
QueueAsyncSimpleEvent("updateend");
|
||||
|
@ -438,23 +413,13 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
|
|||
|
||||
StartUpdating();
|
||||
|
||||
nsCOMPtr<nsIRunnable> task = new BufferAppendRunnable(this, mUpdateID);
|
||||
NS_DispatchToMainThread(task);
|
||||
BufferAppend();
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::BufferAppend(uint32_t aUpdateID)
|
||||
SourceBuffer::BufferAppend()
|
||||
{
|
||||
if (!mUpdating || aUpdateID != mUpdateID) {
|
||||
// The buffer append algorithm has been interrupted by abort().
|
||||
//
|
||||
// If the sequence appendBuffer(), abort(), appendBuffer() occurs before
|
||||
// the first StopUpdating() runnable runs, then a second StopUpdating()
|
||||
// runnable will be scheduled, but still only one (the first) will queue
|
||||
// events.
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mUpdating);
|
||||
MOZ_ASSERT(mMediaSource);
|
||||
MOZ_ASSERT(!mPendingAppend.Exists());
|
||||
|
||||
|
@ -467,11 +432,8 @@ SourceBuffer::BufferAppend(uint32_t aUpdateID)
|
|||
void
|
||||
SourceBuffer::AppendDataCompletedWithSuccess(bool aHasActiveTracks)
|
||||
{
|
||||
MOZ_ASSERT(mUpdating);
|
||||
mPendingAppend.Complete();
|
||||
if (!mUpdating) {
|
||||
// The buffer append algorithm has been interrupted by abort().
|
||||
return;
|
||||
}
|
||||
|
||||
if (aHasActiveTracks) {
|
||||
if (!mActive) {
|
||||
|
@ -494,7 +456,9 @@ SourceBuffer::AppendDataCompletedWithSuccess(bool aHasActiveTracks)
|
|||
void
|
||||
SourceBuffer::AppendDataErrored(nsresult aError)
|
||||
{
|
||||
MOZ_ASSERT(mUpdating);
|
||||
mPendingAppend.Complete();
|
||||
|
||||
switch (aError) {
|
||||
case NS_ERROR_ABORT:
|
||||
// Nothing further to do as the trackbuffer has been shutdown.
|
||||
|
@ -510,10 +474,7 @@ void
|
|||
SourceBuffer::AppendError(bool aDecoderError)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mUpdating) {
|
||||
// The buffer append algorithm has been interrupted by abort().
|
||||
return;
|
||||
}
|
||||
|
||||
mContentManager->ResetParserState();
|
||||
|
||||
mUpdating = false;
|
||||
|
|
|
@ -238,7 +238,7 @@ private:
|
|||
|
||||
// Shared implementation of AppendBuffer overloads.
|
||||
void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv);
|
||||
void BufferAppend(uint32_t aAppendID);
|
||||
void BufferAppend();
|
||||
|
||||
// Implement the "Append Error Algorithm".
|
||||
// Will call endOfStream() with "decode" error if aDecodeError is true.
|
||||
|
@ -266,11 +266,6 @@ private:
|
|||
|
||||
mozilla::Atomic<bool> mActive;
|
||||
|
||||
// Each time mUpdating is set to true, mUpdateID will be incremented.
|
||||
// This allows for a queued AppendData task to identify if it was earlier
|
||||
// aborted and another AppendData queued.
|
||||
uint32_t mUpdateID;
|
||||
|
||||
MozPromiseRequestHolder<SourceBufferContentManager::AppendPromise> mPendingAppend;
|
||||
const nsCString mType;
|
||||
|
||||
|
|
|
@ -108,10 +108,6 @@ public:
|
|||
virtual void RestartGroupStartTimestamp() {}
|
||||
virtual media::TimeUnit GroupEndTimestamp() = 0;
|
||||
|
||||
#if defined(DEBUG)
|
||||
virtual void Dump(const char* aPath) { }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual ~SourceBufferContentManager() { }
|
||||
};
|
||||
|
|
|
@ -96,15 +96,15 @@ TrackBuffersManager::TrackBuffersManager(dom::SourceBufferAttributes* aAttribute
|
|||
, mType(aType)
|
||||
, mParser(ContainerParser::CreateForMIMEType(aType))
|
||||
, mProcessedInput(0)
|
||||
, mAppendRunning(false)
|
||||
, mTaskQueue(aParentDecoder->GetDemuxer()->GetTaskQueue())
|
||||
, mSourceBufferAttributes(aAttributes)
|
||||
, mParentDecoder(new nsMainThreadPtrHolder<MediaSourceDecoder>(aParentDecoder, false /* strict */))
|
||||
, mAbort(false)
|
||||
, mEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold",
|
||||
100 * (1 << 20)))
|
||||
, mEvictionOccurred(false)
|
||||
, mMonitor("TrackBuffersManager")
|
||||
, mAppendRunning(false)
|
||||
, mSegmentParserLoopRunning(false)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be instanciated on the main thread");
|
||||
}
|
||||
|
@ -135,7 +135,6 @@ TrackBuffersManager::AppendIncomingBuffer(IncomingBuffer aData)
|
|||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
mIncomingBuffers.AppendElement(aData);
|
||||
mAbort = false;
|
||||
}
|
||||
|
||||
RefPtr<TrackBuffersManager::AppendPromise>
|
||||
|
@ -144,32 +143,33 @@ TrackBuffersManager::BufferAppend()
|
|||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("");
|
||||
|
||||
mAppendRunning = true;
|
||||
return InvokeAsync(GetTaskQueue(), this,
|
||||
__func__, &TrackBuffersManager::InitSegmentParserLoop);
|
||||
}
|
||||
|
||||
// Abort any pending AppendData.
|
||||
// We don't really care about really aborting our inner loop as by spec the
|
||||
// process is happening asynchronously, as such where and when we would abort is
|
||||
// non-deterministic. The SourceBuffer also makes sure BufferAppend
|
||||
// isn't called should the appendBuffer be immediately aborted.
|
||||
// We do however want to ensure that no new task will be dispatched on our task
|
||||
// queue and only let the current one finish its job. For this we set mAbort
|
||||
// to true.
|
||||
// The MSE spec requires that we abort the current SegmentParserLoop
|
||||
// which is then followed by a call to ResetParserState.
|
||||
// However due to our asynchronous design this causes inherent difficulities.
|
||||
// As the spec behaviour is non deterministic anyway, we instead wait until the
|
||||
// current AppendData has completed its run.
|
||||
void
|
||||
TrackBuffersManager::AbortAppendData()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("");
|
||||
|
||||
mAbort = true;
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
while (mAppendRunning) {
|
||||
mon.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TrackBuffersManager::ResetParserState()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mAppendRunning, "AbortAppendData must have been called");
|
||||
MOZ_RELEASE_ASSERT(!mAppendRunning, "Append is running, abort must have been called");
|
||||
MSE_DEBUG("");
|
||||
|
||||
// 1. If the append state equals PARSING_MEDIA_SEGMENT and the input buffer contains some complete coded frames, then run the coded frame processing algorithm until all of these complete coded frames have been processed.
|
||||
|
@ -195,6 +195,7 @@ RefPtr<TrackBuffersManager::RangeRemovalPromise>
|
|||
TrackBuffersManager::RangeRemoval(TimeUnit aStart, TimeUnit aEnd)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_RELEASE_ASSERT(!mAppendRunning, "Append is running");
|
||||
MSE_DEBUG("From %.2f to %.2f", aStart.ToSeconds(), aEnd.ToSeconds());
|
||||
|
||||
mEnded = false;
|
||||
|
@ -302,20 +303,6 @@ TrackBuffersManager::Detach()
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("");
|
||||
|
||||
// Abort pending operations if any.
|
||||
AbortAppendData();
|
||||
|
||||
RefPtr<TrackBuffersManager> self = this;
|
||||
nsCOMPtr<nsIRunnable> task =
|
||||
NS_NewRunnableFunction([self] () {
|
||||
// Clear our sourcebuffer
|
||||
self->CodedFrameRemoval(TimeInterval(TimeUnit::FromSeconds(0),
|
||||
TimeUnit::FromInfinity()));
|
||||
self->mProcessingPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
|
||||
self->mAppendPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
|
||||
});
|
||||
GetTaskQueue()->Dispatch(task.forget());
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
|
@ -348,7 +335,7 @@ void
|
|||
TrackBuffersManager::CompleteResetParserState()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(!mAppendRunning);
|
||||
MOZ_RELEASE_ASSERT(!mSegmentParserLoopRunning);
|
||||
MSE_DEBUG("");
|
||||
|
||||
for (auto& track : GetTracksList()) {
|
||||
|
@ -484,7 +471,7 @@ bool
|
|||
TrackBuffersManager::CodedFrameRemoval(TimeInterval aInterval)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(!mAppendRunning, "Logic error: Append in progress");
|
||||
MOZ_ASSERT(!mSegmentParserLoopRunning, "Logic error: Append in progress");
|
||||
MSE_DEBUG("From %.2fs to %.2f",
|
||||
aInterval.mStart.ToSeconds(), aInterval.mEnd.ToSeconds());
|
||||
|
||||
|
@ -586,8 +573,9 @@ RefPtr<TrackBuffersManager::AppendPromise>
|
|||
TrackBuffersManager::InitSegmentParserLoop()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_RELEASE_ASSERT(mAppendPromise.IsEmpty());
|
||||
MSE_DEBUG("");
|
||||
|
||||
MOZ_ASSERT(mAppendPromise.IsEmpty() && !mAppendRunning);
|
||||
RefPtr<AppendPromise> p = mAppendPromise.Ensure(__func__);
|
||||
|
||||
AppendIncomingBuffers();
|
||||
|
@ -621,6 +609,9 @@ void
|
|||
TrackBuffersManager::SegmentParserLoop()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
mSegmentParserLoopRunning = true;
|
||||
|
||||
while (true) {
|
||||
// 1. If the input buffer is empty, then jump to the need more data step below.
|
||||
if (!mInputBuffer || mInputBuffer->IsEmpty()) {
|
||||
|
@ -724,7 +715,7 @@ TrackBuffersManager::SegmentParserLoop()
|
|||
->Then(GetTaskQueue(), __func__,
|
||||
[self] (bool aNeedMoreData) {
|
||||
self->mProcessingRequest.Complete();
|
||||
if (aNeedMoreData || self->mAbort) {
|
||||
if (aNeedMoreData) {
|
||||
self->NeedMoreData();
|
||||
} else {
|
||||
self->ScheduleSegmentParserLoop();
|
||||
|
@ -743,10 +734,14 @@ void
|
|||
TrackBuffersManager::NeedMoreData()
|
||||
{
|
||||
MSE_DEBUG("");
|
||||
if (!mAbort) {
|
||||
RestoreCachedVariables();
|
||||
}
|
||||
RestoreCachedVariables();
|
||||
mAppendRunning = false;
|
||||
mSegmentParserLoopRunning = false;
|
||||
{
|
||||
// Wake-up any pending Abort()
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mon.NotifyAll();
|
||||
}
|
||||
mAppendPromise.ResolveIfExists(mActiveTrack, __func__);
|
||||
}
|
||||
|
||||
|
@ -755,6 +750,12 @@ TrackBuffersManager::RejectAppend(nsresult aRejectValue, const char* aName)
|
|||
{
|
||||
MSE_DEBUG("rv=%d", aRejectValue);
|
||||
mAppendRunning = false;
|
||||
mSegmentParserLoopRunning = false;
|
||||
{
|
||||
// Wake-up any pending Abort()
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mon.NotifyAll();
|
||||
}
|
||||
mAppendPromise.RejectIfExists(aRejectValue, aName);
|
||||
}
|
||||
|
||||
|
@ -831,12 +832,7 @@ void
|
|||
TrackBuffersManager::OnDemuxerResetDone(nsresult)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MSE_DEBUG("mAbort:%d", static_cast<bool>(mAbort));
|
||||
mDemuxerInitRequest.Complete();
|
||||
if (mAbort) {
|
||||
RejectAppend(NS_ERROR_ABORT, __func__);
|
||||
return;
|
||||
}
|
||||
// mInputDemuxer shouldn't have been destroyed while a demuxer init/reset
|
||||
// request was being processed. See bug 1239983.
|
||||
MOZ_DIAGNOSTIC_ASSERT(mInputDemuxer);
|
||||
|
@ -908,13 +904,8 @@ void
|
|||
TrackBuffersManager::OnDemuxerInitDone(nsresult)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MSE_DEBUG("mAbort:%d", static_cast<bool>(mAbort));
|
||||
mDemuxerInitRequest.Complete();
|
||||
|
||||
if (mAbort) {
|
||||
RejectAppend(NS_ERROR_ABORT, __func__);
|
||||
return;
|
||||
}
|
||||
// mInputDemuxer shouldn't have been destroyed while a demuxer init/reset
|
||||
// request was being processed. See bug 1239983.
|
||||
MOZ_DIAGNOSTIC_ASSERT(mInputDemuxer);
|
||||
|
@ -1174,9 +1165,8 @@ TrackBuffersManager::OnDemuxFailed(TrackType aTrack,
|
|||
DemuxerFailureReason aFailure)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MSE_DEBUG("Failed to demux %s, failure:%d mAbort:%d",
|
||||
aTrack == TrackType::kVideoTrack ? "video" : "audio",
|
||||
aFailure, static_cast<bool>(mAbort));
|
||||
MSE_DEBUG("Failed to demux %s, failure:%d",
|
||||
aTrack == TrackType::kVideoTrack ? "video" : "audio", aFailure);
|
||||
switch (aFailure) {
|
||||
case DemuxerFailureReason::END_OF_STREAM:
|
||||
case DemuxerFailureReason::WAITING_FOR_DATA:
|
||||
|
@ -1203,15 +1193,10 @@ void
|
|||
TrackBuffersManager::DoDemuxVideo()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MSE_DEBUG("mAbort:%d", static_cast<bool>(mAbort));
|
||||
if (!HasVideo()) {
|
||||
DoDemuxAudio();
|
||||
return;
|
||||
}
|
||||
if (mAbort) {
|
||||
RejectProcessing(NS_ERROR_ABORT, __func__);
|
||||
return;
|
||||
}
|
||||
mVideoTracks.mDemuxRequest.Begin(mVideoTracks.mDemuxer->GetSamples(-1)
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&TrackBuffersManager::OnVideoDemuxCompleted,
|
||||
|
@ -1232,15 +1217,10 @@ void
|
|||
TrackBuffersManager::DoDemuxAudio()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MSE_DEBUG("mAbort:%d", static_cast<bool>(mAbort));
|
||||
if (!HasAudio()) {
|
||||
CompleteCodedFrameProcessing();
|
||||
return;
|
||||
}
|
||||
if (mAbort) {
|
||||
RejectProcessing(NS_ERROR_ABORT, __func__);
|
||||
return;
|
||||
}
|
||||
mAudioTracks.mDemuxRequest.Begin(mAudioTracks.mDemuxer->GetSamples(-1)
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&TrackBuffersManager::OnAudioDemuxCompleted,
|
||||
|
@ -1261,7 +1241,6 @@ void
|
|||
TrackBuffersManager::CompleteCodedFrameProcessing()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MSE_DEBUG("mAbort:%d", static_cast<bool>(mAbort));
|
||||
|
||||
// 1. For each coded frame in the media segment run the following steps:
|
||||
// Coded Frame Processing steps 1.1 to 1.21.
|
||||
|
@ -1332,22 +1311,12 @@ TrackBuffersManager::CompleteCodedFrameProcessing()
|
|||
void
|
||||
TrackBuffersManager::RejectProcessing(nsresult aRejectValue, const char* aName)
|
||||
{
|
||||
if (mAbort) {
|
||||
// mAppendPromise will be resolved immediately upon mProcessingPromise
|
||||
// completing.
|
||||
mAppendRunning = false;
|
||||
}
|
||||
mProcessingPromise.RejectIfExists(aRejectValue, __func__);
|
||||
}
|
||||
|
||||
void
|
||||
TrackBuffersManager::ResolveProcessing(bool aResolveValue, const char* aName)
|
||||
{
|
||||
if (mAbort) {
|
||||
// mAppendPromise will be resolved immediately upon mProcessingPromise
|
||||
// completing.
|
||||
mAppendRunning = false;
|
||||
}
|
||||
mProcessingPromise.ResolveIfExists(aResolveValue, __func__);
|
||||
}
|
||||
|
||||
|
|
|
@ -310,10 +310,6 @@ private:
|
|||
MozPromiseHolder<CodedFrameProcessingPromise> mProcessingPromise;
|
||||
|
||||
MozPromiseHolder<AppendPromise> mAppendPromise;
|
||||
// Set to true while SegmentParserLoop is running. This is used for diagnostic
|
||||
// purposes only. We can't rely on mAppendPromise to be empty as it is only
|
||||
// cleared in a follow up task.
|
||||
bool mAppendRunning;
|
||||
|
||||
// Trackbuffers definition.
|
||||
nsTArray<TrackData*> GetTracksList();
|
||||
|
@ -349,8 +345,6 @@ private:
|
|||
RefPtr<dom::SourceBufferAttributes> mSourceBufferAttributes;
|
||||
nsMainThreadPtrHandle<MediaSourceDecoder> mParentDecoder;
|
||||
|
||||
// Set to true if abort was called.
|
||||
Atomic<bool> mAbort;
|
||||
// Set to true if mediasource state changed to ended.
|
||||
Atomic<bool> mEnded;
|
||||
|
||||
|
@ -360,7 +354,13 @@ private:
|
|||
Atomic<bool> mEvictionOccurred;
|
||||
|
||||
// Monitor to protect following objects accessed across multipple threads.
|
||||
// mMonitor is also notified if the value of mAppendRunning becomes false.
|
||||
mutable Monitor mMonitor;
|
||||
// Set to true while SegmentParserLoop is running.
|
||||
Atomic<bool> mAppendRunning;
|
||||
// Set to true while SegmentParserLoop is running.
|
||||
// This is for diagnostic only. Only accessed on the task queue.
|
||||
bool mSegmentParserLoopRunning;
|
||||
// Stable audio and video track time ranges.
|
||||
media::TimeIntervals mVideoBufferedRanges;
|
||||
media::TimeIntervals mAudioBufferedRanges;
|
||||
|
|
Загрузка…
Ссылка в новой задаче