diff --git a/dom/media/mediasource/MediaSource.cpp b/dom/media/mediasource/MediaSource.cpp index 0b3d77be62..a5b2065599 100644 --- a/dom/media/mediasource/MediaSource.cpp +++ b/dom/media/mediasource/MediaSource.cpp @@ -338,6 +338,58 @@ MediaSource::Enabled(JSContext* cx, JSObject* aGlobal) return Preferences::GetBool("media.mediasource.enabled"); } +void +MediaSource::SetLiveSeekableRange(double aStart, double aEnd, ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + + // 1. If the readyState attribute is not "open" then throw an InvalidStateError + // exception and abort these steps. + // 2. If the updating attribute equals true on any SourceBuffer in + // sourceBuffers, then throw an InvalidStateError exception and abort these + // steps. + if (mReadyState != MediaSourceReadyState::Open || + mSourceBuffers->AnyUpdating()) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + + // 3. If start is negative or greater than end, then throw a TypeError + // exception and abort these steps. + if (aStart < 0 || aStart > aEnd) { + aRv.Throw(NS_ERROR_DOM_TYPE_ERR); + return; + } + + // 4. Set live seekable range to be a new normalized TimeRanges object + // containing a single range whose start position is start and end position is + // end. + mLiveSeekableRange = + Some(media::TimeInterval(media::TimeUnit::FromSeconds(aStart), + media::TimeUnit::FromSeconds(aEnd))); +} + +void +MediaSource::ClearLiveSeekableRange(ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + + // 1. If the readyState attribute is not "open" then throw an InvalidStateError + // exception and abort these steps. + // 2. If the updating attribute equals true on any SourceBuffer in + // sourceBuffers, then throw an InvalidStateError exception and abort these + // steps. + if (mReadyState != MediaSourceReadyState::Open || + mSourceBuffers->AnyUpdating()) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + + // 3. If live seekable range contains a range, then set live seekable range to + // be a new empty TimeRanges object. + mLiveSeekableRange.reset(); +} + bool MediaSource::Attach(MediaSourceDecoder* aDecoder) { diff --git a/dom/media/mediasource/MediaSource.h b/dom/media/mediasource/MediaSource.h index 60106fb0cf..6f6790813b 100644 --- a/dom/media/mediasource/MediaSource.h +++ b/dom/media/mediasource/MediaSource.h @@ -19,6 +19,7 @@ #include "nsID.h" #include "nsISupports.h" #include "nscore.h" +#include "TimeUnits.h" struct JSContext; class JSObject; @@ -64,6 +65,10 @@ public: void RemoveSourceBuffer(SourceBuffer& aSourceBuffer, ErrorResult& aRv); void EndOfStream(const Optional& aError, ErrorResult& aRv); + + void SetLiveSeekableRange(double aStart, double aEnd, ErrorResult& aRv); + void ClearLiveSeekableRange(ErrorResult& aRv); + static bool IsTypeSupported(const GlobalObject&, const nsAString& aType); static bool Enabled(JSContext* cx, JSObject* aGlobal); @@ -118,6 +123,12 @@ public: // buffered data. Used for debugging purposes. void GetMozDebugReaderData(nsAString& aString); + bool HasLiveSeekableRange() const { return mLiveSeekableRange.isSome(); } + media::TimeInterval LiveSeekableRange() const + { + return mLiveSeekableRange.value(); + } + private: // MediaSourceDecoder uses DurationChange to set the duration // without hitting the checks in SetDuration. @@ -154,6 +165,8 @@ private: nsRefPtr mPrincipal; MediaSourceReadyState mReadyState; + + Maybe mLiveSeekableRange; bool mFirstSourceBufferInitialized; }; diff --git a/dom/media/mediasource/MediaSourceDecoder.cpp b/dom/media/mediasource/MediaSourceDecoder.cpp index 711816e278..30d3ab0b25 100644 --- a/dom/media/mediasource/MediaSourceDecoder.cpp +++ b/dom/media/mediasource/MediaSourceDecoder.cpp @@ -91,6 +91,21 @@ MediaSourceDecoder::GetSeekable() // Return empty range. } else if (duration > 0 && mozilla::IsInfinite(duration)) { media::TimeIntervals buffered = mReader->GetBuffered(); + + // 1. If live seekable range is not empty: + if (mMediaSource->HasLiveSeekableRange()) { + // 1. Let union ranges be the union of live seekable range and the + // HTMLMediaElement.buffered attribute. + media::TimeIntervals unionRanges = + buffered + mMediaSource->LiveSeekableRange(); + // 2. Return a single range with a start time equal to the earliest start + // time in union ranges and an end time equal to the highest end time in + // union ranges and abort these steps. + seekable += + media::TimeInterval(unionRanges.GetStart(), unionRanges.GetEnd()); + return seekable; + } + if (buffered.Length()) { seekable += media::TimeInterval(media::TimeUnit::FromSeconds(0), buffered.GetEnd()); diff --git a/dom/webidl/MediaSource.webidl b/dom/webidl/MediaSource.webidl index 562f8ebb8b..95a7026fe9 100644 --- a/dom/webidl/MediaSource.webidl +++ b/dom/webidl/MediaSource.webidl @@ -34,6 +34,10 @@ interface MediaSource : EventTarget { void removeSourceBuffer(SourceBuffer sourceBuffer); [Throws] void endOfStream(optional MediaSourceEndOfStreamError error); + [Throws] + void setLiveSeekableRange(double start, double end); + [Throws] + void clearLiveSeekableRange(); static boolean isTypeSupported(DOMString type); [ChromeOnly] readonly attribute DOMString mozDebugReaderData;