Bug 1175768 - Use mirroring for buffered ranges. r=jya

This commit is contained in:
Bobby Holley 2015-06-17 14:22:10 -07:00 committed by Jean-Yves Avenard
parent 4f31391e14
commit b988b6ea6a
24 changed files with 444 additions and 282 deletions

View File

@ -329,6 +329,7 @@ bool MediaDecoder::IsInfinite()
MediaDecoder::MediaDecoder() :
mWatchManager(this, AbstractThread::MainThread()),
mBuffered(AbstractThread::MainThread(), TimeIntervals(), "MediaDecoder::mBuffered (Mirror)"),
mNextFrameStatus(AbstractThread::MainThread(),
MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED,
"MediaDecoder::mNextFrameStatus (Mirror)"),
@ -1264,10 +1265,12 @@ MediaDecoder::SetStateMachine(MediaDecoderStateMachine* aStateMachine)
if (mDecoderStateMachine) {
mStateMachineDuration.Connect(mDecoderStateMachine->CanonicalDuration());
mBuffered.Connect(mDecoderStateMachine->CanonicalBuffered());
mNextFrameStatus.Connect(mDecoderStateMachine->CanonicalNextFrameStatus());
mCurrentPosition.Connect(mDecoderStateMachine->CanonicalCurrentPosition());
} else {
mStateMachineDuration.DisconnectIfConnected();
mBuffered.DisconnectIfConnected();
mNextFrameStatus.DisconnectIfConnected();
mCurrentPosition.DisconnectIfConnected();
}
@ -1299,8 +1302,7 @@ void MediaDecoder::Invalidate()
// Constructs the time ranges representing what segments of the media
// are buffered and playable.
media::TimeIntervals MediaDecoder::GetBuffered() {
NS_ENSURE_TRUE(mDecoderStateMachine && !mShuttingDown, media::TimeIntervals::Invalid());
return mDecoderStateMachine->GetBuffered();
return mBuffered.Ref();
}
size_t MediaDecoder::SizeOfVideoQueue() {
@ -1318,6 +1320,8 @@ size_t MediaDecoder::SizeOfAudioQueue() {
}
void MediaDecoder::NotifyDataArrived(uint32_t aLength, int64_t aOffset) {
MOZ_ASSERT(NS_IsMainThread());
if (mDecoderStateMachine) {
mDecoderStateMachine->DispatchNotifyDataArrived(aLength, aOffset);
}

View File

@ -892,6 +892,9 @@ protected:
// State-watching manager.
WatchManager<MediaDecoder> mWatchManager;
// Buffered range, mirrored from the reader.
Mirror<media::TimeIntervals> mBuffered;
// NextFrameStatus, mirrored from the state machine.
Mirror<MediaDecoderOwner::NextFrameStatus> mNextFrameStatus;

View File

@ -69,6 +69,8 @@ MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder,
, mTaskQueue(aBorrowedTaskQueue ? aBorrowedTaskQueue
: new MediaTaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
/* aSupportsTailDispatch = */ true))
, mWatchManager(this, mTaskQueue)
, mBuffered(mTaskQueue, TimeIntervals(), "MediaDecoderReader::mBuffered (Canonical)")
, mDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderReader::mDuration (Mirror)")
, mIgnoreAudioOutputFormat(false)
, mStartTime(-1)
@ -92,6 +94,9 @@ MediaDecoderReader::InitializationTask()
if (mDecoder->CanonicalDurationOrNull()) {
mDuration.Connect(mDecoder->CanonicalDurationOrNull());
}
// Initialize watchers.
mWatchManager.Watch(mDuration, &MediaDecoderReader::UpdateBuffered);
}
MediaDecoderReader::~MediaDecoderReader()
@ -161,16 +166,17 @@ VideoData* MediaDecoderReader::DecodeToFirstVideoData()
}
void
MediaDecoderReader::SetStartTime(int64_t aStartTime)
MediaDecoderReader::UpdateBuffered()
{
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
MOZ_ASSERT(mStartTime == -1);
mStartTime = aStartTime;
MOZ_ASSERT(OnTaskQueue());
NS_ENSURE_TRUE_VOID(!mShutdown);
mBuffered = GetBuffered();
}
media::TimeIntervals
MediaDecoderReader::GetBuffered()
{
MOZ_ASSERT(OnTaskQueue());
NS_ENSURE_TRUE(mStartTime >= 0, media::TimeIntervals());
AutoPinned<MediaResource> stream(mDecoder->GetResource());
@ -358,6 +364,10 @@ MediaDecoderReader::Shutdown()
ReleaseMediaResources();
mDuration.DisconnectIfConnected();
mBuffered.DisconnectAll();
// Shut down the watch manager before shutting down our task queue.
mWatchManager.Shutdown();
nsRefPtr<ShutdownPromise> p;

View File

@ -203,8 +203,10 @@ public:
mIgnoreAudioOutputFormat = true;
}
// Populates aBuffered with the time ranges which are buffered. This function
// is called on the main, decode, and state machine threads.
// Populates aBuffered with the time ranges which are buffered. This may only
// be called on the decode task queue, and should only be used internally by
// UpdateBuffered - mBuffered (or mirrors of it) should be used for everything
// else.
//
// This base implementation in MediaDecoderReader estimates the time ranges
// buffered by interpolating the cached byte ranges with the duration
@ -219,6 +221,9 @@ public:
// called.
virtual media::TimeIntervals GetBuffered();
// Recomputes mBuffered.
virtual void UpdateBuffered();
// MediaSourceReader opts out of the start-time-guessing mechanism.
virtual bool ForceZeroStartTime() const { return false; }
@ -240,12 +245,14 @@ public:
virtual size_t SizeOfAudioQueueInFrames();
protected:
friend class TrackBuffer;
virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) { }
void NotifyDataArrived(uint32_t aLength, int64_t aOffset)
{
MOZ_ASSERT(OnTaskQueue());
NS_ENSURE_TRUE_VOID(!mShutdown);
NotifyDataArrivedInternal(aLength, aOffset);
UpdateBuffered();
}
public:
@ -276,7 +283,20 @@ public:
// Indicates if the media is seekable.
// ReadMetada should be called before calling this method.
virtual bool IsMediaSeekable() = 0;
void SetStartTime(int64_t aStartTime);
void DispatchSetStartTime(int64_t aStartTime)
{
nsRefPtr<MediaDecoderReader> self = this;
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableFunction([self, aStartTime] () -> void
{
MOZ_ASSERT(self->OnTaskQueue());
MOZ_ASSERT(self->mStartTime == -1);
self->mStartTime = aStartTime;
self->UpdateBuffered();
});
TaskQueue()->Dispatch(r.forget());
}
MediaTaskQueue* TaskQueue() {
return mTaskQueue;
@ -335,6 +355,15 @@ protected:
// Decode task queue.
nsRefPtr<MediaTaskQueue> mTaskQueue;
// State-watching manager.
WatchManager<MediaDecoderReader> mWatchManager;
// Buffered range.
Canonical<media::TimeIntervals> mBuffered;
public:
AbstractCanonical<media::TimeIntervals>* CanonicalBuffered() { return &mBuffered; }
protected:
// Stores presentation info required for playback.
MediaInfo mInfo;

View File

@ -187,6 +187,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
mDelayedScheduler(this),
mState(DECODER_STATE_DECODING_NONE, "MediaDecoderStateMachine::mState"),
mPlayDuration(0),
mBuffered(mTaskQueue, TimeIntervals(), "MediaDecoderStateMachine::mBuffered (Mirror)"),
mDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderStateMachine::mDuration (Canonical"),
mEstimatedDuration(mTaskQueue, NullableTimeUnit(),
"MediaDecoderStateMachine::mEstimatedDuration (Mirror)"),
@ -296,6 +297,7 @@ MediaDecoderStateMachine::InitializationTask()
MOZ_ASSERT(OnTaskQueue());
// Connect mirrors.
mBuffered.Connect(mReader->CanonicalBuffered());
mEstimatedDuration.Connect(mDecoder->CanonicalEstimatedDuration());
mExplicitDuration.Connect(mDecoder->CanonicalExplicitDuration());
mPlayState.Connect(mDecoder->CanonicalPlayState());
@ -306,6 +308,7 @@ MediaDecoderStateMachine::InitializationTask()
mPreservesPitch.Connect(mDecoder->CanonicalPreservesPitch());
// Initialize watchers.
mWatchManager.Watch(mBuffered, &MediaDecoderStateMachine::BufferedRangeUpdated);
mWatchManager.Watch(mState, &MediaDecoderStateMachine::UpdateNextFrameStatus);
mWatchManager.Watch(mAudioCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus);
mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged);
@ -1639,8 +1642,7 @@ void MediaDecoderStateMachine::LogicallySeekingChanged()
ScheduleStateMachine();
}
void MediaDecoderStateMachine::NotifyDataArrived(uint32_t aLength,
int64_t aOffset)
void MediaDecoderStateMachine::BufferedRangeUpdated()
{
MOZ_ASSERT(OnTaskQueue());
@ -1649,12 +1651,10 @@ void MediaDecoderStateMachine::NotifyDataArrived(uint32_t aLength,
// faster than played, mObserved won't reflect the end of playable data
// since we haven't played the frame at the end of buffered data. So update
// mObservedDuration here as new data is downloaded to prevent such a lag.
media::TimeIntervals buffered{mDecoder->GetBuffered()};
if (!buffered.IsInvalid()) {
if (!mBuffered.Ref().IsInvalid()) {
bool exists;
media::TimeUnit end{buffered.GetEnd(&exists)};
media::TimeUnit end{mBuffered.Ref().GetEnd(&exists)};
if (exists) {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mObservedDuration = std::max(mObservedDuration.Ref(), end);
}
}
@ -2050,17 +2050,16 @@ bool MediaDecoderStateMachine::HasLowUndecodedData(int64_t aUsecs)
MOZ_ASSERT(OnTaskQueue());
AssertCurrentThreadInMonitor();
NS_ASSERTION(mState > DECODER_STATE_DECODING_FIRSTFRAME,
"Must have loaded first frame for GetBuffered() to work");
"Must have loaded first frame for mBuffered to be valid");
// If we don't have a duration, GetBuffered is probably not going to produce
// If we don't have a duration, mBuffered is probably not going to have
// a useful buffered range. Return false here so that we don't get stuck in
// buffering mode for live streams.
if (Duration().IsInfinite()) {
return false;
}
media::TimeIntervals buffered{mReader->GetBuffered()};
if (buffered.IsInvalid()) {
if (mBuffered.Ref().IsInvalid()) {
return false;
}
@ -2082,7 +2081,7 @@ bool MediaDecoderStateMachine::HasLowUndecodedData(int64_t aUsecs)
}
media::TimeInterval interval(media::TimeUnit::FromMicroseconds(endOfDecodedData),
media::TimeUnit::FromMicroseconds(std::min(endOfDecodedData + aUsecs, Duration().ToMicroseconds())));
return endOfDecodedData != INT64_MAX && !buffered.Contains(interval);
return endOfDecodedData != INT64_MAX && !mBuffered.Ref().Contains(interval);
}
void
@ -2134,8 +2133,7 @@ MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata)
mStartTimeRendezvous->AwaitStartTime()->Then(TaskQueue(), __func__,
[self] () -> void {
NS_ENSURE_TRUE_VOID(!self->IsShutdown());
ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
self->mReader->SetStartTime(self->StartTime());
self->mReader->DispatchSetStartTime(self->StartTime());
},
[] () -> void { NS_WARNING("Setting start time on reader failed"); }
);
@ -2483,6 +2481,7 @@ MediaDecoderStateMachine::FinishShutdown()
VideoQueue().ClearListeners();
// Disconnect canonicals and mirrors before shutting down our task queue.
mBuffered.DisconnectIfConnected();
mEstimatedDuration.DisconnectIfConnected();
mExplicitDuration.DisconnectIfConnected();
mPlayState.DisconnectIfConnected();

View File

@ -252,11 +252,6 @@ public:
return mState == DECODER_STATE_SEEKING;
}
media::TimeIntervals GetBuffered() {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
return mReader->GetBuffered();
}
size_t SizeOfVideoQueue() {
if (mReader) {
return mReader->SizeOfVideoQueueInBytes();
@ -271,17 +266,13 @@ public:
return 0;
}
private:
void NotifyDataArrived(uint32_t aLength, int64_t aOffset);
public:
void DispatchNotifyDataArrived(uint32_t aLength, int64_t aOffset)
{
RefPtr<nsRunnable> r =
NS_NewRunnableMethodWithArgs<uint32_t, int64_t>(this, &MediaDecoderStateMachine::NotifyDataArrived, aLength, aOffset);
TaskQueue()->Dispatch(r.forget());
mReader->DispatchNotifyDataArrived(aLength, aOffset);
}
AbstractCanonical<media::TimeIntervals>* CanonicalBuffered() { return mReader->CanonicalBuffered(); }
// Returns the state machine task queue.
MediaTaskQueue* TaskQueue() const { return mTaskQueue; }
@ -402,6 +393,8 @@ protected:
void SetState(State aState);
void BufferedRangeUpdated();
// Inserts MediaData* samples into their respective MediaQueues.
// aSample must not be null.
void Push(AudioData* aSample);
@ -947,6 +940,9 @@ private:
// buffering.
TimeStamp mBufferingStart;
// The buffered range. Mirrored from the decoder thread.
Mirror<media::TimeIntervals> mBuffered;
// Duration of the media. This is guaranteed to be non-null after we finish
// decoding the first frame.
Canonical<media::NullableTimeUnit> mDuration;

View File

@ -73,7 +73,6 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder,
, mSeekable(false)
, mIsEncrypted(false)
, mTrackDemuxersMayBlock(false)
, mCachedTimeRangesStale(true)
#if defined(READER_DORMANT_HEURISTIC)
, mDormantEnabled(Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false))
#endif
@ -794,11 +793,8 @@ MediaFormatReader::UpdateReceivedNewData(TrackType aTrack)
decoder.mWaitingForData = false;
bool hasLastEnd;
media::TimeUnit lastEnd = decoder.mTimeRanges.GetEnd(&hasLastEnd);
{
MonitorAutoLock lock(decoder.mMonitor);
// Update our cached TimeRange.
decoder.mTimeRanges = decoder.mTrackDemuxer->GetBuffered();
}
// Update our cached TimeRange.
decoder.mTimeRanges = decoder.mTrackDemuxer->GetBuffered();
if (decoder.mTimeRanges.Length() &&
(!hasLastEnd || decoder.mTimeRanges.GetEnd() > lastEnd)) {
// New data was added after our previous end, we can clear the EOS flag.
@ -1402,6 +1398,7 @@ MediaFormatReader::GetEvictionOffset(double aTime)
media::TimeIntervals
MediaFormatReader::GetBuffered()
{
MOZ_ASSERT(OnTaskQueue());
media::TimeIntervals videoti;
media::TimeIntervals audioti;
media::TimeIntervals intervals;
@ -1415,54 +1412,25 @@ MediaFormatReader::GetBuffered()
NS_ENSURE_TRUE(mStartTime >= 0, media::TimeIntervals());
startTime = mStartTime;
}
if (NS_IsMainThread()) {
if (mCachedTimeRangesStale) {
MOZ_ASSERT(mMainThreadDemuxer);
if (!mDataRange.IsEmpty()) {
mMainThreadDemuxer->NotifyDataArrived(mDataRange.Length(), mDataRange.mStart);
}
if (mVideoTrackDemuxer) {
videoti = mVideoTrackDemuxer->GetBuffered();
}
if (mAudioTrackDemuxer) {
audioti = mAudioTrackDemuxer->GetBuffered();
}
if (HasAudio() && HasVideo()) {
mCachedTimeRanges = media::Intersection(Move(videoti), Move(audioti));
} else if (HasAudio()) {
mCachedTimeRanges = Move(audioti);
} else if (HasVideo()) {
mCachedTimeRanges = Move(videoti);
}
mDataRange = ByteInterval();
mCachedTimeRangesStale = false;
}
intervals = mCachedTimeRanges;
} else {
if (OnTaskQueue()) {
// Ensure we have up to date buffered time range.
if (HasVideo()) {
UpdateReceivedNewData(TrackType::kVideoTrack);
}
if (HasAudio()) {
UpdateReceivedNewData(TrackType::kAudioTrack);
}
}
if (HasVideo()) {
MonitorAutoLock lock(mVideo.mMonitor);
videoti = mVideo.mTimeRanges;
}
if (HasAudio()) {
MonitorAutoLock lock(mAudio.mMonitor);
audioti = mAudio.mTimeRanges;
}
if (HasAudio() && HasVideo()) {
intervals = media::Intersection(Move(videoti), Move(audioti));
} else if (HasAudio()) {
intervals = Move(audioti);
} else if (HasVideo()) {
intervals = Move(videoti);
}
// Ensure we have up to date buffered time range.
if (HasVideo()) {
UpdateReceivedNewData(TrackType::kVideoTrack);
}
if (HasAudio()) {
UpdateReceivedNewData(TrackType::kAudioTrack);
}
if (HasVideo()) {
videoti = mVideo.mTimeRanges;
}
if (HasAudio()) {
audioti = mAudio.mTimeRanges;
}
if (HasAudio() && HasVideo()) {
intervals = media::Intersection(Move(videoti), Move(audioti));
} else if (HasAudio()) {
intervals = Move(audioti);
} else if (HasVideo()) {
intervals = Move(videoti);
}
return intervals.Shift(media::TimeUnit::FromMicroseconds(-startTime));
@ -1543,46 +1511,39 @@ MediaFormatReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
{
MOZ_ASSERT(OnTaskQueue());
MOZ_ASSERT(aLength);
if (mDataRange.IsEmpty()) {
mDataRange = ByteInterval(aOffset, aOffset + aLength);
} else {
mDataRange = mDataRange.Span(ByteInterval(aOffset, aOffset + aLength));
}
mCachedTimeRangesStale = true;
if (!mInitDone) {
return;
}
// Queue a task to notify our main demuxer.
RefPtr<nsIRunnable> task =
NS_NewRunnableMethodWithArgs<int32_t, uint64_t>(
this, &MediaFormatReader::NotifyDemuxer,
// Queue a task to notify our main thread demuxer.
nsCOMPtr<nsIRunnable> task =
NS_NewRunnableMethodWithArgs<uint32_t, int64_t>(
mMainThreadDemuxer, &MediaDataDemuxer::NotifyDataArrived,
aLength, aOffset);
TaskQueue()->Dispatch(task.forget());
AbstractThread::MainThread()->Dispatch(task.forget());
NotifyDemuxer(aLength, aOffset);
}
void
MediaFormatReader::NotifyDataRemoved()
{
MOZ_ASSERT(NS_IsMainThread());
mDataRange = ByteInterval();
mCachedTimeRangesStale = true;
MOZ_ASSERT(OnTaskQueue());
if (!mInitDone) {
return;
}
MOZ_ASSERT(mMainThreadDemuxer);
mMainThreadDemuxer->NotifyDataRemoved();
// Queue a task to notify our main demuxer.
RefPtr<nsIRunnable> task =
NS_NewRunnableMethodWithArgs<int32_t, uint64_t>(
this, &MediaFormatReader::NotifyDemuxer,
0, 0);
TaskQueue()->Dispatch(task.forget());
// Queue a task to notify our main thread demuxer.
nsCOMPtr<nsIRunnable> task =
NS_NewRunnableMethod(
mMainThreadDemuxer, &MediaDataDemuxer::NotifyDataRemoved);
AbstractThread::MainThread()->Dispatch(task.forget());
NotifyDemuxer(0, 0);
}
bool

View File

@ -9,7 +9,6 @@
#include "mozilla/Atomics.h"
#include "mozilla/Maybe.h"
#include "mozilla/Monitor.h"
#include "MediaDataDemuxer.h"
#include "MediaDecoderReader.h"
#include "MediaTaskQueue.h"
@ -207,8 +206,6 @@ private:
, mNumSamplesOutput(0)
, mSizeOfQueue(0)
, mLastStreamSourceID(UINT32_MAX)
, mMonitor(aType == MediaData::AUDIO_DATA ? "audio decoder data"
: "video decoder data")
{}
MediaFormatReader* mOwner;
@ -295,9 +292,6 @@ private:
Atomic<size_t> mSizeOfQueue;
// Sample format monitoring.
uint32_t mLastStreamSourceID;
// Monitor that protects all non-threadsafe state; the primitives
// that follow.
Monitor mMonitor;
media::TimeIntervals mTimeRanges;
nsRefPtr<SharedTrackInfo> mInfo;
};
@ -420,9 +414,6 @@ private:
nsRefPtr<MediaDataDemuxer> mMainThreadDemuxer;
nsRefPtr<MediaTrackDemuxer> mAudioTrackDemuxer;
nsRefPtr<MediaTrackDemuxer> mVideoTrackDemuxer;
ByteInterval mDataRange;
media::TimeIntervals mCachedTimeRanges;
bool mCachedTimeRangesStale;
#if defined(READER_DORMANT_HEURISTIC)
const bool mDormantEnabled;

View File

@ -1075,6 +1075,7 @@ MP4Reader::GetEvictionOffset(double aTime)
media::TimeIntervals
MP4Reader::GetBuffered()
{
MOZ_ASSERT(OnTaskQueue());
MonitorAutoLock mon(mDemuxerMonitor);
media::TimeIntervals buffered;
if (!mIndexReady) {

View File

@ -875,6 +875,7 @@ GStreamerReader::Seek(int64_t aTarget, int64_t aEndTime)
media::TimeIntervals GStreamerReader::GetBuffered()
{
MOZ_ASSERT(OnTaskQueue());
media::TimeIntervals buffered;
if (!mInfo.HasValidMedia()) {
return buffered;

View File

@ -36,12 +36,9 @@ public:
decoder->SetResource(resource);
reader->Init(nullptr);
{
// This needs to be done before invoking GetBuffered. This is normally
// done by MediaDecoderStateMachine.
ReentrantMonitorAutoEnter mon(decoder->GetReentrantMonitor());
reader->SetStartTime(0);
}
// This needs to be done before invoking GetBuffered. This is normally
// done by MediaDecoderStateMachine.
reader->DispatchSetStartTime(0);
}
void Init() {

View File

@ -87,7 +87,7 @@ MediaSourceDecoder::GetSeekable()
if (IsNaN(duration)) {
// Return empty range.
} else if (duration > 0 && mozilla::IsInfinite(duration)) {
media::TimeIntervals buffered = mReader->GetBuffered();
media::TimeIntervals buffered = GetBuffered();
if (buffered.Length()) {
seekable += media::TimeInterval(buffered.GetStart(), buffered.GetEnd());
}
@ -99,6 +99,44 @@ MediaSourceDecoder::GetSeekable()
return seekable;
}
media::TimeIntervals
MediaSourceDecoder::GetBuffered()
{
MOZ_ASSERT(NS_IsMainThread());
dom::SourceBufferList* sourceBuffers = mMediaSource->ActiveSourceBuffers();
media::TimeUnit highestEndTime;
nsTArray<media::TimeIntervals> activeRanges;
media::TimeIntervals buffered;
for (uint32_t i = 0; i < sourceBuffers->Length(); i++) {
bool found;
dom::SourceBuffer* sb = sourceBuffers->IndexedGetter(i, found);
MOZ_ASSERT(found);
activeRanges.AppendElement(sb->GetTimeIntervals());
highestEndTime =
std::max(highestEndTime, activeRanges.LastElement().GetEnd());
}
buffered +=
media::TimeInterval(media::TimeUnit::FromMicroseconds(0), highestEndTime);
for (auto& range : activeRanges) {
if (mEnded && range.Length()) {
// Set the end time on the last range to highestEndTime by adding a
// new range spanning the current end time to highestEndTime, which
// Normalize() will then merge with the old last range.
range +=
media::TimeInterval(range.GetEnd(), highestEndTime);
}
buffered.Intersection(range);
}
MSE_DEBUG("ranges=%s", DumpTimeRanges(buffered).get());
return buffered;
}
void
MediaSourceDecoder::Shutdown()
{
@ -298,37 +336,6 @@ MediaSourceDecoder::GetDuration()
return ExplicitDuration();
}
already_AddRefed<SourceBufferDecoder>
MediaSourceDecoder::SelectDecoder(int64_t aTarget,
int64_t aTolerance,
const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders)
{
MOZ_ASSERT(!mIsUsingFormatReader);
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
media::TimeUnit target{media::TimeUnit::FromMicroseconds(aTarget)};
media::TimeUnit tolerance{media::TimeUnit::FromMicroseconds(aTolerance + aTarget)};
// aTolerance gives a slight bias toward the start of a range only.
// Consider decoders in order of newest to oldest, as a newer decoder
// providing a given buffered range is expected to replace an older one.
for (int32_t i = aTrackDecoders.Length() - 1; i >= 0; --i) {
nsRefPtr<SourceBufferDecoder> newDecoder = aTrackDecoders[i];
media::TimeIntervals ranges = newDecoder->GetBuffered();
for (uint32_t j = 0; j < ranges.Length(); j++) {
if (target < ranges.End(j) && tolerance >= ranges.Start(j)) {
return newDecoder.forget();
}
}
MSE_DEBUGV("SelectDecoder(%lld fuzz:%lld) newDecoder=%p (%d/%d) target not in ranges=%s",
aTarget, aTolerance, newDecoder.get(), i+1,
aTrackDecoders.Length(), DumpTimeRanges(ranges).get());
}
return nullptr;
}
#undef MSE_DEBUG
#undef MSE_DEBUGV

View File

@ -41,6 +41,7 @@ public:
virtual MediaDecoderStateMachine* CreateStateMachine() override;
virtual nsresult Load(nsIStreamListener**, MediaDecoder*) override;
virtual media::TimeIntervals GetSeekable() override;
media::TimeIntervals GetBuffered() override;
virtual void Shutdown() override;
@ -91,12 +92,6 @@ public:
// reader in this decoders MediaSourceReader.
bool IsActiveReader(MediaDecoderReader* aReader);
// Return a decoder from the set available in aTrackDecoders that has data
// available in the range requested by aTarget.
already_AddRefed<SourceBufferDecoder> SelectDecoder(int64_t aTarget /* microseconds */,
int64_t aTolerance /* microseconds */,
const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders);
// Returns a string describing the state of the MediaSource internal
// buffered data. Used for debugging purposes.
void GetMozDebugReaderData(nsAString& aString);

View File

@ -553,10 +553,32 @@ MediaSourceReader::BreakCycles()
already_AddRefed<SourceBufferDecoder>
MediaSourceReader::SelectDecoder(int64_t aTarget,
int64_t aTolerance,
const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders)
TrackBuffer* aTrackBuffer)
{
return static_cast<MediaSourceDecoder*>(mDecoder)
->SelectDecoder(aTarget, aTolerance, aTrackDecoders);
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
media::TimeUnit target{media::TimeUnit::FromMicroseconds(aTarget)};
media::TimeUnit tolerance{media::TimeUnit::FromMicroseconds(aTolerance + aTarget)};
const nsTArray<nsRefPtr<SourceBufferDecoder>>& decoders{aTrackBuffer->Decoders()};
// aTolerance gives a slight bias toward the start of a range only.
// Consider decoders in order of newest to oldest, as a newer decoder
// providing a given buffered range is expected to replace an older one.
for (int32_t i = decoders.Length() - 1; i >= 0; --i) {
nsRefPtr<SourceBufferDecoder> newDecoder = decoders[i];
media::TimeIntervals ranges = aTrackBuffer->GetBuffered(newDecoder);
for (uint32_t j = 0; j < ranges.Length(); j++) {
if (target < ranges.End(j) && tolerance >= ranges.Start(j)) {
return newDecoder.forget();
}
}
MSE_DEBUGV("SelectDecoder(%lld fuzz:%lld) newDecoder=%p (%d/%d) target not in ranges=%s",
aTarget, aTolerance, newDecoder.get(), i+1,
decoders.Length(), DumpTimeRanges(ranges).get());
}
return nullptr;
}
bool
@ -564,7 +586,7 @@ MediaSourceReader::HaveData(int64_t aTarget, MediaData::Type aType)
{
TrackBuffer* trackBuffer = aType == MediaData::AUDIO_DATA ? mAudioTrack : mVideoTrack;
MOZ_ASSERT(trackBuffer);
nsRefPtr<SourceBufferDecoder> decoder = SelectDecoder(aTarget, EOS_FUZZ_US, trackBuffer->Decoders());
nsRefPtr<SourceBufferDecoder> decoder = SelectDecoder(aTarget, EOS_FUZZ_US, trackBuffer);
return !!decoder;
}
@ -582,9 +604,9 @@ MediaSourceReader::SwitchAudioSource(int64_t* aTarget)
// reader and skip the last few samples of the current one.
bool usedFuzz = false;
nsRefPtr<SourceBufferDecoder> newDecoder =
SelectDecoder(*aTarget, /* aTolerance = */ 0, mAudioTrack->Decoders());
SelectDecoder(*aTarget, /* aTolerance = */ 0, mAudioTrack);
if (!newDecoder) {
newDecoder = SelectDecoder(*aTarget, EOS_FUZZ_US, mAudioTrack->Decoders());
newDecoder = SelectDecoder(*aTarget, EOS_FUZZ_US, mAudioTrack);
usedFuzz = true;
}
if (GetAudioReader() && mAudioSourceDecoder != newDecoder) {
@ -627,9 +649,9 @@ MediaSourceReader::SwitchVideoSource(int64_t* aTarget)
// reader and skip the last few samples of the current one.
bool usedFuzz = false;
nsRefPtr<SourceBufferDecoder> newDecoder =
SelectDecoder(*aTarget, /* aTolerance = */ 0, mVideoTrack->Decoders());
SelectDecoder(*aTarget, /* aTolerance = */ 0, mVideoTrack);
if (!newDecoder) {
newDecoder = SelectDecoder(*aTarget, EOS_FUZZ_US, mVideoTrack->Decoders());
newDecoder = SelectDecoder(*aTarget, EOS_FUZZ_US, mVideoTrack);
usedFuzz = true;
}
if (GetVideoReader() && mVideoSourceDecoder != newDecoder) {
@ -732,10 +754,7 @@ MediaSourceReader::CreateSubDecoder(const nsACString& aType, int64_t aTimestampO
// MSE uses a start time of 0 everywhere. Set that immediately on the
// subreader to make sure that it's always in a state where we can invoke
// GetBuffered on it.
{
ReentrantMonitorAutoEnter mon(decoder->GetReentrantMonitor());
reader->SetStartTime(0);
}
reader->DispatchSetStartTime(0);
#ifdef MOZ_FMP4
reader->SetSharedDecoderManager(mSharedDecoderManager);
@ -1013,6 +1032,7 @@ MediaSourceReader::DoVideoSeek()
media::TimeIntervals
MediaSourceReader::GetBuffered()
{
MOZ_ASSERT(OnTaskQueue());
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
media::TimeIntervals buffered;
@ -1256,25 +1276,27 @@ MediaSourceReader::GetMozDebugReaderData(nsAString& aString)
if (mAudioTrack) {
result += nsPrintfCString("\tDumping Audio Track Decoders: - mLastAudioTime: %f\n", double(mLastAudioTime) / USECS_PER_S);
for (int32_t i = mAudioTrack->Decoders().Length() - 1; i >= 0; --i) {
nsRefPtr<MediaDecoderReader> newReader = mAudioTrack->Decoders()[i]->GetReader();
media::TimeIntervals ranges = mAudioTrack->Decoders()[i]->GetBuffered();
const nsRefPtr<SourceBufferDecoder>& newDecoder{mAudioTrack->Decoders()[i]};
media::TimeIntervals ranges = mAudioTrack->GetBuffered(newDecoder);
result += nsPrintfCString("\t\tReader %d: %p ranges=%s active=%s size=%lld\n",
i, newReader.get(), DumpTimeRanges(ranges).get(),
newReader.get() == GetAudioReader() ? "true" : "false",
mAudioTrack->Decoders()[i]->GetResource()->GetSize());
i,
newDecoder->GetReader(),
DumpTimeRanges(ranges).get(),
newDecoder->GetReader() == GetAudioReader() ? "true" : "false",
newDecoder->GetResource()->GetSize());
}
}
if (mVideoTrack) {
result += nsPrintfCString("\tDumping Video Track Decoders - mLastVideoTime: %f\n", double(mLastVideoTime) / USECS_PER_S);
for (int32_t i = mVideoTrack->Decoders().Length() - 1; i >= 0; --i) {
nsRefPtr<MediaDecoderReader> newReader = mVideoTrack->Decoders()[i]->GetReader();
media::TimeIntervals ranges = mVideoTrack->Decoders()[i]->GetBuffered();
const nsRefPtr<SourceBufferDecoder>& newDecoder{mVideoTrack->Decoders()[i]};
media::TimeIntervals ranges = mVideoTrack->GetBuffered(newDecoder);
result += nsPrintfCString("\t\tReader %d: %p ranges=%s active=%s size=%lld\n",
i, newReader.get(), DumpTimeRanges(ranges).get(),
newReader.get() == GetVideoReader() ? "true" : "false",
i,
newDecoder->GetReader(),
DumpTimeRanges(ranges).get(),
newDecoder->GetReader() == GetVideoReader() ? "true" : "false",
mVideoTrack->Decoders()[i]->GetResource()->GetSize());
}
}

View File

@ -219,9 +219,10 @@ private:
// Return a decoder from the set available in aTrackDecoders that has data
// available in the range requested by aTarget.
friend class TrackBuffer;
already_AddRefed<SourceBufferDecoder> SelectDecoder(int64_t aTarget /* microseconds */,
int64_t aTolerance /* microseconds */,
const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders);
TrackBuffer* aTrackBuffer);
bool HaveData(int64_t aTarget, MediaData::Type aType);
already_AddRefed<SourceBufferDecoder> FirstDecoder(MediaData::Type aType);

View File

@ -147,6 +147,12 @@ SourceBuffer::GetBuffered(ErrorResult& aRv)
return tr.forget();
}
media::TimeIntervals
SourceBuffer::GetTimeIntervals()
{
return mContentManager->Buffered();
}
void
SourceBuffer::SetAppendWindowStart(double aAppendWindowStart, ErrorResult& aRv)
{

View File

@ -60,6 +60,7 @@ public:
}
already_AddRefed<TimeRanges> GetBuffered(ErrorResult& aRv);
TimeIntervals GetTimeIntervals();
double TimestampOffset() const
{

View File

@ -199,13 +199,6 @@ SourceBufferDecoder::GetOwner()
void
SourceBufferDecoder::NotifyDataArrived(uint32_t aLength, int64_t aOffset)
{
mReader->DispatchNotifyDataArrived(aLength, aOffset);
// XXX: Params make no sense to parent decoder as it relates to a
// specific SourceBufferDecoder's data stream. Pass bogus values here to
// force parent decoder's state machine to recompute end time for
// infinite length media.
mParentDecoder->NotifyDataArrived(0, 0);
}
media::TimeIntervals

View File

@ -36,6 +36,9 @@ extern PRLogModuleInfo* GetMediaSourceLog();
#define EOS_FUZZ_US 125000
using media::TimeIntervals;
using media::Interval;
namespace mozilla {
TrackBuffer::TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType)
@ -242,42 +245,97 @@ TrackBuffer::BufferAppend()
mAdjustedTimestamp = starttu;
}
if (!AppendDataToCurrentResource(mInputBuffer, end - start)) {
int64_t offset = AppendDataToCurrentResource(mInputBuffer, end - start);
if (offset < 0) {
mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
return p;
}
mLastAppendRange =
Interval<int64_t>(offset, offset + int64_t(mInputBuffer->Length()));
if (decoders.Length()) {
// We're going to have to wait for the decoder to initialize, the promise
// will be resolved once initialization completes.
return p;
}
// Tell our reader that we have more data to ensure that playback starts if
// required when data is appended.
NotifyTimeRangesChanged();
nsRefPtr<TrackBuffer> self = this;
ProxyMediaCall(mParentDecoder->GetReader()->TaskQueue(), this, __func__,
&TrackBuffer::UpdateBufferedRanges,
mLastAppendRange, /* aNotifyParent */ true)
->Then(mParentDecoder->GetReader()->TaskQueue(), __func__,
[self] {
self->mInitializationPromise.ResolveIfExists(self->HasInitSegment(), __func__);
},
[self] (nsresult) { MOZ_CRASH("Never called."); });
mInitializationPromise.Resolve(HasInitSegment(), __func__);
return p;
}
bool
int64_t
TrackBuffer::AppendDataToCurrentResource(MediaByteBuffer* aData, uint32_t aDuration)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mCurrentDecoder) {
return false;
return -1;
}
SourceBufferResource* resource = mCurrentDecoder->GetResource();
int64_t appendOffset = resource->GetLength();
resource->AppendData(aData);
mCurrentDecoder->SetRealMediaDuration(mCurrentDecoder->GetRealMediaDuration() + aDuration);
mCurrentDecoder->NotifyDataArrived(aData->Length(), appendOffset);
mParentDecoder->NotifyBytesDownloaded();
return appendOffset;
}
nsRefPtr<TrackBuffer::BufferedRangesUpdatedPromise>
TrackBuffer::UpdateBufferedRanges(Interval<int64_t> aByteRange, bool aNotifyParent)
{
if (aByteRange.Length()) {
mCurrentDecoder->GetReader()->NotifyDataArrived(uint32_t(aByteRange.Length()),
aByteRange.mStart);
}
// Recalculate and cache our new buffered range.
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
TimeIntervals buffered;
for (auto& decoder : mInitializedDecoders) {
TimeIntervals decoderBuffered(decoder->GetBuffered());
mReadersBuffered[decoder] = decoderBuffered;
buffered += decoderBuffered;
}
// mParser may not be initialized yet, and will only be so if we have a
// buffered range.
if (buffered.Length()) {
buffered.SetFuzz(TimeUnit::FromMicroseconds(mParser->GetRoundingError()));
}
mBufferedRanges = buffered;
}
if (aNotifyParent) {
nsRefPtr<MediaSourceDecoder> parent = mParentDecoder;
nsCOMPtr<nsIRunnable> task =
NS_NewRunnableFunction([parent] () {
// XXX: Params make no sense to parent decoder as it relates to a
// specific SourceBufferDecoder's data stream. Pass bogus values here to
// force parent decoder's state machine to recompute end time for
// infinite length media.
parent->NotifyDataArrived(0, 0);
parent->NotifyBytesDownloaded();
});
AbstractThread::MainThread()->Dispatch(task.forget());
}
// Tell our reader that we have more data to ensure that playback starts if
// required when data is appended.
NotifyTimeRangesChanged();
return true;
return BufferedRangesUpdatedPromise::CreateAndResolve(true, __func__);
}
void
@ -289,24 +347,49 @@ TrackBuffer::NotifyTimeRangesChanged()
mParentDecoder->GetReader()->TaskQueue()->Dispatch(task.forget());
}
void
TrackBuffer::NotifyReaderDataRemoved(MediaDecoderReader* aReader)
{
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<TrackBuffer> self = this;
nsRefPtr<MediaDecoderReader> reader = aReader;
RefPtr<nsIRunnable> task =
NS_NewRunnableFunction([self, reader] () {
reader->NotifyDataRemoved();
self->UpdateBufferedRanges(Interval<int64_t>(), /* aNotifyParent */ false);
});
aReader->TaskQueue()->Dispatch(task.forget());
}
class DecoderSorter
{
public:
explicit DecoderSorter(const TrackBuffer::DecoderBufferedMap& aMap)
: mMap(aMap)
{}
bool LessThan(SourceBufferDecoder* aFirst, SourceBufferDecoder* aSecond) const
{
TimeIntervals first = aFirst->GetBuffered();
TimeIntervals second = aSecond->GetBuffered();
MOZ_ASSERT(mMap.find(aFirst) != mMap.end());
MOZ_ASSERT(mMap.find(aSecond) != mMap.end());
const TimeIntervals& first = mMap.find(aFirst)->second;
const TimeIntervals& second = mMap.find(aSecond)->second;
return first.GetStart() < second.GetStart();
}
bool Equals(SourceBufferDecoder* aFirst, SourceBufferDecoder* aSecond) const
{
TimeIntervals first = aFirst->GetBuffered();
TimeIntervals second = aSecond->GetBuffered();
MOZ_ASSERT(mMap.find(aFirst) != mMap.end());
MOZ_ASSERT(mMap.find(aSecond) != mMap.end());
const TimeIntervals& first = mMap.find(aFirst)->second;
const TimeIntervals& second = mMap.find(aSecond)->second;
return first.GetStart() == second.GetStart();
}
const TrackBuffer::DecoderBufferedMap& mMap;
};
TrackBuffer::EvictDataResult
@ -329,14 +412,24 @@ TrackBuffer::EvictData(TimeUnit aPlaybackTime,
}
// Get a list of initialized decoders.
nsTArray<SourceBufferDecoder*> decoders;
nsTArray<nsRefPtr<SourceBufferDecoder>> decoders;
decoders.AppendElements(mInitializedDecoders);
const TimeUnit evictThresholdTime{TimeUnit::FromSeconds(MSE_EVICT_THRESHOLD_TIME)};
// Find the reader currently being played with.
SourceBufferDecoder* playingDecoder = nullptr;
for (const auto& decoder : decoders) {
if (mParentDecoder->IsActiveReader(decoder->GetReader())) {
playingDecoder = decoder;
break;
}
}
TimeUnit playingDecoderStartTime{GetBuffered(playingDecoder).GetStart()};
// First try to evict data before the current play position, starting
// with the oldest decoder.
for (uint32_t i = 0; i < decoders.Length() && toEvict > 0; ++i) {
TimeIntervals buffered = decoders[i]->GetBuffered();
TimeIntervals buffered = GetBuffered(decoders[i]);
MSE_DEBUG("Step1. decoder=%u/%u threshold=%u toEvict=%lld",
i, decoders.Length(), aThreshold, toEvict);
@ -364,6 +457,10 @@ TrackBuffer::EvictData(TimeUnit aPlaybackTime,
buffered.GetEnd().ToSeconds(), aPlaybackTime.ToSeconds(),
time, playbackOffset, decoders[i]->GetResource()->GetSize());
if (playbackOffset > 0) {
if (decoders[i] == playingDecoder) {
// This is an approximation only, likely pessimistic.
playingDecoderStartTime = time;
}
ErrorResult rv;
toEvict -= decoders[i]->GetResource()->EvictData(playbackOffset,
playbackOffset,
@ -374,7 +471,7 @@ TrackBuffer::EvictData(TimeUnit aPlaybackTime,
}
}
}
decoders[i]->GetReader()->NotifyDataRemoved();
NotifyReaderDataRemoved(decoders[i]->GetReader());
}
}
@ -382,13 +479,15 @@ TrackBuffer::EvictData(TimeUnit aPlaybackTime,
for (uint32_t i = 0; i < decoders.Length() && toEvict > 0; ++i) {
MSE_DEBUG("Step2. decoder=%u/%u threshold=%u toEvict=%lld",
i, decoders.Length(), aThreshold, toEvict);
if (mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
if (decoders[i] == playingDecoder) {
break;
}
if (decoders[i] == mCurrentDecoder) {
continue;
}
TimeIntervals buffered = decoders[i]->GetBuffered();
// The buffered value is potentially stale should eviction occurred in
// step 1. However this is only used for logging.
TimeIntervals buffered = GetBuffered(decoders[i]);
// Remove data from older decoders than the current one.
MSE_DEBUG("evicting all "
@ -396,7 +495,7 @@ TrackBuffer::EvictData(TimeUnit aPlaybackTime,
buffered.GetStart().ToSeconds(), buffered.GetEnd().ToSeconds(),
aPlaybackTime, decoders[i]->GetResource()->GetSize());
toEvict -= decoders[i]->GetResource()->EvictAll();
decoders[i]->GetReader()->NotifyDataRemoved();
NotifyReaderDataRemoved(decoders[i]->GetReader());
}
// Evict all data from future decoders, starting furthest away from
@ -407,26 +506,21 @@ TrackBuffer::EvictData(TimeUnit aPlaybackTime,
// TODO: This step should be done using RangeRemoval:
// Something like: RangeRemoval(aPlaybackTime + 60s, End);
// Find the reader currently being played with.
SourceBufferDecoder* playingDecoder = nullptr;
for (uint32_t i = 0; i < decoders.Length() && toEvict > 0; ++i) {
if (mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
playingDecoder = decoders[i];
break;
}
}
// Find the next decoder we're likely going to play with.
nsRefPtr<SourceBufferDecoder> nextPlayingDecoder = nullptr;
if (playingDecoder) {
TimeIntervals buffered = playingDecoder->GetBuffered();
// The buffered value is potentially stale should eviction occurred in
// step 1. However step 1 modified the start of the range value, and now
// will use the end value.
TimeIntervals buffered = GetBuffered(playingDecoder);
nextPlayingDecoder =
mParentDecoder->SelectDecoder(buffered.GetEnd().ToMicroseconds() + 1,
EOS_FUZZ_US,
mInitializedDecoders);
mParentDecoder->GetReader()->SelectDecoder(buffered.GetEnd().ToMicroseconds() + 1,
EOS_FUZZ_US,
this);
}
// Sort decoders by their start times.
decoders.Sort(DecoderSorter());
decoders.Sort(DecoderSorter{mReadersBuffered});
for (int32_t i = int32_t(decoders.Length()) - 1; i >= 0 && toEvict > 0; --i) {
MSE_DEBUG("Step3. decoder=%u/%u threshold=%u toEvict=%lld",
@ -435,14 +529,17 @@ TrackBuffer::EvictData(TimeUnit aPlaybackTime,
decoders[i] == mCurrentDecoder) {
continue;
}
TimeIntervals buffered = decoders[i]->GetBuffered();
// The buffered value is potentially stale should eviction occurred in
// step 1 and 2. However step 3 is a last resort step where we will remove
// all content and the buffered value is only used for logging.
TimeIntervals buffered = GetBuffered(decoders[i]);
MSE_DEBUG("evicting all "
"bufferedStart=%f bufferedEnd=%f aPlaybackTime=%f size=%lld",
buffered.GetStart().ToSeconds(), buffered.GetEnd().ToSeconds(),
aPlaybackTime, decoders[i]->GetResource()->GetSize());
toEvict -= decoders[i]->GetResource()->EvictAll();
decoders[i]->GetReader()->NotifyDataRemoved();
NotifyReaderDataRemoved(decoders[i]->GetReader());
}
RemoveEmptyDecoders(decoders);
@ -450,8 +547,8 @@ TrackBuffer::EvictData(TimeUnit aPlaybackTime,
bool evicted = toEvict < (totalSize - aThreshold);
if (evicted) {
if (playingDecoder) {
TimeIntervals ranges = playingDecoder->GetBuffered();
*aBufferStartTime = std::max(TimeUnit::FromSeconds(0), ranges.GetStart());
*aBufferStartTime =
std::max(TimeUnit::FromSeconds(0), playingDecoderStartTime);
} else {
// We do not currently have data to play yet.
// Avoid evicting anymore data to minimize rebuffering time.
@ -459,38 +556,37 @@ TrackBuffer::EvictData(TimeUnit aPlaybackTime,
}
}
if (evicted) {
NotifyTimeRangesChanged();
}
return evicted ?
EvictDataResult::DATA_EVICTED :
(HasOnlyIncompleteMedia() ? EvictDataResult::CANT_EVICT : EvictDataResult::NO_DATA_EVICTED);
}
void
TrackBuffer::RemoveEmptyDecoders(nsTArray<mozilla::SourceBufferDecoder*>& aDecoders)
TrackBuffer::RemoveEmptyDecoders(const nsTArray<nsRefPtr<mozilla::SourceBufferDecoder>>& aDecoders)
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
nsRefPtr<TrackBuffer> self = this;
nsTArray<nsRefPtr<mozilla::SourceBufferDecoder>> decoders(aDecoders);
nsCOMPtr<nsIRunnable> task =
NS_NewRunnableFunction([self, decoders] () {
if (!self->mParentDecoder) {
return;
}
ReentrantMonitorAutoEnter mon(self->mParentDecoder->GetReentrantMonitor());
// Remove decoders that have no data in them
for (uint32_t i = 0; i < aDecoders.Length(); ++i) {
TimeIntervals buffered = aDecoders[i]->GetBuffered();
MSE_DEBUG("maybe remove empty decoders=%d "
"size=%lld start=%f end=%f",
i, aDecoders[i]->GetResource()->GetSize(),
buffered.GetStart().ToSeconds(), buffered.GetEnd().ToSeconds());
if (aDecoders[i] == mCurrentDecoder ||
mParentDecoder->IsActiveReader(aDecoders[i]->GetReader())) {
continue;
}
if (aDecoders[i]->GetResource()->GetSize() == 0 || !buffered.Length() ||
buffered[0].IsEmpty()) {
MSE_DEBUG("remove empty decoders=%d", i);
RemoveDecoder(aDecoders[i]);
}
}
// Remove decoders that have decoders data in them
for (uint32_t i = 0; i < decoders.Length(); ++i) {
if (decoders[i] == self->mCurrentDecoder ||
self->mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
continue;
}
TimeIntervals buffered = self->GetBuffered(decoders[i]);
if (decoders[i]->GetResource()->GetSize() == 0 || !buffered.Length() ||
buffered[0].IsEmpty()) {
self->RemoveDecoder(decoders[i]);
}
}
});
AbstractThread::MainThread()->Dispatch(task.forget());
}
int64_t
@ -509,7 +605,7 @@ TrackBuffer::HasOnlyIncompleteMedia()
if (!mCurrentDecoder) {
return false;
}
TimeIntervals buffered = mCurrentDecoder->GetBuffered();
TimeIntervals buffered = GetBuffered(mCurrentDecoder);
MSE_DEBUG("mCurrentDecoder.size=%lld, start=%f end=%f",
mCurrentDecoder->GetResource()->GetSize(),
buffered.GetStart(), buffered.GetEnd());
@ -532,10 +628,9 @@ TrackBuffer::EvictBefore(TimeUnit aTime)
rv.SuppressException();
return;
}
mInitializedDecoders[i]->GetReader()->NotifyDataRemoved();
NotifyReaderDataRemoved(mInitializedDecoders[i]->GetReader());
}
}
NotifyTimeRangesChanged();
}
TimeIntervals
@ -543,18 +638,20 @@ TrackBuffer::Buffered()
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
TimeIntervals buffered;
return mBufferedRanges;
}
for (auto& decoder : mInitializedDecoders) {
buffered += decoder->GetBuffered();
}
// mParser may not be initialized yet, and will only be so if we have a
// buffered range.
if (buffered.Length()) {
buffered.SetFuzz(TimeUnit::FromMicroseconds(mParser->GetRoundingError()));
}
TimeIntervals
TrackBuffer::GetBuffered(SourceBufferDecoder* aDecoder)
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
return buffered;
DecoderBufferedMap::const_iterator val = mReadersBuffered.find(aDecoder);
if (val == mReadersBuffered.end()) {
return TimeIntervals::Invalid();
}
return val->second;
}
already_AddRefed<SourceBufferDecoder>
@ -665,6 +762,9 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
MSE_DEBUG("Initializing subdecoder %p reader %p",
aDecoder, reader);
reader->NotifyDataArrived(uint32_t(mLastAppendRange.Length()),
mLastAppendRange.mStart);
// HACK WARNING:
// We only reach this point once we know that we have a complete init segment.
// We don't want the demuxer to do a blocking read as no more data can be
@ -821,7 +921,6 @@ TrackBuffer::CompleteInitializeDecoder(SourceBufferDecoder* aDecoder)
return;
}
int64_t duration = mInfo.mMetadataDuration.isSome()
? mInfo.mMetadataDuration.ref().ToMicroseconds() : -1;
if (!duration) {
@ -836,7 +935,15 @@ TrackBuffer::CompleteInitializeDecoder(SourceBufferDecoder* aDecoder)
MSE_DEBUG("Reader %p activated",
aDecoder->GetReader());
mInitializationPromise.ResolveIfExists(true, __func__);
nsRefPtr<TrackBuffer> self = this;
ProxyMediaCall(mParentDecoder->GetReader()->TaskQueue(), this, __func__,
&TrackBuffer::UpdateBufferedRanges,
Interval<int64_t>(), /* aNotifyParent */ true)
->Then(mParentDecoder->GetReader()->TaskQueue(), __func__,
[self] {
self->mInitializationPromise.ResolveIfExists(self->HasInitSegment(), __func__);
},
[self] (nsresult) { MOZ_CRASH("Never called."); });
}
bool
@ -932,7 +1039,7 @@ TrackBuffer::ContainsTime(int64_t aTime, int64_t aTolerance)
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
TimeUnit time{TimeUnit::FromMicroseconds(aTime)};
for (auto& decoder : mInitializedDecoders) {
TimeIntervals r = decoder->GetBuffered();
TimeIntervals r = GetBuffered(decoder);
r.SetFuzz(TimeUnit::FromMicroseconds(aTolerance));
if (r.Contains(time)) {
return true;
@ -1085,6 +1192,8 @@ TrackBuffer::RemoveDecoder(SourceBufferDecoder* aDecoder)
MOZ_ASSERT(!mParentDecoder->IsActiveReader(aDecoder->GetReader()));
mInitializedDecoders.RemoveElement(aDecoder);
mDecoders.RemoveElement(aDecoder);
// Remove associated buffered range from our cache.
mReadersBuffered.erase(aDecoder);
}
aDecoder->GetReader()->TaskQueue()->Dispatch(task.forget());
}
@ -1111,13 +1220,13 @@ TrackBuffer::RangeRemoval(TimeUnit aStart, TimeUnit aEnd)
return RangeRemovalPromise::CreateAndResolve(false, __func__);
}
nsTArray<SourceBufferDecoder*> decoders;
nsTArray<nsRefPtr<SourceBufferDecoder>> decoders;
decoders.AppendElements(mInitializedDecoders);
if (aStart <= bufferedStart && aEnd < bufferedEnd) {
// Evict data from beginning.
for (size_t i = 0; i < decoders.Length(); ++i) {
TimeIntervals buffered = decoders[i]->GetBuffered();
TimeIntervals buffered = GetBuffered(decoders[i]);
if (buffered.GetEnd() < aEnd) {
// Can be fully removed.
MSE_DEBUG("remove all bufferedEnd=%f size=%lld",
@ -1138,7 +1247,7 @@ TrackBuffer::RangeRemoval(TimeUnit aStart, TimeUnit aEnd)
}
}
}
decoders[i]->GetReader()->NotifyDataRemoved();
NotifyReaderDataRemoved(decoders[i]->GetReader());
}
} else {
// Only trimming existing buffers.
@ -1149,14 +1258,26 @@ TrackBuffer::RangeRemoval(TimeUnit aStart, TimeUnit aEnd)
} else {
decoders[i]->Trim(aStart.ToMicroseconds());
}
decoders[i]->GetReader()->NotifyDataRemoved();
NotifyReaderDataRemoved(decoders[i]->GetReader());
}
}
RemoveEmptyDecoders(decoders);
NotifyTimeRangesChanged();
return RangeRemovalPromise::CreateAndResolve(true, __func__);
nsRefPtr<RangeRemovalPromise> p = mRangeRemovalPromise.Ensure(__func__);
// Make sure our buffered ranges got updated before resolving promise.
nsRefPtr<TrackBuffer> self = this;
ProxyMediaCall(mParentDecoder->GetReader()->TaskQueue(), this, __func__,
&TrackBuffer::UpdateBufferedRanges,
Interval<int64_t>(), /* aNotifyParent */ false)
->Then(mParentDecoder->GetReader()->TaskQueue(), __func__,
[self] {
self->mRangeRemovalPromise.ResolveIfExists(true, __func__);
},
[self] (nsresult) { MOZ_CRASH("Never called."); });
return p;
}
void

View File

@ -17,6 +17,7 @@
#include "nsString.h"
#include "nscore.h"
#include "TimeUnits.h"
#include <map>
namespace mozilla {
@ -97,6 +98,9 @@ public:
// currently not playable.
bool HasOnlyIncompleteMedia();
// Return the buffered ranges for given decoder.
media::TimeIntervals GetBuffered(SourceBufferDecoder* aDecoder);
#ifdef MOZ_EME
nsresult SetCDMProxy(CDMProxy* aProxy);
#endif
@ -105,6 +109,8 @@ public:
void Dump(const char* aPath) override;
#endif
typedef std::map<SourceBufferDecoder*, media::TimeIntervals> DecoderBufferedMap;
private:
friend class DecodersToInitialize;
friend class MetadataRecipient;
@ -115,14 +121,19 @@ private:
// for initialization.
// The decoder is not considered initialized until it is added to
// mInitializedDecoders.
already_AddRefed<SourceBufferDecoder> NewDecoder(TimeUnit aTimestampOffset);
already_AddRefed<SourceBufferDecoder> NewDecoder(media::TimeUnit aTimestampOffset);
// Helper for AppendData, ensures NotifyDataArrived is called whenever
// data is appended to the current decoder's SourceBufferResource.
bool AppendDataToCurrentResource(MediaByteBuffer* aData,
int64_t AppendDataToCurrentResource(MediaByteBuffer* aData,
uint32_t aDuration /* microseconds */);
// Queue on the parent's decoder task queue a call to NotifyTimeRangesChanged.
void NotifyTimeRangesChanged();
// Queue on the parent's decoder task queue a call to NotifyDataRemoved.
void NotifyReaderDataRemoved(MediaDecoderReader* aReader);
typedef MediaPromise<bool, nsresult, /* IsExclusive = */ true> BufferedRangesUpdatedPromise;
nsRefPtr<BufferedRangesUpdatedPromise> UpdateBufferedRanges(Interval<int64_t> aByteRange, bool aNotifyParent);
// Queue execution of InitializeDecoder on mTaskQueue.
bool QueueInitializeDecoder(SourceBufferDecoder* aDecoder);
@ -153,7 +164,7 @@ private:
void RemoveDecoder(SourceBufferDecoder* aDecoder);
// Remove all empty decoders from the provided list;
void RemoveEmptyDecoders(nsTArray<SourceBufferDecoder*>& aDecoders);
void RemoveEmptyDecoders(const nsTArray<nsRefPtr<SourceBufferDecoder>>& aDecoders);
void OnMetadataRead(MetadataHolder* aMetadata,
SourceBufferDecoder* aDecoder,
@ -197,9 +208,9 @@ private:
void AdjustDecodersTimestampOffset(TimeUnit aOffset);
// The timestamp offset used by our current decoder.
TimeUnit mLastTimestampOffset;
TimeUnit mTimestampOffset;
TimeUnit mAdjustedTimestamp;
media::TimeUnit mLastTimestampOffset;
media::TimeUnit mTimestampOffset;
media::TimeUnit mAdjustedTimestamp;
// True if at least one of our decoders has encrypted content.
bool mIsWaitingOnCDM;
@ -216,6 +227,15 @@ private:
MediaPromiseHolder<AppendPromise> mInitializationPromise;
// Track our request for metadata from the reader.
MediaPromiseRequestHolder<MediaDecoderReader::MetadataPromise> mMetadataRequest;
MediaPromiseHolder<RangeRemovalPromise> mRangeRemovalPromise;
Interval<int64_t> mLastAppendRange;
// Protected by Parent's decoder Monitor.
media::TimeIntervals mBufferedRanges;
DecoderBufferedMap mReadersBuffered;
};
} // namespace mozilla

View File

@ -1852,6 +1852,7 @@ nsresult OggReader::SeekBisection(int64_t aTarget,
media::TimeIntervals OggReader::GetBuffered()
{
MOZ_ASSERT(OnTaskQueue());
NS_ENSURE_TRUE(mStartTime >= 0, media::TimeIntervals());
{
mozilla::ReentrantMonitorAutoEnter mon(mMonitor);

View File

@ -285,5 +285,6 @@ nsresult RawReader::SeekInternal(int64_t aTime)
media::TimeIntervals RawReader::GetBuffered()
{
MOZ_ASSERT(OnTaskQueue());
return media::TimeIntervals();
}

View File

@ -275,6 +275,7 @@ WaveReader::Seek(int64_t aTarget, int64_t aEndTime)
media::TimeIntervals WaveReader::GetBuffered()
{
MOZ_ASSERT(OnTaskQueue());
if (!mInfo.HasAudio()) {
return media::TimeIntervals();
}

View File

@ -1093,6 +1093,7 @@ nsresult WebMReader::SeekInternal(int64_t aTarget)
media::TimeIntervals WebMReader::GetBuffered()
{
MOZ_ASSERT(OnTaskQueue());
NS_ENSURE_TRUE(mStartTime >= 0, media::TimeIntervals());
AutoPinned<MediaResource> resource(mDecoder->GetResource());