Bug 1286723: [MSE] P1. Add support for live seekable attribute. r?gerald,r?bz draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Thu, 14 Jul 2016 14:34:45 +1000
changeset 387593 48d13ba085b16841c03114ee856830609d0d3903
parent 387427 5fd14a66be3191e5628af3cfb0a456dd70f179b8
child 387594 ccf6d558060484941c064198c1353e89e5f1a801
push id23007
push userbmo:jyavenard@mozilla.com
push dateThu, 14 Jul 2016 10:16:38 +0000
reviewersgerald, bz
bugs1286723
milestone50.0a1
Bug 1286723: [MSE] P1. Add support for live seekable attribute. r?gerald,r?bz See https://github.com/w3c/media-source/issues/5 MozReview-Commit-ID: EP37gRmUAXF
dom/media/mediasource/MediaSource.cpp
dom/media/mediasource/MediaSource.h
dom/media/mediasource/MediaSourceDecoder.cpp
dom/webidl/MediaSource.webidl
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -361,16 +361,68 @@ MediaSource::IsTypeSupported(const Globa
 }
 
 /* static */ bool
 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)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("Attach(aDecoder=%p) owner=%p", aDecoder, aDecoder->GetOwner());
   MOZ_ASSERT(aDecoder);
   MOZ_ASSERT(aDecoder->GetOwner());
   if (mReadyState != MediaSourceReadyState::Closed) {
--- a/dom/media/mediasource/MediaSource.h
+++ b/dom/media/mediasource/MediaSource.h
@@ -14,16 +14,17 @@
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/MediaSourceBinding.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionNoteChild.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsID.h"
 #include "nsISupports.h"
 #include "nscore.h"
+#include "TimeUnits.h"
 
 struct JSContext;
 class JSObject;
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 
 class ErrorResult;
@@ -59,16 +60,20 @@ public:
 
   double Duration();
   void SetDuration(double aDuration, ErrorResult& aRv);
 
   already_AddRefed<SourceBuffer> AddSourceBuffer(const nsAString& aType, ErrorResult& aRv);
   void RemoveSourceBuffer(SourceBuffer& aSourceBuffer, ErrorResult& aRv);
 
   void EndOfStream(const Optional<MediaSourceEndOfStreamError>& 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);
   /** End WebIDL Methods. */
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaSource, DOMEventTargetHelper)
   NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_MEDIASOURCE_IMPLEMENTATION_IID)
@@ -94,16 +99,22 @@ public:
   {
     return mPrincipal;
   }
 
   // Returns a string describing the state of the MediaSource internal
   // 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.
   friend class mozilla::MediaSourceDecoder;
   // SourceBuffer uses SetDuration and SourceBufferIsActive
   friend class mozilla::dom::SourceBuffer;
 
   ~MediaSource();
@@ -128,16 +139,18 @@ private:
   RefPtr<MediaSourceDecoder> mDecoder;
   // Ensures the media element remains alive to dispatch progress and
   // durationchanged events.
   RefPtr<HTMLMediaElement> mMediaElement;
 
   RefPtr<nsIPrincipal> mPrincipal;
 
   MediaSourceReadyState mReadyState;
+
+  Maybe<media::TimeInterval> mLiveSeekableRange;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(MediaSource, MOZILLA_DOM_MEDIASOURCE_IMPLEMENTATION_IID)
 
 } // namespace dom
 
 } // namespace mozilla
 
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -77,16 +77,31 @@ MediaSourceDecoder::GetSeekable()
   }
 
   media::TimeIntervals seekable;
   double duration = mMediaSource->Duration();
   if (IsNaN(duration)) {
     // Return empty range.
   } else if (duration > 0 && mozilla::IsInfinite(duration)) {
     media::TimeIntervals buffered = 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());
     }
   } else {
     seekable += media::TimeInterval(media::TimeUnit::FromSeconds(0),
                                     media::TimeUnit::FromSeconds(duration));
   }
--- a/dom/webidl/MediaSource.webidl
+++ b/dom/webidl/MediaSource.webidl
@@ -29,12 +29,16 @@ interface MediaSource : EventTarget {
   [SetterThrows]
   attribute unrestricted double duration;
   [NewObject, Throws]
   SourceBuffer addSourceBuffer(DOMString type);
   [Throws]
   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;
 };