mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1175768 - Use mirroring for buffered ranges. r=jya
This commit is contained in:
parent
4f31391e14
commit
b988b6ea6a
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -1075,6 +1075,7 @@ MP4Reader::GetEvictionOffset(double aTime)
|
||||
media::TimeIntervals
|
||||
MP4Reader::GetBuffered()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MonitorAutoLock mon(mDemuxerMonitor);
|
||||
media::TimeIntervals buffered;
|
||||
if (!mIndexReady) {
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -60,6 +60,7 @@ public:
|
||||
}
|
||||
|
||||
already_AddRefed<TimeRanges> GetBuffered(ErrorResult& aRv);
|
||||
TimeIntervals GetTimeIntervals();
|
||||
|
||||
double TimestampOffset() const
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -285,5 +285,6 @@ nsresult RawReader::SeekInternal(int64_t aTime)
|
||||
|
||||
media::TimeIntervals RawReader::GetBuffered()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
return media::TimeIntervals();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user