Backed out changeset db3c2a88f79c (bug 1502927) for mochitest failures on test_reduce_time_precision.
authorCosmin Sabou <csabou@mozilla.com>
Mon, 05 Nov 2018 19:46:42 +0200
changeset 444422 4cc1239a7be40e828639a66bfdbdc5c4fdf1d894
parent 444421 caee2a50492816af81ad1ff35221b4bdca47b4ac
child 444423 be98a5ed931b69db01361ca6b7dca785bcfbc503
push id109586
push usercsabou@mozilla.com
push dateMon, 05 Nov 2018 17:47:03 +0000
treeherdermozilla-inbound@4cc1239a7be4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1502927
milestone65.0a1
backs outdb3c2a88f79cb28440f4e13bcf27c5165c6ee060
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
Backed out changeset db3c2a88f79c (bug 1502927) for mochitest failures on test_reduce_time_precision.
dom/media/DOMMediaStream.cpp
dom/media/DOMMediaStream.h
dom/media/MediaManager.cpp
dom/media/PeerConnection.js
dom/media/test/test_streams_element_capture.html
dom/media/tests/mochitest/mediaStreamPlayback.js
dom/media/tests/mochitest/test_peerConnection_callbacks.html
dom/media/tests/mochitest/test_peerConnection_promiseSendOnly.html
dom/media/tests/mochitest/test_peerConnection_scaleResolution.html
dom/webidl/MediaStream.webidl
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -27,20 +27,33 @@
 #include "nsContentUtils.h"
 #include "nsIScriptError.h"
 #include "nsIUUIDGenerator.h"
 #include "nsPIDOMWindow.h"
 #include "nsProxyRelease.h"
 #include "nsRFPService.h"
 #include "nsServiceManagerUtils.h"
 
+// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
+// GetTickCount() and conflicts with NS_DECL_NSIDOMMEDIASTREAM, containing
+// currentTime getter.
+#ifdef GetCurrentTime
+#undef GetCurrentTime
+#endif
+
 #ifdef LOG
 #undef LOG
 #endif
 
+// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
+// GetTickCount() and conflicts with MediaStream::GetCurrentTime.
+#ifdef GetCurrentTime
+#undef GetCurrentTime
+#endif
+
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace mozilla::media;
 
 static LazyLogModule gMediaStreamLog("MediaStream");
 #define LOG(type, msg) MOZ_LOG(gMediaStreamLog, type, msg)
 
@@ -396,19 +409,19 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(DOMAu
 NS_IMPL_ADDREF_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream)
 NS_IMPL_RELEASE_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMAudioNodeMediaStream)
 NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
 
 DOMMediaStream::DOMMediaStream(nsPIDOMWindowInner* aWindow,
                                MediaStreamTrackSourceGetter* aTrackSourceGetter)
-  : mWindow(aWindow), mInputStream(nullptr), mOwnedStream(nullptr),
-    mPlaybackStream(nullptr), mTracksPendingRemoval(0),
-    mTrackSourceGetter(aTrackSourceGetter),
+  : mLogicalStreamStartTime(0), mWindow(aWindow),
+    mInputStream(nullptr), mOwnedStream(nullptr), mPlaybackStream(nullptr),
+    mTracksPendingRemoval(0), mTrackSourceGetter(aTrackSourceGetter),
     mPlaybackTrackListener(MakeAndAddRef<PlaybackTrackListener>(this)),
     mTracksCreated(false), mNotifiedOfMediaStreamGraphShutdown(false),
     mActive(false), mSetInactiveOnFinish(false), mCORSMode(CORS_NONE)
 {
   nsresult rv;
   nsCOMPtr<nsIUUIDGenerator> uuidgen =
     do_GetService("@mozilla.org/uuid-generator;1", &rv);
 
@@ -547,16 +560,30 @@ DOMMediaStream::Constructor(const Global
       MediaStreamGraph::GetInstance(MediaStreamGraph::SYSTEM_THREAD_DRIVER, ownerWindow,
                                     MediaStreamGraph::REQUEST_DEFAULT_SAMPLE_RATE);
     newStream->InitPlaybackStreamCommon(graph);
   }
 
   return newStream.forget();
 }
 
+double
+DOMMediaStream::CurrentTime()
+{
+  if (!mPlaybackStream) {
+    return 0.0;
+  }
+  // The value of a MediaStream's CurrentTime will always advance forward; it will never
+  // reset (even if one rewinds a video.) Therefore we can use a single Random Seed
+  // initialized at the same time as the object.
+  return nsRFPService::ReduceTimePrecisionAsSecs(mPlaybackStream->
+    StreamTimeToSeconds(mPlaybackStream->GetCurrentTime() - mLogicalStreamStartTime),
+    GetRandomTimelineSeed());
+}
+
 already_AddRefed<Promise>
 DOMMediaStream::CountUnderlyingStreams(const GlobalObject& aGlobal, ErrorResult& aRv)
 {
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
   if (!window) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -14,16 +14,22 @@
 #include "StreamTracks.h"
 #include "nsIDOMWindow.h"
 #include "nsIPrincipal.h"
 #include "MediaTrackConstraints.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/RelativeTimeline.h"
 #include "PrincipalChangeObserver.h"
 
+// X11 has a #define for CurrentTime. Unbelievable :-(.
+// See dom/media/webaudio/AudioContext.h for more fun!
+#ifdef CurrentTime
+#undef CurrentTime
+#endif
+
 namespace mozilla {
 
 class AbstractThread;
 class DOMMediaStream;
 class MediaStream;
 class MediaInputPort;
 class MediaStreamGraph;
 class ProcessedMediaStream;
@@ -346,16 +352,18 @@ public:
               const DOMMediaStream& aStream,
               ErrorResult& aRv);
 
   static already_AddRefed<DOMMediaStream>
   Constructor(const dom::GlobalObject& aGlobal,
               const dom::Sequence<OwningNonNull<MediaStreamTrack>>& aTracks,
               ErrorResult& aRv);
 
+  double CurrentTime();
+
   static already_AddRefed<dom::Promise>
   CountUnderlyingStreams(const dom::GlobalObject& aGlobal, ErrorResult& aRv);
 
   void GetId(nsAString& aID) const;
 
   void GetAudioTracks(nsTArray<RefPtr<AudioStreamTrack> >& aTracks) const;
   void GetAudioTracks(nsTArray<RefPtr<MediaStreamTrack> >& aTracks) const;
   void GetVideoTracks(nsTArray<RefPtr<VideoStreamTrack> >& aTracks) const;
@@ -509,16 +517,21 @@ public:
    * Create an DOMMediaStream whose underlying input stream is an
    * AudioCaptureStream.
    */
   static already_AddRefed<DOMMediaStream>
   CreateAudioCaptureStreamAsInput(nsPIDOMWindowInner* aWindow,
                                   nsIPrincipal* aPrincipal,
                                   MediaStreamGraph* aGraph);
 
+  void SetLogicalStreamStartTime(StreamTime aTime)
+  {
+    mLogicalStreamStartTime = aTime;
+  }
+
   /**
    * Adds a MediaStreamTrack to mTracks and raises "addtrack".
    *
    * Note that "addtrack" is raised synchronously and only has an effect if
    * this MediaStream is already exposed to script. For spec compliance this is
    * to be called from an async task.
    */
   void AddTrackInternal(MediaStreamTrack* aTrack);
@@ -644,16 +657,19 @@ protected:
    */
   void NotifyPlaybackTrackBlocked();
 
   // Recomputes the current principal of this stream based on the set of tracks
   // it currently contains. PrincipalChangeObservers will be notified only if
   // the principal changes.
   void RecomputePrincipal();
 
+  // StreamTime at which the currentTime attribute would return 0.
+  StreamTime mLogicalStreamStartTime;
+
   // We need this to track our parent object.
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
 
   // MediaStreams are owned by the graph, but we tell them when to die,
   // and they won't die until we let them.
 
   // This stream contains tracks used as input by us. Cloning happens from this
   // stream. Tracks may exist in these stream but not in |mOwnedStream| if they
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1307,16 +1307,20 @@ public:
     {}
     void NotifyTracksAvailable(DOMMediaStream* aStream) override
     {
       // We're on the main thread, so no worries here.
       if (!mManager->IsWindowListenerStillActive(mWindowListener)) {
         return;
       }
 
+      // Start currentTime from the point where this stream was successfully
+      // returned.
+      aStream->SetLogicalStreamStartTime(aStream->GetPlaybackStream()->GetCurrentTime());
+
       // This is safe since we're on main-thread, and the windowlist can only
       // be invalidated from the main-thread (see OnNavigation)
       LOG(("Returning success for getUserMedia()"));
       CallOnSuccess(mOnSuccess, *aStream);
     }
     RefPtr<GetUserMediaWindowListener> mWindowListener;
     nsMainThreadPtrHandle<MediaManager::GetUserMediaSuccessCallback> mOnSuccess;
     RefPtr<MediaManager> mManager;
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -1216,16 +1216,19 @@ class RTCPeerConnection {
     });
   }
 
   addStream(stream) {
     stream.getTracks().forEach(track => this.addTrack(track, stream));
   }
 
   addTrack(track, stream) {
+    if (stream.currentTime === undefined) {
+      throw new this._win.DOMException("invalid stream.", "InvalidParameterError");
+    }
     this._checkClosed();
 
     if (this._transceivers.some(
           transceiver => transceiver.sender.track == track)) {
       throw new this._win.DOMException("This track is already set on a sender.",
                                        "InvalidAccessError");
     }
 
--- a/dom/media/test/test_streams_element_capture.html
+++ b/dom/media/test/test_streams_element_capture.html
@@ -33,16 +33,17 @@ function isGreaterThanOrEqualEps(a, b, m
 function startTest(test) {
   var v = document.createElement('video');
   var vout = document.createElement('video');
 
   v.src = test.name;
   var stream;
 
   var checkEnded = function() {
+    is(stream.currentTime, vout.currentTime, test.name + " stream final currentTime");
     if (test.duration) {
       isGreaterThanOrEqualEps(vout.currentTime, test.duration,
          test.name + " current time at end");
     }
     is(vout.readyState, vout.HAVE_CURRENT_DATA, test.name + " checking readyState");
     ok(vout.ended, test.name + " checking playback has ended");
     if (test.type.match(/^video/)) {
       checkDrawImage(vout);
@@ -52,16 +53,17 @@ function startTest(test) {
     SimpleTest.finish();
   };
   vout.addEventListener("ended", checkEnded);
 
   document.body.appendChild(vout);
 
   var onloadedmetadata = function (ev) {
     stream = v.mozCaptureStreamUntilEnded();
+    is(stream.currentTime, 0, test.name + " stream initial currentTime");
     vout.srcObject = stream;
     is(vout.srcObject, stream, test.name + " set output element .srcObject correctly");
     v.play();
     vout.play();
   }
 
   v.preload = 'metadata';
   v.addEventListener('loadedmetadata', onloadedmetadata);
--- a/dom/media/tests/mochitest/mediaStreamPlayback.js
+++ b/dom/media/tests/mochitest/mediaStreamPlayback.js
@@ -87,20 +87,22 @@ MediaStreamPlayback.prototype = {
     this.mediaElement.srcObject = this.mediaStream;
     this.mediaElement.play();
   },
 
   /**
    * Verifies that media is playing.
    */
   verifyPlaying : function() {
+    var lastStreamTime = this.mediaStream.currentTime;
     var lastElementTime = this.mediaElement.currentTime;
 
     var mediaTimeProgressed = listenUntil(this.mediaElement, 'timeupdate',
-        () => this.mediaElement.currentTime > lastElementTime);
+        () => this.mediaStream.currentTime > lastStreamTime &&
+              this.mediaElement.currentTime > lastElementTime);
 
     return timeout(Promise.all([this.canPlayThroughFired, mediaTimeProgressed]),
                    VERIFYPLAYING_TIMEOUT_LENGTH, "verifyPlaying timed out")
       .then(() => {
         is(this.mediaElement.paused, false,
            "Media element should be playing");
         is(this.mediaElement.duration, Number.POSITIVE_INFINITY,
            "Duration should be infinity");
--- a/dom/media/tests/mochitest/test_peerConnection_callbacks.html
+++ b/dom/media/tests/mochitest/test_peerConnection_callbacks.html
@@ -70,17 +70,17 @@ runNetworkTest(function() {
     .then(() => pcall(pc1, pc1.createOffer))
     .then(offer => pcall(pc1, pc1.setLocalDescription, offer))
     .then(() => pcall(pc2, pc2.setRemoteDescription, pc1.localDescription))
     .then(() => pcall(pc2, pc2.createAnswer))
     .then(answer => pcall(pc2, pc2.setLocalDescription, answer))
     .then(() => pcall(pc1, pc1.setRemoteDescription, pc2.localDescription))
     .then(() => delivered)
   //    .then(() => canPlayThrough)    // why doesn't this fire?
-    .then(() => waitUntil(() => v2.currentTime > 0))
+    .then(() => waitUntil(() => v2.currentTime > 0 && v2.srcObject.currentTime > 0))
     .then(() => ok(v2.currentTime > 0, "v2.currentTime is moving (" + v2.currentTime + ")"))
     .then(() => ok(true, "Connected."))
     .then(() => pcall(pc1, pc1.getStats, null))
     .then(stats => ok(Object.keys(stats).length > 0, "pc1 has stats"))
     .then(() => pcall(pc2, pc2.getStats, null))
     .then(stats => ok(Object.keys(stats).length > 0, "pc2 has stats"))
     .then(() => { v1.pause(); v2.pause(); })
     .catch(reason => ok(false, "unexpected failure: " + reason))
--- a/dom/media/tests/mochitest/test_peerConnection_promiseSendOnly.html
+++ b/dom/media/tests/mochitest/test_peerConnection_promiseSendOnly.html
@@ -46,17 +46,17 @@
     .then(() => pc1.createOffer({})) // check that createOffer accepts arg.
     .then(offer => pc1.setLocalDescription(offer))
     .then(() => pc2.setRemoteDescription(pc1.localDescription))
     .then(() => pc2.createAnswer({}))  // check that createAnswer accepts arg.
     .then(answer => pc2.setLocalDescription(answer))
     .then(() => pc1.setRemoteDescription(pc2.localDescription))
     .then(() => delivered)
 //    .then(() => canPlayThrough)    // why doesn't this fire?
-    .then(() => waitUntil(() => v2.currentTime > 0))
+    .then(() => waitUntil(() => v2.currentTime > 0 && v2.srcObject.currentTime > 0))
     .then(() => ok(v2.currentTime > 0, "v2.currentTime is moving (" + v2.currentTime + ")"))
     .then(() => ok(true, "Connected."))
     .catch(reason => ok(false, "unexpected failure: " + reason))
     .then(networkTestFinished);
   });
 </script>
 </pre>
 </body>
--- a/dom/media/tests/mochitest/test_peerConnection_scaleResolution.html
+++ b/dom/media/tests/mochitest/test_peerConnection_scaleResolution.html
@@ -59,17 +59,17 @@
     await pc2.setLocalDescription(answer);
     await pc1.setRemoteDescription(pc2.localDescription);
     let trackevent = await ontrackfired;
 
     v2.srcObject = trackevent.streams[0];
 
     await v2loadedmetadata;
 
-    await waitUntil(() => v2.currentTime > 0);
+    await waitUntil(() => v2.currentTime > 0 && v2.srcObject.currentTime > 0);
     ok(v2.currentTime > 0, "v2.currentTime is moving (" + v2.currentTime + ")");
 
     ok(v1.videoWidth > 0, "source width is positive");
     ok(v1.videoHeight > 0, "source height is positive");
     is(v2.videoWidth, v1.videoWidth / 2, "sink is half the width of source");
     is(v2.videoHeight, v1.videoHeight / 2, "sink is half the height of source");
     stream.getTracks().forEach(track => track.stop());
     v1.srcObject = v2.srcObject = null;
--- a/dom/webidl/MediaStream.webidl
+++ b/dom/webidl/MediaStream.webidl
@@ -34,16 +34,17 @@ interface MediaStream : EventTarget {
     sequence<MediaStreamTrack> getTracks ();
     MediaStreamTrack?          getTrackById (DOMString trackId);
     void                       addTrack (MediaStreamTrack track);
     void                       removeTrack (MediaStreamTrack track);
     MediaStream                clone ();
     readonly    attribute boolean      active;
                 attribute EventHandler onaddtrack;
                 attribute EventHandler onremovetrack;
+    readonly attribute double currentTime;
 
     [ChromeOnly, Throws]
     static Promise<long> countUnderlyingStreams();
 
     // Webrtc allows the remote side to name a stream whatever it wants, and we
     // need to surface this to content.
     [ChromeOnly]
     void assignId(DOMString id);