Bug 1280613 - P1. Add experimental SourceBuffer.appendBufferAsync. r?bz draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Fri, 11 May 2018 03:56:50 +0200
changeset 794577 9680a80024e9388d8ab4e344a6890a336a438441
parent 794191 4303d49c53931385892231969e40048f096b4d4c
child 794578 bd59c1348d359ab57e7d5b3c0e4e5df60ab5eeef
push id109720
push userbmo:jyavenard@mozilla.com
push dateSun, 13 May 2018 14:54:13 +0000
reviewersbz
bugs1280613
milestone62.0a1
Bug 1280613 - P1. Add experimental SourceBuffer.appendBufferAsync. r?bz The aim of those changes is to be incubated in the WICG. MozReview-Commit-ID: 5wEUnWz8i7kBug 1280613
dom/media/mediasource/SourceBuffer.cpp
dom/media/mediasource/SourceBuffer.h
dom/webidl/SourceBuffer.webidl
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -201,16 +201,42 @@ SourceBuffer::AppendBuffer(const ArrayBu
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("AppendBuffer(ArrayBufferView)");
   aData.ComputeLengthAndData();
   DDLOG(DDLogCategory::API, "AppendBuffer", aData.Length());
   AppendData(aData.Data(), aData.Length(), aRv);
 }
 
+already_AddRefed<Promise>
+SourceBuffer::AppendBufferAsync(const ArrayBuffer& aData,
+                                ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MSE_API("AppendBufferAsync(ArrayBuffer)");
+  aData.ComputeLengthAndData();
+  DDLOG(DDLogCategory::API, "AppendBufferAsync", aData.Length());
+
+  return AppendDataAsync(aData.Data(), aData.Length(), aRv);
+}
+
+already_AddRefed<Promise>
+SourceBuffer::AppendBufferAsync(const ArrayBufferView& aData,
+                                ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MSE_API("AppendBufferAsync(ArrayBufferView)");
+  aData.ComputeLengthAndData();
+  DDLOG(DDLogCategory::API, "AppendBufferAsync", aData.Length());
+
+  return AppendDataAsync(aData.Data(), aData.Length(), aRv);
+}
+
 void
 SourceBuffer::Abort(ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("Abort()");
   if (!IsAttached()) {
     DDLOG(DDLogCategory::API, "Abort", NS_ERROR_DOM_INVALID_STATE_ERR);
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
@@ -454,25 +480,33 @@ SourceBuffer::StopUpdating()
   if (!mUpdating) {
     // The buffer append or range removal algorithm  has been interrupted by
     // abort().
     return;
   }
   mUpdating = false;
   QueueAsyncSimpleEvent("update");
   QueueAsyncSimpleEvent("updateend");
+  if (mDOMPromise) {
+    mDOMPromise->MaybeResolveWithUndefined();
+    mDOMPromise = nullptr;
+  }
 }
 
 void
 SourceBuffer::AbortUpdating()
 {
   MOZ_ASSERT(NS_IsMainThread());
   mUpdating = false;
   QueueAsyncSimpleEvent("abort");
   QueueAsyncSimpleEvent("updateend");
+  if (mDOMPromise) {
+    mDOMPromise->MaybeReject(NS_ERROR_DOM_MEDIA_ABORT_ERR);
+    mDOMPromise = nullptr;
+  }
 }
 
 void
 SourceBuffer::CheckEndTime()
 {
   MOZ_ASSERT(NS_IsMainThread());
   // Check if we need to update mMediaSource duration
   double endTime = mCurrentAttributes.GetGroupEndTimestamp().ToSeconds();
@@ -480,33 +514,69 @@ SourceBuffer::CheckEndTime()
   if (endTime > duration) {
     mMediaSource->SetDuration(endTime);
   }
 }
 
 void
 SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("AppendData(aLength=%u)", aLength);
 
   RefPtr<MediaByteBuffer> data = PrepareAppend(aData, aLength, aRv);
   if (!data) {
     return;
   }
   StartUpdating();
 
   mTrackBuffersManager->AppendData(data.forget(), mCurrentAttributes)
     ->Then(mAbstractMainThread, __func__, this,
            &SourceBuffer::AppendDataCompletedWithSuccess,
            &SourceBuffer::AppendDataErrored)
     ->Track(mPendingAppend);
 }
 
+already_AddRefed<Promise>
+SourceBuffer::AppendDataAsync(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!IsAttached()) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIGlobalObject> parentObject =
+    do_QueryInterface(mMediaSource->GetParentObject());
+  if (!parentObject) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  RefPtr<Promise> promise = Promise::Create(parentObject, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  AppendData(aData, aLength, aRv);
+
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  MOZ_ASSERT(!mDOMPromise, "Can't have a pending operation going");
+  mDOMPromise = promise;
+
+  return promise.forget();
+}
+
 void
-SourceBuffer::AppendDataCompletedWithSuccess(const SourceBufferTask::AppendBufferResult& aResult)
+SourceBuffer::AppendDataCompletedWithSuccess(
+  const SourceBufferTask::AppendBufferResult& aResult)
 {
   MOZ_ASSERT(mUpdating);
   mPendingAppend.Complete();
   DDLOG(DDLogCategory::API, "AppendBuffer-completed", NS_OK);
 
   if (aResult.first()) {
     if (!mActive) {
       mActive = true;
@@ -565,20 +635,27 @@ SourceBuffer::AppendError(const MediaRes
   mUpdating = false;
 
   QueueAsyncSimpleEvent("error");
   QueueAsyncSimpleEvent("updateend");
 
   MOZ_ASSERT(NS_FAILED(aDecodeError));
 
   mMediaSource->EndOfStream(aDecodeError);
+
+  if (mDOMPromise) {
+    mDOMPromise->MaybeReject(aDecodeError);
+    mDOMPromise = nullptr;
+  }
 }
 
 already_AddRefed<MediaByteBuffer>
-SourceBuffer::PrepareAppend(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
+SourceBuffer::PrepareAppend(const uint8_t* aData,
+                            uint32_t aLength,
+                            ErrorResult& aRv)
 {
   typedef TrackBuffersManager::EvictDataResult Result;
 
   if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
@@ -655,22 +732,24 @@ SourceBuffer::HighestEndTime()
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(SourceBuffer)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SourceBuffer)
   tmp->Detach();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaSource)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mBuffered)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMPromise)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DOMEventTargetHelper)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SourceBuffer,
                                                   DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaSource)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBuffered)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMPromise)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_ADDREF_INHERITED(SourceBuffer, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(SourceBuffer, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SourceBuffer)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
--- a/dom/media/mediasource/SourceBuffer.h
+++ b/dom/media/mediasource/SourceBuffer.h
@@ -83,16 +83,21 @@ public:
     return mCurrentAttributes.GetAppendWindowEnd();
   }
 
   void SetAppendWindowEnd(double aAppendWindowEnd, ErrorResult& aRv);
 
   void AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv);
   void AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv);
 
+  already_AddRefed<Promise> AppendBufferAsync(const ArrayBuffer& aData,
+                                              ErrorResult& aRv);
+  already_AddRefed<Promise> AppendBufferAsync(const ArrayBufferView& aData,
+                                              ErrorResult& aRv);
+
   void Abort(ErrorResult& aRv);
   void AbortBufferAppend();
 
   void Remove(double aStart, double aEnd, ErrorResult& aRv);
 
   void ChangeType(const nsAString& aType, ErrorResult& aRv);
 
   IMPL_EVENT_HANDLER(updatestart);
@@ -152,16 +157,18 @@ private:
 
   // If the media segment contains data beyond the current duration,
   // then run the duration change algorithm with new duration set to the
   // maximum of the current duration and the group end timestamp.
   void CheckEndTime();
 
   // Shared implementation of AppendBuffer overloads.
   void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv);
+  // Shared implementation of AppendBufferAsync overloads.
+  already_AddRefed<Promise> AppendDataAsync(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv);
 
   // Implement the "Append Error Algorithm".
   // Will call endOfStream() with "decode" error if aDecodeError is true.
   // 3.5.3 Append Error Algorithm
   // http://w3c.github.io/media-source/#sourcebuffer-append-error
   void AppendError(const MediaResult& aDecodeError);
 
   // Implements the "Prepare Append Algorithm". Returns MediaByteBuffer object
@@ -185,15 +192,20 @@ private:
 
   MozPromiseRequestHolder<SourceBufferTask::AppendPromise> mPendingAppend;
   MozPromiseRequestHolder<SourceBufferTask::RangeRemovalPromise> mPendingRemoval;
   MediaContainerType mType;
 
   RefPtr<TimeRanges> mBuffered;
 
   MozPromiseRequestHolder<MediaSource::ActiveCompletionPromise> mCompletionPromise;
+
+  // Only used if MSE v2 experimental mode is active.
+  // Contains the current Promise to be resolved following use of
+  // appendBufferAsync and removeAsync. Not set of no operation is pending.
+  RefPtr<Promise> mDOMPromise;
 };
 
 } // namespace dom
 
 } // namespace mozilla
 
 #endif /* mozilla_dom_SourceBuffer_h_ */
--- a/dom/webidl/SourceBuffer.webidl
+++ b/dom/webidl/SourceBuffer.webidl
@@ -35,16 +35,22 @@ interface SourceBuffer : EventTarget {
   attribute EventHandler onupdate;
   attribute EventHandler onupdateend;
   attribute EventHandler onerror;
   attribute EventHandler onabort;
   [Throws]
   void appendBuffer(ArrayBuffer data);
   [Throws]
   void appendBuffer(ArrayBufferView data);
+  // Experimental function as proposed in:
+  // https://github.com/w3c/media-source/issues/100 for promise proposal.
+  [Throws, Func="mozilla::dom::MediaSource::ExperimentalEnabled"]
+  Promise<void> appendBufferAsync(ArrayBuffer data);
+  [Throws, Func="mozilla::dom::MediaSource::ExperimentalEnabled"]
+  Promise<void> appendBufferAsync(ArrayBufferView data);
   //[Throws]
   //void appendStream(Stream stream, [EnforceRange] optional unsigned long long maxSize);
   [Throws]
   void abort();
   [Throws]
   void remove(double start, unrestricted double end);
   // Experimental function as proposed in:
   // https://github.com/w3c/media-source/issues/155