Support PeerConnectionStates::BundlePolicy::MaxBundle when setting rtc configuration
[WebKit-https.git] / Source / WebCore / Modules / mediastream / libwebrtc / LibWebRTCPeerConnectionBackend.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1.  Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  * 2.  Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "config.h"
26 #include "LibWebRTCPeerConnectionBackend.h"
27
28 #if USE(LIBWEBRTC)
29
30 #include "Document.h"
31 #include "ExceptionCode.h"
32 #include "IceCandidate.h"
33 #include "JSRTCStatsReport.h"
34 #include "LibWebRTCDataChannelHandler.h"
35 #include "LibWebRTCMediaEndpoint.h"
36 #include "MediaEndpointConfiguration.h"
37 #include "Page.h"
38 #include "RTCIceCandidate.h"
39 #include "RTCPeerConnection.h"
40 #include "RTCRtpReceiver.h"
41 #include "RTCSessionDescription.h"
42 #include "RealtimeIncomingAudioSource.h"
43 #include "RealtimeIncomingVideoSource.h"
44 #include <wtf/text/StringBuilder.h>
45
46 namespace WebCore {
47
48 static std::unique_ptr<PeerConnectionBackend> createLibWebRTCPeerConnectionBackend(RTCPeerConnection& peerConnection)
49 {
50     if (!LibWebRTCProvider::webRTCAvailable())
51         return nullptr;
52     return std::make_unique<LibWebRTCPeerConnectionBackend>(peerConnection);
53 }
54
55 CreatePeerConnectionBackend PeerConnectionBackend::create = createLibWebRTCPeerConnectionBackend;
56
57 static inline LibWebRTCProvider& libWebRTCProvider(RTCPeerConnection& peerConnection)
58 {
59     ASSERT(peerConnection.scriptExecutionContext()->isDocument());
60     auto* page = static_cast<Document*>(peerConnection.scriptExecutionContext())->page();
61     return page->libWebRTCProvider();
62 }
63
64 LibWebRTCPeerConnectionBackend::LibWebRTCPeerConnectionBackend(RTCPeerConnection& peerConnection)
65     : PeerConnectionBackend(peerConnection)
66     , m_endpoint(LibWebRTCMediaEndpoint::create(*this, libWebRTCProvider(peerConnection)))
67 {
68 }
69
70 LibWebRTCPeerConnectionBackend::~LibWebRTCPeerConnectionBackend()
71 {
72 }
73
74 static inline webrtc::PeerConnectionInterface::BundlePolicy bundlePolicyfromConfiguration(const MediaEndpointConfiguration& configuration)
75 {
76     switch (configuration.bundlePolicy) {
77     case RTCBundlePolicy::MaxCompat:
78         return webrtc::PeerConnectionInterface::kBundlePolicyMaxCompat;
79     case RTCBundlePolicy::MaxBundle:
80         return webrtc::PeerConnectionInterface::kBundlePolicyMaxBundle;
81     case RTCBundlePolicy::Balanced:
82         return webrtc::PeerConnectionInterface::kBundlePolicyBalanced;
83     }
84 }
85
86 static inline webrtc::PeerConnectionInterface::IceTransportsType iceTransportPolicyfromConfiguration(const MediaEndpointConfiguration& configuration)
87 {
88     switch (configuration.iceTransportPolicy) {
89     case RTCIceTransportPolicy::Relay:
90         return webrtc::PeerConnectionInterface::kRelay;
91     case RTCIceTransportPolicy::All:
92         return webrtc::PeerConnectionInterface::kAll;
93     }
94 }
95
96 static webrtc::PeerConnectionInterface::RTCConfiguration configurationFromMediaEndpointConfiguration(MediaEndpointConfiguration&& configuration)
97 {
98     webrtc::PeerConnectionInterface::RTCConfiguration rtcConfiguration;
99
100     rtcConfiguration.type = iceTransportPolicyfromConfiguration(configuration);
101     rtcConfiguration.bundle_policy = bundlePolicyfromConfiguration(configuration);
102
103     for (auto& server : configuration.iceServers) {
104         webrtc::PeerConnectionInterface::IceServer iceServer;
105         iceServer.username = server.username.utf8().data();
106         iceServer.password = server.credential.utf8().data();
107         for (auto& url : server.urls)
108             iceServer.urls.push_back({ url.string().utf8().data() });
109         rtcConfiguration.servers.push_back(WTFMove(iceServer));
110     }
111
112     rtcConfiguration.set_cpu_adaptation(false);
113     rtcConfiguration.ice_candidate_pool_size = configuration.iceCandidatePoolSize;
114
115     return rtcConfiguration;
116 }
117
118 bool LibWebRTCPeerConnectionBackend::setConfiguration(MediaEndpointConfiguration&& configuration)
119 {
120     return m_endpoint->setConfiguration(libWebRTCProvider(m_peerConnection), configurationFromMediaEndpointConfiguration(WTFMove(configuration)));
121 }
122
123 void LibWebRTCPeerConnectionBackend::getStats(MediaStreamTrack* track, Ref<DeferredPromise>&& promise)
124 {
125     if (m_endpoint->isStopped())
126         return;
127
128     auto& statsPromise = promise.get();
129     m_statsPromises.add(&statsPromise, WTFMove(promise));
130     m_endpoint->getStats(track, statsPromise);
131 }
132
133 void LibWebRTCPeerConnectionBackend::getStatsSucceeded(const DeferredPromise& promise, Ref<RTCStatsReport>&& report)
134 {
135     auto statsPromise = m_statsPromises.take(&promise);
136     ASSERT(statsPromise);
137     statsPromise.value()->resolve<IDLInterface<RTCStatsReport>>(WTFMove(report));
138 }
139
140 void LibWebRTCPeerConnectionBackend::getStatsFailed(const DeferredPromise& promise, Exception&& exception)
141 {
142     auto statsPromise = m_statsPromises.take(&promise);
143     ASSERT(statsPromise);
144     statsPromise.value()->reject(WTFMove(exception));
145 }
146
147 void LibWebRTCPeerConnectionBackend::doSetLocalDescription(RTCSessionDescription& description)
148 {
149     m_endpoint->doSetLocalDescription(description);
150     if (!m_isLocalDescriptionSet) {
151         if (m_isRemoteDescriptionSet) {
152             while (m_pendingCandidates.size())
153                 m_endpoint->addIceCandidate(*m_pendingCandidates.takeLast().release());
154         }
155         m_isLocalDescriptionSet = true;
156     }
157 }
158
159 void LibWebRTCPeerConnectionBackend::doSetRemoteDescription(RTCSessionDescription& description)
160 {
161     m_endpoint->doSetRemoteDescription(description);
162     if (!m_isRemoteDescriptionSet) {
163         if (m_isLocalDescriptionSet) {
164             while (m_pendingCandidates.size())
165                 m_endpoint->addIceCandidate(*m_pendingCandidates.takeLast().release());
166         }
167         m_isRemoteDescriptionSet = true;
168     }
169 }
170
171 void LibWebRTCPeerConnectionBackend::doCreateOffer(RTCOfferOptions&& options)
172 {
173     m_endpoint->doCreateOffer(options);
174 }
175
176 void LibWebRTCPeerConnectionBackend::doCreateAnswer(RTCAnswerOptions&&)
177 {
178     if (!m_isRemoteDescriptionSet) {
179         createAnswerFailed(Exception { INVALID_STATE_ERR, "No remote description set" });
180         return;
181     }
182     m_endpoint->doCreateAnswer();
183 }
184
185 void LibWebRTCPeerConnectionBackend::doStop()
186 {
187     for (auto& source : m_audioSources)
188         source->stop();
189     for (auto& source : m_videoSources)
190         source->stop();
191
192     m_endpoint->stop();
193
194     m_remoteStreams.clear();
195     m_pendingReceivers.clear();
196 }
197
198 void LibWebRTCPeerConnectionBackend::doAddIceCandidate(RTCIceCandidate& candidate)
199 {
200     webrtc::SdpParseError error;
201     int sdpMLineIndex = candidate.sdpMLineIndex() ? candidate.sdpMLineIndex().value() : 0;
202     std::unique_ptr<webrtc::IceCandidateInterface> rtcCandidate(webrtc::CreateIceCandidate(candidate.sdpMid().utf8().data(), sdpMLineIndex, candidate.candidate().utf8().data(), &error));
203
204     if (!rtcCandidate) {
205         String message(error.description.data(), error.description.size());
206         addIceCandidateFailed(Exception { OperationError, WTFMove(message) });
207         return;
208     }
209
210     // libwebrtc does not like that ice candidates are set before the description.
211     if (!m_isLocalDescriptionSet || !m_isRemoteDescriptionSet)
212         m_pendingCandidates.append(WTFMove(rtcCandidate));
213     else if (!m_endpoint->addIceCandidate(*rtcCandidate.get())) {
214         ASSERT_NOT_REACHED();
215         addIceCandidateFailed(Exception { OperationError, ASCIILiteral("Failed to apply the received candidate") });
216         return;
217     }
218     addIceCandidateSucceeded();
219 }
220
221 void LibWebRTCPeerConnectionBackend::addAudioSource(Ref<RealtimeOutgoingAudioSource>&& source)
222 {
223     m_audioSources.append(WTFMove(source));
224 }
225
226 void LibWebRTCPeerConnectionBackend::addVideoSource(Ref<RealtimeOutgoingVideoSource>&& source)
227 {
228     m_videoSources.append(WTFMove(source));
229 }
230
231 static inline Ref<RTCRtpReceiver> createReceiverForSource(ScriptExecutionContext& context, Ref<RealtimeMediaSource>&& source)
232 {
233     String id = source->id();
234     auto remoteTrackPrivate = MediaStreamTrackPrivate::create(WTFMove(source), WTFMove(id));
235     auto remoteTrack = MediaStreamTrack::create(context, WTFMove(remoteTrackPrivate));
236
237     return RTCRtpReceiver::create(WTFMove(remoteTrack));
238 }
239
240 static inline Ref<RealtimeMediaSource> createEmptySource(const String& trackKind, String&& trackId)
241 {
242     // FIXME: trackKind should be an enumeration
243     if (trackKind == "audio")
244         return RealtimeIncomingAudioSource::create(nullptr, WTFMove(trackId));
245     ASSERT(trackKind == "video");
246     return RealtimeIncomingVideoSource::create(nullptr, WTFMove(trackId));
247 }
248
249 Ref<RTCRtpReceiver> LibWebRTCPeerConnectionBackend::createReceiver(const String&, const String& trackKind, const String& trackId)
250 {
251     auto receiver = createReceiverForSource(*m_peerConnection.scriptExecutionContext(), createEmptySource(trackKind, String(trackId)));
252     m_pendingReceivers.append(receiver.copyRef());
253     return receiver;
254 }
255
256 LibWebRTCPeerConnectionBackend::VideoReceiver LibWebRTCPeerConnectionBackend::videoReceiver(String&& trackId)
257 {
258     // FIXME: Add to Vector a utility routine for that take-or-create pattern.
259     // FIXME: We should be selecting the receiver based on track id.
260     for (size_t cptr = 0; cptr < m_pendingReceivers.size(); ++cptr) {
261         if (m_pendingReceivers[cptr]->track()->source().type() == RealtimeMediaSource::Type::Video) {
262             Ref<RTCRtpReceiver> receiver = m_pendingReceivers[cptr].copyRef();
263             m_pendingReceivers.remove(cptr);
264             Ref<RealtimeIncomingVideoSource> source = static_cast<RealtimeIncomingVideoSource&>(receiver->track()->source());
265             return { WTFMove(receiver), WTFMove(source) };
266         }
267     }
268     auto source = RealtimeIncomingVideoSource::create(nullptr, WTFMove(trackId));
269     auto receiver = createReceiverForSource(*m_peerConnection.scriptExecutionContext(), source.copyRef());
270
271     auto transceiver = RTCRtpTransceiver::create(RTCRtpSender::create("video", { }, m_peerConnection), receiver.copyRef());
272     transceiver->disableSendingDirection();
273     m_peerConnection.addTransceiver(WTFMove(transceiver));
274
275     return { WTFMove(receiver), WTFMove(source) };
276 }
277
278 LibWebRTCPeerConnectionBackend::AudioReceiver LibWebRTCPeerConnectionBackend::audioReceiver(String&& trackId)
279 {
280     // FIXME: Add to Vector a utility routine for that take-or-create pattern.
281     // FIXME: We should be selecting the receiver based on track id.
282     for (size_t cptr = 0; cptr < m_pendingReceivers.size(); ++cptr) {
283         if (m_pendingReceivers[cptr]->track()->source().type() == RealtimeMediaSource::Type::Audio) {
284             Ref<RTCRtpReceiver> receiver = m_pendingReceivers[cptr].copyRef();
285             m_pendingReceivers.remove(cptr);
286             Ref<RealtimeIncomingAudioSource> source = static_cast<RealtimeIncomingAudioSource&>(receiver->track()->source());
287             return { WTFMove(receiver), WTFMove(source) };
288         }
289     }
290     auto source = RealtimeIncomingAudioSource::create(nullptr, WTFMove(trackId));
291     auto receiver = createReceiverForSource(*m_peerConnection.scriptExecutionContext(), source.copyRef());
292
293     auto transceiver = RTCRtpTransceiver::create(RTCRtpSender::create("audio", { }, m_peerConnection), receiver.copyRef());
294     transceiver->disableSendingDirection();
295     m_peerConnection.addTransceiver(WTFMove(transceiver));
296
297     return { WTFMove(receiver), WTFMove(source) };
298 }
299
300 std::unique_ptr<RTCDataChannelHandler> LibWebRTCPeerConnectionBackend::createDataChannelHandler(const String& label, const RTCDataChannelInit& options)
301 {
302     return m_endpoint->createDataChannel(label, options);
303 }
304
305 RefPtr<RTCSessionDescription> LibWebRTCPeerConnectionBackend::currentLocalDescription() const
306 {
307     auto description = m_endpoint->currentLocalDescription();
308     if (description)
309         description->setSdp(filterSDP(String(description->sdp())));
310     return description;
311 }
312
313 RefPtr<RTCSessionDescription> LibWebRTCPeerConnectionBackend::currentRemoteDescription() const
314 {
315     return m_endpoint->currentRemoteDescription();
316 }
317
318 RefPtr<RTCSessionDescription> LibWebRTCPeerConnectionBackend::pendingLocalDescription() const
319 {
320     auto description = m_endpoint->pendingLocalDescription();
321     if (description)
322         description->setSdp(filterSDP(String(description->sdp())));
323     return description;
324 }
325
326 RefPtr<RTCSessionDescription> LibWebRTCPeerConnectionBackend::pendingRemoteDescription() const
327 {
328     return m_endpoint->pendingRemoteDescription();
329 }
330
331 RefPtr<RTCSessionDescription> LibWebRTCPeerConnectionBackend::localDescription() const
332 {
333     auto description = m_endpoint->localDescription();
334     if (description)
335         description->setSdp(filterSDP(String(description->sdp())));
336     return description;
337 }
338
339 RefPtr<RTCSessionDescription> LibWebRTCPeerConnectionBackend::remoteDescription() const
340 {
341     return m_endpoint->remoteDescription();
342 }
343
344 void LibWebRTCPeerConnectionBackend::notifyAddedTrack(RTCRtpSender& sender)
345 {
346     ASSERT(sender.track());
347     m_endpoint->addTrack(sender, *sender.track(), sender.mediaStreamIds());
348 }
349
350 void LibWebRTCPeerConnectionBackend::notifyRemovedTrack(RTCRtpSender& sender)
351 {
352     m_endpoint->removeTrack(sender);
353 }
354
355 void LibWebRTCPeerConnectionBackend::removeRemoteStream(MediaStream* mediaStream)
356 {
357     m_remoteStreams.removeFirstMatching([mediaStream](const auto& item) {
358         return item.get() == mediaStream;
359     });
360 }
361
362 void LibWebRTCPeerConnectionBackend::addRemoteStream(Ref<MediaStream>&& mediaStream)
363 {
364     m_remoteStreams.append(WTFMove(mediaStream));
365 }
366
367 void LibWebRTCPeerConnectionBackend::replaceTrack(RTCRtpSender& sender, Ref<MediaStreamTrack>&& track, DOMPromiseDeferred<void>&& promise)
368 {
369     ASSERT(sender.track());
370     auto* currentTrack = sender.track();
371
372     ASSERT(currentTrack->source().type() == track->source().type());
373     switch (currentTrack->source().type()) {
374     case RealtimeMediaSource::Type::None:
375         ASSERT_NOT_REACHED();
376         promise.reject(INVALID_MODIFICATION_ERR);
377         break;
378     case RealtimeMediaSource::Type::Audio: {
379         for (auto& audioSource : m_audioSources) {
380             if (&audioSource->source() == &currentTrack->privateTrack()) {
381                 if (!audioSource->setSource(track->privateTrack())) {
382                     promise.reject(INVALID_MODIFICATION_ERR);
383                     return;
384                 }
385                 connection().enqueueReplaceTrackTask(sender, WTFMove(track), WTFMove(promise));
386                 return;
387             }
388         }
389         promise.reject(INVALID_MODIFICATION_ERR);
390         break;
391     }
392     case RealtimeMediaSource::Type::Video: {
393         for (auto& videoSource : m_videoSources) {
394             if (&videoSource->source() == &currentTrack->privateTrack()) {
395                 if (!videoSource->setSource(track->privateTrack())) {
396                     promise.reject(INVALID_MODIFICATION_ERR);
397                     return;
398                 }
399                 connection().enqueueReplaceTrackTask(sender, WTFMove(track), WTFMove(promise));
400                 return;
401             }
402         }
403         promise.reject(INVALID_MODIFICATION_ERR);
404         break;
405     }
406     }
407 }
408
409 RTCRtpParameters LibWebRTCPeerConnectionBackend::getParameters(RTCRtpSender& sender) const
410 {
411     return m_endpoint->getRTCRtpSenderParameters(sender);
412 }
413
414 void LibWebRTCPeerConnectionBackend::applyRotationForOutgoingVideoSources()
415 {
416     for (auto& source : m_videoSources)
417         source->setApplyRotation(true);
418 }
419
420 } // namespace WebCore
421
422 #endif // USE(LIBWEBRTC)