Bug 1054706: Test for painted frames to verify video is flowing in webrtc mochitests r=drno
☠☠ backed out by c7ccab163c19 ☠ ☠
authorRandell Jesup <rjesup@jesup.org>
Fri, 22 Aug 2014 18:11:21 -0400
changeset 224508 324833b277403ab63ca5a9a386edc371fba57a77
parent 224507 e09f215b65f86da6b3e440acf9e3ab9b370a770c
child 224509 bead3ac993fad7adec9d9bdf3ad6595f06388c52
push idunknown
push userunknown
push dateunknown
reviewersdrno
bugs1054706
milestone34.0a1
Bug 1054706: Test for painted frames to verify video is flowing in webrtc mochitests r=drno
dom/media/tests/mochitest/pc.js
dom/media/tests/mochitest/test_dataChannel_basicAudioVideo.html
dom/media/tests/mochitest/test_peerConnection_basicH264Video.html
dom/media/tests/mochitest/test_peerConnection_basicVideo.html
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -271,21 +271,21 @@ CommandChain.prototype = {
  * This class provides a state checker for media elements which store
  * a media stream to check for media attribute state and events fired.
  * When constructed by a caller, an object instance is created with
  * a media element, event state checkers for canplaythrough, timeupdate, and
  * time changing on the media element and stream.
  *
  * @param {HTMLMediaElement} element the media element being analyzed
  */
-function MediaElementChecker(element) {
+function MediaElementChecker(element, expectVideo) {
   this.element = element;
   this.canPlayThroughFired = false;
   this.timeUpdateFired = false;
-  this.timePassed = false;
+  this.hasRenderedMedia = false;
 
   var self = this;
   var elementId = self.element.getAttribute('id');
 
   // When canplaythrough fires, we track that it's fired and remove the
   // event listener.
   var canPlayThroughCallback = function() {
     info('canplaythrough fired for media element ' + elementId);
@@ -300,19 +300,31 @@ function MediaElementChecker(element) {
     self.timeUpdateFired = true;
     info('timeupdate fired for media element ' + elementId);
 
     // If time has passed, then track that and remove the timeupdate event
     // listener.
     if(element.mozSrcObject && element.mozSrcObject.currentTime > 0 &&
        element.currentTime > 0) {
       info('time passed for media element ' + elementId);
-      self.timePassed = true;
-      self.element.removeEventListener('timeupdate', timeUpdateCallback,
-                                       false);
+      // We know element isn't fixed; null tells us it or some parent is display:none if not fixed
+      if(expectVideo && !(element.offsetParent === null)) {
+        if(element.mozPaintedFrames > 0) {
+	  info(element.mozPaintedFrames + ' frames have painted');
+          self.hasRenderedMedia = true;
+        }
+      } else {
+        // XXX punt for now; don't see a good way to find if audio actually played
+        // Maybe mozCaptureStream, route to WebAudio?  Ugh
+        self.hasRenderedMedia = true;
+      }
+      if (self.hasRenderedMedia) {
+        self.element.removeEventListener('timeupdate', timeUpdateCallback,
+                                         false);
+      }
     }
   };
 
   element.addEventListener('canplaythrough', canPlayThroughCallback, false);
   element.addEventListener('timeupdate', timeUpdateCallback, false);
 }
 
 MediaElementChecker.prototype = {
@@ -324,17 +336,17 @@ MediaElementChecker.prototype = {
    * @param {Function} onSuccess the success callback when media flow is
    *                             established
    */
   waitForMediaFlow : function MEC_WaitForMediaFlow(onSuccess) {
     var self = this;
     var elementId = self.element.getAttribute('id');
     info('Analyzing element: ' + elementId);
 
-    if(self.canPlayThroughFired && self.timeUpdateFired && self.timePassed) {
+    if(self.canPlayThroughFired && self.timeUpdateFired && self.hasRenderedMedia) {
       ok(true, 'Media flowing for ' + elementId);
       onSuccess();
     } else {
       setTimeout(function() {
         self.waitForMediaFlow(onSuccess);
       }, 100);
     }
   },
@@ -533,22 +545,22 @@ function PeerConnectionTest(options) {
       }
       if (!options.config_remote.hasOwnProperty("iceServers")) {
         options.config_remote.iceServers = turnServers.remote.iceServers;
       }
     }
   }
 
   if (options.is_local)
-    this.pcLocal = new PeerConnectionWrapper('pcLocal', options.config_local, options.h264);
+    this.pcLocal = new PeerConnectionWrapper('pcLocal', options, true);
   else
     this.pcLocal = null;
 
   if (options.is_remote)
-    this.pcRemote = new PeerConnectionWrapper('pcRemote', options.config_remote || options.config_local, options.h264);
+    this.pcRemote = new PeerConnectionWrapper('pcRemote', options, false);
   else
     this.pcRemote = null;
 
   // Create command chain instance and assign default commands
   this.chain = new CommandChain(this, options.commands);
   if (!options.is_local) {
     this.chain.filterOut(/^PC_LOCAL/);
   }
@@ -1349,35 +1361,39 @@ DataChannelWrapper.prototype = {
 
 
 /**
  * This class acts as a wrapper around a PeerConnection instance.
  *
  * @constructor
  * @param {string} label
  *        Description for the peer connection instance
- * @param {object} configuration
- *        Configuration for the peer connection instance
+ * @param {object} options
+ *        Optional options for the peer connection test
+ * @param {bool} local
+ *        if this is constructing the local or remote peerconnection
  */
-function PeerConnectionWrapper(label, configuration, h264) {
-  this.configuration = configuration;
+function PeerConnectionWrapper(label, options, local) {
+  this.configuration = local ? options.config_local :
+                               (options.config_remote || options.config_local);
   this.label = label;
   this.whenCreated = Date.now();
 
   this.constraints = [ ];
   this.offerOptions = {};
   this.streams = [ ];
   this.mediaCheckers = [ ];
 
   this.dataChannels = [ ];
 
   this.onAddStreamFired = false;
   this.addStreamCallbacks = {};
 
-  this.h264 = typeof h264 !== "undefined" ? true : false;
+  // This is probably overly baroque
+  this.h264 = typeof options.h264 !== "undefined" ? options.h264 : false;
 
   info("Creating " + this);
   this._pc = new mozRTCPeerConnection(this.configuration);
 
   /**
    * Setup callback handlers
    */
   var self = this;
@@ -1529,17 +1545,17 @@ PeerConnectionWrapper.prototype = {
 
   /**
    * Callback when we get media from either side. Also an appropriate
    * HTML media element will be created.
    *
    * @param {MediaStream} stream
    *        Media stream to handle
    * @param {string} type
-   *        The type of media stream ('audio' or 'video')
+   *        The type of media stream ('audio' or 'video' or 'audiovideo')
    * @param {string} side
    *        The location the stream is coming from ('local' or 'remote')
    */
   attachMedia : function PCW_attachMedia(stream, type, side) {
     info("Got media stream: " + type + " (" + side + ")");
     this.streams.push(stream);
 
     if (side === 'local') {
@@ -1550,17 +1566,17 @@ PeerConnectionWrapper.prototype = {
       } else {
         stream.getTracks().forEach(function(track) {
           this._pc.addTrack(track, stream);
         }.bind(this));
       }
     }
 
     var element = createMediaElement(type, this.label + '_' + side);
-    this.mediaCheckers.push(new MediaElementChecker(element));
+    this.mediaCheckers.push(new MediaElementChecker(element, type == "video" || type == "audiovideo"));
     element.mozSrcObject = stream;
     element.play();
   },
 
   /**
    * Requests all the media streams as specified in the constrains property.
    *
    * @param {function} onSuccess
--- a/dom/media/tests/mochitest/test_dataChannel_basicAudioVideo.html
+++ b/dom/media/tests/mochitest/test_dataChannel_basicAudioVideo.html
@@ -8,17 +8,18 @@
   <script type="application/javascript" src="templates.js"></script>
   <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "796891",
-    title: "Basic data channel audio/video connection"
+    title: "Basic data channel audio/video connection",
+    visible: true
   });
 
   var test;
   runNetworkTest(function () {
     test = new DataChannelTest();
     test.setMediaConstraints([{audio: true}, {video: true}],
                              [{audio: true}, {video: true}]);
     test.run();
--- a/dom/media/tests/mochitest/test_peerConnection_basicH264Video.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicH264Video.html
@@ -9,17 +9,18 @@
   <script type="application/javascript" src="templates.js"></script>
   <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript;version=1.8">
   createHTML({
     bug: "1040346",
-    title: "Basic H.264 GMP video-only peer connection"
+    title: "Basic H.264 GMP video-only peer connection",
+    visible: true
   });
 
   var test;
   runNetworkTest(function (options) {
     options = options || { };
     options.h264 = true;
     test = new PeerConnectionTest(options);
     test.setMediaConstraints([{video: true}], [{video: true}]);
--- a/dom/media/tests/mochitest/test_peerConnection_basicVideo.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicVideo.html
@@ -9,17 +9,18 @@
   <script type="application/javascript" src="templates.js"></script>
   <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "796888",
-    title: "Basic video-only peer connection"
+    title: "Basic video-only peer connection",
+    visible: true
   });
 
   var test;
   runNetworkTest(function (options) {
     test = new PeerConnectionTest(options);
     test.setMediaConstraints([{video: true}], [{video: true}]);
     test.run();
   });