|
@@ -1,5 +1,6 @@
|
|
|
/**
|
|
|
* Copyright (c) 2019 Paul-Louis Ageneau
|
|
|
+ * Copyright (c) 2020 Filip Klembara (in2core)
|
|
|
*
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
@@ -31,8 +32,20 @@
|
|
|
#endif
|
|
|
|
|
|
#include <iomanip>
|
|
|
+#include <set>
|
|
|
#include <thread>
|
|
|
|
|
|
+#if __clang__
|
|
|
+namespace {
|
|
|
+template <typename To, typename From>
|
|
|
+inline std::shared_ptr<To> reinterpret_pointer_cast(std::shared_ptr<From> const &ptr) noexcept {
|
|
|
+ return std::shared_ptr<To>(ptr, reinterpret_cast<To *>(ptr.get()));
|
|
|
+}
|
|
|
+} // namespace
|
|
|
+#else
|
|
|
+using std::reinterpret_pointer_cast;
|
|
|
+#endif
|
|
|
+
|
|
|
namespace rtc {
|
|
|
|
|
|
using namespace std::placeholders;
|
|
@@ -311,8 +324,7 @@ std::optional<string> PeerConnection::remoteAddress() const {
|
|
|
return iceTransport ? iceTransport->getRemoteAddress() : nullopt;
|
|
|
}
|
|
|
|
|
|
-shared_ptr<DataChannel> PeerConnection::addDataChannel(string label, string protocol,
|
|
|
- Reliability reliability) {
|
|
|
+shared_ptr<DataChannel> PeerConnection::addDataChannel(string label, DataChannelInit init) {
|
|
|
// RFC 5763: The answerer MUST use either a setup attribute value of setup:active or
|
|
|
// setup:passive. [...] Thus, setup:active is RECOMMENDED.
|
|
|
// See https://tools.ietf.org/html/rfc5763#section-5
|
|
@@ -320,8 +332,7 @@ shared_ptr<DataChannel> PeerConnection::addDataChannel(string label, string prot
|
|
|
auto iceTransport = std::atomic_load(&mIceTransport);
|
|
|
auto role = iceTransport ? iceTransport->role() : Description::Role::Passive;
|
|
|
|
|
|
- auto channel =
|
|
|
- emplaceDataChannel(role, std::move(label), std::move(protocol), std::move(reliability));
|
|
|
+ auto channel = emplaceDataChannel(role, std::move(label), std::move(init));
|
|
|
|
|
|
if (auto transport = std::atomic_load(&mSctpTransport))
|
|
|
if (transport->state() == SctpTransport::State::Connected)
|
|
@@ -335,9 +346,8 @@ shared_ptr<DataChannel> PeerConnection::addDataChannel(string label, string prot
|
|
|
return channel;
|
|
|
}
|
|
|
|
|
|
-shared_ptr<DataChannel> PeerConnection::createDataChannel(string label, string protocol,
|
|
|
- Reliability reliability) {
|
|
|
- auto channel = addDataChannel(label, protocol, reliability);
|
|
|
+shared_ptr<DataChannel> PeerConnection::createDataChannel(string label, DataChannelInit init) {
|
|
|
+ auto channel = addDataChannel(std::move(label), std::move(init));
|
|
|
setLocalDescription();
|
|
|
return channel;
|
|
|
}
|
|
@@ -382,6 +392,7 @@ std::shared_ptr<Track> PeerConnection::addTrack(Description::Media description)
|
|
|
if (!track) {
|
|
|
track = std::make_shared<Track>(std::move(description));
|
|
|
mTracks.emplace(std::make_pair(track->mid(), track));
|
|
|
+ mTrackLines.emplace_back(track);
|
|
|
}
|
|
|
|
|
|
// Renegotiation is needed for the new or updated track
|
|
@@ -634,7 +645,8 @@ void PeerConnection::forwardMessage(message_ptr message) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- auto channel = findDataChannel(uint16_t(message->stream));
|
|
|
+ uint16_t stream = uint16_t(message->stream);
|
|
|
+ auto channel = findDataChannel(stream);
|
|
|
if (!channel) {
|
|
|
auto iceTransport = std::atomic_load(&mIceTransport);
|
|
|
auto sctpTransport = std::atomic_load(&mSctpTransport);
|
|
@@ -642,15 +654,15 @@ void PeerConnection::forwardMessage(message_ptr message) {
|
|
|
return;
|
|
|
|
|
|
const byte dataChannelOpenMessage{0x03};
|
|
|
- unsigned int remoteParity = (iceTransport->role() == Description::Role::Active) ? 1 : 0;
|
|
|
+ uint16_t remoteParity = (iceTransport->role() == Description::Role::Active) ? 1 : 0;
|
|
|
if (message->type == Message::Control && *message->data() == dataChannelOpenMessage &&
|
|
|
- message->stream % 2 == remoteParity) {
|
|
|
+ stream % 2 == remoteParity) {
|
|
|
|
|
|
- channel =
|
|
|
- std::make_shared<DataChannel>(shared_from_this(), sctpTransport, message->stream);
|
|
|
+ channel = std::make_shared<NegociatedDataChannel>(shared_from_this(), sctpTransport,
|
|
|
+ message->stream);
|
|
|
channel->onOpen(weak_bind(&PeerConnection::triggerDataChannel, this,
|
|
|
weak_ptr<DataChannel>{channel}));
|
|
|
- mDataChannels.insert(std::make_pair(message->stream, channel));
|
|
|
+ mDataChannels.emplace(message->stream, channel);
|
|
|
} else {
|
|
|
// Invalid, close the DataChannel
|
|
|
sctpTransport->closeStream(message->stream);
|
|
@@ -665,53 +677,117 @@ void PeerConnection::forwardMedia(message_ptr message) {
|
|
|
if (!message)
|
|
|
return;
|
|
|
|
|
|
- if (message->type == Message::Type::Control) {
|
|
|
+ // Browsers like to compound their packets with a random SSRC.
|
|
|
+ // we have to do this monstrosity to distribute the report blocks
|
|
|
+ if (message->type == Message::Control) {
|
|
|
+ std::set<uint32_t> ssrcs;
|
|
|
+ size_t offset = 0;
|
|
|
+ while ((sizeof(rtc::RTCP_HEADER) + offset) <= message->size()) {
|
|
|
+ auto header = reinterpret_cast<rtc::RTCP_HEADER *>(message->data() + offset);
|
|
|
+ if (header->lengthInBytes() > message->size() - offset) {
|
|
|
+ PLOG_WARNING << "RTCP packet is truncated";
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ offset += header->lengthInBytes();
|
|
|
+ if (header->payloadType() == 205 || header->payloadType() == 206) {
|
|
|
+ auto rtcpfb = reinterpret_cast<RTCP_FB_HEADER *>(header);
|
|
|
+ ssrcs.insert(rtcpfb->getPacketSenderSSRC());
|
|
|
+ ssrcs.insert(rtcpfb->getMediaSourceSSRC());
|
|
|
+
|
|
|
+ } else if (header->payloadType() == 200 || header->payloadType() == 201) {
|
|
|
+ auto rtcpsr = reinterpret_cast<RTCP_SR *>(header);
|
|
|
+ ssrcs.insert(rtcpsr->senderSSRC());
|
|
|
+ for (int i = 0; i < rtcpsr->header.reportCount(); ++i)
|
|
|
+ ssrcs.insert(rtcpsr->getReportBlock(i)->getSSRC());
|
|
|
+ } else {
|
|
|
+ // PT=202 == SDES
|
|
|
+ // PT=207 == Extended Report
|
|
|
+ if (header->payloadType() != 202 && header->payloadType() != 207) {
|
|
|
+ PLOG_WARNING << "Unknown packet type: " << (int)header->version() << " "
|
|
|
+ << header->payloadType() << "";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!ssrcs.empty()) {
|
|
|
+ for (uint32_t ssrc : ssrcs) {
|
|
|
+ if (auto mid = getMidFromSsrc(ssrc)) {
|
|
|
+ std::shared_lock lock(mTracksMutex); // read-only
|
|
|
+ if (auto it = mTracks.find(*mid); it != mTracks.end())
|
|
|
+ if (auto track = it->second.lock())
|
|
|
+ track->incoming(message);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ uint32_t ssrc = uint32_t(message->stream);
|
|
|
+ if (auto mid = getMidFromSsrc(ssrc)) {
|
|
|
std::shared_lock lock(mTracksMutex); // read-only
|
|
|
- for (auto it = mTracks.begin(); it != mTracks.end(); ++it)
|
|
|
+ if (auto it = mTracks.find(*mid); it != mTracks.end())
|
|
|
if (auto track = it->second.lock())
|
|
|
- return track->incoming(message);
|
|
|
-
|
|
|
- PLOG_WARNING << "No track available to receive control, dropping";
|
|
|
+ track->incoming(message);
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * TODO: So the problem is that when stop sending streams, we stop getting report blocks for
|
|
|
+ * those streams Therefore when we get compound RTCP packets, they are empty, and we can't
|
|
|
+ * forward them. Therefore, it is expected that we don't know where to forward packets. Is
|
|
|
+ * this ideal? No! Do I know how to fix it? No!
|
|
|
+ */
|
|
|
+ // PLOG_WARNING << "Track not found for SSRC " << ssrc << ", dropping";
|
|
|
return;
|
|
|
}
|
|
|
+} // namespace rtc
|
|
|
|
|
|
- unsigned int payloadType = message->stream;
|
|
|
- std::optional<string> mid;
|
|
|
- if (auto it = mMidFromPayloadType.find(payloadType); it != mMidFromPayloadType.end()) {
|
|
|
- mid = it->second;
|
|
|
- } else {
|
|
|
- std::lock_guard lock(mLocalDescriptionMutex);
|
|
|
- if (!mLocalDescription)
|
|
|
- return;
|
|
|
+std::optional<std::string> PeerConnection::getMidFromSsrc(uint32_t ssrc) {
|
|
|
+ if (auto it = mMidFromSsrc.find(ssrc); it != mMidFromSsrc.end())
|
|
|
+ return it->second;
|
|
|
|
|
|
- for (int i = 0; i < mLocalDescription->mediaCount(); ++i) {
|
|
|
+ {
|
|
|
+ std::lock_guard lock(mRemoteDescriptionMutex);
|
|
|
+ if (!mRemoteDescription)
|
|
|
+ return nullopt;
|
|
|
+ for (unsigned int i = 0; i < mRemoteDescription->mediaCount(); ++i) {
|
|
|
if (auto found = std::visit(
|
|
|
rtc::overloaded{[&](Description::Application *) -> std::optional<string> {
|
|
|
return std::nullopt;
|
|
|
},
|
|
|
[&](Description::Media *media) -> std::optional<string> {
|
|
|
- return media->hasPayloadType(payloadType)
|
|
|
+ return media->hasSSRC(ssrc)
|
|
|
? std::make_optional(media->mid())
|
|
|
: nullopt;
|
|
|
}},
|
|
|
- mLocalDescription->media(i))) {
|
|
|
+ mRemoteDescription->media(i))) {
|
|
|
|
|
|
- mMidFromPayloadType.emplace(payloadType, *found);
|
|
|
- mid = *found;
|
|
|
- break;
|
|
|
+ mMidFromSsrc.emplace(ssrc, *found);
|
|
|
+ return *found;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ {
|
|
|
+ std::lock_guard lock(mLocalDescriptionMutex);
|
|
|
+ if (!mLocalDescription)
|
|
|
+ return nullopt;
|
|
|
+ for (unsigned int i = 0; i < mLocalDescription->mediaCount(); ++i) {
|
|
|
+ if (auto found = std::visit(
|
|
|
+ rtc::overloaded{[&](Description::Application *) -> std::optional<string> {
|
|
|
+ return std::nullopt;
|
|
|
+ },
|
|
|
+ [&](Description::Media *media) -> std::optional<string> {
|
|
|
+ return media->hasSSRC(ssrc)
|
|
|
+ ? std::make_optional(media->mid())
|
|
|
+ : nullopt;
|
|
|
+ }},
|
|
|
+ mLocalDescription->media(i))) {
|
|
|
|
|
|
- if (!mid) {
|
|
|
- PLOG_WARNING << "Track not found for payload type " << payloadType << ", dropping";
|
|
|
- return;
|
|
|
+ mMidFromSsrc.emplace(ssrc, *found);
|
|
|
+ return *found;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- std::shared_lock lock(mTracksMutex); // read-only
|
|
|
- if (auto it = mTracks.find(*mid); it != mTracks.end())
|
|
|
- if (auto track = it->second.lock())
|
|
|
- track->incoming(message);
|
|
|
+ return nullopt;
|
|
|
}
|
|
|
|
|
|
void PeerConnection::forwardBufferedAmount(uint16_t stream, size_t amount) {
|
|
@@ -720,20 +796,33 @@ void PeerConnection::forwardBufferedAmount(uint16_t stream, size_t amount) {
|
|
|
}
|
|
|
|
|
|
shared_ptr<DataChannel> PeerConnection::emplaceDataChannel(Description::Role role, string label,
|
|
|
- string protocol,
|
|
|
- Reliability reliability) {
|
|
|
- // The active side must use streams with even identifiers, whereas the passive side must use
|
|
|
- // streams with odd identifiers.
|
|
|
- // See https://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-09#section-6
|
|
|
+ DataChannelInit init) {
|
|
|
std::unique_lock lock(mDataChannelsMutex); // we are going to emplace
|
|
|
- unsigned int stream = (role == Description::Role::Active) ? 0 : 1;
|
|
|
- while (mDataChannels.find(stream) != mDataChannels.end()) {
|
|
|
- stream += 2;
|
|
|
- if (stream >= 65535)
|
|
|
- throw std::runtime_error("Too many DataChannels");
|
|
|
+ uint16_t stream;
|
|
|
+ if (init.id) {
|
|
|
+ stream = *init.id;
|
|
|
+ if (stream == 65535)
|
|
|
+ throw std::invalid_argument("Invalid DataChannel id");
|
|
|
+ } else {
|
|
|
+ // The active side must use streams with even identifiers, whereas the passive side must use
|
|
|
+ // streams with odd identifiers.
|
|
|
+ // See https://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-09#section-6
|
|
|
+ stream = (role == Description::Role::Active) ? 0 : 1;
|
|
|
+ while (mDataChannels.find(stream) != mDataChannels.end()) {
|
|
|
+ if (stream >= 65535 - 2)
|
|
|
+ throw std::runtime_error("Too many DataChannels");
|
|
|
+
|
|
|
+ stream += 2;
|
|
|
+ }
|
|
|
}
|
|
|
- auto channel = std::make_shared<DataChannel>(shared_from_this(), stream, std::move(label),
|
|
|
- std::move(protocol), std::move(reliability));
|
|
|
+ // If the DataChannel is user-negotiated, do not negociate it here
|
|
|
+ auto channel =
|
|
|
+ init.negotiated
|
|
|
+ ? std::make_shared<DataChannel>(shared_from_this(), stream, std::move(label),
|
|
|
+ std::move(init.protocol), std::move(init.reliability))
|
|
|
+ : std::make_shared<NegociatedDataChannel>(shared_from_this(), stream, std::move(label),
|
|
|
+ std::move(init.protocol),
|
|
|
+ std::move(init.reliability));
|
|
|
mDataChannels.emplace(std::make_pair(stream, channel));
|
|
|
return channel;
|
|
|
}
|
|
@@ -800,14 +889,15 @@ void PeerConnection::incomingTrack(Description::Media description) {
|
|
|
if (mTracks.find(description.mid()) == mTracks.end()) {
|
|
|
auto track = std::make_shared<Track>(std::move(description));
|
|
|
mTracks.emplace(std::make_pair(track->mid(), track));
|
|
|
- triggerTrack(std::move(track));
|
|
|
+ mTrackLines.emplace_back(track);
|
|
|
+ triggerTrack(track);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void PeerConnection::openTracks() {
|
|
|
#if RTC_ENABLE_MEDIA
|
|
|
if (auto transport = std::atomic_load(&mDtlsTransport)) {
|
|
|
- auto srtpTransport = std::reinterpret_pointer_cast<DtlsSrtpTransport>(transport);
|
|
|
+ auto srtpTransport = reinterpret_pointer_cast<DtlsSrtpTransport>(transport);
|
|
|
std::shared_lock lock(mTracksMutex); // read-only
|
|
|
for (auto it = mTracks.begin(); it != mTracks.end(); ++it)
|
|
|
if (auto track = it->second.lock())
|
|
@@ -831,7 +921,7 @@ void PeerConnection::validateRemoteDescription(const Description &description) {
|
|
|
throw std::invalid_argument("Remote description has no media line");
|
|
|
|
|
|
int activeMediaCount = 0;
|
|
|
- for (int i = 0; i < description.mediaCount(); ++i)
|
|
|
+ for (unsigned int i = 0; i < description.mediaCount(); ++i)
|
|
|
std::visit(rtc::overloaded{[&](const Description::Application *) { ++activeMediaCount; },
|
|
|
[&](const Description::Media *media) {
|
|
|
if (media->direction() != Description::Direction::Inactive)
|
|
@@ -851,9 +941,10 @@ void PeerConnection::validateRemoteDescription(const Description &description) {
|
|
|
}
|
|
|
|
|
|
void PeerConnection::processLocalDescription(Description description) {
|
|
|
+
|
|
|
if (auto remote = remoteDescription()) {
|
|
|
// Reciprocate remote description
|
|
|
- for (int i = 0; i < remote->mediaCount(); ++i)
|
|
|
+ for (unsigned int i = 0; i < remote->mediaCount(); ++i)
|
|
|
std::visit( // reciprocate each media
|
|
|
rtc::overloaded{
|
|
|
[&](Description::Application *remoteApp) {
|
|
@@ -907,6 +998,7 @@ void PeerConnection::processLocalDescription(Description description) {
|
|
|
}
|
|
|
return;
|
|
|
}
|
|
|
+ lock.unlock(); // we are going to call incomingTrack()
|
|
|
|
|
|
auto reciprocated = remoteMedia->reciprocate();
|
|
|
#if !RTC_ENABLE_MEDIA
|
|
@@ -945,11 +1037,11 @@ void PeerConnection::processLocalDescription(Description description) {
|
|
|
|
|
|
// Add media for local tracks
|
|
|
std::shared_lock lock(mTracksMutex);
|
|
|
- for (auto it = mTracks.begin(); it != mTracks.end(); ++it) {
|
|
|
- if (description.hasMid(it->first))
|
|
|
- continue;
|
|
|
+ for (auto it = mTrackLines.begin(); it != mTrackLines.end(); ++it) {
|
|
|
+ if (auto track = it->lock()) {
|
|
|
+ if (description.hasMid(track->mid()))
|
|
|
+ continue;
|
|
|
|
|
|
- if (auto track = it->second.lock()) {
|
|
|
auto media = track->description();
|
|
|
#if !RTC_ENABLE_MEDIA
|
|
|
// No media support, mark as inactive
|
|
@@ -1029,9 +1121,12 @@ void PeerConnection::processRemoteDescription(Description description) {
|
|
|
}
|
|
|
|
|
|
void PeerConnection::processRemoteCandidate(Candidate candidate) {
|
|
|
+ std::lock_guard lock(mRemoteDescriptionMutex);
|
|
|
auto iceTransport = std::atomic_load(&mIceTransport);
|
|
|
- if (!iceTransport)
|
|
|
- throw std::logic_error("Remote candidate set without remote description");
|
|
|
+ if (!mRemoteDescription || !iceTransport)
|
|
|
+ throw std::logic_error("Got a remote candidate without remote description");
|
|
|
+
|
|
|
+ candidate.hintMid(mRemoteDescription->bundleMid());
|
|
|
|
|
|
if (candidate.resolve(Candidate::ResolveMode::Simple)) {
|
|
|
iceTransport->addRemoteCandidate(candidate);
|
|
@@ -1047,13 +1142,7 @@ void PeerConnection::processRemoteCandidate(Candidate candidate) {
|
|
|
t.detach();
|
|
|
}
|
|
|
|
|
|
- {
|
|
|
- std::lock_guard lock(mRemoteDescriptionMutex);
|
|
|
- if (!mRemoteDescription)
|
|
|
- throw std::logic_error("Got a remote candidate without remote description");
|
|
|
-
|
|
|
- mRemoteDescription->addCandidate(candidate);
|
|
|
- }
|
|
|
+ mRemoteDescription->addCandidate(std::move(candidate));
|
|
|
}
|
|
|
|
|
|
void PeerConnection::triggerDataChannel(weak_ptr<DataChannel> weakDataChannel) {
|