diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/Modules/mediastream/libwebrtc | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/Modules/mediastream/libwebrtc')
6 files changed, 1205 insertions, 0 deletions
diff --git a/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCDataChannelHandler.cpp b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCDataChannelHandler.cpp new file mode 100644 index 000000000..564b6d1c8 --- /dev/null +++ b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCDataChannelHandler.cpp @@ -0,0 +1,114 @@ +/* + * 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 "config.h" +#include "LibWebRTCDataChannelHandler.h" + +#if USE(LIBWEBRTC) + +#include "RTCDataChannel.h" +#include <wtf/MainThread.h> + +namespace WebCore { + +LibWebRTCDataChannelHandler::~LibWebRTCDataChannelHandler() +{ + if (m_client) + m_channel->UnregisterObserver(); +} + +void LibWebRTCDataChannelHandler::setClient(RTCDataChannelHandlerClient* client) +{ + m_client = client; + if (m_client) + m_channel->RegisterObserver(this); + else + m_channel->UnregisterObserver(); +} + +bool LibWebRTCDataChannelHandler::sendStringData(const String& text) +{ + return m_channel->Send({rtc::CopyOnWriteBuffer(text.utf8().data(), text.length()), false}); +} + +bool LibWebRTCDataChannelHandler::sendRawData(const char* data, size_t length) +{ + return m_channel->Send({rtc::CopyOnWriteBuffer(data, length), true}); +} + +void LibWebRTCDataChannelHandler::close() +{ + m_channel->Close(); +} + +void LibWebRTCDataChannelHandler::OnStateChange() +{ + RTCDataChannel::ReadyState state; + switch (m_channel->state()) { + case webrtc::DataChannelInterface::kConnecting: + state = RTCDataChannel::ReadyStateConnecting; + break; + case webrtc::DataChannelInterface::kOpen: + state = RTCDataChannel::ReadyStateOpen; + break; + case webrtc::DataChannelInterface::kClosing: + state = RTCDataChannel::ReadyStateClosing; + break; + case webrtc::DataChannelInterface::kClosed: + state = RTCDataChannel::ReadyStateClosed; + break; + } + ASSERT(m_client); + callOnMainThread([protectedClient = makeRef(*m_client), state] { + protectedClient->didChangeReadyState(state); + }); +} + +void LibWebRTCDataChannelHandler::OnMessage(const webrtc::DataBuffer& buffer) +{ + ASSERT(m_client); + std::unique_ptr<webrtc::DataBuffer> protectedBuffer(new webrtc::DataBuffer(buffer)); + callOnMainThread([protectedClient = makeRef(*m_client), buffer = WTFMove(protectedBuffer)] { + // FIXME: Ensure this is correct by adding some tests with non-ASCII characters. + const char* data = reinterpret_cast<const char*>(buffer->data.data()); + if (buffer->binary) + protectedClient->didReceiveRawData(data, buffer->size()); + else + protectedClient->didReceiveStringData(String(data, buffer->size())); + }); +} + +void LibWebRTCDataChannelHandler::OnBufferedAmountChange(uint64_t previousAmount) +{ + if (previousAmount <= m_channel->buffered_amount()) + return; + ASSERT(m_client); + callOnMainThread([protectedClient = makeRef(*m_client)] { + protectedClient->bufferedAmountIsDecreasing(); + }); +} + +} // namespace WebCore + +#endif // USE(LIBWEBRTC) diff --git a/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCDataChannelHandler.h b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCDataChannelHandler.h new file mode 100644 index 000000000..7cc3502b7 --- /dev/null +++ b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCDataChannelHandler.h @@ -0,0 +1,61 @@ +/* + * 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 "LibWebRTCMacros.h" +#include "RTCDataChannelHandler.h" +#include <webrtc/api/datachannelinterface.h> + +namespace WebCore { + +class RTCDataChannelHandlerClient; + +class LibWebRTCDataChannelHandler final : public RTCDataChannelHandler, private webrtc::DataChannelObserver { +public: + explicit LibWebRTCDataChannelHandler(rtc::scoped_refptr<webrtc::DataChannelInterface>&& channel) : m_channel(WTFMove(channel)) { ASSERT(m_channel); } + ~LibWebRTCDataChannelHandler(); + +private: + // RTCDataChannelHandler API + void setClient(RTCDataChannelHandlerClient*) final; + bool sendStringData(const String&) final; + bool sendRawData(const char*, size_t) final; + void close() final; + size_t bufferedAmount() const final { return static_cast<size_t>(m_channel->buffered_amount()); } + + // webrtc::DataChannelObserver API + void OnStateChange(); + void OnMessage(const webrtc::DataBuffer&); + void OnBufferedAmountChange(uint64_t); + + rtc::scoped_refptr<webrtc::DataChannelInterface> m_channel; + RTCDataChannelHandlerClient* m_client { nullptr }; +}; + +} // namespace WebCore + +#endif // USE(LIBWEBRTC) diff --git a/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp new file mode 100644 index 000000000..e46c31c4e --- /dev/null +++ b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp @@ -0,0 +1,527 @@ +/* + * 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 "config.h" +#include "LibWebRTCMediaEndpoint.h" + +#if USE(LIBWEBRTC) + +#include "EventNames.h" +#include "LibWebRTCDataChannelHandler.h" +#include "LibWebRTCPeerConnectionBackend.h" +#include "LibWebRTCProvider.h" +#include "MediaStreamEvent.h" +#include "NotImplemented.h" +#include "PlatformStrategies.h" +#include "RTCDataChannel.h" +#include "RTCDataChannelEvent.h" +#include "RTCIceCandidate.h" +#include "RTCPeerConnection.h" +#include "RTCSessionDescription.h" +#include "RTCTrackEvent.h" +#include "RealtimeIncomingAudioSource.h" +#include "RealtimeIncomingVideoSource.h" +#include <webrtc/api/peerconnectionfactory.h> +#include <webrtc/base/physicalsocketserver.h> +#include <webrtc/p2p/base/basicpacketsocketfactory.h> +#include <webrtc/p2p/client/basicportallocator.h> +#include <wtf/MainThread.h> + +#include "CoreMediaSoftLink.h" + +namespace WebCore { + +LibWebRTCMediaEndpoint::LibWebRTCMediaEndpoint(LibWebRTCPeerConnectionBackend& peerConnection, LibWebRTCProvider& 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 "offer"; + case RTCSessionDescription::SdpType::Pranswer: + return "pranswer"; + case RTCSessionDescription::SdpType::Answer: + return "answer"; + case RTCSessionDescription::SdpType::Rollback: + return "rollback"; + } +} + +static inline RTCSessionDescription::SdpType fromSessionDescriptionType(const webrtc::SessionDescriptionInterface& description) +{ + auto type = description.type(); + if (type == webrtc::SessionDescriptionInterface::kOffer) + return RTCSessionDescription::SdpType::Offer; + if (type == webrtc::SessionDescriptionInterface::kAnswer) + return RTCSessionDescription::SdpType::Answer; + ASSERT(type == webrtc::SessionDescriptionInterface::kPrAnswer); + return RTCSessionDescription::SdpType::Pranswer; +} + +static inline RefPtr<RTCSessionDescription> fromSessionDescription(const webrtc::SessionDescriptionInterface* description) +{ + if (!description) + return nullptr; + + std::string sdp; + description->ToString(&sdp); + String sdpString(sdp.data(), sdp.size()); + + return RTCSessionDescription::create(fromSessionDescriptionType(*description), WTFMove(sdpString)); +} + +RefPtr<RTCSessionDescription> LibWebRTCMediaEndpoint::localDescription() const +{ + // FIXME: We might want to create a new object only if the session actually changed. + return fromSessionDescription(m_backend->local_description()); +} + +RefPtr<RTCSessionDescription> LibWebRTCMediaEndpoint::remoteDescription() const +{ + // FIXME: We might want to create a new object only if the session actually changed. + return fromSessionDescription(m_backend->remote_description()); +} + +void LibWebRTCMediaEndpoint::doSetLocalDescription(RTCSessionDescription& description) +{ + webrtc::SdpParseError error; + std::unique_ptr<webrtc::SessionDescriptionInterface> sessionDescription(webrtc::CreateSessionDescription(sessionDescriptionType(description.type()), description.sdp().utf8().data(), &error)); + + if (!sessionDescription) { + String errorMessage(error.description.data(), error.description.size()); + m_peerConnectionBackend.setLocalDescriptionFailed(Exception { OperationError, WTFMove(errorMessage) }); + return; + } + m_backend->SetLocalDescription(&m_setLocalSessionDescriptionObserver, sessionDescription.release()); +} + +void LibWebRTCMediaEndpoint::doSetRemoteDescription(RTCSessionDescription& description) +{ + webrtc::SdpParseError error; + std::unique_ptr<webrtc::SessionDescriptionInterface> sessionDescription(webrtc::CreateSessionDescription(sessionDescriptionType(description.type()), description.sdp().utf8().data(), &error)); + if (!sessionDescription) { + String errorMessage(error.description.data(), error.description.size()); + m_peerConnectionBackend.setRemoteDescriptionFailed(Exception { OperationError, WTFMove(errorMessage) }); + return; + } + m_backend->SetRemoteDescription(&m_setRemoteSessionDescriptionObserver, sessionDescription.release()); +} + +static inline std::string streamId(RTCPeerConnection& connection) +{ + auto& senders = connection.getSenders(); + if (senders.size()) { + for (RTCRtpSender& sender : senders) { + auto* track = sender.track(); + if (track) { + ASSERT(sender.mediaStreamIds().size() == 1); + return std::string(sender.mediaStreamIds().first().utf8().data()); + } + } + } + return "av_label"; +} + +void LibWebRTCMediaEndpoint::doCreateOffer() +{ + m_isInitiator = true; + auto& senders = m_peerConnectionBackend.connection().getSenders(); + if (senders.size()) { + // FIXME: We only support one stream for the moment. + auto stream = LibWebRTCProvider::factory().CreateLocalMediaStream(streamId(m_peerConnectionBackend.connection())); + for (RTCRtpSender& sender : senders) { + auto* track = sender.track(); + if (track) { + ASSERT(sender.mediaStreamIds().size() == 1); + auto& source = track->source(); + if (source.type() == RealtimeMediaSource::Audio) { + auto trackSource = RealtimeOutgoingAudioSource::create(source); + auto rtcTrack = LibWebRTCProvider::factory().CreateAudioTrack(track->id().utf8().data(), trackSource.ptr()); + trackSource->setTrack(rtc::scoped_refptr<webrtc::AudioTrackInterface>(rtcTrack)); + m_peerConnectionBackend.addAudioSource(WTFMove(trackSource)); + stream->AddTrack(WTFMove(rtcTrack)); + } else { + auto videoSource = RealtimeOutgoingVideoSource::create(source); + auto videoTrack = LibWebRTCProvider::factory().CreateVideoTrack(track->id().utf8().data(), videoSource.ptr()); + m_peerConnectionBackend.addVideoSource(WTFMove(videoSource)); + stream->AddTrack(WTFMove(videoTrack)); + } + } + } + m_backend->AddStream(stream); + } + m_backend->CreateOffer(&m_createSessionDescriptionObserver, nullptr); +} + +void LibWebRTCMediaEndpoint::doCreateAnswer() +{ + m_isInitiator = false; + + auto& senders = m_peerConnectionBackend.connection().getSenders(); + if (senders.size()) { + // FIXME: We only support one stream for the moment. + auto stream = LibWebRTCProvider::factory().CreateLocalMediaStream(streamId(m_peerConnectionBackend.connection())); + for (RTCRtpSender& sender : senders) { + auto* track = sender.track(); + if (track) { + ASSERT(sender.mediaStreamIds().size() == 1); + auto& source = track->source(); + if (source.type() == RealtimeMediaSource::Audio) { + auto trackSource = RealtimeOutgoingAudioSource::create(source); + auto rtcTrack = LibWebRTCProvider::factory().CreateAudioTrack(track->id().utf8().data(), trackSource.ptr()); + trackSource->setTrack(rtc::scoped_refptr<webrtc::AudioTrackInterface>(rtcTrack)); + m_peerConnectionBackend.addAudioSource(WTFMove(trackSource)); + stream->AddTrack(WTFMove(rtcTrack)); + } else { + auto videoSource = RealtimeOutgoingVideoSource::create(source); + auto videoTrack = LibWebRTCProvider::factory().CreateVideoTrack(track->id().utf8().data(), videoSource.ptr()); + m_peerConnectionBackend.addVideoSource(WTFMove(videoSource)); + stream->AddTrack(WTFMove(videoTrack)); + } + } + } + m_backend->AddStream(stream); + } + m_backend->CreateAnswer(&m_createSessionDescriptionObserver, nullptr); +} + +void LibWebRTCMediaEndpoint::getStats(MediaStreamTrack* track, const DeferredPromise& promise) +{ + m_backend->GetStats(StatsCollector::create(*this, promise, track).get()); +} + +LibWebRTCMediaEndpoint::StatsCollector::StatsCollector(LibWebRTCMediaEndpoint& endpoint, const DeferredPromise& promise, MediaStreamTrack* track) + : m_endpoint(endpoint) + , m_promise(promise) +{ + if (track) + m_id = track->id(); +} + +void LibWebRTCMediaEndpoint::StatsCollector::OnStatsDelivered(const rtc::scoped_refptr<const webrtc::RTCStatsReport>& report) +{ + callOnMainThread([protectedThis = rtc::scoped_refptr<LibWebRTCMediaEndpoint::StatsCollector>(this), report] { + if (protectedThis->m_endpoint.isStopped()) + return; + + // FIXME: Fulfill promise with the report + UNUSED_PARAM(report); + + protectedThis->m_endpoint.m_peerConnectionBackend.getStatsFailed(protectedThis->m_promise, Exception { TypeError, ASCIILiteral("Stats API is not yet implemented") }); + }); +} + +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->isStopped()) + return; + protectedThis->m_peerConnectionBackend.updateSignalingState(state); + }); +} + +static inline String trackId(webrtc::MediaStreamTrackInterface& videoTrack) +{ + return String(videoTrack.id().data(), videoTrack.id().size()); +} + +static inline Ref<MediaStreamTrack> createMediaStreamTrack(ScriptExecutionContext& context, Ref<RealtimeMediaSource>&& remoteSource) +{ + String trackId = remoteSource->id(); + return MediaStreamTrack::create(context, MediaStreamTrackPrivate::create(WTFMove(remoteSource), WTFMove(trackId))); +} + +void LibWebRTCMediaEndpoint::addStream(webrtc::MediaStreamInterface& stream) +{ + MediaStreamTrackVector tracks; + for (auto& 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& 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<RefPtr<MediaStream>> streams; + streams.append(newStream.copyRef()); + for (auto& track : newStream->getTracks()) + m_peerConnectionBackend.connection().fireEvent(RTCTrackEvent::create(eventNames().trackEvent, false, false, nullptr, track.get(), Vector<RefPtr<MediaStream>>(streams), nullptr)); +} + +void LibWebRTCMediaEndpoint::OnAddStream(rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) +{ + callOnMainThread([protectedThis = makeRef(*this), stream = WTFMove(stream)] { + if (protectedThis->isStopped()) + return; + ASSERT(stream); + protectedThis->addStream(*stream.get()); + }); +} + +void LibWebRTCMediaEndpoint::OnRemoveStream(rtc::scoped_refptr<webrtc::MediaStreamInterface>) +{ + notImplemented(); +} + +std::unique_ptr<RTCDataChannelHandler> LibWebRTCMediaEndpoint::createDataChannel(const String& label, const RTCDataChannelInit& options) +{ + webrtc::DataChannelInit init; + init.ordered = options.ordered; + init.maxRetransmitTime = options.maxRetransmitTime; + init.maxRetransmits = options.maxRetransmits; + init.protocol = options.protocol.utf8().data(); + init.negotiated = options.negotiated; + init.id = options.id; + + return std::make_unique<LibWebRTCDataChannelHandler>(m_backend->CreateDataChannel(label.utf8().data(), &init)); +} + +void LibWebRTCMediaEndpoint::addDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface>&& dataChannel) +{ + auto protocol = dataChannel->protocol(); + auto label = dataChannel->label(); + + RTCDataChannelInit init; + init.ordered = dataChannel->ordered(); + init.maxRetransmitTime = dataChannel->maxRetransmitTime(); + init.maxRetransmits = dataChannel->maxRetransmits(); + init.protocol = String(protocol.data(), protocol.size()); + init.negotiated = dataChannel->negotiated(); + init.id = dataChannel->id(); + + bool isOpened = dataChannel->state() == webrtc::DataChannelInterface::kOpen; + + auto handler = std::make_unique<LibWebRTCDataChannelHandler>(WTFMove(dataChannel)); + ASSERT(m_peerConnectionBackend.connection().scriptExecutionContext()); + auto channel = RTCDataChannel::create(*m_peerConnectionBackend.connection().scriptExecutionContext(), WTFMove(handler), String(label.data(), label.size()), WTFMove(init)); + + if (isOpened) { + callOnMainThread([channel = channel.copyRef()] { + // FIXME: We should be able to write channel->didChangeReadyState(...) + RTCDataChannelHandlerClient& client = channel.get(); + client.didChangeReadyState(RTCDataChannel::ReadyStateOpen); + }); + } + + m_peerConnectionBackend.connection().fireEvent(RTCDataChannelEvent::create(eventNames().datachannelEvent, false, false, WTFMove(channel))); +} + +void LibWebRTCMediaEndpoint::OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface> dataChannel) +{ + callOnMainThread([protectedThis = makeRef(*this), dataChannel = WTFMove(dataChannel)] { + if (protectedThis->isStopped()) + return; + protectedThis->addDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface>(dataChannel)); + }); +} + +void LibWebRTCMediaEndpoint::stop() +{ + ASSERT(m_backend); + m_backend->Close(); + m_backend = nullptr; +} + +void LibWebRTCMediaEndpoint::OnRenegotiationNeeded() +{ + callOnMainThread([protectedThis = makeRef(*this)] { + if (protectedThis->isStopped()) + return; + protectedThis->m_peerConnectionBackend.markAsNeedingNegotiation(); + }); +} + +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->isStopped()) + return; + if (protectedThis->m_peerConnectionBackend.connection().internalIceConnectionState() != connectionState) + protectedThis->m_peerConnectionBackend.connection().updateIceConnectionState(connectionState); + }); +} + +void LibWebRTCMediaEndpoint::OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState state) +{ + if (state == webrtc::PeerConnectionInterface::kIceGatheringComplete) { + callOnMainThread([protectedThis = makeRef(*this)] { + if (protectedThis->isStopped()) + return; + protectedThis->m_peerConnectionBackend.doneGatheringCandidates(); + }); + } +} + +void LibWebRTCMediaEndpoint::OnIceCandidate(const webrtc::IceCandidateInterface *rtcCandidate) +{ + ASSERT(rtcCandidate); + + std::string sdp; + rtcCandidate->ToString(&sdp); + String candidateSDP(sdp.data(), sdp.size()); + + auto mid = rtcCandidate->sdp_mid(); + String candidateMid(mid.data(), mid.size()); + + callOnMainThread([protectedThis = makeRef(*this), mid = WTFMove(candidateMid), sdp = WTFMove(candidateSDP)] { + if (protectedThis->isStopped()) + return; + protectedThis->m_peerConnectionBackend.fireICECandidateEvent(RTCIceCandidate::create(String(sdp), String(mid), 0)); + }); +} + +void LibWebRTCMediaEndpoint::OnIceCandidatesRemoved(const std::vector<cricket::Candidate>&) +{ + ASSERT_NOT_REACHED(); +} + +void LibWebRTCMediaEndpoint::createSessionDescriptionSucceeded(webrtc::SessionDescriptionInterface* description) +{ + std::string sdp; + description->ToString(&sdp); + String sdpString(sdp.data(), sdp.size()); + + callOnMainThread([protectedThis = makeRef(*this), sdp = WTFMove(sdpString)] { + if (protectedThis->isStopped()) + return; + if (protectedThis->m_isInitiator) + protectedThis->m_peerConnectionBackend.createOfferSucceeded(String(sdp)); + else + protectedThis->m_peerConnectionBackend.createAnswerSucceeded(String(sdp)); + }); +} + +void LibWebRTCMediaEndpoint::createSessionDescriptionFailed(const std::string& errorMessage) +{ + String error(errorMessage.data(), errorMessage.size()); + callOnMainThread([protectedThis = makeRef(*this), error = WTFMove(error)] { + if (protectedThis->isStopped()) + return; + if (protectedThis->m_isInitiator) + protectedThis->m_peerConnectionBackend.createOfferFailed(Exception { OperationError, String(error) }); + else + protectedThis->m_peerConnectionBackend.createAnswerFailed(Exception { OperationError, String(error) }); + }); +} + +void LibWebRTCMediaEndpoint::setLocalSessionDescriptionSucceeded() +{ + callOnMainThread([protectedThis = makeRef(*this)] { + if (protectedThis->isStopped()) + return; + protectedThis->m_peerConnectionBackend.setLocalDescriptionSucceeded(); + }); +} + +void LibWebRTCMediaEndpoint::setLocalSessionDescriptionFailed(const std::string& errorMessage) +{ + String error(errorMessage.data(), errorMessage.size()); + callOnMainThread([protectedThis = makeRef(*this), error = WTFMove(error)] { + if (protectedThis->isStopped()) + return; + protectedThis->m_peerConnectionBackend.setLocalDescriptionFailed(Exception { OperationError, String(error) }); + }); +} + +void LibWebRTCMediaEndpoint::setRemoteSessionDescriptionSucceeded() +{ + callOnMainThread([protectedThis = makeRef(*this)] { + if (protectedThis->isStopped()) + return; + protectedThis->m_peerConnectionBackend.setRemoteDescriptionSucceeded(); + }); +} + +void LibWebRTCMediaEndpoint::setRemoteSessionDescriptionFailed(const std::string& errorMessage) +{ + String error(errorMessage.data(), errorMessage.size()); + callOnMainThread([protectedThis = makeRef(*this), error = WTFMove(error)] { + if (protectedThis->isStopped()) + return; + protectedThis->m_peerConnectionBackend.setRemoteDescriptionFailed(Exception { OperationError, String(error) }); + }); +} + +} // namespace WebCore + +#endif // USE(LIBWEBRTC) diff --git a/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.h b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.h new file mode 100644 index 000000000..7b308e0b1 --- /dev/null +++ b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.h @@ -0,0 +1,175 @@ +/* + * 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 "LibWebRTCProvider.h" +#include "PeerConnectionBackend.h" +#include "RealtimeOutgoingAudioSource.h" +#include "RealtimeOutgoingVideoSource.h" + +#include <webrtc/api/jsep.h> +#include <webrtc/api/peerconnectionfactory.h> +#include <webrtc/api/peerconnectioninterface.h> +#include <webrtc/api/rtcstatscollector.h> + +#include <wtf/ThreadSafeRefCounted.h> + +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<LibWebRTCMediaEndpoint>, private webrtc::PeerConnectionObserver { +public: + static Ref<LibWebRTCMediaEndpoint> create(LibWebRTCPeerConnectionBackend& peerConnection, LibWebRTCProvider& client) { return adoptRef(*new LibWebRTCMediaEndpoint(peerConnection, client)); } + virtual ~LibWebRTCMediaEndpoint() { } + + webrtc::PeerConnectionInterface& backend() const { ASSERT(m_backend); return *m_backend.get(); } + void doSetLocalDescription(RTCSessionDescription&); + void doSetRemoteDescription(RTCSessionDescription&); + void doCreateOffer(); + void doCreateAnswer(); + void getStats(MediaStreamTrack*, const DeferredPromise&); + std::unique_ptr<RTCDataChannelHandler> createDataChannel(const String&, const RTCDataChannelInit&); + bool addIceCandidate(webrtc::IceCandidateInterface& candidate) { return m_backend->AddIceCandidate(&candidate); } + + void stop(); + bool isStopped() const { return !m_backend; } + + RefPtr<RTCSessionDescription> localDescription() const; + RefPtr<RTCSessionDescription> remoteDescription() const; + +private: + LibWebRTCMediaEndpoint(LibWebRTCPeerConnectionBackend&, LibWebRTCProvider&); + + // webrtc::PeerConnectionObserver API + void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState) final; + void OnAddStream(rtc::scoped_refptr<webrtc::MediaStreamInterface>) final; + void OnRemoveStream(rtc::scoped_refptr<webrtc::MediaStreamInterface>) final; + void OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface>) 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<cricket::Candidate>&) final; + + void createSessionDescriptionSucceeded(webrtc::SessionDescriptionInterface*); + void createSessionDescriptionFailed(const std::string&); + void setLocalSessionDescriptionSucceeded(); + void setLocalSessionDescriptionFailed(const std::string&); + void setRemoteSessionDescriptionSucceeded(); + void setRemoteSessionDescriptionFailed(const std::string&); + void addStream(webrtc::MediaStreamInterface&); + void addDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface>&&); + + int AddRef() const { ref(); return static_cast<int>(refCount()); } + int Release() const { deref(); return static_cast<int>(refCount()); } + + class CreateSessionDescriptionObserver final : public webrtc::CreateSessionDescriptionObserver { + public: + explicit CreateSessionDescriptionObserver(LibWebRTCMediaEndpoint &endpoint) : m_endpoint(endpoint) { } + + void OnSuccess(webrtc::SessionDescriptionInterface* sessionDescription) final { m_endpoint.createSessionDescriptionSucceeded(sessionDescription); } + void OnFailure(const std::string& error) final { m_endpoint.createSessionDescriptionFailed(error); } + + int AddRef() const { return m_endpoint.AddRef(); } + int Release() const { return m_endpoint.Release(); } + + private: + LibWebRTCMediaEndpoint& m_endpoint; + }; + + class SetLocalSessionDescriptionObserver final : public webrtc::SetSessionDescriptionObserver { + public: + explicit SetLocalSessionDescriptionObserver(LibWebRTCMediaEndpoint &endpoint) : m_endpoint(endpoint) { } + + void OnSuccess() final { m_endpoint.setLocalSessionDescriptionSucceeded(); } + void OnFailure(const std::string& error) final { m_endpoint.setLocalSessionDescriptionFailed(error); } + + int AddRef() const { return m_endpoint.AddRef(); } + int Release() const { return m_endpoint.Release(); } + + private: + LibWebRTCMediaEndpoint& m_endpoint; + }; + + class SetRemoteSessionDescriptionObserver final : public webrtc::SetSessionDescriptionObserver { + public: + explicit SetRemoteSessionDescriptionObserver(LibWebRTCMediaEndpoint &endpoint) : m_endpoint(endpoint) { } + + void OnSuccess() final { m_endpoint.setRemoteSessionDescriptionSucceeded(); } + void OnFailure(const std::string& error) final { m_endpoint.setRemoteSessionDescriptionFailed(error); } + + int AddRef() const { return m_endpoint.AddRef(); } + int Release() const { return m_endpoint.Release(); } + + private: + LibWebRTCMediaEndpoint& m_endpoint; + }; + + class StatsCollector final : public webrtc::RTCStatsCollectorCallback { + public: + static rtc::scoped_refptr<StatsCollector> create(LibWebRTCMediaEndpoint& endpoint, const DeferredPromise& promise, MediaStreamTrack* track) { return new StatsCollector(endpoint, promise, track); } + + int AddRef() const { return m_endpoint.AddRef(); } + int Release() const { return m_endpoint.Release(); } + + private: + StatsCollector(LibWebRTCMediaEndpoint&, const DeferredPromise&, MediaStreamTrack*); + + void OnStatsDelivered(const rtc::scoped_refptr<const webrtc::RTCStatsReport>&) final; + + LibWebRTCMediaEndpoint& m_endpoint; + const DeferredPromise& m_promise; + String m_id; + }; + + LibWebRTCPeerConnectionBackend& m_peerConnectionBackend; + rtc::scoped_refptr<webrtc::PeerConnectionInterface> m_backend; + + CreateSessionDescriptionObserver m_createSessionDescriptionObserver; + SetLocalSessionDescriptionObserver m_setLocalSessionDescriptionObserver; + SetRemoteSessionDescriptionObserver m_setRemoteSessionDescriptionObserver; + + bool m_isInitiator { false }; +}; + +} // namespace WebCore + +#endif // USE(LIBWEBRTC) diff --git a/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp new file mode 100644 index 000000000..c8a382c5b --- /dev/null +++ b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp @@ -0,0 +1,231 @@ +/* + * 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 "config.h" +#include "LibWebRTCPeerConnectionBackend.h" + +#if USE(LIBWEBRTC) + +#include "Document.h" +#include "IceCandidate.h" +#include "JSRTCStatsReport.h" +#include "LibWebRTCDataChannelHandler.h" +#include "LibWebRTCMediaEndpoint.h" +#include "MediaEndpointConfiguration.h" +#include "Page.h" +#include "RTCIceCandidate.h" +#include "RTCPeerConnection.h" +#include "RTCRtpReceiver.h" +#include "RTCSessionDescription.h" +#include "RealtimeIncomingAudioSource.h" +#include "RealtimeIncomingVideoSource.h" + +namespace WebCore { + +static std::unique_ptr<PeerConnectionBackend> createLibWebRTCPeerConnectionBackend(RTCPeerConnection& peerConnection) +{ + return std::make_unique<LibWebRTCPeerConnectionBackend>(peerConnection); +} + +CreatePeerConnectionBackend PeerConnectionBackend::create = createLibWebRTCPeerConnectionBackend; + +static inline LibWebRTCProvider& libWebRTCProvider(RTCPeerConnection& peerConnection) +{ + ASSERT(peerConnection.scriptExecutionContext()->isDocument()); + auto* page = static_cast<Document*>(peerConnection.scriptExecutionContext())->page(); + return page->libWebRTCProvider(); +} + +LibWebRTCPeerConnectionBackend::LibWebRTCPeerConnectionBackend(RTCPeerConnection& peerConnection) + : PeerConnectionBackend(peerConnection) + , m_endpoint(LibWebRTCMediaEndpoint::create(*this, libWebRTCProvider(peerConnection))) +{ +} + +LibWebRTCPeerConnectionBackend::~LibWebRTCPeerConnectionBackend() +{ +} + +static webrtc::PeerConnectionInterface::RTCConfiguration configurationFromMediaEndpointConfiguration(MediaEndpointConfiguration&& 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& server : configuration.iceServers) { + webrtc::PeerConnectionInterface::IceServer iceServer; + iceServer.username = server.username.utf8().data(); + iceServer.password = server.credential.utf8().data(); + for (auto& url : server.urls) + iceServer.urls.push_back({ url.string().utf8().data() }); + rtcConfiguration.servers.push_back(WTFMove(iceServer)); + } + + return rtcConfiguration; +} + +void LibWebRTCPeerConnectionBackend::setConfiguration(MediaEndpointConfiguration&& configuration) +{ + m_endpoint->backend().SetConfiguration(configurationFromMediaEndpointConfiguration(WTFMove(configuration))); +} + +void LibWebRTCPeerConnectionBackend::getStats(MediaStreamTrack* track, Ref<DeferredPromise>&& promise) +{ + if (m_endpoint->isStopped()) + return; + + auto& statsPromise = promise.get(); + m_statsPromises.add(&statsPromise, WTFMove(promise)); + m_endpoint->getStats(track, statsPromise); +} + +void LibWebRTCPeerConnectionBackend::getStatsSucceeded(const DeferredPromise& promise, Ref<RTCStatsReport>&& report) +{ + auto statsPromise = m_statsPromises.take(&promise); + ASSERT(statsPromise); + statsPromise.value()->resolve<IDLInterface<RTCStatsReport>>(WTFMove(report)); +} + +void LibWebRTCPeerConnectionBackend::getStatsFailed(const DeferredPromise& promise, Exception&& exception) +{ + auto statsPromise = m_statsPromises.take(&promise); + ASSERT(statsPromise); + statsPromise.value()->reject(WTFMove(exception)); +} + +void LibWebRTCPeerConnectionBackend::doSetLocalDescription(RTCSessionDescription& description) +{ + m_endpoint->doSetLocalDescription(description); + if (!m_isLocalDescriptionSet) { + if (m_isRemoteDescriptionSet) { + while (m_pendingCandidates.size()) + m_endpoint->addIceCandidate(*m_pendingCandidates.takeLast().release()); + } + m_isLocalDescriptionSet = true; + } +} + +void LibWebRTCPeerConnectionBackend::doSetRemoteDescription(RTCSessionDescription& description) +{ + m_endpoint->doSetRemoteDescription(description); + if (!m_isRemoteDescriptionSet) { + if (m_isLocalDescriptionSet) { + while (m_pendingCandidates.size()) + m_endpoint->addIceCandidate(*m_pendingCandidates.takeLast().release()); + } + m_isRemoteDescriptionSet = true; + } +} + +void LibWebRTCPeerConnectionBackend::doCreateOffer(RTCOfferOptions&&) +{ + m_endpoint->doCreateOffer(); +} + +void LibWebRTCPeerConnectionBackend::doCreateAnswer(RTCAnswerOptions&&) +{ + if (!m_isRemoteDescriptionSet) { + createAnswerFailed(Exception { INVALID_STATE_ERR, "No remote description set" }); + return; + } + m_endpoint->doCreateAnswer(); +} + +void LibWebRTCPeerConnectionBackend::doStop() +{ + m_endpoint->stop(); +} + +void LibWebRTCPeerConnectionBackend::doAddIceCandidate(RTCIceCandidate& candidate) +{ + if (!m_isRemoteDescriptionSet) { + addIceCandidateFailed(Exception { INVALID_STATE_ERR, "No remote description set" }); + return; + } + + webrtc::SdpParseError error; + int sdpMLineIndex = candidate.sdpMLineIndex() ? candidate.sdpMLineIndex().value() : 0; + std::unique_ptr<webrtc::IceCandidateInterface> rtcCandidate(webrtc::CreateIceCandidate(candidate.sdpMid().utf8().data(), sdpMLineIndex, candidate.candidate().utf8().data(), &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_pendingCandidates.append(WTFMove(rtcCandidate)); + else if (!m_endpoint->addIceCandidate(*rtcCandidate.get())) { + ASSERT_NOT_REACHED(); + addIceCandidateFailed(Exception { OperationError, ASCIILiteral("Failed to apply the received candidate") }); + return; + } + addIceCandidateSucceeded(); +} + +void LibWebRTCPeerConnectionBackend::addAudioSource(Ref<RealtimeOutgoingAudioSource>&& source) +{ + m_audioSources.append(WTFMove(source)); +} + +void LibWebRTCPeerConnectionBackend::addVideoSource(Ref<RealtimeOutgoingVideoSource>&& source) +{ + m_videoSources.append(WTFMove(source)); +} + +Ref<RTCRtpReceiver> LibWebRTCPeerConnectionBackend::createReceiver(const String&, const String& trackKind, const String& 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 == "audio") ? 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<RTCDataChannelHandler> LibWebRTCPeerConnectionBackend::createDataChannelHandler(const String& label, const RTCDataChannelInit& options) +{ + return m_endpoint->createDataChannel(label, options); +} + +RefPtr<RTCSessionDescription> LibWebRTCPeerConnectionBackend::localDescription() const +{ + return m_endpoint->localDescription(); +} + +RefPtr<RTCSessionDescription> LibWebRTCPeerConnectionBackend::remoteDescription() const +{ + return m_endpoint->remoteDescription(); +} + +} // namespace WebCore + +#endif // USE(LIBWEBRTC) diff --git a/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h new file mode 100644 index 000000000..f5b4c565f --- /dev/null +++ b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h @@ -0,0 +1,97 @@ +/* + * 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 "PeerConnectionBackend.h" +#include <wtf/HashMap.h> + +namespace webrtc { +class IceCandidateInterface; +} + +namespace WebCore { + +class LibWebRTCMediaEndpoint; +class RTCRtpReceiver; +class RTCSessionDescription; +class RTCstatsReport; +class RealtimeOutgoingAudioSource; +class RealtimeOutgoingVideoSource; + +class LibWebRTCPeerConnectionBackend final : public PeerConnectionBackend { +public: + explicit LibWebRTCPeerConnectionBackend(RTCPeerConnection&); + ~LibWebRTCPeerConnectionBackend(); + +private: + void doCreateOffer(RTCOfferOptions&&) final; + void doCreateAnswer(RTCAnswerOptions&&) final; + void doSetLocalDescription(RTCSessionDescription&) final; + void doSetRemoteDescription(RTCSessionDescription&) final; + void doAddIceCandidate(RTCIceCandidate&) final; + void doStop() final; + std::unique_ptr<RTCDataChannelHandler> createDataChannelHandler(const String&, const RTCDataChannelInit&) final; + void setConfiguration(MediaEndpointConfiguration&&) final; + void getStats(MediaStreamTrack*, Ref<DeferredPromise>&&) final; + Ref<RTCRtpReceiver> createReceiver(const String& transceiverMid, const String& trackKind, const String& trackId) final; + + RefPtr<RTCSessionDescription> localDescription() const final; + RefPtr<RTCSessionDescription> currentLocalDescription() const final { return localDescription(); } + RefPtr<RTCSessionDescription> pendingLocalDescription() const final { return localDescription(); } + + RefPtr<RTCSessionDescription> remoteDescription() const final; + RefPtr<RTCSessionDescription> currentRemoteDescription() const final { return remoteDescription(); } + RefPtr<RTCSessionDescription> pendingRemoteDescription() const final { return remoteDescription(); } + + // FIXME: API to implement for real + Vector<RefPtr<MediaStream>> getRemoteStreams() const final { return { }; } + void replaceTrack(RTCRtpSender&, RefPtr<MediaStreamTrack>&&, DOMPromise<void>&&) final { } + + void emulatePlatformEvent(const String&) final { } + + friend LibWebRTCMediaEndpoint; + RTCPeerConnection& connection() { return m_peerConnection; } + void addAudioSource(Ref<RealtimeOutgoingAudioSource>&&); + void addVideoSource(Ref<RealtimeOutgoingVideoSource>&&); + + void getStatsSucceeded(const DeferredPromise&, Ref<RTCStatsReport>&&); + void getStatsFailed(const DeferredPromise&, Exception&&); + +private: + Ref<LibWebRTCMediaEndpoint> m_endpoint; + bool m_isLocalDescriptionSet { false }; + bool m_isRemoteDescriptionSet { false }; + + Vector<std::unique_ptr<webrtc::IceCandidateInterface>> m_pendingCandidates; + Vector<Ref<RealtimeOutgoingAudioSource>> m_audioSources; + Vector<Ref<RealtimeOutgoingVideoSource>> m_videoSources; + HashMap<const DeferredPromise*, Ref<DeferredPromise>> m_statsPromises; +}; + +} // namespace WebCore + +#endif // USE(LIBWEBRTC) |