MediaEndpointPeerConnection.cpp [plain text]
#include "config.h"
#include "MediaEndpointPeerConnection.h"
#if ENABLE(WEB_RTC)
#include "EventNames.h"
#include "JSRTCSessionDescription.h"
#include "MediaEndpointSessionConfiguration.h"
#include "MediaEndpointSessionDescription.h"
#include "MediaStream.h"
#include "MediaStreamEvent.h"
#include "MediaStreamTrack.h"
#include "NotImplemented.h"
#include "PeerMediaDescription.h"
#include "RTCAnswerOptions.h"
#include "RTCConfiguration.h"
#include "RTCIceCandidate.h"
#include "RTCOfferOptions.h"
#include "RTCPeerConnection.h"
#include "RTCPeerConnectionIceEvent.h"
#include "RTCRtpTransceiver.h"
#include "RTCTrackEvent.h"
#include "SDPProcessor.h"
#include <wtf/Function.h>
#include <wtf/MainThread.h>
#include <wtf/text/Base64.h>
namespace WebCore {
using namespace PeerConnection;
using MediaDescriptionVector = Vector<PeerMediaDescription>;
using RtpTransceiverVector = Vector<RefPtr<RTCRtpTransceiver>>;
static const size_t cnameSize = 18;
static const size_t iceUfragSize = 6;
static const size_t icePasswordSize = 24;
#if !USE(LIBWEBRTC)
static std::unique_ptr<PeerConnectionBackend> createMediaEndpointPeerConnection(RTCPeerConnection& peerConnection)
{
return std::unique_ptr<PeerConnectionBackend>(new MediaEndpointPeerConnection(peerConnection));
}
CreatePeerConnectionBackend PeerConnectionBackend::create = createMediaEndpointPeerConnection;
#endif
static String randomString(size_t size)
{
unsigned char randomValues[size];
cryptographicallyRandomValues(randomValues, size);
return base64Encode(randomValues, size);
}
MediaEndpointPeerConnection::MediaEndpointPeerConnection(RTCPeerConnection& peerConnection)
: PeerConnectionBackend(peerConnection)
, m_mediaEndpoint(MediaEndpoint::create(*this))
, m_sdpProcessor(std::make_unique<SDPProcessor>(m_peerConnection.scriptExecutionContext()))
, m_cname(randomString(cnameSize))
, m_iceUfrag(randomString(iceUfragSize))
, m_icePassword(randomString(icePasswordSize))
{
ASSERT(m_mediaEndpoint);
m_defaultAudioPayloads = m_mediaEndpoint->getDefaultAudioPayloads();
m_defaultVideoPayloads = m_mediaEndpoint->getDefaultVideoPayloads();
m_mediaEndpoint->generateDtlsInfo();
}
static RTCRtpTransceiver* matchTransceiver(const RtpTransceiverVector& transceivers, const WTF::Function<bool(RTCRtpTransceiver&)>& matchFunction)
{
for (auto& transceiver : transceivers) {
if (matchFunction(*transceiver))
return transceiver.get();
}
return nullptr;
}
static RTCRtpTransceiver* matchTransceiverByMid(const RtpTransceiverVector& transceivers, const String& mid)
{
return matchTransceiver(transceivers, [&mid] (RTCRtpTransceiver& current) {
return current.mid() == mid;
});
}
static bool hasUnassociatedTransceivers(const RtpTransceiverVector& transceivers)
{
return matchTransceiver(transceivers, [] (RTCRtpTransceiver& current) {
return current.mid().isNull() && !current.stopped();
});
}
void MediaEndpointPeerConnection::runTask(Function<void ()>&& task)
{
if (m_dtlsFingerprint.isNull()) {
ASSERT(!m_initialDeferredTask);
m_initialDeferredTask = WTFMove(task);
} else
callOnMainThread(WTFMove(task));
}
void MediaEndpointPeerConnection::startRunningTasks()
{
if (!m_initialDeferredTask)
return;
m_initialDeferredTask();
m_initialDeferredTask = nullptr;
}
void MediaEndpointPeerConnection::doCreateOffer(RTCOfferOptions&& options)
{
runTask([this, protectedOptions = WTFMove(options)]() mutable {
createOfferTask(protectedOptions);
});
}
void MediaEndpointPeerConnection::createOfferTask(const RTCOfferOptions&)
{
ASSERT(!m_dtlsFingerprint.isEmpty());
MediaEndpointSessionDescription* localDescription = internalLocalDescription();
RefPtr<MediaEndpointSessionConfiguration> configurationSnapshot = localDescription ?
localDescription->configuration()->clone() : MediaEndpointSessionConfiguration::create();
configurationSnapshot->setBundlePolicy(m_peerConnection.getConfiguration().bundlePolicy);
configurationSnapshot->setSessionVersion(m_sdpOfferSessionVersion++);
auto transceivers = RtpTransceiverVector(m_peerConnection.getTransceivers());
for (auto& mediaDescription : configurationSnapshot->mediaDescriptions()) {
if (!mediaDescription.port) {
continue;
}
RTCRtpTransceiver* transceiver = matchTransceiverByMid(transceivers, mediaDescription.mid);
if (!transceiver)
continue;
mediaDescription.mode = transceiver->directionString();
if (transceiver->hasSendingDirection()) {
auto& sender = transceiver->sender();
mediaDescription.mediaStreamId = sender.mediaStreamIds()[0];
mediaDescription.mediaStreamTrackId = sender.trackId();
}
transceivers.removeFirst(transceiver);
}
for (auto& transceiver : transceivers) {
PeerMediaDescription mediaDescription;
auto& sender = transceiver->sender();
mediaDescription.mode = transceiver->directionString();
mediaDescription.mid = transceiver->provisionalMid();
mediaDescription.mediaStreamId = sender.mediaStreamIds()[0];
mediaDescription.type = sender.trackKind();
mediaDescription.payloads = sender.trackKind() == "audio" ? m_defaultAudioPayloads : m_defaultVideoPayloads;
mediaDescription.dtlsFingerprintHashFunction = m_dtlsFingerprintFunction;
mediaDescription.dtlsFingerprint = m_dtlsFingerprint;
mediaDescription.cname = m_cname;
mediaDescription.addSsrc(cryptographicallyRandomNumber());
mediaDescription.iceUfrag = m_iceUfrag;
mediaDescription.icePassword = m_icePassword;
if (sender.track())
mediaDescription.mediaStreamTrackId = sender.trackId();
configurationSnapshot->addMediaDescription(WTFMove(mediaDescription));
}
String sdp;
SDPProcessor::Result result = m_sdpProcessor->generate(*configurationSnapshot, sdp);
if (result != SDPProcessor::Result::Success) {
createOfferFailed(Exception { OperationError, "SDPProcessor internal error" });
return;
}
createOfferSucceeded(WTFMove(sdp));
}
void MediaEndpointPeerConnection::doCreateAnswer(RTCAnswerOptions&& options)
{
runTask([this, protectedOptions = WTFMove(options)]() mutable {
createAnswerTask(protectedOptions);
});
}
void MediaEndpointPeerConnection::createAnswerTask(const RTCAnswerOptions&)
{
ASSERT(!m_dtlsFingerprint.isEmpty());
if (!internalRemoteDescription()) {
createAnswerFailed(Exception { INVALID_STATE_ERR, "No remote description set" });
return;
}
MediaEndpointSessionDescription* localDescription = internalLocalDescription();
RefPtr<MediaEndpointSessionConfiguration> configurationSnapshot = localDescription ?
localDescription->configuration()->clone() : MediaEndpointSessionConfiguration::create();
configurationSnapshot->setBundlePolicy(m_peerConnection.getConfiguration().bundlePolicy);
configurationSnapshot->setSessionVersion(m_sdpAnswerSessionVersion++);
auto transceivers = RtpTransceiverVector(m_peerConnection.getTransceivers());
auto& remoteMediaDescriptions = internalRemoteDescription()->configuration()->mediaDescriptions();
for (unsigned i = 0; i < remoteMediaDescriptions.size(); ++i) {
auto& remoteMediaDescription = remoteMediaDescriptions[i];
auto* transceiver = matchTransceiverByMid(transceivers, remoteMediaDescription.mid);
if (!transceiver) {
LOG_ERROR("Could not find a matching transceiver for remote description while creating answer");
continue;
}
if (i >= configurationSnapshot->mediaDescriptions().size()) {
PeerMediaDescription newMediaDescription;
auto& sender = transceiver->sender();
if (sender.track()) {
if (sender.mediaStreamIds().size())
newMediaDescription.mediaStreamId = sender.mediaStreamIds()[0];
newMediaDescription.mediaStreamTrackId = sender.trackId();
newMediaDescription.addSsrc(cryptographicallyRandomNumber());
}
newMediaDescription.mode = transceiver->directionString();
newMediaDescription.type = remoteMediaDescription.type;
newMediaDescription.mid = remoteMediaDescription.mid;
newMediaDescription.dtlsSetup = remoteMediaDescription.dtlsSetup == "active" ? "passive" : "active";
newMediaDescription.dtlsFingerprintHashFunction = m_dtlsFingerprintFunction;
newMediaDescription.dtlsFingerprint = m_dtlsFingerprint;
newMediaDescription.cname = m_cname;
newMediaDescription.iceUfrag = m_iceUfrag;
newMediaDescription.icePassword = m_icePassword;
configurationSnapshot->addMediaDescription(WTFMove(newMediaDescription));
}
PeerMediaDescription& localMediaDescription = configurationSnapshot->mediaDescriptions()[i];
localMediaDescription.payloads = remoteMediaDescription.payloads;
localMediaDescription.rtcpMux = remoteMediaDescription.rtcpMux;
if (!localMediaDescription.ssrcs.size())
localMediaDescription.addSsrc(cryptographicallyRandomNumber());
if (localMediaDescription.dtlsSetup == "actpass")
localMediaDescription.dtlsSetup = "passive";
transceivers.removeFirst(transceiver);
}
if (hasUnassociatedTransceivers(transceivers))
markAsNeedingNegotiation();
String sdp;
SDPProcessor::Result result = m_sdpProcessor->generate(*configurationSnapshot, sdp);
if (result != SDPProcessor::Result::Success) {
createAnswerFailed(Exception { OperationError, "SDPProcessor internal error" });
return;
}
createAnswerSucceeded(WTFMove(sdp));
}
static RealtimeMediaSourceMap createSourceMap(const MediaDescriptionVector& remoteMediaDescriptions, unsigned localMediaDescriptionCount, const RtpTransceiverVector& transceivers)
{
RealtimeMediaSourceMap sourceMap;
for (unsigned i = 0; i < remoteMediaDescriptions.size() && i < localMediaDescriptionCount; ++i) {
auto& remoteMediaDescription = remoteMediaDescriptions[i];
if (remoteMediaDescription.type != "audio" && remoteMediaDescription.type != "video")
continue;
RTCRtpTransceiver* transceiver = matchTransceiverByMid(transceivers, remoteMediaDescription.mid);
if (transceiver) {
if (transceiver->hasSendingDirection() && transceiver->sender().track())
sourceMap.set(transceiver->mid(), &transceiver->sender().track()->source());
}
}
return sourceMap;
}
void MediaEndpointPeerConnection::doSetLocalDescription(RTCSessionDescription& description)
{
runTask([this, protectedDescription = RefPtr<RTCSessionDescription>(&description)]() mutable {
setLocalDescriptionTask(WTFMove(protectedDescription));
});
}
void MediaEndpointPeerConnection::setLocalDescriptionTask(RefPtr<RTCSessionDescription>&& description)
{
if (m_peerConnection.isClosed())
return;
auto result = MediaEndpointSessionDescription::create(WTFMove(description), *m_sdpProcessor);
if (result.hasException()) {
setLocalDescriptionFailed(result.releaseException());
return;
}
auto newDescription = result.releaseReturnValue();
const RtpTransceiverVector& transceivers = m_peerConnection.getTransceivers();
const MediaDescriptionVector& mediaDescriptions = newDescription->configuration()->mediaDescriptions();
MediaEndpointSessionDescription* localDescription = internalLocalDescription();
unsigned previousNumberOfMediaDescriptions = localDescription ? localDescription->configuration()->mediaDescriptions().size() : 0;
bool hasNewMediaDescriptions = mediaDescriptions.size() > previousNumberOfMediaDescriptions;
bool isInitiator = newDescription->type() == RTCSdpType::Offer;
if (hasNewMediaDescriptions) {
MediaEndpoint::UpdateResult result = m_mediaEndpoint->updateReceiveConfiguration(newDescription->configuration(), isInitiator);
if (result == MediaEndpoint::UpdateResult::SuccessWithIceRestart) {
if (m_peerConnection.iceGatheringState() != RTCIceGatheringState::Gathering)
m_peerConnection.updateIceGatheringState(RTCIceGatheringState::Gathering);
if (m_peerConnection.iceConnectionState() != RTCIceConnectionState::Completed)
m_peerConnection.updateIceConnectionState(RTCIceConnectionState::Connected);
LOG_ERROR("ICE restart is not implemented");
notImplemented();
} else if (result == MediaEndpoint::UpdateResult::Failed) {
setLocalDescriptionFailed(Exception { OperationError, "Unable to apply session description" });
return;
}
for (auto& mediaDescription : mediaDescriptions) {
RTCRtpTransceiver* transceiver = matchTransceiver(transceivers, [&mediaDescription] (RTCRtpTransceiver& current) {
return current.provisionalMid() == mediaDescription.mid;
});
if (transceiver)
transceiver->setMid(transceiver->provisionalMid());
}
}
if (internalRemoteDescription()) {
MediaEndpointSessionConfiguration* remoteConfiguration = internalRemoteDescription()->configuration();
RealtimeMediaSourceMap sendSourceMap = createSourceMap(remoteConfiguration->mediaDescriptions(), mediaDescriptions.size(), transceivers);
if (m_mediaEndpoint->updateSendConfiguration(remoteConfiguration, sendSourceMap, isInitiator) == MediaEndpoint::UpdateResult::Failed) {
setLocalDescriptionFailed(Exception { OperationError, "Unable to apply session description" });
return;
}
}
if (!hasUnassociatedTransceivers(transceivers))
clearNegotiationNeededState();
RTCSignalingState newSignalingState;
switch (newDescription->type()) {
case RTCSdpType::Offer:
m_pendingLocalDescription = WTFMove(newDescription);
newSignalingState = RTCSignalingState::HaveLocalOffer;
break;
case RTCSdpType::Answer:
m_currentLocalDescription = WTFMove(newDescription);
m_currentRemoteDescription = m_pendingRemoteDescription;
m_pendingLocalDescription = nullptr;
m_pendingRemoteDescription = nullptr;
newSignalingState = RTCSignalingState::Stable;
break;
case RTCSdpType::Rollback:
m_pendingLocalDescription = nullptr;
newSignalingState = RTCSignalingState::Stable;
break;
case RTCSdpType::Pranswer:
m_pendingLocalDescription = WTFMove(newDescription);
newSignalingState = RTCSignalingState::HaveLocalPranswer;
break;
}
updateSignalingState(newSignalingState);
if (m_peerConnection.iceGatheringState() == RTCIceGatheringState::New && mediaDescriptions.size())
m_peerConnection.updateIceGatheringState(RTCIceGatheringState::Gathering);
markAsNeedingNegotiation();
setLocalDescriptionSucceeded();
}
RefPtr<RTCSessionDescription> MediaEndpointPeerConnection::localDescription() const
{
return createRTCSessionDescription(internalLocalDescription());
}
RefPtr<RTCSessionDescription> MediaEndpointPeerConnection::currentLocalDescription() const
{
return createRTCSessionDescription(m_currentLocalDescription.get());
}
RefPtr<RTCSessionDescription> MediaEndpointPeerConnection::pendingLocalDescription() const
{
return createRTCSessionDescription(m_pendingLocalDescription.get());
}
void MediaEndpointPeerConnection::doSetRemoteDescription(RTCSessionDescription& description)
{
runTask([this, protectedDescription = RefPtr<RTCSessionDescription>(&description)]() mutable {
setRemoteDescriptionTask(WTFMove(protectedDescription));
});
}
void MediaEndpointPeerConnection::setRemoteDescriptionTask(RefPtr<RTCSessionDescription>&& description)
{
auto result = MediaEndpointSessionDescription::create(WTFMove(description), *m_sdpProcessor);
if (result.hasException()) {
setRemoteDescriptionFailed(result.releaseException());
return;
}
auto newDescription = result.releaseReturnValue();
auto& mediaDescriptions = newDescription->configuration()->mediaDescriptions();
for (auto& mediaDescription : mediaDescriptions) {
if (mediaDescription.type != "audio" && mediaDescription.type != "video")
continue;
mediaDescription.payloads = m_mediaEndpoint->filterPayloads(mediaDescription.payloads, mediaDescription.type == "audio" ? m_defaultAudioPayloads : m_defaultVideoPayloads);
}
bool isInitiator = newDescription->type() == RTCSdpType::Answer;
const RtpTransceiverVector& transceivers = m_peerConnection.getTransceivers();
RealtimeMediaSourceMap sendSourceMap;
if (internalLocalDescription())
sendSourceMap = createSourceMap(mediaDescriptions, internalLocalDescription()->configuration()->mediaDescriptions().size(), transceivers);
if (m_mediaEndpoint->updateSendConfiguration(newDescription->configuration(), sendSourceMap, isInitiator) == MediaEndpoint::UpdateResult::Failed) {
setRemoteDescriptionFailed(Exception { OperationError, "Unable to apply session description" });
return;
}
Vector<RefPtr<MediaStreamEvent>> legacyMediaStreamEvents;
for (auto& mediaDescription : mediaDescriptions) {
RTCRtpTransceiver* transceiver = matchTransceiverByMid(transceivers, mediaDescription.mid);
if (!transceiver) {
bool receiveOnlyFlag = false;
if (mediaDescription.mode == "sendrecv" || mediaDescription.mode == "recvonly") {
transceiver = matchTransceiver(transceivers, [&mediaDescription] (RTCRtpTransceiver& current) {
return !current.stopped() && current.mid().isNull() && current.sender().trackKind() == mediaDescription.type;
});
if (transceiver) {
transceiver->setMid(mediaDescription.mid);
m_mediaEndpoint->replaceMutedRemoteSourceMid(transceiver->provisionalMid(), mediaDescription.mid);
} else
receiveOnlyFlag = true;
}
if (!transceiver) {
auto sender = RTCRtpSender::create(String(mediaDescription.type), Vector<String>(), m_peerConnection.senderBackend());
auto receiver = createReceiver(mediaDescription.mid, mediaDescription.type, mediaDescription.mediaStreamTrackId);
auto newTransceiver = RTCRtpTransceiver::create(WTFMove(sender), WTFMove(receiver));
newTransceiver->setMid(mediaDescription.mid);
if (receiveOnlyFlag)
newTransceiver->disableSendingDirection();
transceiver = newTransceiver.ptr();
m_peerConnection.addTransceiver(WTFMove(newTransceiver));
}
}
if (mediaDescription.mode == "sendrecv" || mediaDescription.mode == "sendonly") {
auto& receiver = transceiver->receiver();
if (receiver.isDispatched())
continue;
receiver.setDispatched(true);
Vector<String> mediaStreamIds;
if (!mediaDescription.mediaStreamId.isEmpty())
mediaStreamIds.append(mediaDescription.mediaStreamId);
HashMap<String, RefPtr<MediaStream>> trackEventMediaStreams;
for (auto& id : mediaStreamIds) {
if (m_remoteStreamMap.contains(id)) {
RefPtr<MediaStream> stream = m_remoteStreamMap.get(id);
stream->addTrack(*receiver.track());
trackEventMediaStreams.add(id, WTFMove(stream));
} else {
auto newStream = MediaStream::create(*m_peerConnection.scriptExecutionContext(), MediaStreamTrackVector({ receiver.track() }));
m_remoteStreamMap.add(id, newStream.copyRef());
legacyMediaStreamEvents.append(MediaStreamEvent::create(eventNames().addstreamEvent, false, false, newStream.copyRef()));
trackEventMediaStreams.add(id, WTFMove(newStream));
}
}
Vector<RefPtr<MediaStream>> streams;
copyValuesToVector(trackEventMediaStreams, streams);
m_peerConnection.fireEvent(RTCTrackEvent::create(eventNames().trackEvent, false, false,
&receiver, receiver.track(), WTFMove(streams), transceiver));
}
}
for (auto& event : legacyMediaStreamEvents)
m_peerConnection.fireEvent(*event);
RTCSignalingState newSignalingState;
switch (newDescription->type()) {
case RTCSdpType::Offer:
m_pendingRemoteDescription = WTFMove(newDescription);
newSignalingState = RTCSignalingState::HaveRemoteOffer;
break;
case RTCSdpType::Answer:
m_currentRemoteDescription = WTFMove(newDescription);
m_currentLocalDescription = m_pendingLocalDescription;
m_pendingRemoteDescription = nullptr;
m_pendingLocalDescription = nullptr;
newSignalingState = RTCSignalingState::Stable;
break;
case RTCSdpType::Rollback:
m_pendingRemoteDescription = nullptr;
newSignalingState = RTCSignalingState::Stable;
break;
case RTCSdpType::Pranswer:
m_pendingRemoteDescription = WTFMove(newDescription);
newSignalingState = RTCSignalingState::HaveRemotePranswer;
break;
}
updateSignalingState(newSignalingState);
setRemoteDescriptionSucceeded();
}
RefPtr<RTCSessionDescription> MediaEndpointPeerConnection::remoteDescription() const
{
return createRTCSessionDescription(internalRemoteDescription());
}
RefPtr<RTCSessionDescription> MediaEndpointPeerConnection::currentRemoteDescription() const
{
return createRTCSessionDescription(m_currentRemoteDescription.get());
}
RefPtr<RTCSessionDescription> MediaEndpointPeerConnection::pendingRemoteDescription() const
{
return createRTCSessionDescription(m_pendingRemoteDescription.get());
}
bool MediaEndpointPeerConnection::setConfiguration(MediaEndpointConfiguration&& configuration)
{
m_mediaEndpoint->setConfiguration(WTFMove(configuration));
return true;
}
void MediaEndpointPeerConnection::doAddIceCandidate(RTCIceCandidate& rtcCandidate)
{
runTask([this, protectedCandidate = RefPtr<RTCIceCandidate>(&rtcCandidate)]() mutable {
addIceCandidateTask(*protectedCandidate);
});
}
void MediaEndpointPeerConnection::addIceCandidateTask(RTCIceCandidate& rtcCandidate)
{
if (!internalRemoteDescription()) {
addIceCandidateFailed(Exception { INVALID_STATE_ERR, "No remote description set" });
return;
}
auto& remoteMediaDescriptions = internalRemoteDescription()->configuration()->mediaDescriptions();
PeerMediaDescription* targetMediaDescription = nullptr;
if (!rtcCandidate.sdpMid().isNull()) {
const String& mid = rtcCandidate.sdpMid();
for (auto& description : remoteMediaDescriptions) {
if (description.mid == mid) {
targetMediaDescription = &description;
break;
}
}
if (!targetMediaDescription) {
addIceCandidateFailed(Exception { OperationError, "sdpMid did not match any media description" });
return;
}
} else if (rtcCandidate.sdpMLineIndex()) {
unsigned short sdpMLineIndex = rtcCandidate.sdpMLineIndex().value();
if (sdpMLineIndex >= remoteMediaDescriptions.size()) {
addIceCandidateFailed(Exception { OperationError, "sdpMLineIndex is out of range" });
return;
}
targetMediaDescription = &remoteMediaDescriptions[sdpMLineIndex];
} else {
ASSERT_NOT_REACHED();
return;
}
auto result = m_sdpProcessor->parseCandidateLine(rtcCandidate.candidate());
if (result.parsingStatus() != SDPProcessor::Result::Success) {
if (result.parsingStatus() == SDPProcessor::Result::ParseError)
addIceCandidateFailed(Exception { OperationError, "Invalid candidate content" });
else
LOG_ERROR("SDPProcessor internal error");
return;
}
ASSERT(targetMediaDescription);
m_mediaEndpoint->addRemoteCandidate(result.candidate(), targetMediaDescription->mid, targetMediaDescription->iceUfrag, targetMediaDescription->icePassword);
targetMediaDescription->addIceCandidate(WTFMove(result.candidate()));
addIceCandidateSucceeded();
}
void MediaEndpointPeerConnection::getStats(MediaStreamTrack* track, Ref<DeferredPromise>&& promise)
{
m_mediaEndpoint->getStats(track, WTFMove(promise));
}
Vector<RefPtr<MediaStream>> MediaEndpointPeerConnection::getRemoteStreams() const
{
Vector<RefPtr<MediaStream>> remoteStreams;
copyValuesToVector(m_remoteStreamMap, remoteStreams);
return remoteStreams;
}
Ref<RTCRtpReceiver> MediaEndpointPeerConnection::createReceiver(const String& transceiverMid, const String& trackKind, const String& trackId)
{
RealtimeMediaSource::Type sourceType = trackKind == "audio" ? RealtimeMediaSource::Type::Audio : RealtimeMediaSource::Type::Video;
auto remoteSource = m_mediaEndpoint->createMutedRemoteSource(transceiverMid, sourceType);
auto remoteTrackPrivate = MediaStreamTrackPrivate::create(WTFMove(remoteSource), String(trackId));
auto remoteTrack = MediaStreamTrack::create(*m_peerConnection.scriptExecutionContext(), WTFMove(remoteTrackPrivate));
return RTCRtpReceiver::create(WTFMove(remoteTrack));
}
std::unique_ptr<RTCDataChannelHandler> MediaEndpointPeerConnection::createDataChannelHandler(const String& label, const RTCDataChannelInit& options)
{
return m_mediaEndpoint->createDataChannelHandler(label, options);
}
void MediaEndpointPeerConnection::replaceTrack(RTCRtpSender& sender, Ref<MediaStreamTrack>&& withTrack, DOMPromiseDeferred<void>&& promise)
{
RTCRtpTransceiver* transceiver = matchTransceiver(m_peerConnection.getTransceivers(), [&sender] (RTCRtpTransceiver& current) {
return ¤t.sender() == &sender;
});
ASSERT(transceiver);
const String& mid = transceiver->mid();
if (mid.isNull()) {
sender.setTrack(WTFMove(withTrack));
promise.resolve();
return;
}
runTask([this, protectedSender = RefPtr<RTCRtpSender>(&sender), mid, protectedTrack = WTFMove(withTrack), protectedPromise = WTFMove(promise)]() mutable {
replaceTrackTask(*protectedSender, mid, WTFMove(protectedTrack), protectedPromise);
});
}
void MediaEndpointPeerConnection::replaceTrackTask(RTCRtpSender& sender, const String& mid, Ref<MediaStreamTrack>&& withTrack, DOMPromiseDeferred<void>& promise)
{
if (m_peerConnection.isClosed())
return;
m_mediaEndpoint->replaceSendSource(withTrack->source(), mid);
sender.setTrack(WTFMove(withTrack));
promise.resolve();
}
void MediaEndpointPeerConnection::doStop()
{
m_mediaEndpoint->stop();
}
void MediaEndpointPeerConnection::emulatePlatformEvent(const String& action)
{
m_mediaEndpoint->emulatePlatformEvent(action);
}
MediaEndpointSessionDescription* MediaEndpointPeerConnection::internalLocalDescription() const
{
return m_pendingLocalDescription ? m_pendingLocalDescription.get() : m_currentLocalDescription.get();
}
MediaEndpointSessionDescription* MediaEndpointPeerConnection::internalRemoteDescription() const
{
return m_pendingRemoteDescription ? m_pendingRemoteDescription.get() : m_currentRemoteDescription.get();
}
RefPtr<RTCSessionDescription> MediaEndpointPeerConnection::createRTCSessionDescription(MediaEndpointSessionDescription* description) const
{
return description ? description->toRTCSessionDescription(*m_sdpProcessor) : nullptr;
}
void MediaEndpointPeerConnection::gotDtlsFingerprint(const String& fingerprint, const String& fingerprintFunction)
{
ASSERT(isMainThread());
m_dtlsFingerprint = fingerprint;
m_dtlsFingerprintFunction = fingerprintFunction;
startRunningTasks();
}
void MediaEndpointPeerConnection::gotIceCandidate(const String& mid, IceCandidate&& candidate)
{
ASSERT(isMainThread());
auto& mediaDescriptions = internalLocalDescription()->configuration()->mediaDescriptions();
size_t mediaDescriptionIndex = notFound;
for (size_t i = 0; i < mediaDescriptions.size(); ++i) {
if (mediaDescriptions[i].mid == mid) {
mediaDescriptionIndex = i;
break;
}
}
ASSERT(mediaDescriptionIndex != notFound);
String candidateLine;
auto result = m_sdpProcessor->generateCandidateLine(candidate, candidateLine);
if (result != SDPProcessor::Result::Success) {
LOG_ERROR("SDPProcessor internal error");
return;
}
mediaDescriptions[mediaDescriptionIndex].addIceCandidate(WTFMove(candidate));
fireICECandidateEvent(RTCIceCandidate::create(candidateLine, mid, mediaDescriptionIndex));
}
void MediaEndpointPeerConnection::doneGatheringCandidates(const String& mid)
{
ASSERT(isMainThread());
RtpTransceiverVector transceivers = RtpTransceiverVector(m_peerConnection.getTransceivers());
RTCRtpTransceiver* notifyingTransceiver = matchTransceiverByMid(transceivers, mid);
ASSERT(notifyingTransceiver);
notifyingTransceiver->iceTransport().setGatheringState(RTCIceGatheringState::Complete);
RTCRtpTransceiver* stillGatheringTransceiver = matchTransceiver(transceivers, [] (RTCRtpTransceiver& current) {
return !current.stopped() && !current.mid().isNull()
&& current.iceTransport().gatheringState() != RTCIceGatheringState::Complete;
});
if (!stillGatheringTransceiver)
PeerConnectionBackend::doneGatheringCandidates();
}
static RTCIceConnectionState deriveAggregatedIceConnectionState(const Vector<RTCIceTransportState>& states)
{
unsigned newCount = 0;
unsigned checkingCount = 0;
unsigned connectedCount = 0;
unsigned completedCount = 0;
unsigned failedCount = 0;
unsigned disconnectedCount = 0;
unsigned closedCount = 0;
for (auto& state : states) {
switch (state) {
case RTCIceTransportState::New:
++newCount;
break;
case RTCIceTransportState::Checking:
++checkingCount;
break;
case RTCIceTransportState::Connected:
++connectedCount;
break;
case RTCIceTransportState::Completed:
++completedCount;
break;
case RTCIceTransportState::Failed:
++failedCount;
break;
case RTCIceTransportState::Disconnected:
++disconnectedCount;
break;
case RTCIceTransportState::Closed:
++closedCount;
break;
}
}
if ((newCount > 0 && !checkingCount && !failedCount && !disconnectedCount) || (closedCount == states.size()))
return RTCIceConnectionState::New;
if (checkingCount > 0 && !failedCount && !disconnectedCount)
return RTCIceConnectionState::Checking;
if ((connectedCount + completedCount + closedCount) == states.size() && connectedCount > 0)
return RTCIceConnectionState::Connected;
if ((completedCount + closedCount) == states.size() && completedCount > 0)
return RTCIceConnectionState::Completed;
if (failedCount > 0)
return RTCIceConnectionState::Failed;
if (disconnectedCount > 0) return RTCIceConnectionState::Disconnected;
ASSERT_NOT_REACHED();
return RTCIceConnectionState::New;
}
void MediaEndpointPeerConnection::iceTransportStateChanged(const String& mid, RTCIceTransportState mediaEndpointIceTransportState)
{
ASSERT(isMainThread());
RTCRtpTransceiver* transceiver = matchTransceiverByMid(m_peerConnection.getTransceivers(), mid);
ASSERT(transceiver);
RTCIceTransportState transportState = static_cast<RTCIceTransportState>(mediaEndpointIceTransportState);
transceiver->iceTransport().setState(transportState);
Vector<RTCIceTransportState> transportStates;
transportStates.reserveInitialCapacity(m_peerConnection.getTransceivers().size());
for (auto& transceiver : m_peerConnection.getTransceivers())
transportStates.uncheckedAppend(transceiver->iceTransport().state());
m_peerConnection.updateIceConnectionState(deriveAggregatedIceConnectionState(transportStates));
}
}
#endif // ENABLE(WEB_RTC)