Bug 886194 - Add MediaSource support to HTMLMediaElement.mozSrcObject. r=smaug r=roc
☠☠ backed out by ffa85c1e71e0 ☠ ☠
authorJean-Yves Avenard <jyavenard@mozilla.com>
Mon, 08 Sep 2014 10:24:30 +1000
changeset 204977 408273e953e969d50fa19041086ab0bb129957c8
parent 204976 c2946c97fa7d9434ed750cb63c2d734ee49aa6e2
child 204978 75043539c50934cd6edcdb0a30718c8d7eb66ec1
push id49052
push usercbook@mozilla.com
push dateFri, 12 Sep 2014 07:17:14 +0000
treeherdermozilla-inbound@a5fed405b904 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, roc
bugs886194
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 886194 - Add MediaSource support to HTMLMediaElement.mozSrcObject. r=smaug r=roc
content/html/content/public/HTMLMediaElement.h
content/html/content/src/HTMLMediaElement.cpp
dom/webidl/HTMLMediaElement.webidl
--- a/content/html/content/public/HTMLMediaElement.h
+++ b/content/html/content/public/HTMLMediaElement.h
@@ -73,16 +73,18 @@ namespace dom {
 // Number of milliseconds between timeupdate events as defined by spec
 #define TIMEUPDATE_MS 250
 
 class MediaError;
 class MediaSource;
 class TextTrackList;
 class AudioTrackList;
 class VideoTrackList;
+class OwningMediaStreamOrMediaSource;
+class MediaStreamOrMediaSource;
 
 class HTMLMediaElement : public nsGenericHTMLElement,
                          public nsIDOMHTMLMediaElement,
                          public nsIObserver,
                          public MediaDecoderOwner,
                          public nsIAudioChannelAgentCallback
 {
 public:
@@ -519,19 +521,19 @@ public:
     return mIsCasting;
   }
 
   void SetMozIsCasting(bool aShow)
   {
     mIsCasting = aShow;
   }
 
-  already_AddRefed<DOMMediaStream> GetMozSrcObject() const;
+  void GetMozSrcObject(Nullable<OwningMediaStreamOrMediaSource>& aValue) const;
 
-  void SetMozSrcObject(DOMMediaStream& aValue);
+  void SetMozSrcObject(const Nullable<MediaStreamOrMediaSource>& aValue);
 
   bool MozPreservesPitch() const
   {
     return mPreservesPitch;
   }
 
   // XPCOM MozPreservesPitch() is OK
 
@@ -672,16 +674,22 @@ protected:
    * the poster hiding or showing immediately.
    */
   void SetPlayedOrSeeked(bool aValue);
 
   /**
    * Initialize the media element for playback of aStream
    */
   void SetupSrcMediaStreamPlayback(DOMMediaStream* aStream);
+
+  /**
+   * Initialize the media element for playback of aSource
+   */
+  nsresult SetupSrcMediaSourcePlayback(nsRefPtr<MediaSource>& aSource);
+
   /**
    * Stop playback on mSrcStream.
    */
   void EndSrcMediaStreamPlayback();
 
   /**
    * Returns an nsDOMMediaStream containing the played contents of this
    * element. When aFinishWhenEnded is true, when this element ends playback
@@ -969,16 +977,20 @@ protected:
     nsRefPtr<DOMMediaStream> mStream;
     bool mFinishWhenEnded;
   };
   nsTArray<OutputMediaStream> mOutputStreams;
 
   // Holds a reference to the MediaStreamListener attached to mSrcStream.
   nsRefPtr<StreamListener> mSrcStreamListener;
 
+  // Holds a reference to the MediaSource that has been
+  // set in the srcObject attribute.
+  nsRefPtr<MediaSource> mSrcAttrMediaSource;
+
   // Holds a reference to the MediaSource supplying data for playback.
   nsRefPtr<MediaSource> mMediaSource;
 
   // Holds a reference to the first channel we open to the media resource.
   // Once the decoder is created, control over the channel passes to the
   // decoder, and we null out this reference. We must store this in case
   // we need to cancel the channel before control of it passes to the decoder.
   nsCOMPtr<nsIChannel> mChannel;
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLMediaElementBinding.h"
 #include "mozilla/dom/HTMLSourceElement.h"
 #include "mozilla/dom/ElementInlines.h"
+#include "mozilla/dom/UnionTypes.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #ifdef MOZ_EME
 #include "mozilla/dom/MediaEncryptedEvent.h"
 #endif
 
 #include "base/basictypes.h"
@@ -413,16 +414,17 @@ NS_IMETHODIMP HTMLMediaElement::MediaLoa
 
 NS_IMPL_ADDREF_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
 NS_IMPL_RELEASE_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLMediaElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaSource)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrcAttrMediaSource)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrcStream)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrcAttrStream)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourcePointer)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoadBlockedDoc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceLoadCandidate)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioChannelAgent)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
   for (uint32_t i = 0; i < tmp->mOutputStreams.Length(); ++i) {
@@ -440,16 +442,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
   if (tmp->mSrcStream) {
     // Need to EndMediaStreamPlayback to clear mSrcStream and make sure everything
     // gets unhooked correctly.
     tmp->EndSrcMediaStreamPlayback();
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSrcAttrStream)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaSource)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSrcAttrMediaSource)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourcePointer)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoadBlockedDoc)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceLoadCandidate)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioChannelAgent)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
   for (uint32_t i = 0; i < tmp->mOutputStreams.Length(); ++i) {
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputStreams[i].mStream)
   }
@@ -494,45 +497,62 @@ HTMLMediaElement::SetMozAudioChannelType
 }
 
 NS_IMETHODIMP_(bool)
 HTMLMediaElement::IsVideo()
 {
   return false;
 }
 
-already_AddRefed<DOMMediaStream>
-HTMLMediaElement::GetMozSrcObject() const
+void
+HTMLMediaElement::GetMozSrcObject(Nullable<OwningMediaStreamOrMediaSource>& aValue) const
 {
-  NS_ASSERTION(!mSrcAttrStream || mSrcAttrStream->GetStream(),
-               "MediaStream should have been set up properly");
-  nsRefPtr<DOMMediaStream> stream = mSrcAttrStream;
-  return stream.forget();
+  if (mSrcAttrStream) {
+    NS_ASSERTION(mSrcAttrStream->GetStream(),
+                 "MediaStream should have been set up properly");
+    aValue.SetValue().SetAsMediaStream() = mSrcAttrStream;
+    return;
+  }
+  if (mSrcAttrMediaSource) {
+    aValue.SetValue().SetAsMediaSource() = mSrcAttrMediaSource;
+    return;
+  }
+  aValue.SetNull();
 }
 
 NS_IMETHODIMP
 HTMLMediaElement::GetMozSrcObject(nsIDOMMediaStream** aStream)
 {
-  nsRefPtr<DOMMediaStream> stream = GetMozSrcObject();
+  NS_ASSERTION(!mSrcAttrStream || mSrcAttrStream->GetStream(),
+               "MediaStream should have been set up properly");
+  nsRefPtr<DOMMediaStream> stream = mSrcAttrStream;
   stream.forget(aStream);
   return NS_OK;
 }
 
 void
-HTMLMediaElement::SetMozSrcObject(DOMMediaStream& aValue)
+HTMLMediaElement::SetMozSrcObject(const Nullable<MediaStreamOrMediaSource>& aValue)
 {
-  mSrcAttrStream = &aValue;
+  mSrcAttrStream = nullptr;
+  mSrcAttrMediaSource = nullptr;
+
+  if (aValue.Value().IsMediaStream()) {
+    mSrcAttrStream = &(aValue.Value().GetAsMediaStream());
+  } else if (aValue.Value().IsMediaSource()) {
+    mSrcAttrMediaSource = &(aValue.Value().GetAsMediaSource());
+  }
   Load();
 }
 
 NS_IMETHODIMP
 HTMLMediaElement::SetMozSrcObject(nsIDOMMediaStream* aStream)
 {
-  DOMMediaStream* stream = static_cast<DOMMediaStream*>(aStream);
-  SetMozSrcObject(*stream);
+  mSrcAttrStream = static_cast<DOMMediaStream*>(aStream);
+  mSrcAttrMediaSource = nullptr;
+  Load();
   return NS_OK;
 }
 
 /* readonly attribute nsIDOMHTMLMediaElement mozAutoplayEnabled; */
 NS_IMETHODIMP HTMLMediaElement::GetMozAutoplayEnabled(bool *aAutoplayEnabled)
 {
   *aAutoplayEnabled = mAutoplayEnabled;
 
@@ -610,17 +630,20 @@ HTMLMediaElement::OnChannelRedirect(nsIC
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 void HTMLMediaElement::ShutdownDecoder()
 {
-  RemoveMediaElementFromURITable();
+  // No URI is in use when setting MediaSource via mozSrcObject attribute
+  if (mLoadingSrc) {
+    RemoveMediaElementFromURITable();
+  }
   NS_ASSERTION(mDecoder, "Must have decoder to shut down");
   mDecoder->Shutdown();
   mDecoder = nullptr;
 }
 
 void HTMLMediaElement::AbortExistingLoads()
 {
   // Abort any already-running instance of the resource selection algorithm.
@@ -807,18 +830,18 @@ void HTMLMediaElement::SelectResourceWra
 {
   SelectResource();
   mIsRunningSelectResource = false;
   mHaveQueuedSelectResource = false;
 }
 
 void HTMLMediaElement::SelectResource()
 {
-  if (!mSrcAttrStream && !HasAttr(kNameSpaceID_None, nsGkAtoms::src) &&
-      !HasSourceChildren(this)) {
+  if (!mSrcAttrStream && !mSrcAttrMediaSource &&
+      !HasAttr(kNameSpaceID_None, nsGkAtoms::src) && !HasSourceChildren(this)) {
     // The media element has neither a src attribute nor any source
     // element children, abort the load.
     mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
     // This clears mDelayingLoadEvent, so AddRemoveSelfReference will be called
     ChangeDelayLoadStatus(false);
     return;
   }
 
@@ -834,16 +857,22 @@ void HTMLMediaElement::SelectResource()
   // state update
   UpdatePreloadAction();
   mIsRunningSelectResource = true;
 
   // If we have a 'src' attribute, use that exclusively.
   nsAutoString src;
   if (mSrcAttrStream) {
     SetupSrcMediaStreamPlayback(mSrcAttrStream);
+  } else if (mSrcAttrMediaSource) {
+    nsresult rv = SetupSrcMediaSourcePlayback(mSrcAttrMediaSource);
+    if (NS_SUCCEEDED(rv)) {
+      return;
+    }
+    NoSupportedMediaSourceError();
   } else if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
     nsCOMPtr<nsIURI> uri;
     nsresult rv = NewURIFromString(src, getter_AddRefs(uri));
     if (NS_SUCCEEDED(rv)) {
       LOG(PR_LOG_DEBUG, ("%p Trying load from src=%s", this, NS_ConvertUTF16toUTF8(src).get()));
       NS_ASSERTION(!mIsLoadingFromSourceChildren,
         "Should think we're not loading from source children by default");
 
@@ -1132,17 +1161,17 @@ nsresult HTMLMediaElement::LoadResource(
   }
 
   // Set the media element's CORS mode only when loading a resource
   mCORSMode = AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin));
 
   HTMLMediaElement* other = LookupMediaElementURITable(mLoadingSrc);
   if (other && other->mDecoder) {
     // Clone it.
-    nsresult rv = InitializeDecoderAsClone(other->mDecoder);
+    rv = InitializeDecoderAsClone(other->mDecoder);
     if (NS_SUCCEEDED(rv))
       return rv;
   }
 
   if (IsMediaStreamURI(mLoadingSrc)) {
     nsCOMPtr<nsIDOMMediaStream> stream;
     rv = NS_GetStreamForMediaStreamURI(mLoadingSrc, getter_AddRefs(stream));
     if (NS_FAILED(rv)) {
@@ -1163,26 +1192,17 @@ nsresult HTMLMediaElement::LoadResource(
     if (NS_FAILED(rv)) {
       nsCString specUTF8;
       mLoadingSrc->GetSpec(specUTF8);
       NS_ConvertUTF8toUTF16 spec(specUTF8);
       const char16_t* params[] = { spec.get() };
       ReportLoadError("MediaLoadInvalidURI", params, ArrayLength(params));
       return rv;
     }
-    nsRefPtr<MediaSourceDecoder> decoder = new MediaSourceDecoder(this);
-    if (!source->Attach(decoder)) {
-      // TODO: Handle failure: run "If the media data cannot be fetched at
-      // all, due to network errors, causing the user agent to give up
-      // trying to fetch the resource" section of resource fetch algorithm.
-      return NS_ERROR_FAILURE;
-    }
-    mMediaSource = source.forget();
-    nsRefPtr<MediaResource> resource = MediaSourceDecoder::CreateResource();
-    return FinishDecoderSetup(decoder, resource, nullptr, nullptr);
+    return SetupSrcMediaSourcePlayback(source);
   }
 
   nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
 
   // check for a Content Security Policy to pass down to the channel
   // created to load the media content
   nsCOMPtr<nsIChannelPolicy> channelPolicy;
   nsCOMPtr<nsIContentSecurityPolicy> csp;
@@ -2689,17 +2709,20 @@ nsresult HTMLMediaElement::FinishDecoder
     LOG(PR_LOG_DEBUG, ("%p Failed to load for decoder %p", this, aDecoder));
     return rv;
   }
 
   // Decoder successfully created, the decoder now owns the MediaResource
   // which owns the channel.
   mChannel = nullptr;
 
-  AddMediaElementToURITable();
+  // No URI is in use when setting MediaSource via mozSrcObject attribute
+  if (mLoadingSrc) {
+    AddMediaElementToURITable();
+  }
 
   // We may want to suspend the new stream now.
   // This will also do an AddRemoveSelfReference.
   NotifyOwnerDocumentActivityChanged();
 
   if (!mPaused) {
     SetPlayedOrSeeked(true);
     if (!mPausedForInactiveDocumentOrChannel) {
@@ -2865,16 +2888,32 @@ void HTMLMediaElement::SetupSrcMediaStre
   DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
   DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
   AddRemoveSelfReference();
   // FirstFrameLoaded(false) will be called when the stream has current data,
   // to complete the setup by entering the HAVE_CURRENT_DATA state.
 }
 
+nsresult HTMLMediaElement::SetupSrcMediaSourcePlayback(nsRefPtr<MediaSource>& aSource)
+{
+  NS_ASSERTION(!mMediaSource, "Should have been ended already");
+
+  nsRefPtr<MediaSourceDecoder> decoder = new MediaSourceDecoder(this);
+  if (!aSource->Attach(decoder)) {
+    // TODO: Handle failure: run "If the media data cannot be fetched at
+    // all, due to network errors, causing the user agent to give up
+    // trying to fetch the resource" section of resource fetch algorithm.
+    return NS_ERROR_FAILURE;
+  }
+  mMediaSource = aSource;
+  nsRefPtr<MediaResource> resource = MediaSourceDecoder::CreateResource();
+  return FinishDecoderSetup(decoder, resource, nullptr, nullptr);
+}
+
 void HTMLMediaElement::EndSrcMediaStreamPlayback()
 {
   MediaStream* stream = GetSrcMediaStream();
   if (stream) {
     stream->RemoveListener(mSrcStreamListener);
   }
   // Kill its reference to this element
   mSrcStreamListener->Forget();
@@ -2915,17 +2954,18 @@ void HTMLMediaElement::ProcessMediaFragm
 void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
                                       const MetadataTags* aTags)
 {
   mHasAudio = aInfo->HasAudio();
   mTags = aTags;
   ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
   DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
   DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
-  if (mDecoder && mDecoder->IsTransportSeekable() && mDecoder->IsMediaSeekable()) {
+  if (!mMediaSource &&
+      mDecoder && mDecoder->IsTransportSeekable() && mDecoder->IsMediaSeekable()) {
     ProcessMediaFragmentURI();
     mDecoder->SetFragmentEndTime(mFragmentEnd);
   }
 
   // If this element had a video track, but consists only of an audio track now,
   // delete the VideoFrameContainer. This happens when the src is changed to an
   // audio only file.
   if (!aInfo->HasVideo() && mVideoFrameContainer) {
--- a/dom/webidl/HTMLMediaElement.webidl
+++ b/dom/webidl/HTMLMediaElement.webidl
@@ -95,17 +95,17 @@ interface HTMLMediaElement : HTMLElement
   [Pref="media.webvtt.enabled"]
   TextTrack addTextTrack(TextTrackKind kind,
                          optional DOMString label = "",
                          optional DOMString language = "");
 };
 
 // Mozilla extensions:
 partial interface HTMLMediaElement {
-  attribute MediaStream? mozSrcObject;
+  attribute (MediaStream or MediaSource)? mozSrcObject;
   attribute boolean mozPreservesPitch;
   readonly attribute boolean mozAutoplayEnabled;
 
   // NB: for internal use with the video controls:
   [Func="IsChromeOrXBL"] attribute boolean mozMediaStatisticsShowing;
   [Func="IsChromeOrXBL"] attribute boolean mozAllowCasting;
   [Func="IsChromeOrXBL"] attribute boolean mozIsCasting;