зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 8 changesets (bug 1175768) for frequent media test failures.
Backed out changeset a369cfb95b59 (bug 1175768) Backed out changeset e02dd312d622 (bug 1175768) Backed out changeset 6776ce74b9e5 (bug 1175768) Backed out changeset 6aa5fa1d318e (bug 1175768) Backed out changeset a8bd7a0d2aea (bug 1175768) Backed out changeset 41ffc9a9ac48 (bug 1175768) Backed out changeset 2d2cefa397dc (bug 1175768) Backed out changeset 4e06368496d2 (bug 1175768) CLOSED TREE
This commit is contained in:
Родитель
800d3a7d07
Коммит
d9ca5de3ed
|
@ -74,16 +74,10 @@ public:
|
|||
|
||||
virtual AbstractCanonical<media::NullableTimeUnit>* CanonicalDurationOrNull() { return nullptr; };
|
||||
|
||||
protected:
|
||||
virtual void UpdateEstimatedMediaDuration(int64_t aDuration) {};
|
||||
public:
|
||||
void DispatchUpdateEstimatedMediaDuration(int64_t aDuration)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NS_NewRunnableMethodWithArg<int64_t>(this, &AbstractMediaDecoder::UpdateEstimatedMediaDuration,
|
||||
aDuration);
|
||||
NS_DispatchToMainThread(r);
|
||||
}
|
||||
// Sets the duration of the media in microseconds. The MediaDecoder
|
||||
// fires a durationchange event to its owner (e.g., an HTML audio
|
||||
// tag).
|
||||
virtual void UpdateEstimatedMediaDuration(int64_t aDuration) = 0;
|
||||
|
||||
// Set the media as being seekable or not.
|
||||
virtual void SetMediaSeekable(bool aMediaSeekable) = 0;
|
||||
|
@ -117,8 +111,7 @@ public:
|
|||
|
||||
// Called by the reader's MediaResource as data arrives over the network.
|
||||
// Must be called on the main thread.
|
||||
virtual void NotifyDataArrived(uint32_t aLength, int64_t aOffset,
|
||||
bool aThrottleUpdates) = 0;
|
||||
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) = 0;
|
||||
|
||||
// Set by Reader if the current audio track can be offloaded
|
||||
virtual void SetPlatformCanOffloadAudio(bool aCanOffloadAudio) {}
|
||||
|
|
|
@ -281,16 +281,6 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
bool operator== (const SelfType& aOther) const
|
||||
{
|
||||
return mIntervals == aOther.mIntervals;
|
||||
}
|
||||
|
||||
bool operator!= (const SelfType& aOther) const
|
||||
{
|
||||
return mIntervals != aOther.mIntervals;
|
||||
}
|
||||
|
||||
SelfType& operator= (const SelfType& aOther)
|
||||
{
|
||||
mIntervals = aOther.mIntervals;
|
||||
|
|
|
@ -466,7 +466,7 @@ nsresult MP3FrameParser::ParseBuffer(const uint8_t* aBuffer,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void MP3FrameParser::Parse(const uint8_t* aBuffer, uint32_t aLength, uint64_t aOffset)
|
||||
void MP3FrameParser::Parse(const char* aBuffer, uint32_t aLength, uint64_t aOffset)
|
||||
{
|
||||
MutexAutoLock mon(mLock);
|
||||
|
||||
|
@ -475,7 +475,7 @@ void MP3FrameParser::Parse(const uint8_t* aBuffer, uint32_t aLength, uint64_t aO
|
|||
return;
|
||||
}
|
||||
|
||||
const uint8_t* buffer = aBuffer;
|
||||
const uint8_t* buffer = reinterpret_cast<const uint8_t*>(aBuffer);
|
||||
int32_t length = aLength;
|
||||
uint64_t offset = aOffset;
|
||||
|
||||
|
|
|
@ -112,7 +112,7 @@ public:
|
|||
return mIsMP3 != NOT_MP3;
|
||||
}
|
||||
|
||||
void Parse(const uint8_t* aBuffer, uint32_t aLength, uint64_t aStreamOffset);
|
||||
void Parse(const char* aBuffer, uint32_t aLength, uint64_t aStreamOffset);
|
||||
|
||||
// Returns the duration, in microseconds. If the entire stream has not
|
||||
// been parsed yet, this is an estimate based on the bitrate of the
|
||||
|
|
|
@ -329,7 +329,6 @@ 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)"),
|
||||
|
@ -1106,8 +1105,6 @@ void MediaDecoder::DurationChanged()
|
|||
|
||||
void MediaDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mPlayState <= PLAY_STATE_LOADING) {
|
||||
return;
|
||||
}
|
||||
|
@ -1265,12 +1262,10 @@ 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();
|
||||
}
|
||||
|
@ -1302,7 +1297,8 @@ void MediaDecoder::Invalidate()
|
|||
// Constructs the time ranges representing what segments of the media
|
||||
// are buffered and playable.
|
||||
media::TimeIntervals MediaDecoder::GetBuffered() {
|
||||
return mBuffered.Ref();
|
||||
NS_ENSURE_TRUE(mDecoderStateMachine && !mShuttingDown, media::TimeIntervals::Invalid());
|
||||
return mDecoderStateMachine->GetBuffered();
|
||||
}
|
||||
|
||||
size_t MediaDecoder::SizeOfVideoQueue() {
|
||||
|
@ -1319,11 +1315,9 @@ size_t MediaDecoder::SizeOfAudioQueue() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void MediaDecoder::NotifyDataArrived(uint32_t aLength, int64_t aOffset, bool aThrottleUpdates) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
void MediaDecoder::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) {
|
||||
if (mDecoderStateMachine) {
|
||||
mDecoderStateMachine->DispatchNotifyDataArrived(aLength, aOffset, aThrottleUpdates);
|
||||
mDecoderStateMachine->NotifyDataArrived(aBuffer, aLength, aOffset);
|
||||
}
|
||||
|
||||
// ReadyState computation depends on MediaDecoder::CanPlayThrough, which
|
||||
|
|
|
@ -434,8 +434,7 @@ public:
|
|||
|
||||
// Called as data arrives on the stream and is read into the cache. Called
|
||||
// on the main thread only.
|
||||
virtual void NotifyDataArrived(uint32_t aLength, int64_t aOffset,
|
||||
bool aThrottleUpdates) override;
|
||||
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) override;
|
||||
|
||||
// Called by MediaResource when the principal of the resource has
|
||||
// changed. Called on main thread only.
|
||||
|
@ -455,7 +454,6 @@ public:
|
|||
// Call on the main thread only.
|
||||
virtual bool IsEndedOrShutdown() const;
|
||||
|
||||
protected:
|
||||
// Updates the media duration. This is called while the media is being
|
||||
// played, calls before the media has reached loaded metadata are ignored.
|
||||
// The duration is assumed to be an estimate, and so a degree of
|
||||
|
@ -465,7 +463,6 @@ protected:
|
|||
// changed, this causes a durationchanged event to fire to the media
|
||||
// element.
|
||||
void UpdateEstimatedMediaDuration(int64_t aDuration) override;
|
||||
public:
|
||||
|
||||
// Set a flag indicating whether seeking is supported
|
||||
virtual void SetMediaSeekable(bool aMediaSeekable) override;
|
||||
|
@ -893,9 +890,6 @@ 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,12 +69,7 @@ MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder,
|
|||
, mTaskQueue(aBorrowedTaskQueue ? aBorrowedTaskQueue
|
||||
: new MediaTaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
|
||||
/* aSupportsTailDispatch = */ true))
|
||||
, mWatchManager(this, mTaskQueue)
|
||||
, mTimer(new MediaTimer())
|
||||
, mBuffered(mTaskQueue, TimeIntervals(), "MediaDecoderReader::mBuffered (Canonical)")
|
||||
, mDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderReader::mDuration (Mirror)")
|
||||
, mThrottleDuration(TimeDuration::FromMilliseconds(500))
|
||||
, mLastThrottledNotify(TimeStamp::Now() - mThrottleDuration)
|
||||
, mIgnoreAudioOutputFormat(false)
|
||||
, mStartTime(-1)
|
||||
, mHitAudioDecodeError(false)
|
||||
|
@ -97,9 +92,6 @@ MediaDecoderReader::InitializationTask()
|
|||
if (mDecoder->CanonicalDurationOrNull()) {
|
||||
mDuration.Connect(mDecoder->CanonicalDurationOrNull());
|
||||
}
|
||||
|
||||
// Initialize watchers.
|
||||
mWatchManager.Watch(mDuration, &MediaDecoderReader::UpdateBuffered);
|
||||
}
|
||||
|
||||
MediaDecoderReader::~MediaDecoderReader()
|
||||
|
@ -169,65 +161,16 @@ VideoData* MediaDecoderReader::DecodeToFirstVideoData()
|
|||
}
|
||||
|
||||
void
|
||||
MediaDecoderReader::UpdateBuffered()
|
||||
MediaDecoderReader::SetStartTime(int64_t aStartTime)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
NS_ENSURE_TRUE_VOID(!mShutdown);
|
||||
mBuffered = GetBuffered();
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderReader::ThrottledNotifyDataArrived(const Interval<int64_t>& aInterval)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
NS_ENSURE_TRUE_VOID(!mShutdown);
|
||||
|
||||
if (mThrottledInterval.isNothing()) {
|
||||
mThrottledInterval.emplace(aInterval);
|
||||
} else if (!mThrottledInterval.ref().Contiguous(aInterval)) {
|
||||
DoThrottledNotify();
|
||||
mThrottledInterval.emplace(aInterval);
|
||||
} else {
|
||||
mThrottledInterval = Some(mThrottledInterval.ref().Span(aInterval));
|
||||
}
|
||||
|
||||
// If it's been long enough since our last update, do it.
|
||||
if (TimeStamp::Now() - mLastThrottledNotify > mThrottleDuration) {
|
||||
DoThrottledNotify();
|
||||
} else if (!mThrottledNotify.Exists()) {
|
||||
// Otherwise, schedule an update if one isn't scheduled already.
|
||||
nsRefPtr<MediaDecoderReader> self = this;
|
||||
mThrottledNotify.Begin(
|
||||
mTimer->WaitUntil(mLastThrottledNotify + mThrottleDuration, __func__)
|
||||
->Then(TaskQueue(), __func__,
|
||||
[self] () -> void {
|
||||
self->mThrottledNotify.Complete();
|
||||
NS_ENSURE_TRUE_VOID(!self->mShutdown);
|
||||
self->DoThrottledNotify();
|
||||
},
|
||||
[self] () -> void {
|
||||
self->mThrottledNotify.Complete();
|
||||
NS_WARNING("throttle callback rejected");
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderReader::DoThrottledNotify()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
mLastThrottledNotify = TimeStamp::Now();
|
||||
mThrottledNotify.DisconnectIfExists();
|
||||
Interval<int64_t> interval = mThrottledInterval.ref();
|
||||
mThrottledInterval.reset();
|
||||
NotifyDataArrived(interval);
|
||||
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
MOZ_ASSERT(mStartTime == -1);
|
||||
mStartTime = aStartTime;
|
||||
}
|
||||
|
||||
media::TimeIntervals
|
||||
MediaDecoderReader::GetBuffered()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
NS_ENSURE_TRUE(mStartTime >= 0, media::TimeIntervals());
|
||||
AutoPinned<MediaResource> stream(mDecoder->GetResource());
|
||||
|
||||
|
@ -413,14 +356,8 @@ MediaDecoderReader::Shutdown()
|
|||
mBaseAudioPromise.RejectIfExists(END_OF_STREAM, __func__);
|
||||
mBaseVideoPromise.RejectIfExists(END_OF_STREAM, __func__);
|
||||
|
||||
mThrottledNotify.DisconnectIfExists();
|
||||
|
||||
ReleaseMediaResources();
|
||||
mDuration.DisconnectIfConnected();
|
||||
mBuffered.DisconnectAll();
|
||||
|
||||
// Shut down the watch manager before shutting down our task queue.
|
||||
mWatchManager.Shutdown();
|
||||
|
||||
nsRefPtr<ShutdownPromise> p;
|
||||
|
||||
|
@ -436,7 +373,6 @@ MediaDecoderReader::Shutdown()
|
|||
p = ShutdownPromise::CreateAndResolve(true, __func__);
|
||||
}
|
||||
|
||||
mTimer = nullptr;
|
||||
mDecoder = nullptr;
|
||||
|
||||
return p;
|
||||
|
|
|
@ -11,9 +11,7 @@
|
|||
#include "MediaData.h"
|
||||
#include "MediaPromise.h"
|
||||
#include "MediaQueue.h"
|
||||
#include "MediaTimer.h"
|
||||
#include "AudioCompactor.h"
|
||||
#include "Intervals.h"
|
||||
#include "TimeUnits.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -205,10 +203,8 @@ public:
|
|||
mIgnoreAudioOutputFormat = true;
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Populates aBuffered with the time ranges which are buffered. This function
|
||||
// is called on the main, decode, and state machine threads.
|
||||
//
|
||||
// This base implementation in MediaDecoderReader estimates the time ranges
|
||||
// buffered by interpolating the cached byte ranges with the duration
|
||||
|
@ -223,9 +219,6 @@ 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; }
|
||||
|
||||
|
@ -246,38 +239,9 @@ public:
|
|||
virtual size_t SizeOfVideoQueueInFrames();
|
||||
virtual size_t SizeOfAudioQueueInFrames();
|
||||
|
||||
protected:
|
||||
friend class TrackBuffer;
|
||||
virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) { }
|
||||
|
||||
void NotifyDataArrived(const media::Interval<int64_t>& aInfo)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
NS_ENSURE_TRUE_VOID(!mShutdown);
|
||||
NotifyDataArrivedInternal(aInfo.Length(), aInfo.mStart);
|
||||
UpdateBuffered();
|
||||
}
|
||||
|
||||
// Invokes NotifyDataArrived while throttling the calls to occur at most every mThrottleDuration ms.
|
||||
void ThrottledNotifyDataArrived(const media::Interval<int64_t>& aInterval);
|
||||
void DoThrottledNotify();
|
||||
|
||||
public:
|
||||
// In situations where these notifications come from stochastic network
|
||||
// activity, we can save significant recomputation by throttling the delivery
|
||||
// of these updates to the reader implementation. We don't want to do this
|
||||
// throttling when the update comes from MSE code, since that code needs the
|
||||
// updates to be observable immediately, and is generally less
|
||||
// trigger-happy with notifications anyway.
|
||||
void DispatchNotifyDataArrived(uint32_t aLength, int64_t aOffset, bool aThrottleUpdates)
|
||||
{
|
||||
RefPtr<nsRunnable> r =
|
||||
NS_NewRunnableMethodWithArg<media::Interval<int64_t>>(this, aThrottleUpdates ? &MediaDecoderReader::ThrottledNotifyDataArrived
|
||||
: &MediaDecoderReader::NotifyDataArrived,
|
||||
media::Interval<int64_t>(aOffset, aOffset + aLength));
|
||||
TaskQueue()->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess);
|
||||
}
|
||||
|
||||
// Only used by WebMReader and MediaOmxReader for now, so stub here rather
|
||||
// than in every reader than inherits from MediaDecoderReader.
|
||||
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) {}
|
||||
// Notify the reader that data from the resource was evicted (MediaSource only)
|
||||
virtual void NotifyDataRemoved() {}
|
||||
virtual int64_t GetEvictionOffset(double aTime) { return -1; }
|
||||
|
@ -298,20 +262,7 @@ public:
|
|||
// Indicates if the media is seekable.
|
||||
// ReadMetada should be called before calling this method.
|
||||
virtual bool IsMediaSeekable() = 0;
|
||||
|
||||
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());
|
||||
}
|
||||
void SetStartTime(int64_t aStartTime);
|
||||
|
||||
MediaTaskQueue* TaskQueue() {
|
||||
return mTaskQueue;
|
||||
|
@ -370,30 +321,12 @@ protected:
|
|||
// Decode task queue.
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
|
||||
// State-watching manager.
|
||||
WatchManager<MediaDecoderReader> mWatchManager;
|
||||
|
||||
// MediaTimer.
|
||||
nsRefPtr<MediaTimer> mTimer;
|
||||
|
||||
// Buffered range.
|
||||
Canonical<media::TimeIntervals> mBuffered;
|
||||
public:
|
||||
AbstractCanonical<media::TimeIntervals>* CanonicalBuffered() { return &mBuffered; }
|
||||
protected:
|
||||
|
||||
// Stores presentation info required for playback.
|
||||
MediaInfo mInfo;
|
||||
|
||||
// Duration, mirrored from the state machine task queue.
|
||||
Mirror<media::NullableTimeUnit> mDuration;
|
||||
|
||||
// State for ThrottledNotifyDataArrived.
|
||||
MediaPromiseRequestHolder<MediaTimerPromise> mThrottledNotify;
|
||||
const TimeDuration mThrottleDuration;
|
||||
TimeStamp mLastThrottledNotify;
|
||||
Maybe<media::Interval<int64_t>> mThrottledInterval;
|
||||
|
||||
// Whether we should accept media that we know we can't play
|
||||
// directly, because they have a number of channel higher than
|
||||
// what we support.
|
||||
|
|
|
@ -187,7 +187,6 @@ 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)"),
|
||||
|
@ -297,7 +296,6 @@ MediaDecoderStateMachine::InitializationTask()
|
|||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
// Connect mirrors.
|
||||
mBuffered.Connect(mReader->CanonicalBuffered());
|
||||
mEstimatedDuration.Connect(mDecoder->CanonicalEstimatedDuration());
|
||||
mExplicitDuration.Connect(mDecoder->CanonicalExplicitDuration());
|
||||
mPlayState.Connect(mDecoder->CanonicalPlayState());
|
||||
|
@ -308,7 +306,6 @@ 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);
|
||||
|
@ -1642,20 +1639,33 @@ void MediaDecoderStateMachine::LogicallySeekingChanged()
|
|||
ScheduleStateMachine();
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::BufferedRangeUpdated()
|
||||
void MediaDecoderStateMachine::NotifyDataArrived(const char* aBuffer,
|
||||
uint32_t aLength,
|
||||
int64_t aOffset)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
mReader->NotifyDataArrived(aBuffer, aLength, aOffset);
|
||||
|
||||
// While playing an unseekable stream of unknown duration, mObservedDuration
|
||||
// is updated (in AdvanceFrame()) as we play. But if data is being downloaded
|
||||
// faster than played, mObserved won't reflect the end of playable data
|
||||
// While playing an unseekable stream of unknown duration, mDuration is
|
||||
// updated (in AdvanceFrame()) as we play. But if data is being downloaded
|
||||
// faster than played, mDuration 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.
|
||||
if (!mBuffered.Ref().IsInvalid()) {
|
||||
// mDuration here as new data is downloaded to prevent such a lag.
|
||||
//
|
||||
// Make sure to only do this if we have a start time, otherwise the reader
|
||||
// doesn't know how to compute GetBuffered.
|
||||
if (!mDecoder->IsInfinite() || !HaveStartTime())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
media::TimeIntervals buffered{mDecoder->GetBuffered()};
|
||||
if (!buffered.IsInvalid()) {
|
||||
bool exists;
|
||||
media::TimeUnit end{mBuffered.Ref().GetEnd(&exists)};
|
||||
media::TimeUnit end{buffered.GetEnd(&exists)};
|
||||
if (exists) {
|
||||
mObservedDuration = std::max(mObservedDuration.Ref(), end);
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mDuration = Some(std::max<TimeUnit>(Duration(), end));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2050,16 +2060,17 @@ bool MediaDecoderStateMachine::HasLowUndecodedData(int64_t aUsecs)
|
|||
MOZ_ASSERT(OnTaskQueue());
|
||||
AssertCurrentThreadInMonitor();
|
||||
NS_ASSERTION(mState > DECODER_STATE_DECODING_FIRSTFRAME,
|
||||
"Must have loaded first frame for mBuffered to be valid");
|
||||
"Must have loaded first frame for GetBuffered() to work");
|
||||
|
||||
// If we don't have a duration, mBuffered is probably not going to have
|
||||
// If we don't have a duration, GetBuffered is probably not going to produce
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (mBuffered.Ref().IsInvalid()) {
|
||||
media::TimeIntervals buffered{mReader->GetBuffered()};
|
||||
if (buffered.IsInvalid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2081,7 +2092,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 && !mBuffered.Ref().Contains(interval);
|
||||
return endOfDecodedData != INT64_MAX && !buffered.Contains(interval);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2133,7 +2144,8 @@ MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata)
|
|||
mStartTimeRendezvous->AwaitStartTime()->Then(TaskQueue(), __func__,
|
||||
[self] () -> void {
|
||||
NS_ENSURE_TRUE_VOID(!self->IsShutdown());
|
||||
self->mReader->DispatchSetStartTime(self->StartTime());
|
||||
ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
|
||||
self->mReader->SetStartTime(self->StartTime());
|
||||
},
|
||||
[] () -> void { NS_WARNING("Setting start time on reader failed"); }
|
||||
);
|
||||
|
@ -2481,7 +2493,6 @@ MediaDecoderStateMachine::FinishShutdown()
|
|||
VideoQueue().ClearListeners();
|
||||
|
||||
// Disconnect canonicals and mirrors before shutting down our task queue.
|
||||
mBuffered.DisconnectIfConnected();
|
||||
mEstimatedDuration.DisconnectIfConnected();
|
||||
mExplicitDuration.DisconnectIfConnected();
|
||||
mPlayState.DisconnectIfConnected();
|
||||
|
|
|
@ -252,6 +252,11 @@ public:
|
|||
return mState == DECODER_STATE_SEEKING;
|
||||
}
|
||||
|
||||
media::TimeIntervals GetBuffered() {
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
return mReader->GetBuffered();
|
||||
}
|
||||
|
||||
size_t SizeOfVideoQueue() {
|
||||
if (mReader) {
|
||||
return mReader->SizeOfVideoQueueInBytes();
|
||||
|
@ -266,12 +271,7 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
void DispatchNotifyDataArrived(uint32_t aLength, int64_t aOffset, bool aThrottleUpdates)
|
||||
{
|
||||
mReader->DispatchNotifyDataArrived(aLength, aOffset, aThrottleUpdates);
|
||||
}
|
||||
|
||||
AbstractCanonical<media::TimeIntervals>* CanonicalBuffered() { return mReader->CanonicalBuffered(); }
|
||||
void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
|
||||
|
||||
// Returns the state machine task queue.
|
||||
MediaTaskQueue* TaskQueue() const { return mTaskQueue; }
|
||||
|
@ -393,8 +393,6 @@ protected:
|
|||
|
||||
void SetState(State aState);
|
||||
|
||||
void BufferedRangeUpdated();
|
||||
|
||||
// Inserts MediaData* samples into their respective MediaQueues.
|
||||
// aSample must not be null.
|
||||
void Push(AudioData* aSample);
|
||||
|
@ -940,9 +938,6 @@ 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,6 +73,7 @@ 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
|
||||
|
@ -793,8 +794,11 @@ MediaFormatReader::UpdateReceivedNewData(TrackType aTrack)
|
|||
decoder.mWaitingForData = false;
|
||||
bool hasLastEnd;
|
||||
media::TimeUnit lastEnd = decoder.mTimeRanges.GetEnd(&hasLastEnd);
|
||||
// Update our cached TimeRange.
|
||||
decoder.mTimeRanges = decoder.mTrackDemuxer->GetBuffered();
|
||||
{
|
||||
MonitorAutoLock lock(decoder.mMonitor);
|
||||
// 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.
|
||||
|
@ -1398,7 +1402,6 @@ MediaFormatReader::GetEvictionOffset(double aTime)
|
|||
media::TimeIntervals
|
||||
MediaFormatReader::GetBuffered()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
media::TimeIntervals videoti;
|
||||
media::TimeIntervals audioti;
|
||||
media::TimeIntervals intervals;
|
||||
|
@ -1412,25 +1415,54 @@ MediaFormatReader::GetBuffered()
|
|||
NS_ENSURE_TRUE(mStartTime >= 0, media::TimeIntervals());
|
||||
startTime = mStartTime;
|
||||
}
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return intervals.Shift(media::TimeUnit::FromMicroseconds(-startTime));
|
||||
|
@ -1507,43 +1539,51 @@ MediaFormatReader::NotifyDemuxer(uint32_t aLength, int64_t aOffset)
|
|||
}
|
||||
|
||||
void
|
||||
MediaFormatReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
|
||||
MediaFormatReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(aLength);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
MOZ_ASSERT(aBuffer || 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 thread demuxer.
|
||||
nsCOMPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethodWithArgs<uint32_t, int64_t>(
|
||||
mMainThreadDemuxer, &MediaDataDemuxer::NotifyDataArrived,
|
||||
// Queue a task to notify our main demuxer.
|
||||
RefPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethodWithArgs<int32_t, uint64_t>(
|
||||
this, &MediaFormatReader::NotifyDemuxer,
|
||||
aLength, aOffset);
|
||||
AbstractThread::MainThread()->Dispatch(task.forget());
|
||||
|
||||
NotifyDemuxer(aLength, aOffset);
|
||||
TaskQueue()->Dispatch(task.forget());
|
||||
}
|
||||
|
||||
void
|
||||
MediaFormatReader::NotifyDataRemoved()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mDataRange = ByteInterval();
|
||||
mCachedTimeRangesStale = true;
|
||||
|
||||
if (!mInitDone) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mMainThreadDemuxer);
|
||||
mMainThreadDemuxer->NotifyDataRemoved();
|
||||
|
||||
// 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);
|
||||
// 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());
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "MediaDataDemuxer.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "MediaTaskQueue.h"
|
||||
|
@ -67,9 +68,9 @@ public:
|
|||
}
|
||||
|
||||
int64_t GetEvictionOffset(double aTime) override;
|
||||
protected:
|
||||
void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) override;
|
||||
public:
|
||||
void NotifyDataArrived(const char* aBuffer,
|
||||
uint32_t aLength,
|
||||
int64_t aOffset) override;
|
||||
void NotifyDataRemoved() override;
|
||||
|
||||
media::TimeIntervals GetBuffered() override;
|
||||
|
@ -206,6 +207,8 @@ private:
|
|||
, mNumSamplesOutput(0)
|
||||
, mSizeOfQueue(0)
|
||||
, mLastStreamSourceID(UINT32_MAX)
|
||||
, mMonitor(aType == MediaData::AUDIO_DATA ? "audio decoder data"
|
||||
: "video decoder data")
|
||||
{}
|
||||
|
||||
MediaFormatReader* mOwner;
|
||||
|
@ -292,6 +295,9 @@ 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;
|
||||
};
|
||||
|
@ -414,6 +420,9 @@ 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;
|
||||
|
|
|
@ -463,8 +463,7 @@ ChannelMediaResource::CopySegmentToCache(nsIInputStream *aInStream,
|
|||
{
|
||||
CopySegmentClosure* closure = static_cast<CopySegmentClosure*>(aClosure);
|
||||
|
||||
closure->mResource->mDecoder->NotifyDataArrived(aCount, closure->mResource->mOffset,
|
||||
/* aThrottleUpdates = */ true);
|
||||
closure->mResource->mDecoder->NotifyDataArrived(aFromSegment, aCount, closure->mResource->mOffset);
|
||||
|
||||
// Keep track of where we're up to.
|
||||
RESOURCE_LOG("%p [ChannelMediaResource]: CopySegmentToCache at mOffset [%lld] add "
|
||||
|
@ -758,28 +757,6 @@ nsresult ChannelMediaResource::ReadAt(int64_t aOffset,
|
|||
return rv;
|
||||
}
|
||||
|
||||
already_AddRefed<MediaByteBuffer>
|
||||
ChannelMediaResource::SilentReadAt(int64_t aOffset, uint32_t aCount)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
|
||||
|
||||
nsRefPtr<MediaByteBuffer> bytes = new MediaByteBuffer();
|
||||
bool ok = bytes->SetCapacity(aCount, fallible);
|
||||
NS_ENSURE_TRUE(ok, nullptr);
|
||||
int64_t pos = mCacheStream.Tell();
|
||||
char* curr = reinterpret_cast<char*>(bytes->Elements());
|
||||
while (aCount > 0) {
|
||||
uint32_t bytesRead;
|
||||
nsresult rv = mCacheStream.ReadAt(aOffset, curr, aCount, &bytesRead);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
NS_ENSURE_TRUE(bytesRead > 0, nullptr);
|
||||
aCount -= bytesRead;
|
||||
curr += bytesRead;
|
||||
}
|
||||
mCacheStream.Seek(nsISeekableStream::NS_SEEK_SET, pos);
|
||||
return bytes.forget();
|
||||
}
|
||||
|
||||
nsresult ChannelMediaResource::Seek(int32_t aWhence, int64_t aOffset)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
|
||||
|
@ -1216,7 +1193,6 @@ public:
|
|||
virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes) override;
|
||||
virtual nsresult ReadAt(int64_t aOffset, char* aBuffer,
|
||||
uint32_t aCount, uint32_t* aBytes) override;
|
||||
virtual already_AddRefed<MediaByteBuffer> SilentReadAt(int64_t aOffset, uint32_t aCount) override;
|
||||
virtual nsresult Seek(int32_t aWhence, int64_t aOffset) override;
|
||||
virtual int64_t Tell() override;
|
||||
|
||||
|
@ -1540,34 +1516,6 @@ nsresult FileMediaResource::ReadAt(int64_t aOffset, char* aBuffer,
|
|||
return rv;
|
||||
}
|
||||
|
||||
already_AddRefed<MediaByteBuffer>
|
||||
FileMediaResource::SilentReadAt(int64_t aOffset, uint32_t aCount)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
|
||||
|
||||
MutexAutoLock lock(mLock);
|
||||
nsRefPtr<MediaByteBuffer> bytes = new MediaByteBuffer();
|
||||
bool ok = bytes->SetCapacity(aCount, fallible);
|
||||
NS_ENSURE_TRUE(ok, nullptr);
|
||||
int64_t pos = 0;
|
||||
NS_ENSURE_TRUE(mSeekable, nullptr);
|
||||
nsresult rv = mSeekable->Tell(&pos);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
rv = UnsafeSeek(nsISeekableStream::NS_SEEK_SET, aOffset);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
char* curr = reinterpret_cast<char*>(bytes->Elements());
|
||||
while (aCount > 0) {
|
||||
uint32_t bytesRead;
|
||||
rv = UnsafeRead(curr, aCount, &bytesRead);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
NS_ENSURE_TRUE(bytesRead > 0, nullptr);
|
||||
aCount -= bytesRead;
|
||||
curr += bytesRead;
|
||||
}
|
||||
UnsafeSeek(nsISeekableStream::NS_SEEK_SET, pos);
|
||||
return bytes.forget();
|
||||
}
|
||||
|
||||
nsresult FileMediaResource::Seek(int32_t aWhence, int64_t aOffset)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
|
||||
|
|
|
@ -9,13 +9,11 @@
|
|||
#include "mozilla/Mutex.h"
|
||||
#include "nsIChannel.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "nsIStreamingProtocolController.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIChannelEventSink.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "MediaCache.h"
|
||||
#include "MediaData.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
@ -289,33 +287,6 @@ public:
|
|||
// results and requirements are the same as per the Read method.
|
||||
virtual nsresult ReadAt(int64_t aOffset, char* aBuffer,
|
||||
uint32_t aCount, uint32_t* aBytes) = 0;
|
||||
|
||||
// ReadAt without side-effects. Given that our MediaResource infrastructure
|
||||
// is very side-effecty, this accomplishes its job by checking the initial
|
||||
// position and seeking back to it. If the seek were to fail, a side-effect
|
||||
// might be observable.
|
||||
//
|
||||
// This method returns null if anything fails, including the failure to read
|
||||
// aCount bytes. Otherwise, it returns an owned buffer.
|
||||
virtual already_AddRefed<MediaByteBuffer> SilentReadAt(int64_t aOffset, uint32_t aCount)
|
||||
{
|
||||
nsRefPtr<MediaByteBuffer> bytes = new MediaByteBuffer();
|
||||
bool ok = bytes->SetCapacity(aCount, fallible);
|
||||
NS_ENSURE_TRUE(ok, nullptr);
|
||||
int64_t pos = Tell();
|
||||
char* curr = reinterpret_cast<char*>(bytes->Elements());
|
||||
while (aCount > 0) {
|
||||
uint32_t bytesRead;
|
||||
nsresult rv = ReadAt(aOffset, curr, aCount, &bytesRead);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
NS_ENSURE_TRUE(bytesRead > 0, nullptr);
|
||||
aCount -= bytesRead;
|
||||
curr += bytesRead;
|
||||
}
|
||||
Seek(nsISeekableStream::NS_SEEK_SET, pos);
|
||||
return bytes.forget();
|
||||
}
|
||||
|
||||
// Seek to the given bytes offset in the stream. aWhence can be
|
||||
// one of:
|
||||
// NS_SEEK_SET
|
||||
|
@ -639,7 +610,6 @@ public:
|
|||
virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes) override;
|
||||
virtual nsresult ReadAt(int64_t offset, char* aBuffer,
|
||||
uint32_t aCount, uint32_t* aBytes) override;
|
||||
virtual already_AddRefed<MediaByteBuffer> SilentReadAt(int64_t aOffset, uint32_t aCount) override;
|
||||
virtual nsresult Seek(int32_t aWhence, int64_t aOffset) override;
|
||||
virtual int64_t Tell() override;
|
||||
|
||||
|
|
|
@ -254,7 +254,7 @@ public:
|
|||
return TimeIntervals(TimeInterval(TimeUnit::FromMicroseconds(INT64_MIN),
|
||||
TimeUnit::FromMicroseconds(INT64_MIN)));
|
||||
}
|
||||
bool IsInvalid() const
|
||||
bool IsInvalid()
|
||||
{
|
||||
return Length() == 1 && Start(0).ToMicroseconds() == INT64_MIN &&
|
||||
End(0).ToMicroseconds() == INT64_MIN;
|
||||
|
|
|
@ -387,7 +387,7 @@ AppleMP3Reader::ReadMetadata(MediaInfo* aInfo,
|
|||
bytes,
|
||||
0 /* flags */);
|
||||
|
||||
mMP3FrameParser.Parse(reinterpret_cast<uint8_t*>(bytes), numBytes, offset);
|
||||
mMP3FrameParser.Parse(bytes, numBytes, offset);
|
||||
|
||||
offset += numBytes;
|
||||
|
||||
|
@ -525,16 +525,16 @@ AppleMP3Reader::Seek(int64_t aTime, int64_t aEndTime)
|
|||
}
|
||||
|
||||
void
|
||||
AppleMP3Reader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
|
||||
AppleMP3Reader::NotifyDataArrived(const char* aBuffer,
|
||||
uint32_t aLength,
|
||||
int64_t aOffset)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mMP3FrameParser.NeedsData()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaByteBuffer> bytes = mDecoder->GetResource()->SilentReadAt(aOffset, aLength);
|
||||
NS_ENSURE_TRUE_VOID(bytes);
|
||||
mMP3FrameParser.Parse(bytes->Elements(), aLength, aOffset);
|
||||
mMP3FrameParser.Parse(aBuffer, aLength, aOffset);
|
||||
if (!mMP3FrameParser.IsMP3()) {
|
||||
return;
|
||||
}
|
||||
|
@ -543,8 +543,9 @@ AppleMP3Reader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
|
|||
if (duration != mDuration) {
|
||||
LOGD("Updating media duration to %lluus\n", duration);
|
||||
MOZ_ASSERT(mDecoder);
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mDuration = duration;
|
||||
mDecoder->DispatchUpdateEstimatedMediaDuration(duration);
|
||||
mDecoder->UpdateEstimatedMediaDuration(duration);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,10 +45,9 @@ public:
|
|||
AudioFileStreamPropertyID aPropertyID,
|
||||
UInt32 *aFlags);
|
||||
|
||||
protected:
|
||||
virtual void NotifyDataArrivedInternal(uint32_t aLength,
|
||||
int64_t aOffset) override;
|
||||
public:
|
||||
virtual void NotifyDataArrived(const char* aBuffer,
|
||||
uint32_t aLength,
|
||||
int64_t aOffset) override;
|
||||
|
||||
virtual bool IsMediaSeekable() override;
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ ParseMP3Headers(MP3FrameParser *aParser, MediaResource *aResource)
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
aParser->Parse(reinterpret_cast<uint8_t*>(buffer), bytesRead, offset);
|
||||
aParser->Parse(buffer, bytesRead, offset);
|
||||
offset += bytesRead;
|
||||
}
|
||||
|
||||
|
@ -401,16 +401,14 @@ DirectShowReader::SeekInternal(int64_t aTargetUs)
|
|||
}
|
||||
|
||||
void
|
||||
DirectShowReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
|
||||
DirectShowReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mMP3FrameParser.NeedsData()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaByteBuffer> bytes = mDecoder->GetResource()->SilentReadAt(aOffset, aLength);
|
||||
NS_ENSURE_TRUE_VOID(bytes);
|
||||
mMP3FrameParser.Parse(bytes->Elements(), aLength, aOffset);
|
||||
mMP3FrameParser.Parse(aBuffer, aLength, aOffset);
|
||||
if (!mMP3FrameParser.IsMP3()) {
|
||||
return;
|
||||
}
|
||||
|
@ -418,8 +416,9 @@ DirectShowReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
|
|||
int64_t duration = mMP3FrameParser.GetDuration();
|
||||
if (duration != mDuration) {
|
||||
MOZ_ASSERT(mDecoder);
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mDuration = duration;
|
||||
mDecoder->DispatchUpdateEstimatedMediaDuration(mDuration);
|
||||
mDecoder->UpdateEstimatedMediaDuration(mDuration);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,10 +58,9 @@ public:
|
|||
nsRefPtr<SeekPromise>
|
||||
Seek(int64_t aTime, int64_t aEndTime) override;
|
||||
|
||||
protected:
|
||||
void NotifyDataArrivedInternal(uint32_t aLength,
|
||||
int64_t aOffset) override;
|
||||
public:
|
||||
void NotifyDataArrived(const char* aBuffer,
|
||||
uint32_t aLength,
|
||||
int64_t aOffset) override;
|
||||
|
||||
bool IsMediaSeekable() override;
|
||||
|
||||
|
|
|
@ -1075,7 +1075,6 @@ MP4Reader::GetEvictionOffset(double aTime)
|
|||
media::TimeIntervals
|
||||
MP4Reader::GetBuffered()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MonitorAutoLock mon(mDemuxerMonitor);
|
||||
media::TimeIntervals buffered;
|
||||
if (!mIndexReady) {
|
||||
|
@ -1147,9 +1146,14 @@ MP4Reader::VideoIsHardwareAccelerated() const
|
|||
}
|
||||
|
||||
void
|
||||
MP4Reader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
|
||||
MP4Reader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mShutdown) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mLastSeenEnd < 0) {
|
||||
MonitorAutoLock mon(mDemuxerMonitor);
|
||||
mLastSeenEnd = mDecoder->GetResource()->GetLength();
|
||||
|
|
|
@ -62,10 +62,7 @@ public:
|
|||
virtual bool IsMediaSeekable() override;
|
||||
|
||||
virtual int64_t GetEvictionOffset(double aTime) override;
|
||||
|
||||
protected:
|
||||
virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) override;
|
||||
public:
|
||||
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) override;
|
||||
|
||||
virtual media::TimeIntervals GetBuffered() override;
|
||||
|
||||
|
|
|
@ -335,7 +335,7 @@ nsresult GStreamerReader::ParseMP3Headers()
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(bytesRead, NS_ERROR_FAILURE);
|
||||
|
||||
mMP3FrameParser.Parse(reinterpret_cast<uint8_t*>(bytes), bytesRead, offset);
|
||||
mMP3FrameParser.Parse(bytes, bytesRead, offset);
|
||||
offset += bytesRead;
|
||||
} while (!mMP3FrameParser.ParsedHeaders());
|
||||
|
||||
|
@ -875,7 +875,6 @@ GStreamerReader::Seek(int64_t aTarget, int64_t aEndTime)
|
|||
|
||||
media::TimeIntervals GStreamerReader::GetBuffered()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
media::TimeIntervals buffered;
|
||||
if (!mInfo.HasValidMedia()) {
|
||||
return buffered;
|
||||
|
@ -1273,10 +1272,11 @@ GStreamerReader::AutoplugSortCb(GstElement* aElement, GstPad* aPad,
|
|||
* If this is an MP3 stream, pass any new data we get to the MP3 frame parser
|
||||
* for duration estimation.
|
||||
*/
|
||||
void GStreamerReader::NotifyDataArrivedInternal(uint32_t aLength,
|
||||
int64_t aOffset)
|
||||
void GStreamerReader::NotifyDataArrived(const char *aBuffer,
|
||||
uint32_t aLength,
|
||||
int64_t aOffset)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (HasVideo()) {
|
||||
return;
|
||||
}
|
||||
|
@ -1284,9 +1284,7 @@ void GStreamerReader::NotifyDataArrivedInternal(uint32_t aLength,
|
|||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaByteBuffer> bytes = mDecoder->GetResource()->SilentReadAt(aOffset, aLength);
|
||||
NS_ENSURE_TRUE_VOID(bytes);
|
||||
mMP3FrameParser.Parse(bytes->Elements(), aLength, aOffset);
|
||||
mMP3FrameParser.Parse(aBuffer, aLength, aOffset);
|
||||
if (!mMP3FrameParser.IsMP3()) {
|
||||
return;
|
||||
}
|
||||
|
@ -1294,8 +1292,9 @@ void GStreamerReader::NotifyDataArrivedInternal(uint32_t aLength,
|
|||
int64_t duration = mMP3FrameParser.GetDuration();
|
||||
if (duration != mLastParserDuration && mUseParserDuration) {
|
||||
MOZ_ASSERT(mDecoder);
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mLastParserDuration = duration;
|
||||
mDecoder->DispatchUpdateEstimatedMediaDuration(mLastParserDuration);
|
||||
mDecoder->UpdateEstimatedMediaDuration(mLastParserDuration);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -51,10 +51,9 @@ public:
|
|||
Seek(int64_t aTime, int64_t aEndTime) override;
|
||||
virtual media::TimeIntervals GetBuffered() override;
|
||||
|
||||
protected:
|
||||
virtual void NotifyDataArrivedInternal(uint32_t aLength,
|
||||
int64_t aOffset) override;
|
||||
public:
|
||||
virtual void NotifyDataArrived(const char *aBuffer,
|
||||
uint32_t aLength,
|
||||
int64_t aOffset) override;
|
||||
|
||||
virtual bool HasAudio() override {
|
||||
return mInfo.HasAudio();
|
||||
|
|
|
@ -36,9 +36,12 @@ public:
|
|||
decoder->SetResource(resource);
|
||||
|
||||
reader->Init(nullptr);
|
||||
// This needs to be done before invoking GetBuffered. This is normally
|
||||
// done by MediaDecoderStateMachine.
|
||||
reader->DispatchSetStartTime(0);
|
||||
{
|
||||
// This needs to be done before invoking GetBuffered. This is normally
|
||||
// done by MediaDecoderStateMachine.
|
||||
ReentrantMonitorAutoEnter mon(decoder->GetReentrantMonitor());
|
||||
reader->SetStartTime(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 = GetBuffered();
|
||||
media::TimeIntervals buffered = mReader->GetBuffered();
|
||||
if (buffered.Length()) {
|
||||
seekable += media::TimeInterval(buffered.GetStart(), buffered.GetEnd());
|
||||
}
|
||||
|
@ -99,44 +99,6 @@ 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()
|
||||
{
|
||||
|
@ -336,6 +298,37 @@ 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,7 +41,6 @@ public:
|
|||
virtual MediaDecoderStateMachine* CreateStateMachine() override;
|
||||
virtual nsresult Load(nsIStreamListener**, MediaDecoder*) override;
|
||||
virtual media::TimeIntervals GetSeekable() override;
|
||||
media::TimeIntervals GetBuffered() override;
|
||||
|
||||
virtual void Shutdown() override;
|
||||
|
||||
|
@ -92,6 +91,12 @@ 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,32 +553,10 @@ MediaSourceReader::BreakCycles()
|
|||
already_AddRefed<SourceBufferDecoder>
|
||||
MediaSourceReader::SelectDecoder(int64_t aTarget,
|
||||
int64_t aTolerance,
|
||||
TrackBuffer* aTrackBuffer)
|
||||
const nsTArray<nsRefPtr<SourceBufferDecoder>>& 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;
|
||||
return static_cast<MediaSourceDecoder*>(mDecoder)
|
||||
->SelectDecoder(aTarget, aTolerance, aTrackDecoders);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -586,7 +564,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);
|
||||
nsRefPtr<SourceBufferDecoder> decoder = SelectDecoder(aTarget, EOS_FUZZ_US, trackBuffer->Decoders());
|
||||
return !!decoder;
|
||||
}
|
||||
|
||||
|
@ -604,9 +582,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);
|
||||
SelectDecoder(*aTarget, /* aTolerance = */ 0, mAudioTrack->Decoders());
|
||||
if (!newDecoder) {
|
||||
newDecoder = SelectDecoder(*aTarget, EOS_FUZZ_US, mAudioTrack);
|
||||
newDecoder = SelectDecoder(*aTarget, EOS_FUZZ_US, mAudioTrack->Decoders());
|
||||
usedFuzz = true;
|
||||
}
|
||||
if (GetAudioReader() && mAudioSourceDecoder != newDecoder) {
|
||||
|
@ -649,9 +627,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);
|
||||
SelectDecoder(*aTarget, /* aTolerance = */ 0, mVideoTrack->Decoders());
|
||||
if (!newDecoder) {
|
||||
newDecoder = SelectDecoder(*aTarget, EOS_FUZZ_US, mVideoTrack);
|
||||
newDecoder = SelectDecoder(*aTarget, EOS_FUZZ_US, mVideoTrack->Decoders());
|
||||
usedFuzz = true;
|
||||
}
|
||||
if (GetVideoReader() && mVideoSourceDecoder != newDecoder) {
|
||||
|
@ -754,7 +732,10 @@ 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.
|
||||
reader->DispatchSetStartTime(0);
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(decoder->GetReentrantMonitor());
|
||||
reader->SetStartTime(0);
|
||||
}
|
||||
|
||||
#ifdef MOZ_FMP4
|
||||
reader->SetSharedDecoderManager(mSharedDecoderManager);
|
||||
|
@ -1032,7 +1013,6 @@ MediaSourceReader::DoVideoSeek()
|
|||
media::TimeIntervals
|
||||
MediaSourceReader::GetBuffered()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
media::TimeIntervals buffered;
|
||||
|
||||
|
@ -1276,27 +1256,25 @@ 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) {
|
||||
const nsRefPtr<SourceBufferDecoder>& newDecoder{mAudioTrack->Decoders()[i]};
|
||||
media::TimeIntervals ranges = mAudioTrack->GetBuffered(newDecoder);
|
||||
nsRefPtr<MediaDecoderReader> newReader = mAudioTrack->Decoders()[i]->GetReader();
|
||||
|
||||
media::TimeIntervals ranges = mAudioTrack->Decoders()[i]->GetBuffered();
|
||||
result += nsPrintfCString("\t\tReader %d: %p ranges=%s active=%s size=%lld\n",
|
||||
i,
|
||||
newDecoder->GetReader(),
|
||||
DumpTimeRanges(ranges).get(),
|
||||
newDecoder->GetReader() == GetAudioReader() ? "true" : "false",
|
||||
newDecoder->GetResource()->GetSize());
|
||||
i, newReader.get(), DumpTimeRanges(ranges).get(),
|
||||
newReader.get() == GetAudioReader() ? "true" : "false",
|
||||
mAudioTrack->Decoders()[i]->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) {
|
||||
const nsRefPtr<SourceBufferDecoder>& newDecoder{mVideoTrack->Decoders()[i]};
|
||||
media::TimeIntervals ranges = mVideoTrack->GetBuffered(newDecoder);
|
||||
nsRefPtr<MediaDecoderReader> newReader = mVideoTrack->Decoders()[i]->GetReader();
|
||||
|
||||
media::TimeIntervals ranges = mVideoTrack->Decoders()[i]->GetBuffered();
|
||||
result += nsPrintfCString("\t\tReader %d: %p ranges=%s active=%s size=%lld\n",
|
||||
i,
|
||||
newDecoder->GetReader(),
|
||||
DumpTimeRanges(ranges).get(),
|
||||
newDecoder->GetReader() == GetVideoReader() ? "true" : "false",
|
||||
i, newReader.get(), DumpTimeRanges(ranges).get(),
|
||||
newReader.get() == GetVideoReader() ? "true" : "false",
|
||||
mVideoTrack->Decoders()[i]->GetResource()->GetSize());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -219,10 +219,9 @@ 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 */,
|
||||
TrackBuffer* aTrackBuffer);
|
||||
const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders);
|
||||
bool HaveData(int64_t aTarget, MediaData::Type aType);
|
||||
already_AddRefed<SourceBufferDecoder> FirstDecoder(MediaData::Type aType);
|
||||
|
||||
|
|
|
@ -147,12 +147,6 @@ SourceBuffer::GetBuffered(ErrorResult& aRv)
|
|||
return tr.forget();
|
||||
}
|
||||
|
||||
media::TimeIntervals
|
||||
SourceBuffer::GetTimeIntervals()
|
||||
{
|
||||
return mContentManager->Buffered();
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::SetAppendWindowStart(double aAppendWindowStart, ErrorResult& aRv)
|
||||
{
|
||||
|
@ -303,7 +297,7 @@ SourceBuffer::Ended()
|
|||
mContentManager->Ended();
|
||||
// We want the MediaSourceReader to refresh its buffered range as it may
|
||||
// have been modified (end lined up).
|
||||
mMediaSource->GetDecoder()->NotifyDataArrived(1, mReportedOffset++, /* aThrottleUpdates = */ false);
|
||||
mMediaSource->GetDecoder()->NotifyDataArrived(nullptr, 1, mReportedOffset++);
|
||||
}
|
||||
|
||||
SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
|
||||
|
@ -498,7 +492,7 @@ SourceBuffer::AppendDataCompletedWithSuccess(bool aHasActiveTracks)
|
|||
// Tell our parent decoder that we have received new data.
|
||||
// The information provided do not matter much so long as it is monotonically
|
||||
// increasing.
|
||||
mMediaSource->GetDecoder()->NotifyDataArrived(1, mReportedOffset++, /* aThrottleUpdates = */ false);
|
||||
mMediaSource->GetDecoder()->NotifyDataArrived(nullptr, 1, mReportedOffset++);
|
||||
// Send progress event.
|
||||
mMediaSource->GetDecoder()->NotifyBytesDownloaded();
|
||||
}
|
||||
|
|
|
@ -60,7 +60,6 @@ public:
|
|||
}
|
||||
|
||||
already_AddRefed<TimeRanges> GetBuffered(ErrorResult& aRv);
|
||||
TimeIntervals GetTimeIntervals();
|
||||
|
||||
double TimestampOffset() const
|
||||
{
|
||||
|
|
|
@ -178,6 +178,12 @@ SourceBufferDecoder::Trim(int64_t aDuration)
|
|||
mTrimmedOffset = (double)aDuration / USECS_PER_S;
|
||||
}
|
||||
|
||||
void
|
||||
SourceBufferDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
|
||||
{
|
||||
MSE_DEBUG("UNIMPLEMENTED");
|
||||
}
|
||||
|
||||
void
|
||||
SourceBufferDecoder::SetMediaSeekable(bool aMediaSeekable)
|
||||
{
|
||||
|
@ -197,8 +203,15 @@ SourceBufferDecoder::GetOwner()
|
|||
}
|
||||
|
||||
void
|
||||
SourceBufferDecoder::NotifyDataArrived(uint32_t aLength, int64_t aOffset, bool aThrottleUpdates)
|
||||
SourceBufferDecoder::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
|
||||
{
|
||||
mReader->NotifyDataArrived(aBuffer, 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(nullptr, 0, 0);
|
||||
}
|
||||
|
||||
media::TimeIntervals
|
||||
|
|
|
@ -47,13 +47,14 @@ public:
|
|||
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
|
||||
MediaDecoderEventVisibility aEventVisibility) final override;
|
||||
virtual void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) final override;
|
||||
virtual void NotifyDataArrived(uint32_t aLength, int64_t aOffset, bool aThrottleUpdates) final override;
|
||||
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) final override;
|
||||
virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded, uint32_t aDropped) final override;
|
||||
virtual void NotifyWaitingForResourcesStatusChanged() final override;
|
||||
virtual void OnReadMetadataCompleted() final override;
|
||||
virtual void QueueMetadata(int64_t aTime, nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) final override;
|
||||
virtual void RemoveMediaTracks() final override;
|
||||
virtual void SetMediaSeekable(bool aMediaSeekable) final override;
|
||||
virtual void UpdateEstimatedMediaDuration(int64_t aDuration) final override;
|
||||
virtual bool HasInitializationData() final override;
|
||||
|
||||
// SourceBufferResource specific interface below.
|
||||
|
|
|
@ -36,9 +36,6 @@ extern PRLogModuleInfo* GetMediaSourceLog();
|
|||
|
||||
#define EOS_FUZZ_US 125000
|
||||
|
||||
using media::TimeIntervals;
|
||||
using media::Interval;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
TrackBuffer::TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType)
|
||||
|
@ -245,96 +242,44 @@ TrackBuffer::BufferAppend()
|
|||
mAdjustedTimestamp = starttu;
|
||||
}
|
||||
|
||||
int64_t offset = AppendDataToCurrentResource(mInputBuffer, end - start);
|
||||
if (offset < 0) {
|
||||
if (!AppendDataToCurrentResource(mInputBuffer, end - start)) {
|
||||
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;
|
||||
}
|
||||
|
||||
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."); });
|
||||
// Tell our reader that we have more data to ensure that playback starts if
|
||||
// required when data is appended.
|
||||
NotifyTimeRangesChanged();
|
||||
|
||||
mInitializationPromise.Resolve(HasInitSegment(), __func__);
|
||||
return p;
|
||||
}
|
||||
|
||||
int64_t
|
||||
bool
|
||||
TrackBuffer::AppendDataToCurrentResource(MediaByteBuffer* aData, uint32_t aDuration)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mCurrentDecoder) {
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
SourceBufferResource* resource = mCurrentDecoder->GetResource();
|
||||
int64_t appendOffset = resource->GetLength();
|
||||
resource->AppendData(aData);
|
||||
mCurrentDecoder->SetRealMediaDuration(mCurrentDecoder->GetRealMediaDuration() + aDuration);
|
||||
|
||||
return appendOffset;
|
||||
}
|
||||
|
||||
nsRefPtr<TrackBuffer::BufferedRangesUpdatedPromise>
|
||||
TrackBuffer::UpdateBufferedRanges(Interval<int64_t> aByteRange, bool aNotifyParent)
|
||||
{
|
||||
if (aByteRange.Length()) {
|
||||
mCurrentDecoder->GetReader()->NotifyDataArrived(aByteRange);
|
||||
}
|
||||
|
||||
// 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, /* aThrottleUpdates = */ false);
|
||||
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.
|
||||
// XXX: For future reference: NDA call must run on the main thread.
|
||||
mCurrentDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData->Elements()),
|
||||
aData->Length(), appendOffset);
|
||||
mParentDecoder->NotifyBytesDownloaded();
|
||||
NotifyTimeRangesChanged();
|
||||
|
||||
return BufferedRangesUpdatedPromise::CreateAndResolve(true, __func__);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -346,49 +291,24 @@ 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
|
||||
{
|
||||
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;
|
||||
TimeIntervals first = aFirst->GetBuffered();
|
||||
TimeIntervals second = aSecond->GetBuffered();
|
||||
|
||||
return first.GetStart() < second.GetStart();
|
||||
}
|
||||
|
||||
bool Equals(SourceBufferDecoder* aFirst, SourceBufferDecoder* aSecond) const
|
||||
{
|
||||
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;
|
||||
TimeIntervals first = aFirst->GetBuffered();
|
||||
TimeIntervals second = aSecond->GetBuffered();
|
||||
|
||||
return first.GetStart() == second.GetStart();
|
||||
}
|
||||
|
||||
const TrackBuffer::DecoderBufferedMap& mMap;
|
||||
};
|
||||
|
||||
TrackBuffer::EvictDataResult
|
||||
|
@ -411,24 +331,14 @@ TrackBuffer::EvictData(TimeUnit aPlaybackTime,
|
|||
}
|
||||
|
||||
// Get a list of initialized decoders.
|
||||
nsTArray<nsRefPtr<SourceBufferDecoder>> decoders;
|
||||
nsTArray<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 = GetBuffered(decoders[i]);
|
||||
TimeIntervals buffered = decoders[i]->GetBuffered();
|
||||
|
||||
MSE_DEBUG("Step1. decoder=%u/%u threshold=%u toEvict=%lld",
|
||||
i, decoders.Length(), aThreshold, toEvict);
|
||||
|
@ -456,10 +366,6 @@ 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,
|
||||
|
@ -470,7 +376,7 @@ TrackBuffer::EvictData(TimeUnit aPlaybackTime,
|
|||
}
|
||||
}
|
||||
}
|
||||
NotifyReaderDataRemoved(decoders[i]->GetReader());
|
||||
decoders[i]->GetReader()->NotifyDataRemoved();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -478,15 +384,13 @@ 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 (decoders[i] == playingDecoder) {
|
||||
if (mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
|
||||
break;
|
||||
}
|
||||
if (decoders[i] == mCurrentDecoder) {
|
||||
continue;
|
||||
}
|
||||
// The buffered value is potentially stale should eviction occurred in
|
||||
// step 1. However this is only used for logging.
|
||||
TimeIntervals buffered = GetBuffered(decoders[i]);
|
||||
TimeIntervals buffered = decoders[i]->GetBuffered();
|
||||
|
||||
// Remove data from older decoders than the current one.
|
||||
MSE_DEBUG("evicting all "
|
||||
|
@ -494,7 +398,7 @@ TrackBuffer::EvictData(TimeUnit aPlaybackTime,
|
|||
buffered.GetStart().ToSeconds(), buffered.GetEnd().ToSeconds(),
|
||||
aPlaybackTime, decoders[i]->GetResource()->GetSize());
|
||||
toEvict -= decoders[i]->GetResource()->EvictAll();
|
||||
NotifyReaderDataRemoved(decoders[i]->GetReader());
|
||||
decoders[i]->GetReader()->NotifyDataRemoved();
|
||||
}
|
||||
|
||||
// Evict all data from future decoders, starting furthest away from
|
||||
|
@ -505,21 +409,26 @@ 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) {
|
||||
// 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);
|
||||
TimeIntervals buffered = playingDecoder->GetBuffered();
|
||||
nextPlayingDecoder =
|
||||
mParentDecoder->GetReader()->SelectDecoder(buffered.GetEnd().ToMicroseconds() + 1,
|
||||
EOS_FUZZ_US,
|
||||
this);
|
||||
mParentDecoder->SelectDecoder(buffered.GetEnd().ToMicroseconds() + 1,
|
||||
EOS_FUZZ_US,
|
||||
mInitializedDecoders);
|
||||
}
|
||||
|
||||
// Sort decoders by their start times.
|
||||
decoders.Sort(DecoderSorter{mReadersBuffered});
|
||||
decoders.Sort(DecoderSorter());
|
||||
|
||||
for (int32_t i = int32_t(decoders.Length()) - 1; i >= 0 && toEvict > 0; --i) {
|
||||
MSE_DEBUG("Step3. decoder=%u/%u threshold=%u toEvict=%lld",
|
||||
|
@ -528,17 +437,14 @@ TrackBuffer::EvictData(TimeUnit aPlaybackTime,
|
|||
decoders[i] == mCurrentDecoder) {
|
||||
continue;
|
||||
}
|
||||
// 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]);
|
||||
TimeIntervals buffered = decoders[i]->GetBuffered();
|
||||
|
||||
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();
|
||||
NotifyReaderDataRemoved(decoders[i]->GetReader());
|
||||
decoders[i]->GetReader()->NotifyDataRemoved();
|
||||
}
|
||||
|
||||
RemoveEmptyDecoders(decoders);
|
||||
|
@ -546,8 +452,8 @@ TrackBuffer::EvictData(TimeUnit aPlaybackTime,
|
|||
bool evicted = toEvict < (totalSize - aThreshold);
|
||||
if (evicted) {
|
||||
if (playingDecoder) {
|
||||
*aBufferStartTime =
|
||||
std::max(TimeUnit::FromSeconds(0), playingDecoderStartTime);
|
||||
TimeIntervals ranges = playingDecoder->GetBuffered();
|
||||
*aBufferStartTime = std::max(TimeUnit::FromSeconds(0), ranges.GetStart());
|
||||
} else {
|
||||
// We do not currently have data to play yet.
|
||||
// Avoid evicting anymore data to minimize rebuffering time.
|
||||
|
@ -555,37 +461,38 @@ TrackBuffer::EvictData(TimeUnit aPlaybackTime,
|
|||
}
|
||||
}
|
||||
|
||||
if (evicted) {
|
||||
NotifyTimeRangesChanged();
|
||||
}
|
||||
|
||||
return evicted ?
|
||||
EvictDataResult::DATA_EVICTED :
|
||||
(HasOnlyIncompleteMedia() ? EvictDataResult::CANT_EVICT : EvictDataResult::NO_DATA_EVICTED);
|
||||
}
|
||||
|
||||
void
|
||||
TrackBuffer::RemoveEmptyDecoders(const nsTArray<nsRefPtr<mozilla::SourceBufferDecoder>>& aDecoders)
|
||||
TrackBuffer::RemoveEmptyDecoders(nsTArray<mozilla::SourceBufferDecoder*>& aDecoders)
|
||||
{
|
||||
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());
|
||||
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
|
||||
|
||||
// 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());
|
||||
// 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64_t
|
||||
|
@ -604,7 +511,7 @@ TrackBuffer::HasOnlyIncompleteMedia()
|
|||
if (!mCurrentDecoder) {
|
||||
return false;
|
||||
}
|
||||
TimeIntervals buffered = GetBuffered(mCurrentDecoder);
|
||||
TimeIntervals buffered = mCurrentDecoder->GetBuffered();
|
||||
MSE_DEBUG("mCurrentDecoder.size=%lld, start=%f end=%f",
|
||||
mCurrentDecoder->GetResource()->GetSize(),
|
||||
buffered.GetStart(), buffered.GetEnd());
|
||||
|
@ -627,9 +534,10 @@ TrackBuffer::EvictBefore(TimeUnit aTime)
|
|||
rv.SuppressException();
|
||||
return;
|
||||
}
|
||||
NotifyReaderDataRemoved(mInitializedDecoders[i]->GetReader());
|
||||
mInitializedDecoders[i]->GetReader()->NotifyDataRemoved();
|
||||
}
|
||||
}
|
||||
NotifyTimeRangesChanged();
|
||||
}
|
||||
|
||||
TimeIntervals
|
||||
|
@ -637,20 +545,18 @@ TrackBuffer::Buffered()
|
|||
{
|
||||
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
|
||||
|
||||
return mBufferedRanges;
|
||||
}
|
||||
TimeIntervals buffered;
|
||||
|
||||
TimeIntervals
|
||||
TrackBuffer::GetBuffered(SourceBufferDecoder* aDecoder)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
|
||||
|
||||
DecoderBufferedMap::const_iterator val = mReadersBuffered.find(aDecoder);
|
||||
|
||||
if (val == mReadersBuffered.end()) {
|
||||
return TimeIntervals::Invalid();
|
||||
for (auto& decoder : mInitializedDecoders) {
|
||||
buffered += decoder->GetBuffered();
|
||||
}
|
||||
return val->second;
|
||||
// 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()));
|
||||
}
|
||||
|
||||
return buffered;
|
||||
}
|
||||
|
||||
already_AddRefed<SourceBufferDecoder>
|
||||
|
@ -761,8 +667,6 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
|
|||
MSE_DEBUG("Initializing subdecoder %p reader %p",
|
||||
aDecoder, reader);
|
||||
|
||||
reader->NotifyDataArrived(mLastAppendRange);
|
||||
|
||||
// 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
|
||||
|
@ -919,6 +823,7 @@ TrackBuffer::CompleteInitializeDecoder(SourceBufferDecoder* aDecoder)
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
int64_t duration = mInfo.mMetadataDuration.isSome()
|
||||
? mInfo.mMetadataDuration.ref().ToMicroseconds() : -1;
|
||||
if (!duration) {
|
||||
|
@ -933,15 +838,7 @@ TrackBuffer::CompleteInitializeDecoder(SourceBufferDecoder* aDecoder)
|
|||
|
||||
MSE_DEBUG("Reader %p activated",
|
||||
aDecoder->GetReader());
|
||||
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."); });
|
||||
mInitializationPromise.ResolveIfExists(true, __func__);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -1037,7 +934,7 @@ TrackBuffer::ContainsTime(int64_t aTime, int64_t aTolerance)
|
|||
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
|
||||
TimeUnit time{TimeUnit::FromMicroseconds(aTime)};
|
||||
for (auto& decoder : mInitializedDecoders) {
|
||||
TimeIntervals r = GetBuffered(decoder);
|
||||
TimeIntervals r = decoder->GetBuffered();
|
||||
r.SetFuzz(TimeUnit::FromMicroseconds(aTolerance));
|
||||
if (r.Contains(time)) {
|
||||
return true;
|
||||
|
@ -1190,8 +1087,6 @@ 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());
|
||||
}
|
||||
|
@ -1218,13 +1113,13 @@ TrackBuffer::RangeRemoval(TimeUnit aStart, TimeUnit aEnd)
|
|||
return RangeRemovalPromise::CreateAndResolve(false, __func__);
|
||||
}
|
||||
|
||||
nsTArray<nsRefPtr<SourceBufferDecoder>> decoders;
|
||||
nsTArray<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 = GetBuffered(decoders[i]);
|
||||
TimeIntervals buffered = decoders[i]->GetBuffered();
|
||||
if (buffered.GetEnd() < aEnd) {
|
||||
// Can be fully removed.
|
||||
MSE_DEBUG("remove all bufferedEnd=%f size=%lld",
|
||||
|
@ -1245,7 +1140,7 @@ TrackBuffer::RangeRemoval(TimeUnit aStart, TimeUnit aEnd)
|
|||
}
|
||||
}
|
||||
}
|
||||
NotifyReaderDataRemoved(decoders[i]->GetReader());
|
||||
decoders[i]->GetReader()->NotifyDataRemoved();
|
||||
}
|
||||
} else {
|
||||
// Only trimming existing buffers.
|
||||
|
@ -1256,26 +1151,14 @@ TrackBuffer::RangeRemoval(TimeUnit aStart, TimeUnit aEnd)
|
|||
} else {
|
||||
decoders[i]->Trim(aStart.ToMicroseconds());
|
||||
}
|
||||
NotifyReaderDataRemoved(decoders[i]->GetReader());
|
||||
decoders[i]->GetReader()->NotifyDataRemoved();
|
||||
}
|
||||
}
|
||||
|
||||
RemoveEmptyDecoders(decoders);
|
||||
|
||||
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;
|
||||
NotifyTimeRangesChanged();
|
||||
return RangeRemovalPromise::CreateAndResolve(true, __func__);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include "nsString.h"
|
||||
#include "nscore.h"
|
||||
#include "TimeUnits.h"
|
||||
#include <map>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -98,9 +97,6 @@ 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
|
||||
|
@ -109,8 +105,6 @@ public:
|
|||
void Dump(const char* aPath) override;
|
||||
#endif
|
||||
|
||||
typedef std::map<SourceBufferDecoder*, media::TimeIntervals> DecoderBufferedMap;
|
||||
|
||||
private:
|
||||
friend class DecodersToInitialize;
|
||||
friend class MetadataRecipient;
|
||||
|
@ -121,19 +115,14 @@ private:
|
|||
// for initialization.
|
||||
// The decoder is not considered initialized until it is added to
|
||||
// mInitializedDecoders.
|
||||
already_AddRefed<SourceBufferDecoder> NewDecoder(media::TimeUnit aTimestampOffset);
|
||||
already_AddRefed<SourceBufferDecoder> NewDecoder(TimeUnit aTimestampOffset);
|
||||
|
||||
// Helper for AppendData, ensures NotifyDataArrived is called whenever
|
||||
// data is appended to the current decoder's SourceBufferResource.
|
||||
int64_t AppendDataToCurrentResource(MediaByteBuffer* aData,
|
||||
bool 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);
|
||||
|
@ -164,7 +153,7 @@ private:
|
|||
void RemoveDecoder(SourceBufferDecoder* aDecoder);
|
||||
|
||||
// Remove all empty decoders from the provided list;
|
||||
void RemoveEmptyDecoders(const nsTArray<nsRefPtr<SourceBufferDecoder>>& aDecoders);
|
||||
void RemoveEmptyDecoders(nsTArray<SourceBufferDecoder*>& aDecoders);
|
||||
|
||||
void OnMetadataRead(MetadataHolder* aMetadata,
|
||||
SourceBufferDecoder* aDecoder,
|
||||
|
@ -208,9 +197,9 @@ private:
|
|||
void AdjustDecodersTimestampOffset(TimeUnit aOffset);
|
||||
|
||||
// The timestamp offset used by our current decoder.
|
||||
media::TimeUnit mLastTimestampOffset;
|
||||
media::TimeUnit mTimestampOffset;
|
||||
media::TimeUnit mAdjustedTimestamp;
|
||||
TimeUnit mLastTimestampOffset;
|
||||
TimeUnit mTimestampOffset;
|
||||
TimeUnit mAdjustedTimestamp;
|
||||
|
||||
// True if at least one of our decoders has encrypted content.
|
||||
bool mIsWaitingOnCDM;
|
||||
|
@ -227,15 +216,6 @@ 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
|
||||
|
|
|
@ -41,6 +41,7 @@ function do_seeked(e) {
|
|||
is(v.currentTime, duration, "current time was updated");
|
||||
is(v.duration, duration, "element duration was updated");
|
||||
is(v._sb.buffered.length, 1, "One buffered range");
|
||||
is(v._sb.buffered.end(0), duration, "sourcebuffer truncated");
|
||||
// Truncated mediasource duration will cause the video element to seek.
|
||||
v.addEventListener("seeking", do_seeking, false);
|
||||
}
|
||||
|
|
|
@ -1852,7 +1852,6 @@ nsresult OggReader::SeekBisection(int64_t aTarget,
|
|||
|
||||
media::TimeIntervals OggReader::GetBuffered()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
NS_ENSURE_TRUE(mStartTime >= 0, media::TimeIntervals());
|
||||
{
|
||||
mozilla::ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
|
|
|
@ -522,12 +522,10 @@ MediaCodecReader::HasVideo()
|
|||
}
|
||||
|
||||
void
|
||||
MediaCodecReader::NotifyDataArrivedInternal(uint32_t aLength,
|
||||
int64_t aOffset)
|
||||
MediaCodecReader::NotifyDataArrived(const char* aBuffer,
|
||||
uint32_t aLength,
|
||||
int64_t aOffset)
|
||||
{
|
||||
nsRefPtr<MediaByteBuffer> bytes = mDecoder->GetResource()->SilentReadAt(aOffset, aLength);
|
||||
NS_ENSURE_TRUE_VOID(bytes);
|
||||
|
||||
MonitorAutoLock monLock(mParserMonitor);
|
||||
if (mNextParserPosition == mParsedDataLength &&
|
||||
mNextParserPosition >= aOffset &&
|
||||
|
@ -535,7 +533,7 @@ MediaCodecReader::NotifyDataArrivedInternal(uint32_t aLength,
|
|||
// No pending parsing runnable currently. And available data are adjacent to
|
||||
// parsed data.
|
||||
int64_t shift = mNextParserPosition - aOffset;
|
||||
const char* buffer = reinterpret_cast<const char*>(bytes->Elements()) + shift;
|
||||
const char* buffer = aBuffer + shift;
|
||||
uint32_t length = aLength - shift;
|
||||
int64_t offset = mNextParserPosition;
|
||||
if (length > 0) {
|
||||
|
@ -632,7 +630,7 @@ MediaCodecReader::ParseDataSegment(const char* aBuffer,
|
|||
return true; // NO-OP
|
||||
}
|
||||
|
||||
mMP3FrameParser->Parse(reinterpret_cast<const uint8_t*>(aBuffer), aLength, aOffset);
|
||||
mMP3FrameParser->Parse(aBuffer, aLength, aOffset);
|
||||
|
||||
duration = mMP3FrameParser->GetDuration();
|
||||
}
|
||||
|
@ -654,7 +652,8 @@ MediaCodecReader::ParseDataSegment(const char* aBuffer,
|
|||
|
||||
if (durationUpdateRequired) {
|
||||
MOZ_ASSERT(mDecoder);
|
||||
mDecoder->DispatchUpdateEstimatedMediaDuration(duration);
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mDecoder->UpdateEstimatedMediaDuration(duration);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -73,12 +73,10 @@ public:
|
|||
// irreversible, whereas ReleaseMediaResources() is reversible.
|
||||
virtual nsRefPtr<ShutdownPromise> Shutdown();
|
||||
|
||||
protected:
|
||||
// Used to retrieve some special information that can only be retrieved after
|
||||
// all contents have been continuously parsed. (ex. total duration of some
|
||||
// variable-bit-rate MP3 files.)
|
||||
virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) override;
|
||||
public:
|
||||
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
|
||||
|
||||
// Flush the MediaTaskQueue, flush MediaCodec and raise the mDiscontinuity.
|
||||
virtual nsresult ResetDecode() override;
|
||||
|
|
|
@ -43,7 +43,7 @@ public:
|
|||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(mOmxReader.get());
|
||||
mOmxReader->ProcessCachedData(mOffset);
|
||||
mOmxReader->ProcessCachedData(mOffset, false);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -70,20 +70,24 @@ class MediaOmxReader::NotifyDataArrivedRunnable : public nsRunnable
|
|||
{
|
||||
public:
|
||||
NotifyDataArrivedRunnable(MediaOmxReader* aOmxReader,
|
||||
uint64_t aLength,
|
||||
int64_t aOffset, uint64_t aFullLength)
|
||||
const char* aBuffer, uint64_t aLength,
|
||||
int64_t aOffset, uint64_t aFullLength)
|
||||
: mOmxReader(aOmxReader),
|
||||
mBuffer(aBuffer),
|
||||
mLength(aLength),
|
||||
mOffset(aOffset),
|
||||
mFullLength(aFullLength)
|
||||
{
|
||||
MOZ_ASSERT(mOmxReader.get());
|
||||
MOZ_ASSERT(mBuffer.get() || !mLength);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MOZ_ASSERT(mOmxReader->OnTaskQueue());
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
NotifyDataArrived();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -93,10 +97,13 @@ private:
|
|||
if (mOmxReader->IsShutdown()) {
|
||||
return;
|
||||
}
|
||||
const char* buffer = mBuffer.get();
|
||||
|
||||
while (mLength) {
|
||||
uint32_t length = std::min<uint64_t>(mLength, UINT32_MAX);
|
||||
mOmxReader->NotifyDataArrived(Interval<int64_t>(mOffset, mOffset + length));
|
||||
mOmxReader->NotifyDataArrived(buffer, length,
|
||||
mOffset);
|
||||
buffer += length;
|
||||
mLength -= length;
|
||||
mOffset += length;
|
||||
}
|
||||
|
@ -111,6 +118,7 @@ private:
|
|||
}
|
||||
|
||||
nsRefPtr<MediaOmxReader> mOmxReader;
|
||||
nsAutoArrayPtr<const char> mBuffer;
|
||||
uint64_t mLength;
|
||||
int64_t mOffset;
|
||||
uint64_t mFullLength;
|
||||
|
@ -237,7 +245,7 @@ MediaOmxReader::AsyncReadMetadata()
|
|||
// the mDecoder->GetResource()->GetLength() would return -1.
|
||||
// Delay set the total duration on this function.
|
||||
mMP3FrameParser.SetLength(mDecoder->GetResource()->GetLength());
|
||||
ProcessCachedData(0);
|
||||
ProcessCachedData(0, true);
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDecoderReader::MetadataPromise> p = mMetadataPromise.Ensure(__func__);
|
||||
|
@ -460,9 +468,9 @@ bool MediaOmxReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
|||
return true;
|
||||
}
|
||||
|
||||
void MediaOmxReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
|
||||
void MediaOmxReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsRefPtr<AbstractMediaDecoder> decoder = SafeGetDecoder();
|
||||
if (!decoder) { // reader has shut down
|
||||
return;
|
||||
|
@ -474,17 +482,16 @@ void MediaOmxReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset
|
|||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaByteBuffer> bytes = mDecoder->GetResource()->SilentReadAt(aOffset, aLength);
|
||||
NS_ENSURE_TRUE_VOID(bytes);
|
||||
mMP3FrameParser.Parse(bytes->Elements(), aLength, aOffset);
|
||||
mMP3FrameParser.Parse(aBuffer, aLength, aOffset);
|
||||
if (!mMP3FrameParser.IsMP3()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t duration = mMP3FrameParser.GetDuration();
|
||||
if (duration != mLastParserDuration) {
|
||||
ReentrantMonitorAutoEnter mon(decoder->GetReentrantMonitor());
|
||||
mLastParserDuration = duration;
|
||||
decoder->DispatchUpdateEstimatedMediaDuration(mLastParserDuration);
|
||||
decoder->UpdateEstimatedMediaDuration(mLastParserDuration);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -567,7 +574,7 @@ void MediaOmxReader::EnsureActive() {
|
|||
NS_ASSERTION(result == NS_OK, "OmxDecoder should be in play state to continue decoding");
|
||||
}
|
||||
|
||||
int64_t MediaOmxReader::ProcessCachedData(int64_t aOffset)
|
||||
int64_t MediaOmxReader::ProcessCachedData(int64_t aOffset, bool aWaitForCompletion)
|
||||
{
|
||||
// Could run on decoder thread or IO thread.
|
||||
nsRefPtr<AbstractMediaDecoder> decoder = SafeGetDecoder();
|
||||
|
@ -591,14 +598,22 @@ int64_t MediaOmxReader::ProcessCachedData(int64_t aOffset)
|
|||
}
|
||||
|
||||
int64_t bufferLength = std::min<int64_t>(resourceLength-aOffset, sReadSize);
|
||||
nsRefPtr<NotifyDataArrivedRunnable> runnable(
|
||||
new NotifyDataArrivedRunnable(this, bufferLength, aOffset, resourceLength));
|
||||
|
||||
if (OnTaskQueue()) {
|
||||
runnable->Run();
|
||||
nsAutoArrayPtr<char> buffer(new char[bufferLength]);
|
||||
|
||||
nsresult rv = decoder->GetResource()->ReadFromCache(buffer.get(),
|
||||
aOffset, bufferLength);
|
||||
NS_ENSURE_SUCCESS(rv, -1);
|
||||
|
||||
nsRefPtr<NotifyDataArrivedRunnable> runnable(
|
||||
new NotifyDataArrivedRunnable(this, buffer.forget(), bufferLength,
|
||||
aOffset, resourceLength));
|
||||
if (aWaitForCompletion) {
|
||||
rv = NS_DispatchToMainThread(runnable.get(), NS_DISPATCH_SYNC);
|
||||
} else {
|
||||
TaskQueue()->Dispatch(runnable.forget());
|
||||
rv = NS_DispatchToMainThread(runnable.get());
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, -1);
|
||||
|
||||
return resourceLength - aOffset - bufferLength;
|
||||
}
|
||||
|
|
|
@ -69,9 +69,7 @@ public:
|
|||
|
||||
virtual nsresult Init(MediaDecoderReader* aCloneDonor);
|
||||
|
||||
protected:
|
||||
virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) override;
|
||||
public:
|
||||
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
|
||||
|
||||
virtual bool DecodeAudioData();
|
||||
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
|
@ -116,7 +114,7 @@ private:
|
|||
return mIsShutdown;
|
||||
}
|
||||
|
||||
int64_t ProcessCachedData(int64_t aOffset);
|
||||
int64_t ProcessCachedData(int64_t aOffset, bool aWaitForCompletion);
|
||||
|
||||
already_AddRefed<AbstractMediaDecoder> SafeGetDecoder();
|
||||
};
|
||||
|
|
|
@ -285,6 +285,5 @@ nsresult RawReader::SeekInternal(int64_t aTime)
|
|||
|
||||
media::TimeIntervals RawReader::GetBuffered()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
return media::TimeIntervals();
|
||||
}
|
||||
|
|
|
@ -275,7 +275,6 @@ WaveReader::Seek(int64_t aTarget, int64_t aEndTime)
|
|||
|
||||
media::TimeIntervals WaveReader::GetBuffered()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
if (!mInfo.HasAudio()) {
|
||||
return media::TimeIntervals();
|
||||
}
|
||||
|
|
|
@ -85,6 +85,12 @@ BufferDecoder::NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
|
|||
// ignore
|
||||
}
|
||||
|
||||
void
|
||||
BufferDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
void
|
||||
BufferDecoder::SetMediaSeekable(bool aMediaSeekable)
|
||||
{
|
||||
|
@ -153,6 +159,12 @@ BufferDecoder::NotifyWaitingForResourcesStatusChanged()
|
|||
// ignore
|
||||
}
|
||||
|
||||
void
|
||||
BufferDecoder::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
MediaDecoderOwner*
|
||||
BufferDecoder::GetOwner()
|
||||
{
|
||||
|
|
|
@ -45,6 +45,8 @@ public:
|
|||
virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
|
||||
uint32_t aDropped) final override;
|
||||
|
||||
virtual void UpdateEstimatedMediaDuration(int64_t aDuration) final override;
|
||||
|
||||
virtual void SetMediaSeekable(bool aMediaSeekable) final override;
|
||||
|
||||
virtual VideoFrameContainer* GetVideoFrameContainer() final override;
|
||||
|
@ -69,7 +71,7 @@ public:
|
|||
|
||||
virtual void NotifyWaitingForResourcesStatusChanged() final override;
|
||||
|
||||
virtual void NotifyDataArrived(uint32_t, int64_t, bool) final override {};
|
||||
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) final override;
|
||||
|
||||
private:
|
||||
virtual ~BufferDecoder();
|
||||
|
|
|
@ -300,8 +300,9 @@ bool WebMBufferedState::GetOffsetForTime(uint64_t aTime, int64_t* aOffset)
|
|||
return true;
|
||||
}
|
||||
|
||||
void WebMBufferedState::NotifyDataArrived(const unsigned char* aBuffer, uint32_t aLength, int64_t aOffset)
|
||||
void WebMBufferedState::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
uint32_t idx = mRangeParsers.IndexOfFirstElementGt(aOffset - 1);
|
||||
if (idx == 0 || !(mRangeParsers[idx-1] == aOffset)) {
|
||||
// If the incoming data overlaps an already parsed range, adjust the
|
||||
|
@ -328,7 +329,7 @@ void WebMBufferedState::NotifyDataArrived(const unsigned char* aBuffer, uint32_t
|
|||
}
|
||||
}
|
||||
|
||||
mRangeParsers[idx].Append(aBuffer,
|
||||
mRangeParsers[idx].Append(reinterpret_cast<const unsigned char*>(aBuffer),
|
||||
aLength,
|
||||
mTimeMapping,
|
||||
mReentrantMonitor);
|
||||
|
|
|
@ -228,7 +228,7 @@ public:
|
|||
MOZ_COUNT_CTOR(WebMBufferedState);
|
||||
}
|
||||
|
||||
void NotifyDataArrived(const unsigned char* aBuffer, uint32_t aLength, int64_t aOffset);
|
||||
void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
|
||||
bool CalculateBufferedForRange(int64_t aStartOffset, int64_t aEndOffset,
|
||||
uint64_t* aStartTime, uint64_t* aEndTime);
|
||||
|
||||
|
|
|
@ -1093,7 +1093,6 @@ 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());
|
||||
|
||||
|
@ -1146,12 +1145,10 @@ media::TimeIntervals WebMReader::GetBuffered()
|
|||
return buffered;
|
||||
}
|
||||
|
||||
void WebMReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
|
||||
void WebMReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength,
|
||||
int64_t aOffset)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
nsRefPtr<MediaByteBuffer> bytes = mDecoder->GetResource()->SilentReadAt(aOffset, aLength);
|
||||
NS_ENSURE_TRUE_VOID(bytes);
|
||||
mBufferedState->NotifyDataArrived(bytes->Elements(), aLength, aOffset);
|
||||
mBufferedState->NotifyDataArrived(aBuffer, aLength, aOffset);
|
||||
}
|
||||
|
||||
int64_t WebMReader::GetEvictionOffset(double aTime)
|
||||
|
|
|
@ -173,6 +173,8 @@ public:
|
|||
Seek(int64_t aTime, int64_t aEndTime) override;
|
||||
|
||||
virtual media::TimeIntervals GetBuffered() override;
|
||||
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength,
|
||||
int64_t aOffset) override;
|
||||
virtual int64_t GetEvictionOffset(double aTime) override;
|
||||
|
||||
virtual bool IsMediaSeekable() override;
|
||||
|
@ -204,8 +206,6 @@ protected:
|
|||
// Setup opus decoder
|
||||
bool InitOpusDecoder();
|
||||
|
||||
virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) override;
|
||||
|
||||
// Decode a nestegg packet of audio data. Push the audio data on the
|
||||
// audio queue. Returns true when there's more audio to decode,
|
||||
// false if the audio is finished, end of file has been reached,
|
||||
|
|
Загрузка…
Ссылка в новой задаче