Bug 462959 - Implement HTMLMediaElement.played; r=kinetik
☠☠ backed out by c8f939f67ab5 ☠ ☠
authorPaul ADENOT <paul@paul.cx>
Sun, 03 Jul 2011 11:53:38 +0200
changeset 74471 e0aab5011b705bc41e1307590c51320d5380cfe4
parent 74470 cfea4859f45809693f6d00080ba427c5750c191d
child 74472 0040f406ea6d85fac4e773a2f44487175917b2d8
push id235
push userbzbarsky@mozilla.com
push dateTue, 27 Sep 2011 17:13:04 +0000
treeherdermozilla-beta@2d1e082d176a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs462959
milestone8.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 462959 - Implement HTMLMediaElement.played; r=kinetik
content/html/content/public/nsHTMLMediaElement.h
content/html/content/src/nsHTMLMediaElement.cpp
content/html/content/src/nsTimeRanges.cpp
content/html/content/src/nsTimeRanges.h
dom/interfaces/html/nsIDOMHTMLAudioElement.idl
dom/interfaces/html/nsIDOMHTMLMediaElement.idl
dom/interfaces/html/nsIDOMHTMLVideoElement.idl
--- a/content/html/content/public/nsHTMLMediaElement.h
+++ b/content/html/content/public/nsHTMLMediaElement.h
@@ -45,16 +45,17 @@
 #include "nsIHttpChannel.h"
 #include "nsThreadUtils.h"
 #include "nsIDOMRange.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsILoadGroup.h"
 #include "nsIObserver.h"
 #include "ImageLayers.h"
 #include "nsAudioStream.h"
+#include "nsTimeRanges.h"
 
 // Define to output information on decoding and painting framerate
 /* #define DEBUG_FRAME_RATE 1 */
 
 typedef PRUint16 nsMediaNetworkState;
 typedef PRUint16 nsMediaReadyState;
 
 class nsHTMLMediaElement : public nsGenericHTMLElement,
@@ -619,16 +620,22 @@ protected:
 
   // An audio stream for writing audio directly from JS.
   nsRefPtr<nsAudioStream> mAudioStream;
 
   // PR_TRUE if MozAudioAvailable events can be safely dispatched, based on
   // a media and element same-origin check.
   PRBool mAllowAudioData;
 
+  // Range of time played.
+  nsTimeRanges mPlayed;
+
+  // Temporary variable for storing a time, when the stream starts to play
+  double mCurrentPlayRangeStart;
+
   // If true then we have begun downloading the media content.
   // Set to false when completed, or not yet started.
   PRPackedBool mBegun;
 
   // True when the decoder has loaded enough data to display the
   // first frame of the content.
   PRPackedBool mLoadedFirstFrame;
 
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -34,17 +34,16 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsIDOMHTMLMediaElement.h"
 #include "nsIDOMHTMLSourceElement.h"
 #include "nsHTMLMediaElement.h"
-#include "nsTimeRanges.h"
 #include "nsGenericHTMLElement.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsGkAtoms.h"
 #include "nsSize.h"
 #include "nsIFrame.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
@@ -1087,16 +1086,26 @@ NS_IMETHODIMP nsHTMLMediaElement::GetCur
   *aCurrentTime = mDecoder ? mDecoder->GetCurrentTime() : 0.0;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsHTMLMediaElement::SetCurrentTime(double aCurrentTime)
 {
   StopSuspendingAfterFirstFrame();
 
+  if (mCurrentPlayRangeStart != -1) {
+    double oldCurrentTime = 0;
+    GetCurrentTime(&oldCurrentTime);
+    LOG(PR_LOG_DEBUG, ("Adding a range: [%f, %f]", mCurrentPlayRangeStart, oldCurrentTime));
+    // Multiple seek without playing
+    if (mCurrentPlayRangeStart != oldCurrentTime) {
+      mPlayed.Add(mCurrentPlayRangeStart, oldCurrentTime);
+    }
+  }
+
   if (!mDecoder) {
     LOG(PR_LOG_DEBUG, ("%p SetCurrentTime(%f) failed: no decoder", this, aCurrentTime));
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
     LOG(PR_LOG_DEBUG, ("%p SetCurrentTime(%f) failed: no source", this, aCurrentTime));
     return NS_ERROR_DOM_INVALID_STATE_ERR;
@@ -1116,16 +1125,19 @@ NS_IMETHODIMP nsHTMLMediaElement::SetCur
   }
 
   mPlayingBeforeSeek = IsPotentiallyPlaying();
   // The media backend is responsible for dispatching the timeupdate
   // event if it changes the playback position as a result of the seek.
   LOG(PR_LOG_DEBUG, ("%p SetCurrentTime(%f) starting seek", this, aCurrentTime));
   nsresult rv = mDecoder->Seek(clampedTime);
 
+  // Start a new range at position we seeked to
+  mCurrentPlayRangeStart = clampedTime;
+
   // We changed whether we're seeking so we need to AddRemoveSelfReference
   AddRemoveSelfReference();
 
   return rv;
 }
 
 /* readonly attribute double duration; */
 NS_IMETHODIMP nsHTMLMediaElement::GetDuration(double *aDuration)
@@ -1137,16 +1149,45 @@ NS_IMETHODIMP nsHTMLMediaElement::GetDur
 /* readonly attribute boolean paused; */
 NS_IMETHODIMP nsHTMLMediaElement::GetPaused(PRBool *aPaused)
 {
   *aPaused = mPaused;
 
   return NS_OK;
 }
 
+/* readonly attribute nsIDOMHTMLTimeRanges played; */
+NS_IMETHODIMP nsHTMLMediaElement::GetPlayed(nsIDOMTimeRanges** aPlayed)
+{
+  nsRefPtr<nsTimeRanges> ranges = new nsTimeRanges();
+
+  PRUint32 timeRangeCount = 0;
+  mPlayed.GetLength(&timeRangeCount);
+  for (PRUint32 i = 0; i < timeRangeCount; i++) {
+    double begin;
+    double end;
+    mPlayed.Start(i, &begin);
+    mPlayed.End(i, &end);
+    ranges->Add(begin, end);
+  }
+
+  if (mCurrentPlayRangeStart != -1.0) {
+    double now = 0.0;
+    GetCurrentTime(&now);
+    if (mCurrentPlayRangeStart != now) {
+      ranges->Add(mCurrentPlayRangeStart, now);
+    }
+  }
+
+  ranges->Normalize();
+
+  ranges.forget(aPlayed);
+  return NS_OK;
+}
+
 /* void pause (); */
 NS_IMETHODIMP nsHTMLMediaElement::Pause()
 {
   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
     LOG(PR_LOG_DEBUG, ("Loading due to Pause()"));
     nsresult rv = Load();
     NS_ENSURE_SUCCESS(rv, rv);
   } else if (mDecoder) {
@@ -1275,16 +1316,17 @@ nsHTMLMediaElement::nsHTMLMediaElement(a
     mLoadWaitStatus(NOT_WAITING),
     mVolume(1.0),
     mChannels(0),
     mRate(0),
     mPreloadAction(PRELOAD_UNDEFINED),
     mMediaSize(-1,-1),
     mLastCurrentTime(0.0),
     mAllowAudioData(PR_FALSE),
+    mCurrentPlayRangeStart(-1.0),
     mBegun(PR_FALSE),
     mLoadedFirstFrame(PR_FALSE),
     mAutoplaying(PR_TRUE),
     mAutoplayEnabled(PR_TRUE),
     mPaused(PR_TRUE),
     mMuted(PR_FALSE),
     mPlayingBeforeSeek(PR_FALSE),
     mPausedForInactiveDocument(PR_FALSE),
@@ -1375,16 +1417,20 @@ NS_IMETHODIMP nsHTMLMediaElement::Play()
       SetCurrentTime(0);
     }
     if (!mPausedForInactiveDocument) {
       nsresult rv = mDecoder->Play();
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
+  if (mCurrentPlayRangeStart == -1.0) {
+    GetCurrentTime(&mCurrentPlayRangeStart);
+  }
+
   // TODO: If the playback has ended, then the user agent must set
   // seek to the effective start.
   // TODO: The playback rate must be set to the default playback rate.
   if (mPaused) {
     DispatchAsyncEvent(NS_LITERAL_STRING("play"));
     switch (mReadyState) {
     case nsIDOMHTMLMediaElement::HAVE_NOTHING:
       DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
@@ -2029,16 +2075,23 @@ void nsHTMLMediaElement::Error(PRUint16 
 }
 
 void nsHTMLMediaElement::PlaybackEnded()
 {
   NS_ASSERTION(mDecoder->IsEnded(), "Decoder fired ended, but not in ended state");
   // We changed the state of IsPlaybackEnded which can affect AddRemoveSelfReference
   AddRemoveSelfReference();
 
+  double end = 0.0;
+  GetCurrentTime(&end);
+  if (mCurrentPlayRangeStart != end) {
+    mPlayed.Add(mCurrentPlayRangeStart, end);
+  }
+  mCurrentPlayRangeStart = -1.0;
+
   if (mDecoder && mDecoder->IsInfinite()) {
     LOG(PR_LOG_DEBUG, ("%p, got duration by reaching the end of the stream", this));
     DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
   }
 
   FireTimeUpdate(PR_FALSE);
   DispatchAsyncEvent(NS_LITERAL_STRING("ended"));
 }
@@ -2450,17 +2503,17 @@ void nsHTMLMediaElement::NotifyAddedSour
   // resource selection algorithm.
   if (!HasAttr(kNameSpaceID_None, nsGkAtoms::src) &&
       mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY)
   {
     QueueSelectResourceTask();
   }
 
   // A load was paused in the resource selection algorithm, waiting for
-  // a new source child to be added, resume the resource selction algorithm.
+  // a new source child to be added, resume the resource selection algorithm.
   if (mLoadWaitStatus == WAITING_FOR_SOURCE) {
     QueueLoadFromSourceTask();
   }
 }
 
 nsIContent* nsHTMLMediaElement::GetNextSource()
 {
   nsresult rv = NS_OK;
--- a/content/html/content/src/nsTimeRanges.cpp
+++ b/content/html/content/src/nsTimeRanges.cpp
@@ -80,8 +80,33 @@ nsTimeRanges::End(PRUint32 aIndex, doubl
   *aTime = mRanges[aIndex].mEnd;
   return NS_OK;
 }
 
 void
 nsTimeRanges::Add(double aStart, double aEnd) {
   mRanges.AppendElement(TimeRange(aStart,aEnd));
 }
+
+void
+nsTimeRanges::Normalize() {
+  if (mRanges.Length() <= 1) {
+    return;
+  }
+  nsAutoTArray<TimeRange, 4> normalized;
+
+  mRanges.Sort(CompareTimeRanges());
+
+  // This merges the intervals
+  TimeRange current(mRanges[0]);
+  for (PRUint32 i = 1; i < mRanges.Length(); i++) {
+    if (current.mEnd >= mRanges[i].mStart) {
+      current.mEnd = NS_MAX(current.mEnd, mRanges[i].mEnd);
+    } else {
+      normalized.AppendElement(current);
+      current = mRanges[i];
+    }
+  }
+
+  normalized.AppendElement(current);
+
+  mRanges = normalized;
+}
--- a/content/html/content/src/nsTimeRanges.h
+++ b/content/html/content/src/nsTimeRanges.h
@@ -50,22 +50,40 @@ public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMTIMERANGES
 
   nsTimeRanges();
   ~nsTimeRanges();
 
   void Add(double aStart, double aEnd);
 
+  // See <http://www.whatwg.org/html/#normalized-timeranges-object>.
+  void Normalize();
+
 private:
 
   struct TimeRange {
     TimeRange(double aStart, double aEnd)
       : mStart(aStart),
         mEnd(aEnd) {}
     double mStart;
     double mEnd;
   };
 
+  struct CompareTimeRanges
+  {
+    PRBool Equals(const TimeRange& tr1, const TimeRange& tr2) const
+    {
+      return tr1.mStart == tr2.mStart && tr1.mEnd == tr2.mEnd;
+    }
+
+    // Here, we aim at time range normalization. That why we order only by start
+    // time, since the ranges can overlap.
+    PRBool LessThan(const TimeRange& tr1, const TimeRange& tr2) const
+    {
+      return tr1.mStart < tr2.mStart;
+    }
+  };
+
   nsAutoTArray<TimeRange,4> mRanges;
 };
 
 #endif // nsTimeRanges_h__
--- a/dom/interfaces/html/nsIDOMHTMLAudioElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLAudioElement.idl
@@ -47,17 +47,17 @@
  * <audio> element.
  *
  * For more information on this interface, please see
  * http://www.whatwg.org/specs/web-apps/current-work/#audio
  *
  * @status UNDER_DEVELOPMENT
  */
 
-[scriptable, uuid(f0d4977c-9632-4fab-bc9b-91c250a6ef96)]
+[scriptable, uuid(1f2437f1-6037-40c4-bfb6-105c6c60f0ca)]
 interface nsIDOMHTMLAudioElement : nsIDOMHTMLMediaElement
 {
   // Setup the audio stream for writing
   void mozSetup(in PRUint32 channels, in PRUint32 rate);
 
   // Write audio to the audio stream
   [implicit_jscontext]
   unsigned long mozWriteAudio(in jsval data);
--- a/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
@@ -52,17 +52,17 @@
 
 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
 %{C++
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
 %}
 
-[scriptable, uuid(d8213322-46d8-47ca-a15c-2abae47ddfde)]
+[scriptable, uuid(c8a5f714-97de-4e2c-8394-2397870224bb)]
 interface nsIDOMHTMLMediaElement : nsIDOMHTMLElement
 {
   // error state
   readonly attribute nsIDOMMediaError error;
 
   // network state
            attribute DOMString src;
   readonly attribute DOMString currentSrc;
@@ -84,16 +84,17 @@ interface nsIDOMHTMLMediaElement : nsIDO
   const unsigned short HAVE_ENOUGH_DATA = 4;
   readonly attribute unsigned short readyState;
   readonly attribute boolean seeking;
 
   // playback state
            attribute double currentTime;
   readonly attribute double duration;
   readonly attribute boolean paused;
+  readonly attribute nsIDOMTimeRanges played;
   readonly attribute boolean ended;
   readonly attribute boolean mozAutoplayEnabled;
            attribute boolean autoplay;
   void play();
   void pause();
 
   // controls
            attribute boolean controls;
--- a/dom/interfaces/html/nsIDOMHTMLVideoElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLVideoElement.idl
@@ -43,25 +43,25 @@
  * <video> element.
  *
  * For more information on this interface, please see
  * http://www.whatwg.org/specs/web-apps/current-work/#video
  *
  * @status UNDER_DEVELOPMENT
  */
 
-[scriptable, uuid(00c757ec-db7b-477e-95cd-b2a03b0f8634)]
+[scriptable, uuid(169f0ff1-511a-453d-86b6-346c1e936122)]
 interface nsIDOMHTMLVideoElement : nsIDOMHTMLMediaElement
 {
-           attribute long width; 
+           attribute long width;
            attribute long height;
   readonly attribute unsigned long videoWidth;
   readonly attribute unsigned long videoHeight;
            attribute DOMString poster;
-           
+
   // A count of the number of video frames that have demuxed from the media
   // resource. If we were playing perfectly, we'd be able to paint this many
   // frames.
   readonly attribute unsigned long mozParsedFrames;
 
   // A count of the number of frames that have been decoded. We may drop
   // frames if the decode is taking too much time.
   readonly attribute unsigned long mozDecodedFrames;