<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[214357] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/214357">214357</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2017-03-24 11:01:18 -0700 (Fri, 24 Mar 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>Add libwebrtc backend support for RTCRtpSender::replaceTrack
https://bugs.webkit.org/show_bug.cgi?id=169841

Patch by Youenn Fablet &lt;youenn@apple.com&gt; on 2017-03-24
Reviewed by Alex Christensen.

Source/WebCore:

Tests: webrtc/audio-replace-track.html
       webrtc/video-replace-track.html

Adding support for replaceTrack for audio and video sources.
Replacing tracks will always succeed for audio sources.
For video tracks, it will only succeed if the video resolution is not greater.
LibWebRTCPeerConnectionBackend will delegate the track replacing by replacing the source of the outgoing sources with the source wrapped in the replacing track.

Video test is not fully passing as size constraints for mock video sources are not providing the right video stream resolution.

* Modules/mediastream/RTCRtpSender.cpp:
(WebCore::RTCRtpSender::replaceTrack):
* Modules/mediastream/RTCRtpSender.h:
* Modules/mediastream/RTCRtpSender.idl:
* Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp:
(WebCore::LibWebRTCPeerConnectionBackend::replaceTrack):
* Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h:
* platform/mediastream/mac/RealtimeOutgoingAudioSource.cpp:
(WebCore::RealtimeOutgoingAudioSource::setSource):
* platform/mediastream/mac/RealtimeOutgoingAudioSource.h:
* platform/mediastream/mac/RealtimeOutgoingVideoSource.cpp:
(WebCore::RealtimeOutgoingVideoSource::setSource):
* platform/mediastream/mac/RealtimeOutgoingVideoSource.h:
* platform/mock/MockRealtimeVideoSource.cpp:
(WebCore::MockRealtimeVideoSource::drawText):
(WebCore::MockRealtimeVideoSource::generateFrame):

LayoutTests:

* webrtc/audio-replace-track-expected.txt: Added.
* webrtc/audio-replace-track.html: Added.
* webrtc/video-replace-track-expected.txt: Added.
* webrtc/video-replace-track.html: Added.
* webrtc/video-replace-track-to-null-expected.txt: Added.
* webrtc/video-replace-track-to-null.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreModulesmediastreamRTCPeerConnectioncpp">trunk/Source/WebCore/Modules/mediastream/RTCPeerConnection.cpp</a></li>
<li><a href="#trunkSourceWebCoreModulesmediastreamRTCPeerConnectionh">trunk/Source/WebCore/Modules/mediastream/RTCPeerConnection.h</a></li>
<li><a href="#trunkSourceWebCoreModulesmediastreamRTCRtpSendercpp">trunk/Source/WebCore/Modules/mediastream/RTCRtpSender.cpp</a></li>
<li><a href="#trunkSourceWebCoreModulesmediastreamRTCRtpSenderh">trunk/Source/WebCore/Modules/mediastream/RTCRtpSender.h</a></li>
<li><a href="#trunkSourceWebCoreModulesmediastreamRTCRtpSenderidl">trunk/Source/WebCore/Modules/mediastream/RTCRtpSender.idl</a></li>
<li><a href="#trunkSourceWebCoreModulesmediastreamlibwebrtcLibWebRTCPeerConnectionBackendcpp">trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp</a></li>
<li><a href="#trunkSourceWebCoreModulesmediastreamlibwebrtcLibWebRTCPeerConnectionBackendh">trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h</a></li>
<li><a href="#trunkSourceWebCoreplatformmediastreammacRealtimeOutgoingAudioSourcecpp">trunk/Source/WebCore/platform/mediastream/mac/RealtimeOutgoingAudioSource.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformmediastreammacRealtimeOutgoingAudioSourceh">trunk/Source/WebCore/platform/mediastream/mac/RealtimeOutgoingAudioSource.h</a></li>
<li><a href="#trunkSourceWebCoreplatformmediastreammacRealtimeOutgoingVideoSourcecpp">trunk/Source/WebCore/platform/mediastream/mac/RealtimeOutgoingVideoSource.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformmediastreammacRealtimeOutgoingVideoSourceh">trunk/Source/WebCore/platform/mediastream/mac/RealtimeOutgoingVideoSource.h</a></li>
<li><a href="#trunkSourceWebCoreplatformmockMockRealtimeVideoSourcecpp">trunk/Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestswebrtcaudioreplacetrackexpectedtxt">trunk/LayoutTests/webrtc/audio-replace-track-expected.txt</a></li>
<li><a href="#trunkLayoutTestswebrtcaudioreplacetrackhtml">trunk/LayoutTests/webrtc/audio-replace-track.html</a></li>
<li><a href="#trunkLayoutTestswebrtcvideoreplacetrackexpectedtxt">trunk/LayoutTests/webrtc/video-replace-track-expected.txt</a></li>
<li><a href="#trunkLayoutTestswebrtcvideoreplacetracktonullexpectedtxt">trunk/LayoutTests/webrtc/video-replace-track-to-null-expected.txt</a></li>
<li><a href="#trunkLayoutTestswebrtcvideoreplacetracktonullhtml">trunk/LayoutTests/webrtc/video-replace-track-to-null.html</a></li>
<li><a href="#trunkLayoutTestswebrtcvideoreplacetrackhtml">trunk/LayoutTests/webrtc/video-replace-track.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (214356 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2017-03-24 17:46:36 UTC (rev 214356)
+++ trunk/LayoutTests/ChangeLog        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -1,3 +1,17 @@
</span><ins>+2017-03-24  Youenn Fablet  &lt;youenn@apple.com&gt;
+
+        Add libwebrtc backend support for RTCRtpSender::replaceTrack
+        https://bugs.webkit.org/show_bug.cgi?id=169841
+
+        Reviewed by Alex Christensen.
+
+        * webrtc/audio-replace-track-expected.txt: Added.
+        * webrtc/audio-replace-track.html: Added.
+        * webrtc/video-replace-track-expected.txt: Added.
+        * webrtc/video-replace-track.html: Added.
+        * webrtc/video-replace-track-to-null-expected.txt: Added.
+        * webrtc/video-replace-track-to-null.html: Added.
+
</ins><span class="cx"> 2017-03-24  Ryan Haddad  &lt;ryanhaddad@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Update TestExpectations for media/restore-from-page-cache.html.
</span></span></pre></div>
<a id="trunkLayoutTestswebrtcaudioreplacetrackexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/webrtc/audio-replace-track-expected.txt (0 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/webrtc/audio-replace-track-expected.txt                                (rev 0)
+++ trunk/LayoutTests/webrtc/audio-replace-track-expected.txt        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -0,0 +1,3 @@
</span><ins>+
+PASS Replacing audio track from a peer connection 
+
</ins></span></pre></div>
<a id="trunkLayoutTestswebrtcaudioreplacetrackhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/webrtc/audio-replace-track.html (0 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/webrtc/audio-replace-track.html                                (rev 0)
+++ trunk/LayoutTests/webrtc/audio-replace-track.html        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -0,0 +1,52 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;head&gt;
+    &lt;meta charset=&quot;utf-8&quot;&gt;
+    &lt;title&gt;Testing local audio capture playback causes &quot;playing&quot; event to fire&lt;/title&gt;
+    &lt;script src=&quot;../resources/testharness.js&quot;&gt;&lt;/script&gt;
+    &lt;script src=&quot;../resources/testharnessreport.js&quot;&gt;&lt;/script&gt;
+    &lt;script src =&quot;routines.js&quot;&gt;&lt;/script&gt;
+    &lt;script&gt;
+    promise_test((test) =&gt; {
+        if (window.testRunner)
+            testRunner.setUserMediaPermission(true);
+
+        var sender;
+        var firsStream;
+        var secondStream;
+        var remoteStream;
+        return navigator.mediaDevices.getUserMedia({ audio: { sampleRate: { exact: 48000 } } }).then((stream) =&gt; {
+            firstStream = stream;
+            return navigator.mediaDevices.getUserMedia({ audio: { sampleRate: { exact: 48000 } } });
+        }).then((stream) =&gt; {
+            secondStream = stream;
+            if (window.internals)
+                internals.useMockRTCPeerConnectionFactory(&quot;TwoRealPeerConnections&quot;);
+            return new Promise((resolve, reject) =&gt; {
+                createConnections((firstConnection) =&gt; {
+                    sender = firstConnection.addTrack(firstStream.getAudioTracks()[0], firstStream);
+                }, (secondConnection) =&gt; {
+                    secondConnection.ontrack = (trackEvent) =&gt; { resolve(trackEvent.streams[0]); };
+                });
+                setTimeout(() =&gt; reject(&quot;Test timed out&quot;), 5000);
+            });
+        }).then((stream) =&gt; {
+            remoteStream = stream;
+            return analyseAudio(remoteStream, 1000);
+        }).then((results) =&gt; {
+            assert_true(results.heardHum, &quot;heard hum&quot;);
+            return sender.replaceTrack(secondStream.getAudioTracks()[0], secondStream);
+        }).then(() =&gt; {
+            assert_true(sender.track === secondStream.getAudioTracks()[0]);
+            return waitFor(500);
+        }).then((results) =&gt; {
+            return analyseAudio(remoteStream, 1000);
+        }).then((results) =&gt; {
+            assert_true(results.heardHum, &quot;heard hum&quot;);
+        });
+    }, &quot;Replacing audio track from a peer connection&quot;);
+    &lt;/script&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestswebrtcvideoreplacetrackexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/webrtc/video-replace-track-expected.txt (0 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/webrtc/video-replace-track-expected.txt                                (rev 0)
+++ trunk/LayoutTests/webrtc/video-replace-track-expected.txt        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -0,0 +1,5 @@
</span><ins>+
+PASS Switching from front to back camera 
+FAIL Switching from front to back camera, with lower resolution assert_true: backStream should be smaller expected true got false
+FAIL Switching from front to back camera, with higher resolution assert_true: front stream should be smaller expected true got false
+
</ins></span></pre></div>
<a id="trunkLayoutTestswebrtcvideoreplacetracktonullexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/webrtc/video-replace-track-to-null-expected.txt (0 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/webrtc/video-replace-track-to-null-expected.txt                                (rev 0)
+++ trunk/LayoutTests/webrtc/video-replace-track-to-null-expected.txt        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -0,0 +1,3 @@
</span><ins>+
+PASS Stopping sending video using replaceTrack 
+
</ins></span></pre></div>
<a id="trunkLayoutTestswebrtcvideoreplacetracktonullhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/webrtc/video-replace-track-to-null.html (0 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/webrtc/video-replace-track-to-null.html                                (rev 0)
+++ trunk/LayoutTests/webrtc/video-replace-track-to-null.html        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -0,0 +1,103 @@
</span><ins>+&lt;!doctype html&gt;
+&lt;html&gt;
+    &lt;head&gt;
+        &lt;meta charset=&quot;utf-8&quot;&gt;
+        &lt;title&gt;Testing basic video exchange from offerer to receiver&lt;/title&gt;
+        &lt;script src=&quot;../resources/testharness.js&quot;&gt;&lt;/script&gt;
+        &lt;script src=&quot;../resources/testharnessreport.js&quot;&gt;&lt;/script&gt;
+    &lt;/head&gt;
+    &lt;body&gt;
+&lt;div id=&quot;log&quot;&gt;&lt;/div&gt;
+        &lt;video id=&quot;video&quot; autoplay=&quot;&quot;&gt;&lt;/video&gt;
+        &lt;canvas id=&quot;canvas&quot; width=&quot;640&quot; height=&quot;480&quot;&gt;&lt;/canvas&gt;
+        &lt;script src =&quot;routines.js&quot;&gt;&lt;/script&gt;
+        &lt;script&gt;
+video = document.getElementById(&quot;video&quot;);
+canvas = document.getElementById(&quot;canvas&quot;);
+
+function grabImagePixels()
+{
+    canvas.width = video.videoWidth;
+    canvas.height = video.videoHeight;
+    canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
+
+    imageData = canvas.getContext('2d').getImageData(20, 20, 100, 100);
+    return imageData.data;
+ }
+
+var firstFrameData;
+
+function storeFrame()
+{
+    firstFrameData = grabImagePixels();
+}
+
+function testCameraImage()
+{
+    data = grabImagePixels();
+
+    assert_true(data[0] &lt; 20);
+    assert_true(data[1] &lt; 20);
+    assert_true(data[2] &lt; 20);
+
+    var same = true;
+    for (var cptr = 0; cptr &lt; data.length; ++cptr) {
+        if (data[cptr] != firstFrameData[cptr]) {
+            same = false;
+            break;
+        }
+    }
+    assert_false(same);
+}
+
+function testStoppedImage()
+{
+    assert_array_equals(grabImagePixels(), firstFrameData);
+}
+
+promise_test((test) =&gt; {
+    if (window.testRunner)
+        testRunner.setUserMediaPermission(true);
+
+    var sender;
+    var frontStream;
+    return navigator.mediaDevices.getUserMedia({ video: true }).then((stream) =&gt; {
+        frontStream = stream;
+    }).then(() =&gt; {
+        return new Promise((resolve, reject) =&gt; {
+            if (window.internals)
+                internals.useMockRTCPeerConnectionFactory(&quot;TwoRealPeerConnections&quot;);
+
+            createConnections((firstConnection) =&gt; {
+                sender = firstConnection.addTrack(frontStream.getVideoTracks()[0], frontStream);
+            }, (secondConnection) =&gt; {
+                secondConnection.ontrack = (trackEvent) =&gt; {
+                    resolve(trackEvent.streams[0]);
+                };
+            });
+            setTimeout(() =&gt; reject(&quot;Test timed out&quot;), 5000);
+        });
+    }).then((remoteStream) =&gt; {
+        video.srcObject = remoteStream;
+        return video.play();
+    }).then(() =&gt; {
+        storeFrame();
+        return waitFor(100);
+    }).then(() =&gt; {
+        testCameraImage();
+    }).then(() =&gt; {
+        promise = sender.replaceTrack(null);
+        assert_true(!!sender.track);
+        return promise;
+    }).then(() =&gt; {
+        return waitFor(100);
+    }).then(() =&gt; {
+        storeFrame();
+        return waitFor(100);
+    }).then(() =&gt; {
+        testStoppedImage();
+    });
+}, &quot;Stopping sending video using replaceTrack&quot;);
+        &lt;/script&gt;
+    &lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestswebrtcvideoreplacetrackhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/webrtc/video-replace-track.html (0 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/webrtc/video-replace-track.html                                (rev 0)
+++ trunk/LayoutTests/webrtc/video-replace-track.html        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -0,0 +1,181 @@
</span><ins>+&lt;!doctype html&gt;
+&lt;html&gt;
+    &lt;head&gt;
+        &lt;meta charset=&quot;utf-8&quot;&gt;
+        &lt;title&gt;Testing basic video exchange from offerer to receiver&lt;/title&gt;
+        &lt;script src=&quot;../resources/testharness.js&quot;&gt;&lt;/script&gt;
+        &lt;script src=&quot;../resources/testharnessreport.js&quot;&gt;&lt;/script&gt;
+    &lt;/head&gt;
+    &lt;body&gt;
+&lt;div id=&quot;log&quot;&gt;&lt;/div&gt;
+        &lt;video id=&quot;video&quot; autoplay=&quot;&quot;&gt;&lt;/video&gt;
+        &lt;canvas id=&quot;canvas&quot; width=&quot;640&quot; height=&quot;480&quot;&gt;&lt;/canvas&gt;
+        &lt;script src =&quot;routines.js&quot;&gt;&lt;/script&gt;
+        &lt;script&gt;
+video = document.getElementById(&quot;video&quot;);
+canvas = document.getElementById(&quot;canvas&quot;);
+
+function grabImagePixels()
+{
+    canvas.width = video.videoWidth;
+    canvas.height = video.videoHeight;
+    canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
+
+    imageData = canvas.getContext('2d').getImageData(20, 20, 2, 2);
+    return imageData.data;
+ }
+
+function testFrontCameraImage()
+{
+    data = grabImagePixels();
+
+    assert_true(data[0] &lt; 20);
+    assert_true(data[1] &lt; 20);
+    assert_true(data[2] &lt; 20);
+}
+
+function testBackCameraImage()
+{
+    data = grabImagePixels();
+
+    assert_true(data[0] &gt; 100);
+    assert_true(data[1] &gt; 100);
+    assert_true(data[2] &gt; 100);
+
+    assert_true(data[0] &lt; 200);
+    assert_true(data[1] &lt; 200);
+    assert_true(data[2] &lt; 200);
+}
+
+promise_test((test) =&gt; {
+    if (window.testRunner)
+        testRunner.setUserMediaPermission(true);
+
+    var sender;
+    var frontStream;
+    var backStream;
+    return navigator.mediaDevices.getUserMedia({ video: { facingMode: { exact: [&quot;user&quot;] } } }).then((stream) =&gt; {
+        frontStream = stream;
+        return navigator.mediaDevices.getUserMedia({ video: { facingMode: { exact: [&quot;environment&quot;] } } });
+    }).then((stream) =&gt; {
+        backStream = stream;
+    }).then(() =&gt; {
+        return new Promise((resolve, reject) =&gt; {
+            if (window.internals)
+                internals.useMockRTCPeerConnectionFactory(&quot;TwoRealPeerConnections&quot;);
+
+            createConnections((firstConnection) =&gt; {
+                sender = firstConnection.addTrack(frontStream.getVideoTracks()[0], frontStream);
+            }, (secondConnection) =&gt; {
+                secondConnection.ontrack = (trackEvent) =&gt; {
+                    resolve(trackEvent.streams[0]);
+                };
+            });
+            setTimeout(() =&gt; reject(&quot;Test timed out&quot;), 5000);
+        });
+    }).then((remoteStream) =&gt; {
+        video.srcObject = remoteStream;
+        return video.play();
+    }).then(() =&gt; {
+        testFrontCameraImage();
+    }).then(() =&gt; {
+        var currentTrack = sender.track;
+        promise = sender.replaceTrack(backStream.getVideoTracks()[0]);
+        assert_true(currentTrack === sender.track);
+        return promise;
+    }).then(() =&gt; {
+        assert_true(sender.track === backStream.getVideoTracks()[0]);
+        return waitFor(500);
+    }).then(() =&gt; {
+        testBackCameraImage();
+    });
+}, &quot;Switching from front to back camera&quot;);
+
+promise_test((test) =&gt; {
+    if (window.testRunner)
+        testRunner.setUserMediaPermission(true);
+
+    var sender;
+    var frontStream;
+    var backStream;
+
+    return navigator.mediaDevices.getUserMedia({ video: { height: { min: 400 }, facingMode: { exact: [&quot;user&quot;] } } }).then((stream) =&gt; {
+        frontStream = stream;
+        return navigator.mediaDevices.getUserMedia({ video: { height: { max: 400 }, facingMode: { exact: [&quot;environment&quot;] } } });
+    }).then((stream) =&gt; {
+        backStream = stream;
+        assert_true(frontStream.getVideoTracks()[0].getSettings().height &gt;= 400, &quot;frontStream should be bigger&quot;);
+        assert_true(backStream.getVideoTracks()[0].getSettings().height &lt; 400, &quot;backStream should be smaller&quot;);
+    }).then(() =&gt; {
+        return new Promise((resolve, reject) =&gt; {
+            if (window.internals)
+                internals.useMockRTCPeerConnectionFactory(&quot;TwoRealPeerConnections&quot;);
+
+            createConnections((firstConnection) =&gt; {
+                sender = firstConnection.addTrack(frontStream.getVideoTracks()[0], frontStream);
+            }, (secondConnection) =&gt; {
+                secondConnection.ontrack = (trackEvent) =&gt; {
+                    resolve(trackEvent.streams[0]);
+                };
+            });
+            setTimeout(() =&gt; reject(&quot;Test timed out&quot;), 5000);
+        });
+    }).then((remoteStream) =&gt; {
+        video.srcObject = remoteStream;
+        return video.play();
+    }).then(() =&gt; {
+        testFrontCameraImage();
+    }).then(() =&gt; {
+        return sender.replaceTrack(backStream.getVideoTracks()[0]);
+    }).then(() =&gt; {
+        return waitFor(500);
+    }).then(() =&gt; {
+        testBackCameraImage();
+    });
+}, &quot;Switching from front to back camera, with lower resolution&quot;);
+
+promise_test((test) =&gt; {
+    if (window.testRunner)
+        testRunner.setUserMediaPermission(true);
+
+    var sender;
+    var frontStream;
+    var backStream;
+
+    return navigator.mediaDevices.getUserMedia({ video: { height: { max: 400 }, facingMode: { exact: [&quot;user&quot;] } } }).then((stream) =&gt; {
+        frontStream = stream;
+        return navigator.mediaDevices.getUserMedia({ video: { height: { min: 400 }, facingMode: { exact: [&quot;environment&quot;] } } });
+    }).then((stream) =&gt; {
+        backStream = stream;
+        assert_true(frontStream.getVideoTracks()[0].getSettings().height &lt; 400, &quot;front stream should be smaller&quot;);
+        assert_true(backStream.getVideoTracks()[0].getSettings().height &gt;= 400, &quot;back stream should be bigger&quot;);
+    }).then(() =&gt; {
+        return new Promise((resolve, reject) =&gt; {
+            if (window.internals)
+                internals.useMockRTCPeerConnectionFactory(&quot;TwoRealPeerConnections&quot;);
+
+            createConnections((firstConnection) =&gt; {
+                sender = firstConnection.addTrack(frontStream.getVideoTracks()[0], frontStream);
+            }, (secondConnection) =&gt; {
+                secondConnection.ontrack = (trackEvent) =&gt; {
+                    resolve(trackEvent.streams[0]);
+                };
+            });
+            setTimeout(() =&gt; reject(&quot;Test timed out&quot;), 5000);
+        });
+    }).then((remoteStream) =&gt; {
+        video.srcObject = remoteStream;
+        return video.play();
+    }).then(() =&gt; {
+        testFrontCameraImage();
+    }).then(() =&gt; {
+        return promise_rejects(test, &quot;InvalidModificationError&quot;, sender.replaceTrack(backStream.getVideoTracks()[0]));
+    }).then(() =&gt; {
+        return waitFor(500);
+    }).then(() =&gt; {
+        testBackCameraImage();
+    });
+}, &quot;Switching from front to back camera, with higher resolution&quot;);
+        &lt;/script&gt;
+    &lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (214356 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2017-03-24 17:46:36 UTC (rev 214356)
+++ trunk/Source/WebCore/ChangeLog        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -1,3 +1,37 @@
</span><ins>+2017-03-24  Youenn Fablet  &lt;youenn@apple.com&gt;
+
+        Add libwebrtc backend support for RTCRtpSender::replaceTrack
+        https://bugs.webkit.org/show_bug.cgi?id=169841
+
+        Reviewed by Alex Christensen.
+
+        Tests: webrtc/audio-replace-track.html
+               webrtc/video-replace-track.html
+
+        Adding support for replaceTrack for audio and video sources.
+        Replacing tracks will always succeed for audio sources.
+        For video tracks, it will only succeed if the video resolution is not greater.
+        LibWebRTCPeerConnectionBackend will delegate the track replacing by replacing the source of the outgoing sources with the source wrapped in the replacing track.
+
+        Video test is not fully passing as size constraints for mock video sources are not providing the right video stream resolution.
+
+        * Modules/mediastream/RTCRtpSender.cpp:
+        (WebCore::RTCRtpSender::replaceTrack):
+        * Modules/mediastream/RTCRtpSender.h:
+        * Modules/mediastream/RTCRtpSender.idl:
+        * Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp:
+        (WebCore::LibWebRTCPeerConnectionBackend::replaceTrack):
+        * Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h:
+        * platform/mediastream/mac/RealtimeOutgoingAudioSource.cpp:
+        (WebCore::RealtimeOutgoingAudioSource::setSource):
+        * platform/mediastream/mac/RealtimeOutgoingAudioSource.h:
+        * platform/mediastream/mac/RealtimeOutgoingVideoSource.cpp:
+        (WebCore::RealtimeOutgoingVideoSource::setSource):
+        * platform/mediastream/mac/RealtimeOutgoingVideoSource.h:
+        * platform/mock/MockRealtimeVideoSource.cpp:
+        (WebCore::MockRealtimeVideoSource::drawText):
+        (WebCore::MockRealtimeVideoSource::generateFrame):
+
</ins><span class="cx"> 2017-03-24  Jon Lee  &lt;jonlee@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Remove comment from RTCStatsReport.idl to convert ssrc to DOMString.
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmediastreamRTCPeerConnectioncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/mediastream/RTCPeerConnection.cpp (214356 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/mediastream/RTCPeerConnection.cpp        2017-03-24 17:46:36 UTC (rev 214356)
+++ trunk/Source/WebCore/Modules/mediastream/RTCPeerConnection.cpp        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -496,11 +496,32 @@
</span><span class="cx">     dispatchEvent(event);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void RTCPeerConnection::replaceTrack(RTCRtpSender&amp; sender, Ref&lt;MediaStreamTrack&gt;&amp;&amp; withTrack, DOMPromise&lt;void&gt;&amp;&amp; promise)
</del><ins>+void RTCPeerConnection::enqueueReplaceTrackTask(RTCRtpSender&amp; sender, Ref&lt;MediaStreamTrack&gt;&amp;&amp; withTrack, DOMPromise&lt;void&gt;&amp;&amp; promise)
</ins><span class="cx"> {
</span><del>-    m_backend-&gt;replaceTrack(sender, WTFMove(withTrack), WTFMove(promise));
</del><ins>+    scriptExecutionContext()-&gt;postTask([protectedSender = makeRef(sender), promise = WTFMove(promise), withTrack = WTFMove(withTrack)](ScriptExecutionContext&amp;) mutable {
+        protectedSender-&gt;setTrack(WTFMove(withTrack));
+        promise.resolve();
+    });
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void RTCPeerConnection::replaceTrack(RTCRtpSender&amp; sender, RefPtr&lt;MediaStreamTrack&gt;&amp;&amp; withTrack, DOMPromise&lt;void&gt;&amp;&amp; promise)
+{
+    if (!withTrack) {
+        scriptExecutionContext()-&gt;postTask([protectedSender = makeRef(sender), promise = WTFMove(promise)](ScriptExecutionContext&amp;) mutable {
+            protectedSender-&gt;setTrackToNull();
+            promise.resolve();
+        });
+        return;
+    }
+    
+    if (!sender.track()) {
+        enqueueReplaceTrackTask(sender, withTrack.releaseNonNull(), WTFMove(promise));
+        return;
+    }
+
+    m_backend-&gt;replaceTrack(sender, withTrack.releaseNonNull(), WTFMove(promise));
+}
+
</ins><span class="cx"> } // namespace WebCore
</span><span class="cx"> 
</span><span class="cx"> #endif // ENABLE(WEB_RTC)
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmediastreamRTCPeerConnectionh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/mediastream/RTCPeerConnection.h (214356 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/mediastream/RTCPeerConnection.h        2017-03-24 17:46:36 UTC (rev 214356)
+++ trunk/Source/WebCore/Modules/mediastream/RTCPeerConnection.h        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -140,6 +140,8 @@
</span><span class="cx">     void disableICECandidateFiltering() { m_backend-&gt;disableICECandidateFiltering(); }
</span><span class="cx">     void enableICECandidateFiltering() { m_backend-&gt;enableICECandidateFiltering(); }
</span><span class="cx"> 
</span><ins>+    void enqueueReplaceTrackTask(RTCRtpSender&amp;, Ref&lt;MediaStreamTrack&gt;&amp;&amp;, DOMPromise&lt;void&gt;&amp;&amp;);
+
</ins><span class="cx"> private:
</span><span class="cx">     RTCPeerConnection(ScriptExecutionContext&amp;);
</span><span class="cx"> 
</span><span class="lines">@@ -159,7 +161,7 @@
</span><span class="cx">     bool canSuspendForDocumentSuspension() const final;
</span><span class="cx"> 
</span><span class="cx">     // RTCRtpSenderClient
</span><del>-    void replaceTrack(RTCRtpSender&amp;, Ref&lt;MediaStreamTrack&gt;&amp;&amp;, DOMPromise&lt;void&gt;&amp;&amp;) final;
</del><ins>+    void replaceTrack(RTCRtpSender&amp;, RefPtr&lt;MediaStreamTrack&gt;&amp;&amp;, DOMPromise&lt;void&gt;&amp;&amp;) final;
</ins><span class="cx"> 
</span><span class="cx">     void updateConnectionState();
</span><span class="cx">     bool doClose();
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmediastreamRTCRtpSendercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/mediastream/RTCRtpSender.cpp (214356 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/mediastream/RTCRtpSender.cpp        2017-03-24 17:46:36 UTC (rev 214356)
+++ trunk/Source/WebCore/Modules/mediastream/RTCRtpSender.cpp        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -57,27 +57,37 @@
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void RTCRtpSender::setTrackToNull()
+{
+    ASSERT(m_track);
+    m_trackId = { };
+    m_track = nullptr;
+}
+
</ins><span class="cx"> void RTCRtpSender::setTrack(Ref&lt;MediaStreamTrack&gt;&amp;&amp; track)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(!isStopped());
</span><del>-    ASSERT(!m_track);
-    m_trackId = track-&gt;id();
</del><ins>+    if (!m_track)
+        m_trackId = track-&gt;id();
</ins><span class="cx">     m_track = WTFMove(track);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-ExceptionOr&lt;void&gt; RTCRtpSender::replaceTrack(Ref&lt;MediaStreamTrack&gt;&amp;&amp; withTrack, DOMPromise&lt;void&gt;&amp;&amp; promise)
</del><ins>+void RTCRtpSender::replaceTrack(RefPtr&lt;MediaStreamTrack&gt;&amp;&amp; withTrack, DOMPromise&lt;void&gt;&amp;&amp; promise)
</ins><span class="cx"> {
</span><span class="cx">     if (isStopped()) {
</span><span class="cx">         promise.reject(INVALID_STATE_ERR);
</span><del>-        return { };
</del><ins>+        return;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (m_trackKind != withTrack-&gt;kind())
-        return Exception { TypeError };
</del><ins>+    if (withTrack &amp;&amp; m_trackKind != withTrack-&gt;kind()) {
+        promise.reject(TypeError);
+        return;
+    }
</ins><span class="cx"> 
</span><ins>+    if (!withTrack &amp;&amp; m_track)
+        m_track-&gt;stopProducingData();
+
</ins><span class="cx">     m_client-&gt;replaceTrack(*this, WTFMove(withTrack), WTFMove(promise));
</span><del>-
-    return { };
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmediastreamRTCRtpSenderh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/mediastream/RTCRtpSender.h (214356 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/mediastream/RTCRtpSender.h        2017-03-24 17:46:36 UTC (rev 214356)
+++ trunk/Source/WebCore/Modules/mediastream/RTCRtpSender.h        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -39,7 +39,7 @@
</span><span class="cx"> 
</span><span class="cx"> class RTCRtpSenderClient {
</span><span class="cx"> public:
</span><del>-    virtual void replaceTrack(RTCRtpSender&amp;, Ref&lt;MediaStreamTrack&gt;&amp;&amp;, DOMPromise&lt;void&gt;&amp;&amp;) = 0;
</del><ins>+    virtual void replaceTrack(RTCRtpSender&amp;, RefPtr&lt;MediaStreamTrack&gt;&amp;&amp;, DOMPromise&lt;void&gt;&amp;&amp;) = 0;
</ins><span class="cx"> 
</span><span class="cx">     virtual ~RTCRtpSenderClient() { }
</span><span class="cx"> };
</span><span class="lines">@@ -58,8 +58,9 @@
</span><span class="cx">     bool isStopped() const { return !m_client; }
</span><span class="cx">     void stop() { m_client = nullptr; }
</span><span class="cx">     void setTrack(Ref&lt;MediaStreamTrack&gt;&amp;&amp;);
</span><ins>+    void setTrackToNull();
</ins><span class="cx"> 
</span><del>-    ExceptionOr&lt;void&gt; replaceTrack(Ref&lt;MediaStreamTrack&gt;&amp;&amp;, DOMPromise&lt;void&gt;&amp;&amp;);
</del><ins>+    void replaceTrack(RefPtr&lt;MediaStreamTrack&gt;&amp;&amp;, DOMPromise&lt;void&gt;&amp;&amp;);
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     RTCRtpSender(String&amp;&amp; trackKind, Vector&lt;String&gt;&amp;&amp; mediaStreamIds, RTCRtpSenderClient&amp;);
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmediastreamRTCRtpSenderidl"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/mediastream/RTCRtpSender.idl (214356 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/mediastream/RTCRtpSender.idl        2017-03-24 17:46:36 UTC (rev 214356)
+++ trunk/Source/WebCore/Modules/mediastream/RTCRtpSender.idl        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -39,5 +39,5 @@
</span><span class="cx">     // FIXME 169662: missing getCapabilities
</span><span class="cx">     // FIXME 169662: missing setParameters
</span><span class="cx">     // FIXME 169662: missing getParameters
</span><del>-    [MayThrowException] Promise&lt;void&gt; replaceTrack(MediaStreamTrack withTrack);
</del><ins>+    Promise&lt;void&gt; replaceTrack(MediaStreamTrack? withTrack);
</ins><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmediastreamlibwebrtcLibWebRTCPeerConnectionBackendcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp (214356 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp        2017-03-24 17:46:36 UTC (rev 214356)
+++ trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -28,6 +28,7 @@
</span><span class="cx"> #if USE(LIBWEBRTC)
</span><span class="cx"> 
</span><span class="cx"> #include &quot;Document.h&quot;
</span><ins>+#include &quot;ExceptionCode.h&quot;
</ins><span class="cx"> #include &quot;IceCandidate.h&quot;
</span><span class="cx"> #include &quot;JSRTCStatsReport.h&quot;
</span><span class="cx"> #include &quot;LibWebRTCDataChannelHandler.h&quot;
</span><span class="lines">@@ -321,6 +322,48 @@
</span><span class="cx">     m_remoteStreams.append(WTFMove(mediaStream));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void LibWebRTCPeerConnectionBackend::replaceTrack(RTCRtpSender&amp; sender, Ref&lt;MediaStreamTrack&gt;&amp;&amp; track, DOMPromise&lt;void&gt;&amp;&amp; promise)
+{
+    ASSERT(sender.track());
+    auto* currentTrack = sender.track();
+
+    ASSERT(currentTrack-&gt;source().type() == track-&gt;source().type());
+    switch (currentTrack-&gt;source().type()) {
+    case RealtimeMediaSource::Type::None:
+        ASSERT_NOT_REACHED();
+        promise.reject(INVALID_MODIFICATION_ERR);
+        break;
+    case RealtimeMediaSource::Type::Audio: {
+        for (auto&amp; audioSource : m_audioSources) {
+            if (&amp;audioSource-&gt;source() == &amp;currentTrack-&gt;source()) {
+                if (!audioSource-&gt;setSource(track-&gt;source())) {
+                    promise.reject(INVALID_MODIFICATION_ERR);
+                    return;
+                }
+                connection().enqueueReplaceTrackTask(sender, WTFMove(track), WTFMove(promise));
+                return;
+            }
+        }
+        promise.reject(INVALID_MODIFICATION_ERR);
+        break;
+    }
+    case RealtimeMediaSource::Type::Video: {
+        for (auto&amp; videoSource : m_videoSources) {
+            if (&amp;videoSource-&gt;source() == &amp;currentTrack-&gt;source()) {
+                if (!videoSource-&gt;setSource(track-&gt;source())) {
+                    promise.reject(INVALID_MODIFICATION_ERR);
+                    return;
+                }
+                connection().enqueueReplaceTrackTask(sender, WTFMove(track), WTFMove(promise));
+                return;
+            }
+        }
+        promise.reject(INVALID_MODIFICATION_ERR);
+        break;
+    }
+    }
+}
+
</ins><span class="cx"> } // namespace WebCore
</span><span class="cx"> 
</span><span class="cx"> #endif // USE(LIBWEBRTC)
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmediastreamlibwebrtcLibWebRTCPeerConnectionBackendh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h (214356 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h        2017-03-24 17:46:36 UTC (rev 214356)
+++ trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -69,8 +69,7 @@
</span><span class="cx">     RefPtr&lt;RTCSessionDescription&gt; currentRemoteDescription() const final;
</span><span class="cx">     RefPtr&lt;RTCSessionDescription&gt; pendingRemoteDescription() const final;
</span><span class="cx"> 
</span><del>-    // FIXME: API to implement for real
-    void replaceTrack(RTCRtpSender&amp;, Ref&lt;MediaStreamTrack&gt;&amp;&amp;, DOMPromise&lt;void&gt;&amp;&amp;) final { }
</del><ins>+    void replaceTrack(RTCRtpSender&amp;, Ref&lt;MediaStreamTrack&gt;&amp;&amp;, DOMPromise&lt;void&gt;&amp;&amp;) final;
</ins><span class="cx"> 
</span><span class="cx">     void emulatePlatformEvent(const String&amp;) final { }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformmediastreammacRealtimeOutgoingAudioSourcecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/mediastream/mac/RealtimeOutgoingAudioSource.cpp (214356 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/mediastream/mac/RealtimeOutgoingAudioSource.cpp        2017-03-24 17:46:36 UTC (rev 214356)
+++ trunk/Source/WebCore/platform/mediastream/mac/RealtimeOutgoingAudioSource.cpp        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -51,6 +51,14 @@
</span><span class="cx">     m_audioSource-&gt;addObserver(*this);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool RealtimeOutgoingAudioSource::setSource(Ref&lt;RealtimeMediaSource&gt;&amp;&amp; newSource)
+{
+    m_audioSource-&gt;removeObserver(*this);
+    m_audioSource = WTFMove(newSource);
+    m_audioSource-&gt;addObserver(*this);
+    return true;
+}
+
</ins><span class="cx"> void RealtimeOutgoingAudioSource::stop()
</span><span class="cx"> {
</span><span class="cx">     m_audioSource-&gt;removeObserver(*this);
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformmediastreammacRealtimeOutgoingAudioSourceh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/mediastream/mac/RealtimeOutgoingAudioSource.h (214356 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/mediastream/mac/RealtimeOutgoingAudioSource.h        2017-03-24 17:46:36 UTC (rev 214356)
+++ trunk/Source/WebCore/platform/mediastream/mac/RealtimeOutgoingAudioSource.h        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -51,6 +51,9 @@
</span><span class="cx">     void setTrack(rtc::scoped_refptr&lt;webrtc::AudioTrackInterface&gt;&amp;&amp; track) { m_track = WTFMove(track); }
</span><span class="cx">     void stop();
</span><span class="cx"> 
</span><ins>+    bool setSource(Ref&lt;RealtimeMediaSource&gt;&amp;&amp;);
+    RealtimeMediaSource&amp; source() const { return m_audioSource.get(); }
+
</ins><span class="cx"> private:
</span><span class="cx">     explicit RealtimeOutgoingAudioSource(Ref&lt;RealtimeMediaSource&gt;&amp;&amp;);
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformmediastreammacRealtimeOutgoingVideoSourcecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/mediastream/mac/RealtimeOutgoingVideoSource.cpp (214356 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/mediastream/mac/RealtimeOutgoingVideoSource.cpp        2017-03-24 17:46:36 UTC (rev 214356)
+++ trunk/Source/WebCore/platform/mediastream/mac/RealtimeOutgoingVideoSource.cpp        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -46,6 +46,22 @@
</span><span class="cx">     m_videoSource-&gt;addObserver(*this);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool RealtimeOutgoingVideoSource::setSource(Ref&lt;RealtimeMediaSource&gt;&amp;&amp; newSource)
+{
+    if (!m_initialSettings)
+        m_initialSettings = m_videoSource-&gt;settings();
+
+    auto newSettings = newSource-&gt;settings();
+
+    if (m_initialSettings-&gt;width() &lt; newSettings.width() || m_initialSettings-&gt;height() &lt; newSettings.height())
+        return false;
+
+    m_videoSource-&gt;removeObserver(*this);
+    m_videoSource = WTFMove(newSource);
+    m_videoSource-&gt;addObserver(*this);
+    return true;
+}
+
</ins><span class="cx"> void RealtimeOutgoingVideoSource::stop()
</span><span class="cx"> {
</span><span class="cx">     m_videoSource-&gt;removeObserver(*this);
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformmediastreammacRealtimeOutgoingVideoSourceh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/mediastream/mac/RealtimeOutgoingVideoSource.h (214356 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/mediastream/mac/RealtimeOutgoingVideoSource.h        2017-03-24 17:46:36 UTC (rev 214356)
+++ trunk/Source/WebCore/platform/mediastream/mac/RealtimeOutgoingVideoSource.h        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -36,6 +36,7 @@
</span><span class="cx"> #include &lt;webrtc/base/optional.h&gt;
</span><span class="cx"> #include &lt;webrtc/common_video/include/i420_buffer_pool.h&gt;
</span><span class="cx"> #include &lt;webrtc/media/base/videosinkinterface.h&gt;
</span><ins>+#include &lt;wtf/Optional.h&gt;
</ins><span class="cx"> #include &lt;wtf/ThreadSafeRefCounted.h&gt;
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="lines">@@ -46,6 +47,8 @@
</span><span class="cx">     ~RealtimeOutgoingVideoSource() { stop(); }
</span><span class="cx"> 
</span><span class="cx">     void stop();
</span><ins>+    bool setSource(Ref&lt;RealtimeMediaSource&gt;&amp;&amp;);
+    RealtimeMediaSource&amp; source() const { return m_videoSource.get(); }
</ins><span class="cx"> 
</span><span class="cx">     int AddRef() const final { ref(); return refCount(); }
</span><span class="cx">     int Release() const final { deref(); return refCount(); }
</span><span class="lines">@@ -82,6 +85,7 @@
</span><span class="cx">     Ref&lt;RealtimeMediaSource&gt; m_videoSource;
</span><span class="cx">     bool m_enabled { true };
</span><span class="cx">     bool m_muted { false };
</span><ins>+    std::optional&lt;RealtimeMediaSourceSettings&gt; m_initialSettings;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformmockMockRealtimeVideoSourcecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp (214356 => 214357)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp        2017-03-24 17:46:36 UTC (rev 214356)
+++ trunk/Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp        2017-03-24 18:01:18 UTC (rev 214357)
</span><span class="lines">@@ -327,11 +327,11 @@
</span><span class="cx">     FloatPoint bipBopLocation(size.width() * .6, size.height() * .6);
</span><span class="cx">     unsigned frameMod = m_frameNumber % 60;
</span><span class="cx">     if (frameMod &lt;= 15) {
</span><del>-        context.setFillColor(Color::gray);
</del><ins>+        context.setFillColor(Color::cyan);
</ins><span class="cx">         String bip(ASCIILiteral(&quot;Bip&quot;));
</span><span class="cx">         context.drawText(m_bipBopFont, TextRun(StringView(bip)), bipBopLocation);
</span><span class="cx">     } else if (frameMod &gt; 30 &amp;&amp; frameMod &lt;= 45) {
</span><del>-        context.setFillColor(Color::white);
</del><ins>+        context.setFillColor(Color::yellow);
</ins><span class="cx">         String bop(ASCIILiteral(&quot;Bop&quot;));
</span><span class="cx">         context.drawText(m_bipBopFont, TextRun(StringView(bop)), bipBopLocation);
</span><span class="cx">     }
</span><span class="lines">@@ -348,7 +348,7 @@
</span><span class="cx"> 
</span><span class="cx">     IntSize size = this-&gt;size();
</span><span class="cx">     FloatRect frameRect(FloatPoint(), size);
</span><del>-    context.fillRect(FloatRect(FloatPoint(), size), Color::black);
</del><ins>+    context.fillRect(FloatRect(FloatPoint(), size), facingMode() ==  RealtimeMediaSourceSettings::User ? Color::black : Color::gray);
</ins><span class="cx"> 
</span><span class="cx">     if (!m_muted &amp;&amp; m_enabled) {
</span><span class="cx">         drawText(context);
</span></span></pre>
</div>
</div>

</body>
</html>