<!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>[211255] trunk/Source/WebCore</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/211255">211255</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2017-01-26 22:09:41 -0800 (Thu, 26 Jan 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>[WebRTC] Implement WebRTC PeerConnection backend based on libwebrtc
https://bugs.webkit.org/show_bug.cgi?id=167289

Patch by Youenn Fablet &lt;youennf@gmail.com&gt; on 2017-01-26
Reviewed by Alex Christensen.

Introducing an initial implementation of WebRTC peer connection backend based on LibWebRTC.
This is done behind a compilation flag which is off by default, until the implementation is ready.

The backend implements the PeerConnectionBackend API.
The implementation is split into LibWebRTCPeerConnectionBackend which contains mostly generic code and
passes the real work to LibWebRTCMediaEndpoint.
The latter class is implementing the API using LibWebRTC.

* Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp: Added.
(WebCore::LibWebRTCMediaEndpoint::LibWebRTCMediaEndpoint):
(WebCore::sessionDescriptionType):
(WebCore::LibWebRTCMediaEndpoint::doSetLocalDescription):
(WebCore::LibWebRTCMediaEndpoint::doSetRemoteDescription):
(WebCore::LibWebRTCMediaEndpoint::addPendingIceCandidates):
(WebCore::streamId):
(WebCore::LibWebRTCMediaEndpoint::doCreateOffer):
(WebCore::LibWebRTCMediaEndpoint::doCreateAnswer):
(WebCore::LibWebRTCMediaEndpoint::getStats):
(WebCore::LibWebRTCMediaEndpoint::StatsCollector::StatsCollector):
(WebCore::LibWebRTCMediaEndpoint::StatsCollector::OnStatsDelivered):
(WebCore::signalingState):
(WebCore::LibWebRTCMediaEndpoint::OnSignalingChange):
(WebCore::trackId):
(WebCore::createMediaStreamTrack):
(WebCore::LibWebRTCMediaEndpoint::addStream):
(WebCore::LibWebRTCMediaEndpoint::OnAddStream):
(WebCore::LibWebRTCMediaEndpoint::OnRemoveStream):
(WebCore::LibWebRTCMediaEndpoint::OnDataChannel):
(WebCore::LibWebRTCMediaEndpoint::stop):
(WebCore::LibWebRTCMediaEndpoint::OnRenegotiationNeeded):
(WebCore::iceConnectionState):
(WebCore::LibWebRTCMediaEndpoint::OnIceConnectionChange):
(WebCore::LibWebRTCMediaEndpoint::OnIceGatheringChange):
(WebCore::LibWebRTCMediaEndpoint::OnIceCandidate):
(WebCore::LibWebRTCMediaEndpoint::OnIceCandidatesRemoved):
(WebCore::LibWebRTCMediaEndpoint::createSessionDescriptionSucceeded):
(WebCore::LibWebRTCMediaEndpoint::createSessionDescriptionFailed):
(WebCore::LibWebRTCMediaEndpoint::setLocalSessionDescriptionSucceeded):
(WebCore::LibWebRTCMediaEndpoint::setLocalSessionDescriptionFailed):
(WebCore::LibWebRTCMediaEndpoint::setRemoteSessionDescriptionSucceeded):
(WebCore::LibWebRTCMediaEndpoint::setRemoteSessionDescriptionFailed):
* Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.h: Added.
* Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp: Added.
(WebCore::createLibWebRTCPeerConnectionBackend):
(WebCore::libWebRTCProvider):
(WebCore::LibWebRTCPeerConnectionBackend::LibWebRTCPeerConnectionBackend):
(WebCore::configurationFromMediaEndpointConfiguration):
(WebCore::LibWebRTCPeerConnectionBackend::setConfiguration):
(WebCore::LibWebRTCPeerConnectionBackend::getStats):
(WebCore::LibWebRTCPeerConnectionBackend::doSetLocalDescription):
(WebCore::LibWebRTCPeerConnectionBackend::doSetRemoteDescription):
(WebCore::LibWebRTCPeerConnectionBackend::doCreateOffer):
(WebCore::LibWebRTCPeerConnectionBackend::doCreateAnswer):
(WebCore::LibWebRTCPeerConnectionBackend::doStop):
(WebCore::LibWebRTCPeerConnectionBackend::doAddIceCandidate):
(WebCore::LibWebRTCPeerConnectionBackend::markAsNeedingNegotiation):
(WebCore::LibWebRTCPeerConnectionBackend::createReceiver):
(WebCore::LibWebRTCPeerConnectionBackend::createDataChannelHandler):
* Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h: Added.
* WebCore.xcodeproj/project.pbxproj:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreWebCorexcodeprojprojectpbxproj">trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>trunk/Source/WebCore/Modules/mediastream/libwebrtc/</li>
<li><a href="#trunkSourceWebCoreModulesmediastreamlibwebrtcLibWebRTCMediaEndpointcpp">trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp</a></li>
<li><a href="#trunkSourceWebCoreModulesmediastreamlibwebrtcLibWebRTCMediaEndpointh">trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.h</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>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (211254 => 211255)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2017-01-27 05:36:19 UTC (rev 211254)
+++ trunk/Source/WebCore/ChangeLog        2017-01-27 06:09:41 UTC (rev 211255)
</span><span class="lines">@@ -1,3 +1,71 @@
</span><ins>+2017-01-26  Youenn Fablet  &lt;youennf@gmail.com&gt;
+
+        [WebRTC] Implement WebRTC PeerConnection backend based on libwebrtc
+        https://bugs.webkit.org/show_bug.cgi?id=167289
+
+        Reviewed by Alex Christensen.
+
+        Introducing an initial implementation of WebRTC peer connection backend based on LibWebRTC.
+        This is done behind a compilation flag which is off by default, until the implementation is ready.
+
+        The backend implements the PeerConnectionBackend API.
+        The implementation is split into LibWebRTCPeerConnectionBackend which contains mostly generic code and
+        passes the real work to LibWebRTCMediaEndpoint.
+        The latter class is implementing the API using LibWebRTC.
+
+        * Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp: Added.
+        (WebCore::LibWebRTCMediaEndpoint::LibWebRTCMediaEndpoint):
+        (WebCore::sessionDescriptionType):
+        (WebCore::LibWebRTCMediaEndpoint::doSetLocalDescription):
+        (WebCore::LibWebRTCMediaEndpoint::doSetRemoteDescription):
+        (WebCore::LibWebRTCMediaEndpoint::addPendingIceCandidates):
+        (WebCore::streamId):
+        (WebCore::LibWebRTCMediaEndpoint::doCreateOffer):
+        (WebCore::LibWebRTCMediaEndpoint::doCreateAnswer):
+        (WebCore::LibWebRTCMediaEndpoint::getStats):
+        (WebCore::LibWebRTCMediaEndpoint::StatsCollector::StatsCollector):
+        (WebCore::LibWebRTCMediaEndpoint::StatsCollector::OnStatsDelivered):
+        (WebCore::signalingState):
+        (WebCore::LibWebRTCMediaEndpoint::OnSignalingChange):
+        (WebCore::trackId):
+        (WebCore::createMediaStreamTrack):
+        (WebCore::LibWebRTCMediaEndpoint::addStream):
+        (WebCore::LibWebRTCMediaEndpoint::OnAddStream):
+        (WebCore::LibWebRTCMediaEndpoint::OnRemoveStream):
+        (WebCore::LibWebRTCMediaEndpoint::OnDataChannel):
+        (WebCore::LibWebRTCMediaEndpoint::stop):
+        (WebCore::LibWebRTCMediaEndpoint::OnRenegotiationNeeded):
+        (WebCore::iceConnectionState):
+        (WebCore::LibWebRTCMediaEndpoint::OnIceConnectionChange):
+        (WebCore::LibWebRTCMediaEndpoint::OnIceGatheringChange):
+        (WebCore::LibWebRTCMediaEndpoint::OnIceCandidate):
+        (WebCore::LibWebRTCMediaEndpoint::OnIceCandidatesRemoved):
+        (WebCore::LibWebRTCMediaEndpoint::createSessionDescriptionSucceeded):
+        (WebCore::LibWebRTCMediaEndpoint::createSessionDescriptionFailed):
+        (WebCore::LibWebRTCMediaEndpoint::setLocalSessionDescriptionSucceeded):
+        (WebCore::LibWebRTCMediaEndpoint::setLocalSessionDescriptionFailed):
+        (WebCore::LibWebRTCMediaEndpoint::setRemoteSessionDescriptionSucceeded):
+        (WebCore::LibWebRTCMediaEndpoint::setRemoteSessionDescriptionFailed):
+        * Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.h: Added.
+        * Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp: Added.
+        (WebCore::createLibWebRTCPeerConnectionBackend):
+        (WebCore::libWebRTCProvider):
+        (WebCore::LibWebRTCPeerConnectionBackend::LibWebRTCPeerConnectionBackend):
+        (WebCore::configurationFromMediaEndpointConfiguration):
+        (WebCore::LibWebRTCPeerConnectionBackend::setConfiguration):
+        (WebCore::LibWebRTCPeerConnectionBackend::getStats):
+        (WebCore::LibWebRTCPeerConnectionBackend::doSetLocalDescription):
+        (WebCore::LibWebRTCPeerConnectionBackend::doSetRemoteDescription):
+        (WebCore::LibWebRTCPeerConnectionBackend::doCreateOffer):
+        (WebCore::LibWebRTCPeerConnectionBackend::doCreateAnswer):
+        (WebCore::LibWebRTCPeerConnectionBackend::doStop):
+        (WebCore::LibWebRTCPeerConnectionBackend::doAddIceCandidate):
+        (WebCore::LibWebRTCPeerConnectionBackend::markAsNeedingNegotiation):
+        (WebCore::LibWebRTCPeerConnectionBackend::createReceiver):
+        (WebCore::LibWebRTCPeerConnectionBackend::createDataChannelHandler):
+        * Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h: Added.
+        * WebCore.xcodeproj/project.pbxproj:
+
</ins><span class="cx"> 2017-01-26  Chris Dumez  &lt;cdumez@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Crash when navigating back to a page in PacheCache when one of its frames has been removed
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmediastreamlibwebrtcLibWebRTCMediaEndpointcpp"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp (0 => 211255)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp                                (rev 0)
+++ trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp        2017-01-27 06:09:41 UTC (rev 211255)
</span><span class="lines">@@ -0,0 +1,449 @@
</span><ins>+/*
+ * Copyright (C) 2017 Apple Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include &quot;config.h&quot;
+#include &quot;LibWebRTCMediaEndpoint.h&quot;
+
+#if USE(LIBWEBRTC)
+
+#include &quot;EventNames.h&quot;
+#include &quot;LibWebRTCPeerConnectionBackend.h&quot;
+#include &quot;LibWebRTCProvider.h&quot;
+#include &quot;LibWebRTCUtils.h&quot;
+#include &quot;MediaStreamEvent.h&quot;
+#include &quot;NotImplemented.h&quot;
+#include &quot;PlatformStrategies.h&quot;
+#include &quot;RTCDataChannel.h&quot;
+#include &quot;RTCDataChannelEvent.h&quot;
+#include &quot;RTCIceCandidate.h&quot;
+#include &quot;RTCPeerConnection.h&quot;
+#include &quot;RTCSessionDescription.h&quot;
+#include &quot;RTCTrackEvent.h&quot;
+#include &quot;RealtimeIncomingAudioSource.h&quot;
+#include &quot;RealtimeIncomingVideoSource.h&quot;
+#include &lt;webrtc/api/peerconnectionfactory.h&gt;
+#include &lt;webrtc/base/physicalsocketserver.h&gt;
+#include &lt;webrtc/p2p/base/basicpacketsocketfactory.h&gt;
+#include &lt;webrtc/p2p/client/basicportallocator.h&gt;
+#include &lt;wtf/MainThread.h&gt;
+
+#include &quot;CoreMediaSoftLink.h&quot;
+
+namespace WebCore {
+
+LibWebRTCMediaEndpoint::LibWebRTCMediaEndpoint(LibWebRTCPeerConnectionBackend&amp; peerConnection, LibWebRTCProvider&amp; client)
+    : m_peerConnectionBackend(peerConnection)
+    , m_backend(client.createPeerConnection(*this))
+    , m_createSessionDescriptionObserver(*this)
+    , m_setLocalSessionDescriptionObserver(*this)
+    , m_setRemoteSessionDescriptionObserver(*this)
+{
+    ASSERT(m_backend);
+}
+
+static inline const char* sessionDescriptionType(RTCSessionDescription::SdpType sdpType)
+{
+    switch (sdpType) {
+    case RTCSessionDescription::SdpType::Offer:
+        return &quot;offer&quot;;
+    case RTCSessionDescription::SdpType::Pranswer:
+        return &quot;pranswer&quot;;
+    case RTCSessionDescription::SdpType::Answer:
+        return &quot;answer&quot;;
+    case RTCSessionDescription::SdpType::Rollback:
+        return &quot;rollback&quot;;
+    }
+}
+
+void LibWebRTCMediaEndpoint::doSetLocalDescription(RTCSessionDescription&amp; description)
+{
+    webrtc::SdpParseError error;
+    std::unique_ptr&lt;webrtc::SessionDescriptionInterface&gt; sessionDescription(webrtc::CreateSessionDescription(sessionDescriptionType(description.type()), description.sdp().utf8().data(), &amp;error));
+
+    if (!sessionDescription) {
+        String errorMessage(error.description.data(), error.description.size());
+        m_peerConnectionBackend.setLocalDescriptionFailed(Exception { OperationError, WTFMove(errorMessage) });
+        return;
+    }
+    m_backend-&gt;SetLocalDescription(&amp;m_setLocalSessionDescriptionObserver, sessionDescription.release());
+}
+
+void LibWebRTCMediaEndpoint::doSetRemoteDescription(RTCSessionDescription&amp; description)
+{
+    webrtc::SdpParseError error;
+    std::unique_ptr&lt;webrtc::SessionDescriptionInterface&gt; sessionDescription(webrtc::CreateSessionDescription(sessionDescriptionType(description.type()), description.sdp().utf8().data(), &amp;error));
+    if (!sessionDescription) {
+        String errorMessage(error.description.data(), error.description.size());
+        m_peerConnectionBackend.setRemoteDescriptionFailed(Exception { OperationError, WTFMove(errorMessage) });
+        return;
+    }
+    m_backend-&gt;SetRemoteDescription(&amp;m_setRemoteSessionDescriptionObserver, sessionDescription.release());
+}
+
+void LibWebRTCMediaEndpoint::addPendingIceCandidates()
+{
+    while (m_pendingCandidates.size())
+        m_backend-&gt;AddIceCandidate(m_pendingCandidates.takeLast().release());
+}
+
+static inline std::string streamId(RTCPeerConnection&amp; connection)
+{
+    auto&amp; senders = connection.getSenders();
+    if (senders.size()) {
+        for (RTCRtpSender&amp; sender : senders) {
+            auto* track = sender.track();
+            if (track) {
+                ASSERT(sender.mediaStreamIds().size() == 1);
+                return std::string(sender.mediaStreamIds().first().utf8().data());
+            }
+        }
+    }
+    return &quot;av_label&quot;;
+}
+
+void LibWebRTCMediaEndpoint::doCreateOffer()
+{
+    m_isInitiator = true;
+    auto&amp; senders = m_peerConnectionBackend.connection().getSenders();
+    if (senders.size()) {
+        // FIXME: We only support one stream for the moment.
+        auto stream = peerConnectionFactory().CreateLocalMediaStream(streamId(m_peerConnectionBackend.connection()));
+        for (RTCRtpSender&amp; sender : senders) {
+            auto* track = sender.track();
+            if (track) {
+                ASSERT(sender.mediaStreamIds().size() == 1);
+                auto&amp; source = track-&gt;source();
+                if (source.type() == RealtimeMediaSource::Audio) {
+                    auto trackSource = RealtimeOutgoingAudioSource::create(source);
+                    auto rtcTrack = peerConnectionFactory().CreateAudioTrack(track-&gt;id().utf8().data(), trackSource.ptr());
+                    trackSource-&gt;setTrack(rtc::scoped_refptr&lt;webrtc::AudioTrackInterface&gt;(rtcTrack));
+                    m_audioSources.append(WTFMove(trackSource));
+                    stream-&gt;AddTrack(WTFMove(rtcTrack));
+                } else {
+                    m_videoSources.append(RealtimeOutgoingVideoSource::create(source));
+                    stream-&gt;AddTrack(peerConnectionFactory().CreateVideoTrack(track-&gt;id().utf8().data(), m_videoSources.last().ptr()));
+                }
+            }
+        }
+        m_backend-&gt;AddStream(stream);
+    }
+    m_backend-&gt;CreateOffer(&amp;m_createSessionDescriptionObserver, nullptr);
+}
+
+void LibWebRTCMediaEndpoint::doCreateAnswer()
+{
+    m_isInitiator = false;
+
+    auto&amp; senders = m_peerConnectionBackend.connection().getSenders();
+    if (senders.size()) {
+        // FIXME: We only support one stream for the moment.
+        auto stream = peerConnectionFactory().CreateLocalMediaStream(streamId(m_peerConnectionBackend.connection()));
+        for (RTCRtpSender&amp; sender : senders) {
+            auto* track = sender.track();
+            if (track) {
+                ASSERT(sender.mediaStreamIds().size() == 1);
+                auto&amp; source = track-&gt;source();
+                if (source.type() == RealtimeMediaSource::Audio) {
+                    auto trackSource = RealtimeOutgoingAudioSource::create(source);
+                    auto rtcTrack = peerConnectionFactory().CreateAudioTrack(track-&gt;id().utf8().data(), trackSource.ptr());
+                    trackSource-&gt;setTrack(rtc::scoped_refptr&lt;webrtc::AudioTrackInterface&gt;(rtcTrack));
+                    m_audioSources.append(WTFMove(trackSource));
+                    stream-&gt;AddTrack(WTFMove(rtcTrack));
+                } else {
+                    m_videoSources.append(RealtimeOutgoingVideoSource::create(source));
+                    stream-&gt;AddTrack(peerConnectionFactory().CreateVideoTrack(track-&gt;id().utf8().data(), m_videoSources.last().ptr()));
+                }
+            }
+        }
+        m_backend-&gt;AddStream(stream);
+    }
+    m_backend-&gt;CreateAnswer(&amp;m_createSessionDescriptionObserver, nullptr);
+}
+
+void LibWebRTCMediaEndpoint::getStats(MediaStreamTrack* track, PeerConnection::StatsPromise&amp;&amp; promise)
+{
+    auto collector = StatsCollector::create(*this, WTFMove(promise), track);
+    m_backend-&gt;GetStats(collector.ptr());
+    m_statsCollectors.append(WTFMove(collector));
+}
+
+LibWebRTCMediaEndpoint::StatsCollector::StatsCollector(LibWebRTCMediaEndpoint&amp; endpoint, PeerConnection::StatsPromise&amp;&amp; promise, MediaStreamTrack* track)
+    : m_endpoint(endpoint)
+    , m_promise(WTFMove(promise))
+{
+    if (track)
+        m_id = track-&gt;id();
+}
+
+void LibWebRTCMediaEndpoint::StatsCollector::OnStatsDelivered(const rtc::scoped_refptr&lt;const webrtc::RTCStatsReport&gt;&amp; report)
+{
+    callOnMainThread([this, report, protector = makeRef(m_endpoint)] {
+        // FIXME: Fulfill promise with the report
+        UNUSED_PARAM(report);
+        m_promise.reject(TypeError, ASCIILiteral(&quot;Stats API is not yet implemented&quot;));
+
+        m_endpoint.m_statsCollectors.removeFirstMatching([this](const Ref&lt;StatsCollector&gt;&amp; collector) {
+            return this == collector.ptr();
+        });
+    });
+}
+
+static PeerConnectionStates::SignalingState signalingState(webrtc::PeerConnectionInterface::SignalingState state)
+{
+    switch (state) {
+    case webrtc::PeerConnectionInterface::kStable:
+        return PeerConnectionStates::SignalingState::Stable;
+    case webrtc::PeerConnectionInterface::kHaveLocalOffer:
+        return PeerConnectionStates::SignalingState::HaveLocalOffer;
+    case webrtc::PeerConnectionInterface::kHaveLocalPrAnswer:
+        return PeerConnectionStates::SignalingState::HaveLocalPrAnswer;
+    case webrtc::PeerConnectionInterface::kHaveRemoteOffer:
+        return PeerConnectionStates::SignalingState::HaveRemoteOffer;
+    case webrtc::PeerConnectionInterface::kHaveRemotePrAnswer:
+        return PeerConnectionStates::SignalingState::HaveRemotePrAnswer;
+    case webrtc::PeerConnectionInterface::kClosed:
+        return PeerConnectionStates::SignalingState::Closed;
+    }
+}
+
+void LibWebRTCMediaEndpoint::OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState rtcState)
+{
+    auto state = signalingState(rtcState);
+    callOnMainThread([protectedThis = makeRef(*this), state] {
+        if (protectedThis-&gt;isStopped())
+            return;
+        protectedThis-&gt;m_peerConnectionBackend.updateSignalingState(state);
+    });
+}
+
+static inline String trackId(webrtc::MediaStreamTrackInterface&amp; videoTrack)
+{
+    return String(videoTrack.id().data(), videoTrack.id().size());
+}
+
+static inline Ref&lt;MediaStreamTrack&gt; createMediaStreamTrack(ScriptExecutionContext&amp; context, Ref&lt;RealtimeMediaSource&gt;&amp;&amp; remoteSource)
+{
+    String trackId = remoteSource-&gt;id();
+    return MediaStreamTrack::create(context, MediaStreamTrackPrivate::create(WTFMove(remoteSource), WTFMove(trackId)));
+}
+
+void LibWebRTCMediaEndpoint::addStream(webrtc::MediaStreamInterface&amp; stream)
+{
+    MediaStreamTrackVector tracks;
+    for (auto&amp; videoTrack : stream.GetVideoTracks()) {
+        ASSERT(videoTrack);
+        String id = trackId(*videoTrack);
+        auto remoteSource = RealtimeIncomingVideoSource::create(WTFMove(videoTrack), WTFMove(id));
+        tracks.append(createMediaStreamTrack(*m_peerConnectionBackend.connection().scriptExecutionContext(), WTFMove(remoteSource)));
+    }
+    for (auto&amp; audioTrack : stream.GetAudioTracks()) {
+        ASSERT(audioTrack);
+        String id = trackId(*audioTrack);
+        auto remoteSource = RealtimeIncomingAudioSource::create(WTFMove(audioTrack), WTFMove(id));
+        tracks.append(createMediaStreamTrack(*m_peerConnectionBackend.connection().scriptExecutionContext(), WTFMove(remoteSource)));
+    }
+
+    auto newStream = MediaStream::create(*m_peerConnectionBackend.connection().scriptExecutionContext(), WTFMove(tracks));
+    m_peerConnectionBackend.connection().fireEvent(MediaStreamEvent::create(eventNames().addstreamEvent, false, false, newStream.copyRef()));
+
+    Vector&lt;RefPtr&lt;MediaStream&gt;&gt; streams;
+    streams.append(newStream.copyRef());
+    for (auto&amp; track : newStream-&gt;getTracks())
+        m_peerConnectionBackend.connection().fireEvent(RTCTrackEvent::create(eventNames().trackEvent, false, false, nullptr, track.get(), Vector&lt;RefPtr&lt;MediaStream&gt;&gt;(streams), nullptr));
+}
+
+void LibWebRTCMediaEndpoint::OnAddStream(rtc::scoped_refptr&lt;webrtc::MediaStreamInterface&gt; stream)
+{
+    callOnMainThread([protectedThis = makeRef(*this), stream = WTFMove(stream)] {
+        if (protectedThis-&gt;isStopped())
+            return;
+        ASSERT(stream);
+        protectedThis-&gt;addStream(*stream.get());
+    });
+}
+
+void LibWebRTCMediaEndpoint::OnRemoveStream(rtc::scoped_refptr&lt;webrtc::MediaStreamInterface&gt;)
+{
+    ASSERT_NOT_REACHED();
+}
+
+void LibWebRTCMediaEndpoint::OnDataChannel(rtc::scoped_refptr&lt;webrtc::DataChannelInterface&gt; dataChannel)
+{
+    callOnMainThread([protectedThis = makeRef(*this), dataChannel = WTFMove(dataChannel)] {
+        if (protectedThis-&gt;isStopped())
+            return;
+        protectedThis-&gt;addDataChannel(rtc::scoped_refptr&lt;webrtc::DataChannelInterface&gt;(dataChannel));
+    });
+}
+
+void LibWebRTCMediaEndpoint::stop()
+{
+    ASSERT(m_backend);
+    m_backend-&gt;Close();
+    m_backend = nullptr;
+}
+
+void LibWebRTCMediaEndpoint::OnRenegotiationNeeded()
+{
+    notImplemented();
+}
+
+static inline PeerConnectionStates::IceConnectionState iceConnectionState(webrtc::PeerConnectionInterface::IceConnectionState state)
+{
+    switch (state) {
+    case webrtc::PeerConnectionInterface::kIceConnectionNew:
+        return PeerConnectionStates::IceConnectionState::New;
+    case webrtc::PeerConnectionInterface::kIceConnectionChecking:
+        return PeerConnectionStates::IceConnectionState::Checking;
+    case webrtc::PeerConnectionInterface::kIceConnectionConnected:
+        return PeerConnectionStates::IceConnectionState::Connected;
+    case webrtc::PeerConnectionInterface::kIceConnectionCompleted:
+        return PeerConnectionStates::IceConnectionState::Completed;
+    case webrtc::PeerConnectionInterface::kIceConnectionFailed:
+        return PeerConnectionStates::IceConnectionState::Failed;
+    case webrtc::PeerConnectionInterface::kIceConnectionDisconnected:
+        return PeerConnectionStates::IceConnectionState::Disconnected;
+    case webrtc::PeerConnectionInterface::kIceConnectionClosed:
+        return PeerConnectionStates::IceConnectionState::Closed;
+    case webrtc::PeerConnectionInterface::kIceConnectionMax:
+        ASSERT_NOT_REACHED();
+        return PeerConnectionStates::IceConnectionState::New;
+    }
+}
+
+void LibWebRTCMediaEndpoint::OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState state)
+{
+    auto connectionState = iceConnectionState(state);
+    callOnMainThread([protectedThis = makeRef(*this), connectionState] {
+        if (protectedThis-&gt;isStopped())
+            return;
+        if (protectedThis-&gt;m_peerConnectionBackend.connection().internalIceConnectionState() != connectionState)
+            protectedThis-&gt;m_peerConnectionBackend.connection().updateIceConnectionState(connectionState);
+    });
+}
+
+void LibWebRTCMediaEndpoint::OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState state)
+{
+    if (state == webrtc::PeerConnectionInterface::kIceGatheringComplete) {
+        callOnMainThread([protectedThis = makeRef(*this)] {
+            if (protectedThis-&gt;isStopped())
+                return;
+            protectedThis-&gt;m_peerConnectionBackend.doneGatheringCandidates();
+        });
+    }
+}
+
+void LibWebRTCMediaEndpoint::OnIceCandidate(const webrtc::IceCandidateInterface *rtcCandidate)
+{
+    ASSERT(rtcCandidate);
+
+    std::string sdp;
+    rtcCandidate-&gt;ToString(&amp;sdp);
+    String candidateSDP(sdp.data(), sdp.size());
+
+    auto mid = rtcCandidate-&gt;sdp_mid();
+    String candidateMid(mid.data(), mid.size());
+
+    callOnMainThread([protectedThis = makeRef(*this), mid = WTFMove(candidateMid), sdp = WTFMove(candidateSDP)] {
+        if (protectedThis-&gt;isStopped())
+            return;
+        protectedThis-&gt;m_peerConnectionBackend.fireICECandidateEvent(RTCIceCandidate::create(String(sdp), String(mid), 0));
+    });
+}
+
+void LibWebRTCMediaEndpoint::OnIceCandidatesRemoved(const std::vector&lt;cricket::Candidate&gt;&amp;)
+{
+    ASSERT_NOT_REACHED();
+}
+
+void LibWebRTCMediaEndpoint::createSessionDescriptionSucceeded(webrtc::SessionDescriptionInterface* description)
+{
+    std::string sdp;
+    description-&gt;ToString(&amp;sdp);
+    String sdpString(sdp.data(), sdp.size());
+
+    callOnMainThread([protectedThis = makeRef(*this), sdp = WTFMove(sdpString)] {
+        if (protectedThis-&gt;isStopped())
+            return;
+        if (protectedThis-&gt;m_isInitiator)
+            protectedThis-&gt;m_peerConnectionBackend.createOfferSucceeded(String(sdp));
+        else
+            protectedThis-&gt;m_peerConnectionBackend.createAnswerSucceeded(String(sdp));
+    });
+}
+
+void LibWebRTCMediaEndpoint::createSessionDescriptionFailed(const std::string&amp; errorMessage)
+{
+    String error(errorMessage.data(), errorMessage.size());
+    callOnMainThread([protectedThis = makeRef(*this), error = WTFMove(error)] {
+        if (protectedThis-&gt;isStopped())
+            return;
+        if (protectedThis-&gt;m_isInitiator)
+            protectedThis-&gt;m_peerConnectionBackend.createOfferFailed(Exception { OperationError, String(error) });
+        else
+            protectedThis-&gt;m_peerConnectionBackend.createAnswerFailed(Exception { OperationError, String(error) });
+    });
+}
+
+void LibWebRTCMediaEndpoint::setLocalSessionDescriptionSucceeded()
+{
+    callOnMainThread([protectedThis = makeRef(*this)] {
+        if (protectedThis-&gt;isStopped())
+            return;
+        protectedThis-&gt;m_peerConnectionBackend.setLocalDescriptionSucceeded();
+    });
+}
+
+void LibWebRTCMediaEndpoint::setLocalSessionDescriptionFailed(const std::string&amp; errorMessage)
+{
+    String error(errorMessage.data(), errorMessage.size());
+    callOnMainThread([protectedThis = makeRef(*this), error = WTFMove(error)] {
+        if (protectedThis-&gt;isStopped())
+            return;
+        protectedThis-&gt;m_peerConnectionBackend.setLocalDescriptionFailed(Exception { OperationError, String(error) });
+    });
+}
+
+void LibWebRTCMediaEndpoint::setRemoteSessionDescriptionSucceeded()
+{
+    callOnMainThread([protectedThis = makeRef(*this)] {
+        if (protectedThis-&gt;isStopped())
+            return;
+        protectedThis-&gt;m_peerConnectionBackend.setRemoteDescriptionSucceeded();
+    });
+}
+
+void LibWebRTCMediaEndpoint::setRemoteSessionDescriptionFailed(const std::string&amp; errorMessage)
+{
+    String error(errorMessage.data(), errorMessage.size());
+    callOnMainThread([protectedThis = makeRef(*this), error = WTFMove(error)] {
+        if (protectedThis-&gt;isStopped())
+            return;
+        protectedThis-&gt;m_peerConnectionBackend.setRemoteDescriptionFailed(Exception { OperationError, String(error) });
+    });
+}
+
+} // namespace WebCore
+
+#endif // USE(LIBWEBRTC)
</ins></span></pre></div>
<a id="trunkSourceWebCoreModulesmediastreamlibwebrtcLibWebRTCMediaEndpointh"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.h (0 => 211255)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.h                                (rev 0)
+++ trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.h        2017-01-27 06:09:41 UTC (rev 211255)
</span><span class="lines">@@ -0,0 +1,178 @@
</span><ins>+/*
+ * Copyright (C) 2017 Apple Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if USE(LIBWEBRTC)
+
+#include &quot;LibWebRTCUtils.h&quot;
+#include &quot;PeerConnectionBackend.h&quot;
+#include &quot;RealtimeOutgoingAudioSource.h&quot;
+#include &quot;RealtimeOutgoingVideoSource.h&quot;
+
+#include &lt;webrtc/api/jsep.h&gt;
+#include &lt;webrtc/api/peerconnectionfactory.h&gt;
+#include &lt;webrtc/api/peerconnectioninterface.h&gt;
+#include &lt;webrtc/api/rtcstatscollector.h&gt;
+
+#include &lt;wtf/ThreadSafeRefCounted.h&gt;
+
+namespace webrtc {
+class CreateSessionDescriptionObserver;
+class DataChannelInterface;
+class IceCandidateInterface;
+class MediaStreamInterface;
+class PeerConnectionObserver;
+class SessionDescriptionInterface;
+class SetSessionDescriptionObserver;
+}
+
+namespace WebCore {
+
+class LibWebRTCProvider;
+class LibWebRTCPeerConnectionBackend;
+class MediaStreamTrack;
+class RTCSessionDescription;
+
+class LibWebRTCMediaEndpoint : public ThreadSafeRefCounted&lt;LibWebRTCMediaEndpoint&gt;, private webrtc::PeerConnectionObserver {
+public:
+    static Ref&lt;LibWebRTCMediaEndpoint&gt; create(LibWebRTCPeerConnectionBackend&amp; peerConnection, LibWebRTCProvider&amp; client) { return adoptRef(*new LibWebRTCMediaEndpoint(peerConnection, client)); }
+    virtual ~LibWebRTCMediaEndpoint() { }
+
+    webrtc::PeerConnectionInterface&amp; backend() const { ASSERT(m_backend); return *m_backend.get(); }
+    void doSetLocalDescription(RTCSessionDescription&amp;);
+    void doSetRemoteDescription(RTCSessionDescription&amp;);
+    void doCreateOffer();
+    void doCreateAnswer();
+    void getStats(MediaStreamTrack*, PeerConnection::StatsPromise&amp;&amp;);
+
+    void storeIceCandidate(std::unique_ptr&lt;webrtc::IceCandidateInterface&gt;&amp;&amp; candidate) { m_pendingCandidates.append(WTFMove(candidate)); }
+    void addPendingIceCandidates();
+
+    void stop();
+
+private:
+    LibWebRTCMediaEndpoint(LibWebRTCPeerConnectionBackend&amp;, LibWebRTCProvider&amp;);
+
+    // webrtc::PeerConnectionObserver API
+    void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState) final;
+    void OnAddStream(rtc::scoped_refptr&lt;webrtc::MediaStreamInterface&gt;) final;
+    void OnRemoveStream(rtc::scoped_refptr&lt;webrtc::MediaStreamInterface&gt;) final;
+    void OnDataChannel(rtc::scoped_refptr&lt;webrtc::DataChannelInterface&gt;) final;
+    void OnRenegotiationNeeded() final;
+    void OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState) final;
+    void OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState) final;
+    void OnIceCandidate(const webrtc::IceCandidateInterface*) final;
+    void OnIceCandidatesRemoved(const std::vector&lt;cricket::Candidate&gt;&amp;) final;
+
+    void createSessionDescriptionSucceeded(webrtc::SessionDescriptionInterface*);
+    void createSessionDescriptionFailed(const std::string&amp;);
+    void setLocalSessionDescriptionSucceeded();
+    void setLocalSessionDescriptionFailed(const std::string&amp;);
+    void setRemoteSessionDescriptionSucceeded();
+    void setRemoteSessionDescriptionFailed(const std::string&amp;);
+    void addStream(webrtc::MediaStreamInterface&amp;);
+    void addDataChannel(rtc::scoped_refptr&lt;webrtc::DataChannelInterface&gt;&amp;&amp;);
+
+    bool isStopped() const { return !m_backend; }
+
+    int AddRef() const { ref(); return static_cast&lt;int&gt;(refCount()); }
+    int Release() const { deref(); return static_cast&lt;int&gt;(refCount()); }
+
+    class CreateSessionDescriptionObserver final : public webrtc::CreateSessionDescriptionObserver {
+    public:
+        explicit CreateSessionDescriptionObserver(LibWebRTCMediaEndpoint &amp;endpoint) : m_endpoint(endpoint) { }
+
+        void OnSuccess(webrtc::SessionDescriptionInterface* sessionDescription) final { m_endpoint.createSessionDescriptionSucceeded(sessionDescription); }
+        void OnFailure(const std::string&amp; error) final { m_endpoint.createSessionDescriptionFailed(error); }
+
+        int AddRef() const { return m_endpoint.AddRef(); }
+        int Release() const { return m_endpoint.Release(); }
+
+    private:
+        LibWebRTCMediaEndpoint&amp; m_endpoint;
+    };
+
+    class SetLocalSessionDescriptionObserver final : public webrtc::SetSessionDescriptionObserver {
+    public:
+        explicit SetLocalSessionDescriptionObserver(LibWebRTCMediaEndpoint &amp;endpoint) : m_endpoint(endpoint) { }
+
+        void OnSuccess() final { m_endpoint.setLocalSessionDescriptionSucceeded(); }
+        void OnFailure(const std::string&amp; error) final { m_endpoint.setLocalSessionDescriptionFailed(error); }
+
+        int AddRef() const { return m_endpoint.AddRef(); }
+        int Release() const { return m_endpoint.Release(); }
+
+    private:
+        LibWebRTCMediaEndpoint&amp; m_endpoint;
+    };
+
+    class SetRemoteSessionDescriptionObserver final : public webrtc::SetSessionDescriptionObserver {
+    public:
+        explicit SetRemoteSessionDescriptionObserver(LibWebRTCMediaEndpoint &amp;endpoint) : m_endpoint(endpoint) { }
+
+        void OnSuccess() final { m_endpoint.setRemoteSessionDescriptionSucceeded(); }
+        void OnFailure(const std::string&amp; error) final { m_endpoint.setRemoteSessionDescriptionFailed(error); }
+
+        int AddRef() const { return m_endpoint.AddRef(); }
+        int Release() const { return m_endpoint.Release(); }
+
+    private:
+        LibWebRTCMediaEndpoint&amp; m_endpoint;
+    };
+
+    class StatsCollector final : public RefCounted&lt;StatsCollector&gt;, public webrtc::RTCStatsCollectorCallback {
+    public:
+        static Ref&lt;StatsCollector&gt; create(LibWebRTCMediaEndpoint&amp; endpoint, PeerConnection::StatsPromise&amp;&amp; promise, MediaStreamTrack* track) { return adoptRef(* new StatsCollector(endpoint, WTFMove(promise), track)); }
+    private:
+        StatsCollector(LibWebRTCMediaEndpoint&amp;, PeerConnection::StatsPromise&amp;&amp;, MediaStreamTrack*);
+
+        void OnStatsDelivered(const rtc::scoped_refptr&lt;const webrtc::RTCStatsReport&gt;&amp;) final;
+
+        int AddRef() const final { ref(); return static_cast&lt;int&gt;(refCount()); }
+        int Release() const final { deref(); return static_cast&lt;int&gt;(refCount()); }
+
+        LibWebRTCMediaEndpoint&amp; m_endpoint;
+        PeerConnection::StatsPromise m_promise;
+        String m_id;
+    };
+
+    LibWebRTCPeerConnectionBackend&amp; m_peerConnectionBackend;
+    rtc::scoped_refptr&lt;webrtc::PeerConnectionInterface&gt; m_backend;
+
+    CreateSessionDescriptionObserver m_createSessionDescriptionObserver;
+    SetLocalSessionDescriptionObserver m_setLocalSessionDescriptionObserver;
+    SetRemoteSessionDescriptionObserver m_setRemoteSessionDescriptionObserver;
+
+    bool m_isInitiator { false };
+
+    Vector&lt;std::unique_ptr&lt;webrtc::IceCandidateInterface&gt;&gt; m_pendingCandidates;
+    Vector&lt;Ref&lt;RealtimeOutgoingAudioSource&gt;&gt; m_audioSources;
+    Vector&lt;Ref&lt;RealtimeOutgoingVideoSource&gt;&gt; m_videoSources;
+    Vector&lt;Ref&lt;StatsCollector&gt;&gt; m_statsCollectors;
+};
+
+} // namespace WebCore
+
+#endif // USE(LIBWEBRTC)
</ins></span></pre></div>
<a id="trunkSourceWebCoreModulesmediastreamlibwebrtcLibWebRTCPeerConnectionBackendcpp"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp (0 => 211255)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp                                (rev 0)
+++ trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp        2017-01-27 06:09:41 UTC (rev 211255)
</span><span class="lines">@@ -0,0 +1,190 @@
</span><ins>+/*
+ * Copyright (C) 2017 Apple Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include &quot;config.h&quot;
+#include &quot;LibWebRTCPeerConnectionBackend.h&quot;
+
+#if USE(LIBWEBRTC)
+
+#include &quot;Document.h&quot;
+#include &quot;IceCandidate.h&quot;
+#include &quot;LibWebRTCMediaEndpoint.h&quot;
+#include &quot;MediaEndpointConfiguration.h&quot;
+#include &quot;Page.h&quot;
+#include &quot;RTCIceCandidate.h&quot;
+#include &quot;RTCPeerConnection.h&quot;
+#include &quot;RTCRtpReceiver.h&quot;
+#include &quot;RTCSessionDescription.h&quot;
+#include &quot;RealtimeIncomingAudioSource.h&quot;
+#include &quot;RealtimeIncomingVideoSource.h&quot;
+
+namespace WebCore {
+
+static std::unique_ptr&lt;PeerConnectionBackend&gt; createLibWebRTCPeerConnectionBackend(RTCPeerConnection&amp; peerConnection)
+{
+    return std::make_unique&lt;LibWebRTCPeerConnectionBackend&gt;(peerConnection);
+}
+
+CreatePeerConnectionBackend PeerConnectionBackend::create = createLibWebRTCPeerConnectionBackend;
+
+static inline LibWebRTCProvider&amp; libWebRTCProvider(RTCPeerConnection&amp; peerConnection)
+{
+    ASSERT(peerConnection.scriptExecutionContext()-&gt;isDocument());
+    auto* page = static_cast&lt;Document*&gt;(peerConnection.scriptExecutionContext())-&gt;page();
+    return page-&gt;libWebRTCProvider();
+}
+
+LibWebRTCPeerConnectionBackend::LibWebRTCPeerConnectionBackend(RTCPeerConnection&amp; peerConnection)
+    : PeerConnectionBackend(peerConnection)
+    , m_endpoint(LibWebRTCMediaEndpoint::create(*this, libWebRTCProvider(peerConnection)))
+{
+}
+
+static webrtc::PeerConnectionInterface::RTCConfiguration configurationFromMediaEndpointConfiguration(MediaEndpointConfiguration&amp;&amp; configuration)
+{
+    webrtc::PeerConnectionInterface::RTCConfiguration rtcConfiguration(webrtc::PeerConnectionInterface::RTCConfigurationType::kAggressive);
+
+    if (configuration.iceTransportPolicy == PeerConnectionStates::IceTransportPolicy::Relay)
+        rtcConfiguration.type = webrtc::PeerConnectionInterface::kRelay;
+
+    if (configuration.bundlePolicy == PeerConnectionStates::BundlePolicy::MaxBundle)
+        rtcConfiguration.bundle_policy = webrtc::PeerConnectionInterface::kBundlePolicyMaxBundle;
+    else if (configuration.bundlePolicy == PeerConnectionStates::BundlePolicy::MaxCompat)
+        rtcConfiguration.bundle_policy = webrtc::PeerConnectionInterface::kBundlePolicyMaxCompat;
+
+    for (auto&amp; server : configuration.iceServers) {
+        webrtc::PeerConnectionInterface::IceServer iceServer;
+        iceServer.username = server.username.utf8().data();
+        iceServer.password = server.credential.utf8().data();
+        for (auto&amp; url : server.urls)
+            iceServer.urls.push_back({ url.string().utf8().data() });
+        rtcConfiguration.servers.push_back(WTFMove(iceServer));
+    }
+
+    return rtcConfiguration;
+}
+
+void LibWebRTCPeerConnectionBackend::setConfiguration(MediaEndpointConfiguration&amp;&amp; configuration)
+{
+    m_endpoint-&gt;backend().SetConfiguration(configurationFromMediaEndpointConfiguration(WTFMove(configuration)));
+}
+
+void LibWebRTCPeerConnectionBackend::getStats(MediaStreamTrack* track, PeerConnection::StatsPromise&amp;&amp; promise)
+{
+    m_endpoint-&gt;getStats(track, WTFMove(promise));
+}
+
+void LibWebRTCPeerConnectionBackend::doSetLocalDescription(RTCSessionDescription&amp; description)
+{
+    m_endpoint-&gt;doSetLocalDescription(description);
+    if (!m_isLocalDescriptionSet) {
+        if (m_isRemoteDescriptionSet)
+            m_endpoint-&gt;addPendingIceCandidates();
+        m_isLocalDescriptionSet = true;
+    }
+}
+
+void LibWebRTCPeerConnectionBackend::doSetRemoteDescription(RTCSessionDescription&amp; description)
+{
+    m_endpoint-&gt;doSetRemoteDescription(description);
+    if (!m_isRemoteDescriptionSet) {
+        if (m_isLocalDescriptionSet)
+            m_endpoint-&gt;addPendingIceCandidates();
+        m_isRemoteDescriptionSet = true;
+    }
+}
+
+void LibWebRTCPeerConnectionBackend::doCreateOffer(RTCOfferOptions&amp;&amp;)
+{
+    m_endpoint-&gt;doCreateOffer();
+}
+
+void LibWebRTCPeerConnectionBackend::doCreateAnswer(RTCAnswerOptions&amp;&amp;)
+{
+    if (!m_isRemoteDescriptionSet) {
+        createAnswerFailed(Exception { INVALID_STATE_ERR, &quot;No remote description set&quot; });
+        return;
+    }
+    m_endpoint-&gt;doCreateAnswer();
+}
+
+void LibWebRTCPeerConnectionBackend::doStop()
+{
+    m_endpoint-&gt;stop();
+}
+
+void LibWebRTCPeerConnectionBackend::doAddIceCandidate(RTCIceCandidate&amp; candidate)
+{
+    if (!m_isRemoteDescriptionSet) {
+        addIceCandidateFailed(Exception { INVALID_STATE_ERR, &quot;No remote description set&quot; });
+        return;
+    }
+
+    webrtc::SdpParseError error;
+    int sdpMLineIndex = candidate.sdpMLineIndex() ? candidate.sdpMLineIndex().value() : 0;
+    std::unique_ptr&lt;webrtc::IceCandidateInterface&gt; rtcCandidate(webrtc::CreateIceCandidate(candidate.sdpMid().utf8().data(), sdpMLineIndex, candidate.candidate().utf8().data(), &amp;error));
+
+    if (!rtcCandidate) {
+        String message(error.description.data(), error.description.size());
+        addIceCandidateFailed(Exception { OperationError, WTFMove(message) });
+        return;
+    }
+
+    // libwebrtc does not like that ice candidates are set before the description.
+    if (!m_isLocalDescriptionSet || !m_isRemoteDescriptionSet)
+        m_endpoint-&gt;storeIceCandidate(WTFMove(rtcCandidate));
+    else if (!m_endpoint-&gt;backend().AddIceCandidate(rtcCandidate.get())) {
+        ASSERT_NOT_REACHED();
+        addIceCandidateFailed(Exception { OperationError, ASCIILiteral(&quot;Failed to apply the received candidate&quot;) });
+        return;
+    }
+    addIceCandidateSucceeded();
+}
+
+void LibWebRTCPeerConnectionBackend::markAsNeedingNegotiation()
+{
+    // FIXME: Implement this
+}
+
+Ref&lt;RTCRtpReceiver&gt; LibWebRTCPeerConnectionBackend::createReceiver(const String&amp;, const String&amp; trackKind, const String&amp; trackId)
+{
+    // FIXME: We need to create a source that will get fueled once we will receive OnAddStream.
+    // For the moment, we create an empty one.
+    auto remoteTrackPrivate = (trackKind == &quot;audio&quot;) ? MediaStreamTrackPrivate::create(RealtimeIncomingAudioSource::create(nullptr, String(trackId))) : MediaStreamTrackPrivate::create(RealtimeIncomingVideoSource::create(nullptr, String(trackId)));
+    auto remoteTrack = MediaStreamTrack::create(*m_peerConnection.scriptExecutionContext(), WTFMove(remoteTrackPrivate));
+
+    return RTCRtpReceiver::create(WTFMove(remoteTrack));
+}
+
+std::unique_ptr&lt;RTCDataChannelHandler&gt; LibWebRTCPeerConnectionBackend::createDataChannelHandler(const String&amp; label, const RTCDataChannelInit&amp; options)
+{
+    UNUSED_PARAM(label);
+    UNUSED_PARAM(options);
+    ASSERT_NOT_REACHED();
+    return nullptr;
+}
+
+} // namespace WebCore
+
+#endif // USE(LIBWEBRTC)
</ins></span></pre></div>
<a id="trunkSourceWebCoreModulesmediastreamlibwebrtcLibWebRTCPeerConnectionBackendh"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h (0 => 211255)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h                                (rev 0)
+++ trunk/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h        2017-01-27 06:09:41 UTC (rev 211255)
</span><span class="lines">@@ -0,0 +1,84 @@
</span><ins>+/*
+ * Copyright (C) 2017 Apple Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if USE(LIBWEBRTC)
+
+#include &quot;PeerConnectionBackend.h&quot;
+
+namespace WebCore {
+
+class LibWebRTCMediaEndpoint;
+class RTCRtpReceiver;
+class RTCSessionDescription;
+
+class LibWebRTCPeerConnectionBackend final : public PeerConnectionBackend {
+public:
+    explicit LibWebRTCPeerConnectionBackend(RTCPeerConnection&amp;);
+
+private:
+    void doCreateOffer(RTCOfferOptions&amp;&amp;) final;
+    void doCreateAnswer(RTCAnswerOptions&amp;&amp;) final;
+    void doSetLocalDescription(RTCSessionDescription&amp;) final;
+    void doSetRemoteDescription(RTCSessionDescription&amp;) final;
+    void doAddIceCandidate(RTCIceCandidate&amp;) final;
+    void doStop() final;
+    std::unique_ptr&lt;RTCDataChannelHandler&gt; createDataChannelHandler(const String&amp;, const RTCDataChannelInit&amp;) final;
+    void setConfiguration(MediaEndpointConfiguration&amp;&amp;) final;
+    void getStats(MediaStreamTrack*, PeerConnection::StatsPromise&amp;&amp;) final;
+    Ref&lt;RTCRtpReceiver&gt; createReceiver(const String&amp; transceiverMid, const String&amp; trackKind, const String&amp; trackId) final;
+
+    // FIXME: API to implement for real
+    RefPtr&lt;RTCSessionDescription&gt; localDescription() const final { return nullptr; }
+    RefPtr&lt;RTCSessionDescription&gt; currentLocalDescription() const final { return nullptr; }
+    RefPtr&lt;RTCSessionDescription&gt; pendingLocalDescription() const final { return nullptr; }
+
+    RefPtr&lt;RTCSessionDescription&gt; remoteDescription() const final { return nullptr; }
+    RefPtr&lt;RTCSessionDescription&gt; currentRemoteDescription() const final { return nullptr; }
+    RefPtr&lt;RTCSessionDescription&gt; pendingRemoteDescription() const final { return nullptr; }
+
+
+    Vector&lt;RefPtr&lt;MediaStream&gt;&gt; getRemoteStreams() const final { return { }; }
+
+    void replaceTrack(RTCRtpSender&amp;, RefPtr&lt;MediaStreamTrack&gt;&amp;&amp;, DOMPromise&lt;void&gt;&amp;&amp;) final { }
+
+    bool isNegotiationNeeded() const final { return false; }
+    void markAsNeedingNegotiation() final;
+    void clearNegotiationNeededState() final { }
+
+    void emulatePlatformEvent(const String&amp;) final { }
+
+    friend LibWebRTCMediaEndpoint;
+    RTCPeerConnection&amp; connection() { return m_peerConnection; }
+
+private:
+    Ref&lt;LibWebRTCMediaEndpoint&gt; m_endpoint;
+    bool m_isLocalDescriptionSet { false };
+    bool m_isRemoteDescriptionSet { false };
+};
+
+} // namespace WebCore
+
+#endif // USE(LIBWEBRTC)
</ins></span></pre></div>
<a id="trunkSourceWebCoreWebCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (211254 => 211255)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj        2017-01-27 05:36:19 UTC (rev 211254)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj        2017-01-27 06:09:41 UTC (rev 211255)
</span><span class="lines">@@ -1563,6 +1563,10 @@
</span><span class="cx">                 4170A2EA1D8C0CCA00318452 /* JSDOMWrapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4170A2E91D8C0CC000318452 /* JSDOMWrapper.cpp */; };
</span><span class="cx">                 417253AA1354BBBC00360F2A /* MediaControlElements.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 417253A81354BBBC00360F2A /* MediaControlElements.cpp */; };
</span><span class="cx">                 417253AB1354BBBC00360F2A /* MediaControlElements.h in Headers */ = {isa = PBXBuildFile; fileRef = 417253A91354BBBC00360F2A /* MediaControlElements.h */; };
</span><ins>+                417612AF1E3A994000C3D81D /* LibWebRTCMediaEndpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 417612AB1E3A993B00C3D81D /* LibWebRTCMediaEndpoint.cpp */; };
+                417612B01E3A994000C3D81D /* LibWebRTCMediaEndpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 417612AC1E3A993B00C3D81D /* LibWebRTCMediaEndpoint.h */; };
+                417612B11E3A994000C3D81D /* LibWebRTCPeerConnectionBackend.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 417612AD1E3A993B00C3D81D /* LibWebRTCPeerConnectionBackend.cpp */; };
+                417612B21E3A994000C3D81D /* LibWebRTCPeerConnectionBackend.h in Headers */ = {isa = PBXBuildFile; fileRef = 417612AE1E3A993B00C3D81D /* LibWebRTCPeerConnectionBackend.h */; };
</ins><span class="cx">                 417DA6D913734E6E007C57FB /* Internals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 417DA4CF13734326007C57FB /* Internals.cpp */; };
</span><span class="cx">                 417DA6DA13734E6E007C57FB /* Internals.h in Headers */ = {isa = PBXBuildFile; fileRef = 417DA4CE13734326007C57FB /* Internals.h */; };
</span><span class="cx">                 417DA71D13735DFA007C57FB /* JSInternals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 417DA71B13735DFA007C57FB /* JSInternals.cpp */; };
</span><span class="lines">@@ -8740,6 +8744,10 @@
</span><span class="cx">                 4170A2E91D8C0CC000318452 /* JSDOMWrapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSDOMWrapper.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 417253A81354BBBC00360F2A /* MediaControlElements.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MediaControlElements.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 417253A91354BBBC00360F2A /* MediaControlElements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaControlElements.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                417612AB1E3A993B00C3D81D /* LibWebRTCMediaEndpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LibWebRTCMediaEndpoint.cpp; path = libwebrtc/LibWebRTCMediaEndpoint.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
+                417612AC1E3A993B00C3D81D /* LibWebRTCMediaEndpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LibWebRTCMediaEndpoint.h; path = libwebrtc/LibWebRTCMediaEndpoint.h; sourceTree = &quot;&lt;group&gt;&quot;; };
+                417612AD1E3A993B00C3D81D /* LibWebRTCPeerConnectionBackend.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LibWebRTCPeerConnectionBackend.cpp; path = libwebrtc/LibWebRTCPeerConnectionBackend.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
+                417612AE1E3A993B00C3D81D /* LibWebRTCPeerConnectionBackend.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LibWebRTCPeerConnectionBackend.h; path = libwebrtc/LibWebRTCPeerConnectionBackend.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 417DA4CE13734326007C57FB /* Internals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Internals.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 417DA4CF13734326007C57FB /* Internals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Internals.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 417DA6D013734E02007C57FB /* libWebCoreTestSupport.dylib */ = {isa = PBXFileReference; explicitFileType = &quot;compiled.mach-o.dylib&quot;; includeInIndex = 0; path = libWebCoreTestSupport.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
</span><span class="lines">@@ -15115,6 +15123,7 @@
</span><span class="cx">                 07221B4617CEC32700848E51 /* mediastream */ = {
</span><span class="cx">                         isa = PBXGroup;
</span><span class="cx">                         children = (
</span><ins>+                                417612AA1E3A992300C3D81D /* libwebrtc */,
</ins><span class="cx">                                 073794ED19EE364200E5A045 /* DOMURLMediaStream.cpp */,
</span><span class="cx">                                 15FCC9FD1B4DF85600E72326 /* DOMURLMediaStream.h */,
</span><span class="cx">                                 073794EE19EE364200E5A045 /* DOMURLMediaStream.idl */,
</span><span class="lines">@@ -16664,6 +16673,17 @@
</span><span class="cx">                         name = libwebrtc;
</span><span class="cx">                         sourceTree = &quot;&lt;group&gt;&quot;;
</span><span class="cx">                 };
</span><ins>+                417612AA1E3A992300C3D81D /* libwebrtc */ = {
+                        isa = PBXGroup;
+                        children = (
+                                417612AB1E3A993B00C3D81D /* LibWebRTCMediaEndpoint.cpp */,
+                                417612AC1E3A993B00C3D81D /* LibWebRTCMediaEndpoint.h */,
+                                417612AD1E3A993B00C3D81D /* LibWebRTCPeerConnectionBackend.cpp */,
+                                417612AE1E3A993B00C3D81D /* LibWebRTCPeerConnectionBackend.h */,
+                        );
+                        name = libwebrtc;
+                        sourceTree = &quot;&lt;group&gt;&quot;;
+                };
</ins><span class="cx">                 417DA4CD13734204007C57FB /* testing */ = {
</span><span class="cx">                         isa = PBXGroup;
</span><span class="cx">                         children = (
</span><span class="lines">@@ -24977,6 +24997,7 @@
</span><span class="cx">                                 319848011A1D817B00A13318 /* AnimationEvent.h in Headers */,
</span><span class="cx">                                 49E912AD0EFAC906009D0CAF /* AnimationList.h in Headers */,
</span><span class="cx">                                 31DCD29D1AB4FBDE0072E817 /* AnimationTrigger.h in Headers */,
</span><ins>+                                417612B01E3A994000C3D81D /* LibWebRTCMediaEndpoint.h in Headers */,
</ins><span class="cx">                                 0F580FAF149800D400FB5BD8 /* AnimationUtilities.h in Headers */,
</span><span class="cx">                                 93309DD7099E64920056E581 /* AppendNodeCommand.h in Headers */,
</span><span class="cx">                                 7C6579E31E00827000E3A27A /* ApplePayLineItem.h in Headers */,
</span><span class="lines">@@ -27840,6 +27861,7 @@
</span><span class="cx">                                 A8EA800C0A19516E00A8EF5F /* StyleSheet.h in Headers */,
</span><span class="cx">                                 E4F9EEF3156DA00700D23E7E /* StyleSheetContents.h in Headers */,
</span><span class="cx">                                 A8EA800A0A19516E00A8EF5F /* StyleSheetList.h in Headers */,
</span><ins>+                                417612B21E3A994000C3D81D /* LibWebRTCPeerConnectionBackend.h in Headers */,
</ins><span class="cx">                                 BC5EB5E50E81BF6D00B25965 /* StyleSurroundData.h in Headers */,
</span><span class="cx">                                 BC5EB8100E81F2CE00B25965 /* StyleTransformData.h in Headers */,
</span><span class="cx">                                 E4DEAA1817A93DC3000E0430 /* StyleTreeResolver.h in Headers */,
</span><span class="lines">@@ -29169,6 +29191,7 @@
</span><span class="cx">                                 E1B4CD2510B322E200BFFD7E /* CredentialStorageMac.mm in Sources */,
</span><span class="cx">                                 2D481F00146B5C4C00AA7834 /* CrossfadeGeneratedImage.cpp in Sources */,
</span><span class="cx">                                 E1C416170F6563180092D2FB /* CrossOriginAccessControl.cpp in Sources */,
</span><ins>+                                417612B11E3A994000C3D81D /* LibWebRTCPeerConnectionBackend.cpp in Sources */,
</ins><span class="cx">                                 41ABE67C1D0580E0006D862D /* CrossOriginPreflightChecker.cpp in Sources */,
</span><span class="cx">                                 E1C415DE0F655D7C0092D2FB /* CrossOriginPreflightResultCache.cpp in Sources */,
</span><span class="cx">                                 E16980491133644700894115 /* CRuntimeObject.cpp in Sources */,
</span><span class="lines">@@ -30483,6 +30506,7 @@
</span><span class="cx">                                 B2FA3DBA0AB75A6F000E5AC4 /* JSSVGPathSegArcRel.cpp in Sources */,
</span><span class="cx">                                 B2FA3DBC0AB75A6F000E5AC4 /* JSSVGPathSegClosePath.cpp in Sources */,
</span><span class="cx">                                 B2FA3DBE0AB75A6F000E5AC4 /* JSSVGPathSegCurvetoCubicAbs.cpp in Sources */,
</span><ins>+                                417612AF1E3A994000C3D81D /* LibWebRTCMediaEndpoint.cpp in Sources */,
</ins><span class="cx">                                 B2FA3DC00AB75A6F000E5AC4 /* JSSVGPathSegCurvetoCubicRel.cpp in Sources */,
</span><span class="cx">                                 B2FA3DC20AB75A6F000E5AC4 /* JSSVGPathSegCurvetoCubicSmoothAbs.cpp in Sources */,
</span><span class="cx">                                 B2FA3DC40AB75A6F000E5AC4 /* JSSVGPathSegCurvetoCubicSmoothRel.cpp in Sources */,
</span></span></pre>
</div>
</div>

</body>
</html>