| 1 | /* |
| 2 | * Copyright (C) 2017-2018 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 "IceCandidate.h" |
| 32 | #include "LibWebRTCDataChannelHandler.h" |
| 33 | #include "LibWebRTCMediaEndpoint.h" |
| 34 | #include "LibWebRTCRtpReceiverBackend.h" |
| 35 | #include "LibWebRTCRtpSenderBackend.h" |
| 36 | #include "LibWebRTCRtpTransceiverBackend.h" |
| 37 | #include "MediaEndpointConfiguration.h" |
| 38 | #include "Page.h" |
| 39 | #include "RTCIceCandidate.h" |
| 40 | #include "RTCPeerConnection.h" |
| 41 | #include "RTCRtpCapabilities.h" |
| 42 | #include "RTCRtpReceiver.h" |
| 43 | #include "RTCSessionDescription.h" |
| 44 | #include "RealtimeIncomingAudioSource.h" |
| 45 | #include "RealtimeIncomingVideoSource.h" |
| 46 | #include "RealtimeOutgoingAudioSource.h" |
| 47 | #include "RealtimeOutgoingVideoSource.h" |
| 48 | #include "RuntimeEnabledFeatures.h" |
| 49 | |
| 50 | namespace WebCore { |
| 51 | |
| 52 | static std::unique_ptr<PeerConnectionBackend> createLibWebRTCPeerConnectionBackend(RTCPeerConnection& peerConnection) |
| 53 | { |
| 54 | if (!LibWebRTCProvider::webRTCAvailable()) |
| 55 | return nullptr; |
| 56 | |
| 57 | auto* page = downcast<Document>(*peerConnection.scriptExecutionContext()).page(); |
| 58 | if (!page) |
| 59 | return nullptr; |
| 60 | |
| 61 | return std::make_unique<LibWebRTCPeerConnectionBackend>(peerConnection, page->libWebRTCProvider()); |
| 62 | } |
| 63 | |
| 64 | CreatePeerConnectionBackend PeerConnectionBackend::create = createLibWebRTCPeerConnectionBackend; |
| 65 | |
| 66 | Optional<RTCRtpCapabilities> PeerConnectionBackend::receiverCapabilities(ScriptExecutionContext& context, const String& kind) |
| 67 | { |
| 68 | auto* page = downcast<Document>(context).page(); |
| 69 | if (!page) |
| 70 | return { }; |
| 71 | return page->libWebRTCProvider().receiverCapabilities(kind); |
| 72 | } |
| 73 | |
| 74 | Optional<RTCRtpCapabilities> PeerConnectionBackend::senderCapabilities(ScriptExecutionContext& context, const String& kind) |
| 75 | { |
| 76 | auto* page = downcast<Document>(context).page(); |
| 77 | if (!page) |
| 78 | return { }; |
| 79 | return page->libWebRTCProvider().senderCapabilities(kind); |
| 80 | } |
| 81 | |
| 82 | LibWebRTCPeerConnectionBackend::LibWebRTCPeerConnectionBackend(RTCPeerConnection& peerConnection, LibWebRTCProvider& provider) |
| 83 | : PeerConnectionBackend(peerConnection) |
| 84 | , m_endpoint(LibWebRTCMediaEndpoint::create(*this, provider)) |
| 85 | { |
| 86 | } |
| 87 | |
| 88 | LibWebRTCPeerConnectionBackend::~LibWebRTCPeerConnectionBackend() = default; |
| 89 | |
| 90 | static inline webrtc::PeerConnectionInterface::BundlePolicy bundlePolicyfromConfiguration(const MediaEndpointConfiguration& configuration) |
| 91 | { |
| 92 | switch (configuration.bundlePolicy) { |
| 93 | case RTCBundlePolicy::MaxCompat: |
| 94 | return webrtc::PeerConnectionInterface::kBundlePolicyMaxCompat; |
| 95 | case RTCBundlePolicy::MaxBundle: |
| 96 | return webrtc::PeerConnectionInterface::kBundlePolicyMaxBundle; |
| 97 | case RTCBundlePolicy::Balanced: |
| 98 | return webrtc::PeerConnectionInterface::kBundlePolicyBalanced; |
| 99 | } |
| 100 | |
| 101 | ASSERT_NOT_REACHED(); |
| 102 | return webrtc::PeerConnectionInterface::kBundlePolicyMaxCompat; |
| 103 | } |
| 104 | |
| 105 | static inline webrtc::PeerConnectionInterface::RtcpMuxPolicy rtcpMuxPolicyfromConfiguration(const MediaEndpointConfiguration& configuration) |
| 106 | { |
| 107 | switch (configuration.rtcpMuxPolicy) { |
| 108 | case RTCPMuxPolicy::Negotiate: |
| 109 | return webrtc::PeerConnectionInterface::kRtcpMuxPolicyNegotiate; |
| 110 | case RTCPMuxPolicy::Require: |
| 111 | return webrtc::PeerConnectionInterface::kRtcpMuxPolicyRequire; |
| 112 | } |
| 113 | |
| 114 | ASSERT_NOT_REACHED(); |
| 115 | return webrtc::PeerConnectionInterface::kRtcpMuxPolicyRequire; |
| 116 | } |
| 117 | |
| 118 | static inline webrtc::PeerConnectionInterface::IceTransportsType iceTransportPolicyfromConfiguration(const MediaEndpointConfiguration& configuration) |
| 119 | { |
| 120 | switch (configuration.iceTransportPolicy) { |
| 121 | case RTCIceTransportPolicy::Relay: |
| 122 | return webrtc::PeerConnectionInterface::kRelay; |
| 123 | case RTCIceTransportPolicy::All: |
| 124 | return webrtc::PeerConnectionInterface::kAll; |
| 125 | } |
| 126 | |
| 127 | ASSERT_NOT_REACHED(); |
| 128 | return webrtc::PeerConnectionInterface::kNone; |
| 129 | } |
| 130 | |
| 131 | static webrtc::PeerConnectionInterface::RTCConfiguration configurationFromMediaEndpointConfiguration(MediaEndpointConfiguration&& configuration) |
| 132 | { |
| 133 | webrtc::PeerConnectionInterface::RTCConfiguration rtcConfiguration; |
| 134 | |
| 135 | rtcConfiguration.type = iceTransportPolicyfromConfiguration(configuration); |
| 136 | rtcConfiguration.bundle_policy = bundlePolicyfromConfiguration(configuration); |
| 137 | rtcConfiguration.rtcp_mux_policy = rtcpMuxPolicyfromConfiguration(configuration); |
| 138 | |
| 139 | for (auto& server : configuration.iceServers) { |
| 140 | webrtc::PeerConnectionInterface::IceServer iceServer; |
| 141 | iceServer.username = server.username.utf8().data(); |
| 142 | iceServer.password = server.credential.utf8().data(); |
| 143 | for (auto& url : server.urls) |
| 144 | iceServer.urls.push_back({ url.string().utf8().data() }); |
| 145 | rtcConfiguration.servers.push_back(WTFMove(iceServer)); |
| 146 | } |
| 147 | |
| 148 | rtcConfiguration.set_cpu_adaptation(false); |
| 149 | // FIXME: Activate ice candidate pool size once it no longer bothers test bots. |
| 150 | // rtcConfiguration.ice_candidate_pool_size = configuration.iceCandidatePoolSize; |
| 151 | |
| 152 | for (auto& pem : configuration.certificates) { |
| 153 | rtcConfiguration.certificates.push_back(rtc::RTCCertificate::FromPEM(rtc::RTCCertificatePEM { |
| 154 | pem.privateKey.utf8().data(), pem.certificate.utf8().data() |
| 155 | })); |
| 156 | } |
| 157 | |
| 158 | return rtcConfiguration; |
| 159 | } |
| 160 | |
| 161 | bool LibWebRTCPeerConnectionBackend::setConfiguration(MediaEndpointConfiguration&& configuration) |
| 162 | { |
| 163 | auto* page = downcast<Document>(*m_peerConnection.scriptExecutionContext()).page(); |
| 164 | if (!page) |
| 165 | return false; |
| 166 | |
| 167 | return m_endpoint->setConfiguration(page->libWebRTCProvider(), configurationFromMediaEndpointConfiguration(WTFMove(configuration))); |
| 168 | } |
| 169 | |
| 170 | void LibWebRTCPeerConnectionBackend::getStats(Ref<DeferredPromise>&& promise) |
| 171 | { |
| 172 | m_endpoint->getStats(WTFMove(promise)); |
| 173 | } |
| 174 | |
| 175 | static inline LibWebRTCRtpSenderBackend& backendFromRTPSender(RTCRtpSender& sender) |
| 176 | { |
| 177 | ASSERT(!sender.isStopped()); |
| 178 | return static_cast<LibWebRTCRtpSenderBackend&>(*sender.backend()); |
| 179 | } |
| 180 | |
| 181 | void LibWebRTCPeerConnectionBackend::getStats(RTCRtpSender& sender, Ref<DeferredPromise>&& promise) |
| 182 | { |
| 183 | webrtc::RtpSenderInterface* rtcSender = sender.backend() ? backendFromRTPSender(sender).rtcSender() : nullptr; |
| 184 | |
| 185 | if (!rtcSender) { |
| 186 | m_endpoint->getStats(WTFMove(promise)); |
| 187 | return; |
| 188 | } |
| 189 | m_endpoint->getStats(*rtcSender, WTFMove(promise)); |
| 190 | } |
| 191 | |
| 192 | void LibWebRTCPeerConnectionBackend::getStats(RTCRtpReceiver& receiver, Ref<DeferredPromise>&& promise) |
| 193 | { |
| 194 | webrtc::RtpReceiverInterface* rtcReceiver = receiver.backend() ? static_cast<LibWebRTCRtpReceiverBackend*>(receiver.backend())->rtcReceiver() : nullptr; |
| 195 | |
| 196 | if (!rtcReceiver) { |
| 197 | m_endpoint->getStats(WTFMove(promise)); |
| 198 | return; |
| 199 | } |
| 200 | m_endpoint->getStats(*rtcReceiver, WTFMove(promise)); |
| 201 | } |
| 202 | |
| 203 | void LibWebRTCPeerConnectionBackend::doSetLocalDescription(RTCSessionDescription& description) |
| 204 | { |
| 205 | m_endpoint->doSetLocalDescription(description); |
| 206 | if (!m_isLocalDescriptionSet) { |
| 207 | if (m_isRemoteDescriptionSet) { |
| 208 | for (auto& candidate : m_pendingCandidates) |
| 209 | m_endpoint->addIceCandidate(*candidate); |
| 210 | m_pendingCandidates.clear(); |
| 211 | } |
| 212 | m_isLocalDescriptionSet = true; |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | void LibWebRTCPeerConnectionBackend::doSetRemoteDescription(RTCSessionDescription& description) |
| 217 | { |
| 218 | m_endpoint->doSetRemoteDescription(description); |
| 219 | if (!m_isRemoteDescriptionSet) { |
| 220 | if (m_isLocalDescriptionSet) { |
| 221 | for (auto& candidate : m_pendingCandidates) |
| 222 | m_endpoint->addIceCandidate(*candidate); |
| 223 | } |
| 224 | m_isRemoteDescriptionSet = true; |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | void LibWebRTCPeerConnectionBackend::doCreateOffer(RTCOfferOptions&& options) |
| 229 | { |
| 230 | m_endpoint->doCreateOffer(options); |
| 231 | } |
| 232 | |
| 233 | void LibWebRTCPeerConnectionBackend::doCreateAnswer(RTCAnswerOptions&&) |
| 234 | { |
| 235 | if (!m_isRemoteDescriptionSet) { |
| 236 | createAnswerFailed(Exception { InvalidStateError, "No remote description set" }); |
| 237 | return; |
| 238 | } |
| 239 | m_endpoint->doCreateAnswer(); |
| 240 | } |
| 241 | |
| 242 | void LibWebRTCPeerConnectionBackend::doStop() |
| 243 | { |
| 244 | m_endpoint->stop(); |
| 245 | m_pendingReceivers.clear(); |
| 246 | } |
| 247 | |
| 248 | void LibWebRTCPeerConnectionBackend::doAddIceCandidate(RTCIceCandidate& candidate) |
| 249 | { |
| 250 | webrtc::SdpParseError error; |
| 251 | int sdpMLineIndex = candidate.sdpMLineIndex() ? candidate.sdpMLineIndex().value() : 0; |
| 252 | std::unique_ptr<webrtc::IceCandidateInterface> rtcCandidate(webrtc::CreateIceCandidate(candidate.sdpMid().utf8().data(), sdpMLineIndex, candidate.candidate().utf8().data(), &error)); |
| 253 | |
| 254 | if (!rtcCandidate) { |
| 255 | addIceCandidateFailed(Exception { OperationError, String::fromUTF8(error.description.data(), error.description.length()) }); |
| 256 | return; |
| 257 | } |
| 258 | |
| 259 | // libwebrtc does not like that ice candidates are set before the description. |
| 260 | if (!m_isLocalDescriptionSet || !m_isRemoteDescriptionSet) |
| 261 | m_pendingCandidates.append(WTFMove(rtcCandidate)); |
| 262 | else if (!m_endpoint->addIceCandidate(*rtcCandidate.get())) { |
| 263 | ASSERT_NOT_REACHED(); |
| 264 | addIceCandidateFailed(Exception { OperationError, "Failed to apply the received candidate"_s }); |
| 265 | return; |
| 266 | } |
| 267 | addIceCandidateSucceeded(); |
| 268 | } |
| 269 | |
| 270 | Ref<RTCRtpReceiver> LibWebRTCPeerConnectionBackend::createReceiverForSource(Ref<RealtimeMediaSource>&& source, std::unique_ptr<RTCRtpReceiverBackend>&& backend) |
| 271 | { |
| 272 | String trackID = source->persistentID(); |
| 273 | auto remoteTrackPrivate = MediaStreamTrackPrivate::create(WTFMove(source), WTFMove(trackID)); |
| 274 | auto remoteTrack = MediaStreamTrack::create(*m_peerConnection.scriptExecutionContext(), WTFMove(remoteTrackPrivate)); |
| 275 | |
| 276 | return RTCRtpReceiver::create(*this, WTFMove(remoteTrack), WTFMove(backend)); |
| 277 | } |
| 278 | |
| 279 | static inline Ref<RealtimeMediaSource> createEmptySource(const String& trackKind, String&& trackId) |
| 280 | { |
| 281 | // FIXME: trackKind should be an enumeration |
| 282 | if (trackKind == "audio" ) |
| 283 | return RealtimeIncomingAudioSource::create(nullptr, WTFMove(trackId)); |
| 284 | ASSERT(trackKind == "video" ); |
| 285 | return RealtimeIncomingVideoSource::create(nullptr, WTFMove(trackId)); |
| 286 | } |
| 287 | |
| 288 | Ref<RTCRtpReceiver> LibWebRTCPeerConnectionBackend::createReceiver(const String& trackKind, const String& trackId) |
| 289 | { |
| 290 | auto receiver = createReceiverForSource(createEmptySource(trackKind, String(trackId)), nullptr); |
| 291 | m_pendingReceivers.append(receiver.copyRef()); |
| 292 | return receiver; |
| 293 | } |
| 294 | |
| 295 | LibWebRTCPeerConnectionBackend::VideoReceiver LibWebRTCPeerConnectionBackend::videoReceiver(String&& trackId) |
| 296 | { |
| 297 | // FIXME: Add to Vector a utility routine for that take-or-create pattern. |
| 298 | // FIXME: We should be selecting the receiver based on track id. |
| 299 | for (size_t cptr = 0; cptr < m_pendingReceivers.size(); ++cptr) { |
| 300 | if (m_pendingReceivers[cptr]->track().source().type() == RealtimeMediaSource::Type::Video) { |
| 301 | Ref<RTCRtpReceiver> receiver = m_pendingReceivers[cptr].copyRef(); |
| 302 | m_pendingReceivers.remove(cptr); |
| 303 | Ref<RealtimeIncomingVideoSource> source = static_cast<RealtimeIncomingVideoSource&>(receiver->track().source()); |
| 304 | return { WTFMove(receiver), WTFMove(source) }; |
| 305 | } |
| 306 | } |
| 307 | auto source = RealtimeIncomingVideoSource::create(nullptr, WTFMove(trackId)); |
| 308 | auto receiver = createReceiverForSource(source.copyRef(), nullptr); |
| 309 | |
| 310 | auto senderBackend = std::make_unique<LibWebRTCRtpSenderBackend>(*this, nullptr); |
| 311 | auto transceiver = RTCRtpTransceiver::create(RTCRtpSender::create(*this, "video"_s , { }, WTFMove(senderBackend)), receiver.copyRef(), nullptr); |
| 312 | transceiver->disableSendingDirection(); |
| 313 | m_peerConnection.addTransceiver(WTFMove(transceiver)); |
| 314 | |
| 315 | return { WTFMove(receiver), WTFMove(source) }; |
| 316 | } |
| 317 | |
| 318 | LibWebRTCPeerConnectionBackend::AudioReceiver LibWebRTCPeerConnectionBackend::audioReceiver(String&& trackId) |
| 319 | { |
| 320 | // FIXME: Add to Vector a utility routine for that take-or-create pattern. |
| 321 | // FIXME: We should be selecting the receiver based on track id. |
| 322 | for (size_t cptr = 0; cptr < m_pendingReceivers.size(); ++cptr) { |
| 323 | if (m_pendingReceivers[cptr]->track().source().type() == RealtimeMediaSource::Type::Audio) { |
| 324 | Ref<RTCRtpReceiver> receiver = m_pendingReceivers[cptr].copyRef(); |
| 325 | m_pendingReceivers.remove(cptr); |
| 326 | Ref<RealtimeIncomingAudioSource> source = static_cast<RealtimeIncomingAudioSource&>(receiver->track().source()); |
| 327 | return { WTFMove(receiver), WTFMove(source) }; |
| 328 | } |
| 329 | } |
| 330 | auto source = RealtimeIncomingAudioSource::create(nullptr, WTFMove(trackId)); |
| 331 | auto receiver = createReceiverForSource(source.copyRef(), nullptr); |
| 332 | |
| 333 | auto senderBackend = std::make_unique<LibWebRTCRtpSenderBackend>(*this, nullptr); |
| 334 | auto transceiver = RTCRtpTransceiver::create(RTCRtpSender::create(*this, "audio"_s , { }, WTFMove(senderBackend)), receiver.copyRef(), nullptr); |
| 335 | transceiver->disableSendingDirection(); |
| 336 | m_peerConnection.addTransceiver(WTFMove(transceiver)); |
| 337 | |
| 338 | return { WTFMove(receiver), WTFMove(source) }; |
| 339 | } |
| 340 | |
| 341 | std::unique_ptr<RTCDataChannelHandler> LibWebRTCPeerConnectionBackend::createDataChannelHandler(const String& label, const RTCDataChannelInit& options) |
| 342 | { |
| 343 | return m_endpoint->createDataChannel(label, options); |
| 344 | } |
| 345 | |
| 346 | RefPtr<RTCSessionDescription> LibWebRTCPeerConnectionBackend::currentLocalDescription() const |
| 347 | { |
| 348 | auto description = m_endpoint->currentLocalDescription(); |
| 349 | if (description) |
| 350 | description->setSdp(filterSDP(String(description->sdp()))); |
| 351 | return description; |
| 352 | } |
| 353 | |
| 354 | RefPtr<RTCSessionDescription> LibWebRTCPeerConnectionBackend::currentRemoteDescription() const |
| 355 | { |
| 356 | return m_endpoint->currentRemoteDescription(); |
| 357 | } |
| 358 | |
| 359 | RefPtr<RTCSessionDescription> LibWebRTCPeerConnectionBackend::pendingLocalDescription() const |
| 360 | { |
| 361 | auto description = m_endpoint->pendingLocalDescription(); |
| 362 | if (description) |
| 363 | description->setSdp(filterSDP(String(description->sdp()))); |
| 364 | return description; |
| 365 | } |
| 366 | |
| 367 | RefPtr<RTCSessionDescription> LibWebRTCPeerConnectionBackend::pendingRemoteDescription() const |
| 368 | { |
| 369 | return m_endpoint->pendingRemoteDescription(); |
| 370 | } |
| 371 | |
| 372 | RefPtr<RTCSessionDescription> LibWebRTCPeerConnectionBackend::localDescription() const |
| 373 | { |
| 374 | auto description = m_endpoint->localDescription(); |
| 375 | if (description) |
| 376 | description->setSdp(filterSDP(String(description->sdp()))); |
| 377 | return description; |
| 378 | } |
| 379 | |
| 380 | RefPtr<RTCSessionDescription> LibWebRTCPeerConnectionBackend::remoteDescription() const |
| 381 | { |
| 382 | return m_endpoint->remoteDescription(); |
| 383 | } |
| 384 | |
| 385 | static inline RefPtr<RTCRtpSender> findExistingSender(const Vector<RefPtr<RTCRtpTransceiver>>& transceivers, LibWebRTCRtpSenderBackend& senderBackend) |
| 386 | { |
| 387 | ASSERT(senderBackend.rtcSender()); |
| 388 | for (auto& transceiver : transceivers) { |
| 389 | auto& sender = transceiver->sender(); |
| 390 | if (!sender.isStopped() && senderBackend.rtcSender() == backendFromRTPSender(sender).rtcSender()) |
| 391 | return makeRef(sender); |
| 392 | } |
| 393 | return nullptr; |
| 394 | } |
| 395 | |
| 396 | ExceptionOr<Ref<RTCRtpSender>> LibWebRTCPeerConnectionBackend::addTrack(MediaStreamTrack& track, Vector<String>&& mediaStreamIds) |
| 397 | { |
| 398 | if (RuntimeEnabledFeatures::sharedFeatures().webRTCUnifiedPlanEnabled()) { |
| 399 | auto senderBackend = std::make_unique<LibWebRTCRtpSenderBackend>(*this, nullptr); |
| 400 | if (!m_endpoint->addTrack(*senderBackend, track, mediaStreamIds)) |
| 401 | return Exception { TypeError, "Unable to add track"_s }; |
| 402 | |
| 403 | if (auto sender = findExistingSender(m_peerConnection.currentTransceivers(), *senderBackend)) { |
| 404 | backendFromRTPSender(*sender).takeSource(*senderBackend); |
| 405 | sender->setTrack(makeRef(track)); |
| 406 | sender->setMediaStreamIds(WTFMove(mediaStreamIds)); |
| 407 | return sender.releaseNonNull(); |
| 408 | } |
| 409 | |
| 410 | auto transceiverBackend = m_endpoint->transceiverBackendFromSender(*senderBackend); |
| 411 | |
| 412 | auto sender = RTCRtpSender::create(*this, makeRef(track), WTFMove(mediaStreamIds), WTFMove(senderBackend)); |
| 413 | auto receiver = createReceiverForSource(createEmptySource(track.kind(), createCanonicalUUIDString()), transceiverBackend->createReceiverBackend()); |
| 414 | auto transceiver = RTCRtpTransceiver::create(sender.copyRef(), WTFMove(receiver), WTFMove(transceiverBackend)); |
| 415 | m_peerConnection.addInternalTransceiver(WTFMove(transceiver)); |
| 416 | return sender; |
| 417 | } |
| 418 | |
| 419 | RTCRtpSender* sender = nullptr; |
| 420 | // Reuse an existing sender with the same track kind if it has never been used to send before. |
| 421 | for (auto& transceiver : m_peerConnection.currentTransceivers()) { |
| 422 | auto& existingSender = transceiver->sender(); |
| 423 | if (!existingSender.isStopped() && existingSender.trackKind() == track.kind() && existingSender.trackId().isNull() && !transceiver->hasSendingDirection()) { |
| 424 | existingSender.setTrack(makeRef(track)); |
| 425 | existingSender.setMediaStreamIds(WTFMove(mediaStreamIds)); |
| 426 | transceiver->enableSendingDirection(); |
| 427 | sender = &existingSender; |
| 428 | |
| 429 | break; |
| 430 | } |
| 431 | } |
| 432 | |
| 433 | if (!sender) { |
| 434 | const String& trackKind = track.kind(); |
| 435 | String trackId = createCanonicalUUIDString(); |
| 436 | |
| 437 | auto senderBackend = std::make_unique<LibWebRTCRtpSenderBackend>(*this, nullptr); |
| 438 | auto newSender = RTCRtpSender::create(*this, makeRef(track), Vector<String> { mediaStreamIds }, WTFMove(senderBackend)); |
| 439 | auto receiver = createReceiver(trackKind, trackId); |
| 440 | auto transceiver = RTCRtpTransceiver::create(WTFMove(newSender), WTFMove(receiver), nullptr); |
| 441 | |
| 442 | sender = &transceiver->sender(); |
| 443 | m_peerConnection.addInternalTransceiver(WTFMove(transceiver)); |
| 444 | } |
| 445 | |
| 446 | if (!m_endpoint->addTrack(backendFromRTPSender(*sender), track, mediaStreamIds)) |
| 447 | return Exception { TypeError, "Unable to add track"_s }; |
| 448 | |
| 449 | return makeRef(*sender); |
| 450 | } |
| 451 | |
| 452 | template<typename T> |
| 453 | ExceptionOr<Ref<RTCRtpTransceiver>> LibWebRTCPeerConnectionBackend::addUnifiedPlanTransceiver(T&& trackOrKind, const RTCRtpTransceiverInit& init) |
| 454 | { |
| 455 | auto backends = m_endpoint->addTransceiver(trackOrKind, init); |
| 456 | if (!backends) |
| 457 | return Exception { InvalidAccessError, "Unable to add transceiver"_s }; |
| 458 | |
| 459 | auto sender = RTCRtpSender::create(*this, WTFMove(trackOrKind), Vector<String> { }, WTFMove(backends->senderBackend)); |
| 460 | auto receiver = createReceiverForSource(createEmptySource(sender->trackKind(), createCanonicalUUIDString()), WTFMove(backends->receiverBackend)); |
| 461 | auto transceiver = RTCRtpTransceiver::create(WTFMove(sender), WTFMove(receiver), WTFMove(backends->transceiverBackend)); |
| 462 | m_peerConnection.addInternalTransceiver(transceiver.copyRef()); |
| 463 | return transceiver; |
| 464 | } |
| 465 | |
| 466 | ExceptionOr<Ref<RTCRtpTransceiver>> LibWebRTCPeerConnectionBackend::addTransceiver(const String& trackKind, const RTCRtpTransceiverInit& init) |
| 467 | { |
| 468 | if (RuntimeEnabledFeatures::sharedFeatures().webRTCUnifiedPlanEnabled()) |
| 469 | return addUnifiedPlanTransceiver(String { trackKind }, init); |
| 470 | |
| 471 | auto senderBackend = std::make_unique<LibWebRTCRtpSenderBackend>(*this, nullptr); |
| 472 | auto newSender = RTCRtpSender::create(*this, String(trackKind), Vector<String>(), WTFMove(senderBackend)); |
| 473 | return completeAddTransceiver(WTFMove(newSender), init, createCanonicalUUIDString(), trackKind); |
| 474 | } |
| 475 | |
| 476 | ExceptionOr<Ref<RTCRtpTransceiver>> LibWebRTCPeerConnectionBackend::addTransceiver(Ref<MediaStreamTrack>&& track, const RTCRtpTransceiverInit& init) |
| 477 | { |
| 478 | if (RuntimeEnabledFeatures::sharedFeatures().webRTCUnifiedPlanEnabled()) |
| 479 | return addUnifiedPlanTransceiver(WTFMove(track), init); |
| 480 | |
| 481 | auto senderBackend = std::make_unique<LibWebRTCRtpSenderBackend>(*this, nullptr); |
| 482 | auto& backend = *senderBackend; |
| 483 | auto sender = RTCRtpSender::create(*this, track.copyRef(), Vector<String>(), WTFMove(senderBackend)); |
| 484 | if (!m_endpoint->addTrack(backend, track, Vector<String> { })) |
| 485 | return Exception { InvalidAccessError, "Unable to add track"_s }; |
| 486 | |
| 487 | return completeAddTransceiver(WTFMove(sender), init, track->id(), track->kind()); |
| 488 | } |
| 489 | |
| 490 | void LibWebRTCPeerConnectionBackend::setSenderSourceFromTrack(LibWebRTCRtpSenderBackend& sender, MediaStreamTrack& track) |
| 491 | { |
| 492 | m_endpoint->setSenderSourceFromTrack(sender, track); |
| 493 | } |
| 494 | |
| 495 | static inline LibWebRTCRtpTransceiverBackend& backendFromRTPTransceiver(RTCRtpTransceiver& transceiver) |
| 496 | { |
| 497 | return static_cast<LibWebRTCRtpTransceiverBackend&>(*transceiver.backend()); |
| 498 | } |
| 499 | |
| 500 | RTCRtpTransceiver* LibWebRTCPeerConnectionBackend::existingTransceiver(WTF::Function<bool(LibWebRTCRtpTransceiverBackend&)>&& matchingFunction) |
| 501 | { |
| 502 | for (auto& transceiver : m_peerConnection.currentTransceivers()) { |
| 503 | if (matchingFunction(backendFromRTPTransceiver(*transceiver))) |
| 504 | return transceiver.get(); |
| 505 | } |
| 506 | return nullptr; |
| 507 | } |
| 508 | |
| 509 | RTCRtpTransceiver& LibWebRTCPeerConnectionBackend::newRemoteTransceiver(std::unique_ptr<LibWebRTCRtpTransceiverBackend>&& transceiverBackend, Ref<RealtimeMediaSource>&& receiverSource) |
| 510 | { |
| 511 | auto sender = RTCRtpSender::create(*this, receiverSource->type() == RealtimeMediaSource::Type::Audio ? "audio"_s : "video"_s , Vector<String> { }, transceiverBackend->createSenderBackend(*this, nullptr)); |
| 512 | auto receiver = createReceiverForSource(WTFMove(receiverSource), transceiverBackend->createReceiverBackend()); |
| 513 | auto transceiver = RTCRtpTransceiver::create(WTFMove(sender), WTFMove(receiver), WTFMove(transceiverBackend)); |
| 514 | m_peerConnection.addInternalTransceiver(transceiver.copyRef()); |
| 515 | return transceiver.get(); |
| 516 | } |
| 517 | |
| 518 | Ref<RTCRtpTransceiver> LibWebRTCPeerConnectionBackend::completeAddTransceiver(Ref<RTCRtpSender>&& sender, const RTCRtpTransceiverInit& init, const String& trackId, const String& trackKind) |
| 519 | { |
| 520 | auto transceiver = RTCRtpTransceiver::create(WTFMove(sender), createReceiver(trackKind, trackId), nullptr); |
| 521 | |
| 522 | transceiver->setDirection(init.direction); |
| 523 | |
| 524 | m_peerConnection.addInternalTransceiver(transceiver.copyRef()); |
| 525 | return transceiver; |
| 526 | } |
| 527 | |
| 528 | void LibWebRTCPeerConnectionBackend::collectTransceivers() |
| 529 | { |
| 530 | m_endpoint->collectTransceivers(); |
| 531 | } |
| 532 | |
| 533 | void LibWebRTCPeerConnectionBackend::removeTrack(RTCRtpSender& sender) |
| 534 | { |
| 535 | m_endpoint->removeTrack(backendFromRTPSender(sender)); |
| 536 | } |
| 537 | |
| 538 | void LibWebRTCPeerConnectionBackend::applyRotationForOutgoingVideoSources() |
| 539 | { |
| 540 | for (auto& transceiver : m_peerConnection.currentTransceivers()) { |
| 541 | if (!transceiver->sender().isStopped()) { |
| 542 | if (auto* videoSource = backendFromRTPSender(transceiver->sender()).videoSource()) |
| 543 | videoSource->setApplyRotation(true); |
| 544 | } |
| 545 | } |
| 546 | } |
| 547 | |
| 548 | bool LibWebRTCPeerConnectionBackend::shouldOfferAllowToReceive(const char* kind) const |
| 549 | { |
| 550 | ASSERT(!RuntimeEnabledFeatures::sharedFeatures().webRTCUnifiedPlanEnabled()); |
| 551 | for (const auto& transceiver : m_peerConnection.currentTransceivers()) { |
| 552 | if (transceiver->sender().trackKind() != kind) |
| 553 | continue; |
| 554 | |
| 555 | if (transceiver->direction() == RTCRtpTransceiverDirection::Recvonly) |
| 556 | return true; |
| 557 | |
| 558 | if (transceiver->direction() != RTCRtpTransceiverDirection::Sendrecv) |
| 559 | continue; |
| 560 | |
| 561 | auto* backend = static_cast<LibWebRTCRtpSenderBackend*>(transceiver->sender().backend()); |
| 562 | if (backend && !backend->rtcSender()) |
| 563 | return true; |
| 564 | } |
| 565 | return false; |
| 566 | } |
| 567 | |
| 568 | } // namespace WebCore |
| 569 | |
| 570 | #endif // USE(LIBWEBRTC) |
| 571 | |