Browse Source

Merged libdatachannel/master into api_updates

Staz M 4 years ago
parent
commit
03e9eca7d1

+ 1 - 1
examples/web/script.js

@@ -55,7 +55,7 @@ function openSignaling(url) {
     const ws = new WebSocket(url);
     ws.onopen = () => resolve(ws);
     ws.onerror = () => reject(new Error('WebSocket error'));
-    ws.onclosed = () => console.error('WebSocket disconnected');
+    ws.onclose = () => console.error('WebSocket disconnected');
     ws.onmessage = (e) => {
       if(typeof(e.data) != 'string') return;
       const message = JSON.parse(e.data);

+ 6 - 2
include/rtc/candidate.hpp

@@ -31,7 +31,11 @@ public:
 	enum class Type { Unknown, Host, ServerReflexive, PeerReflexive, Relayed };
 	enum class TransportType { Unknown, Udp, TcpActive, TcpPassive, TcpSo, TcpUnknown };
 
-	Candidate(string candidate = "", string mid = "");
+	Candidate();
+	Candidate(string candidate);
+	Candidate(string candidate, string mid);
+
+	void hintMid(string mid);
 
 	enum class ResolveMode { Simple, Lookup };
 	bool resolve(ResolveMode mode = ResolveMode::Simple);
@@ -50,7 +54,7 @@ public:
 
 private:
 	string mCandidate;
-	string mMid;
+	std::optional<string> mMid;
 
 	// Extracted on resolution
 	Family mFamily;

+ 11 - 7
include/rtc/description.hpp

@@ -38,9 +38,8 @@ public:
 	enum class Role { ActPass, Passive, Active };
 	enum class Direction { SendOnly, RecvOnly, SendRecv, Inactive, Unknown };
 
-	Description(const string &sdp, const string &typeString = "");
-	Description(const string &sdp, Type type);
-	Description(const string &sdp, Type type, Role role);
+	Description(const string &sdp, Type type = Type::Unspec, Role role = Role::ActPass);
+	Description(const string &sdp, string typeString);
 
 	Type type() const;
 	string typeString() const;
@@ -95,8 +94,7 @@ public:
 	struct Application : public Entry {
 	public:
 		Application(string mid = "data");
-		Application(const Application &other) = default;
-		Application(Application &&other) = default;
+		virtual ~Application() = default;
 
 		string description() const override;
 		Application reciprocate() const;
@@ -122,8 +120,6 @@ public:
 	public:
 		Media(const string &sdp);
 		Media(const string &mline, string mid, Direction dir = Direction::SendOnly);
-		Media(const Media &other) = default;
-		Media(Media &&other) = default;
 		virtual ~Media() = default;
 
 		string description() const override;
@@ -146,6 +142,7 @@ public:
 
         struct RTPMap {
             RTPMap(string_view mline);
+            RTPMap() {}
 
             void removeFB(const string &string);
             void addFB(const string &string);
@@ -161,8 +158,14 @@ public:
 
             std::vector<string> rtcpFbs;
             std::vector<string> fmtps;
+
+            static int parsePT(string_view view);
+            void setMLine(string_view view);
         };
 
+        std::map<int, RTPMap>::iterator beginMaps();
+        std::map<int, RTPMap>::iterator endMaps();
+
 	private:
 		virtual string generateSdpLines(string_view eol) const override;
 
@@ -172,6 +175,7 @@ public:
 		Media::RTPMap &getFormat(const string &fmt);
 
 		std::map<int, RTPMap> mRtpMap;
+		std::vector<uint32_t> mSsrcs;
 
 	public:
         void addRTPMap(const RTPMap& map);

+ 4 - 2
include/rtc/peerconnection.hpp

@@ -75,7 +75,7 @@ public:
 		HaveRemotePranswer = RTC_SIGNALING_HAVE_REMOTE_PRANSWER,
 	} rtcSignalingState;
 
-	PeerConnection(void);
+	PeerConnection();
 	PeerConnection(const Configuration &config);
 	~PeerConnection();
 
@@ -135,6 +135,7 @@ private:
 	void forwardMedia(message_ptr message);
 	void forwardBufferedAmount(uint16_t stream, size_t amount);
     std::optional<std::string> getMidFromSSRC(SSRC ssrc);
+    std::optional<uint32_t> getMLineFromSSRC(SSRC ssrc);
 
 	std::shared_ptr<DataChannel> emplaceDataChannel(Description::Role role, string label,
 	                                                string protocol, Reliability reliability);
@@ -181,7 +182,8 @@ private:
 	std::vector<std::weak_ptr<Track>> mTrackLines;                              // by SDP order
 	std::shared_mutex mDataChannelsMutex, mTracksMutex;
 
-	std::unordered_map<unsigned int, string> mMidFromSssrc; // cache
+	std::unordered_map<uint32_t, string> mMidFromSssrc; // cache
+    std::unordered_map<uint32_t , unsigned int> mMLineFromSssrc; // cache
 
 	std::atomic<State> mState;
 	std::atomic<GatheringState> mGatheringState;

+ 2 - 2
include/rtc/rtp.hpp

@@ -69,7 +69,7 @@ public:
         _seqNumber = htons(newSeqNo);
     }
     inline void setPayloadType(uint8_t newPayloadType) {
-        _payloadType = 0b01111111u & newPayloadType;
+        _payloadType = (_payloadType & 0b10000000u) | (0b01111111u & newPayloadType);
     }
     inline void setSsrc(uint32_t ssrc) {
         _ssrc = htonl(ssrc);
@@ -187,7 +187,7 @@ public:
     }
 
     inline void log() const {
-        PLOG_VERBOSE << "RTCP header: "
+        PLOG_INFO << "RTCP header: "
                    << "version=" << unsigned(version()) << ", padding=" << padding()
                    << ", reportCount=" << unsigned(reportCount())
                    << ", payloadType=" << unsigned(payloadType()) << ", length=" << length();

+ 2 - 1
include/rtc/track.hpp

@@ -42,7 +42,8 @@ public:
 
 	string mid() const;
 	Description::Media description() const;
-    void replaceSSRC(SSRC oldSSRC, SSRC ssrc, string cname);
+
+	void setDescription(Description::Media description);
 
 	void close(void) override;
 	bool send(message_variant data) override;

+ 30 - 24
src/candidate.cpp

@@ -28,8 +28,8 @@
 #include <ws2tcpip.h>
 #else
 #include <netdb.h>
-#include <sys/socket.h>
 #include <netinet/in.h>
+#include <sys/socket.h>
 #endif
 
 #include <sys/types.h>
@@ -48,23 +48,32 @@ inline bool hasprefix(const string &str, const string &prefix) {
 
 namespace rtc {
 
-Candidate::Candidate(string candidate, string mid)
+Candidate::Candidate()
     : mFamily(Family::Unresolved), mType(Type::Unknown), mTransportType(TransportType::Unknown),
-      mPort(0), mPriority(0) {
+      mPort(0), mPriority(0) {}
 
-	if (!candidate.empty()) {
-		const std::array prefixes{"a=", "candidate:"};
-		for (const string &prefix : prefixes)
-			if (hasprefix(candidate, prefix))
-				candidate.erase(0, prefix.size());
-	}
+Candidate::Candidate(string candidate) : Candidate() {
+	const std::array prefixes{"a=", "candidate:"};
+	for (const string &prefix : prefixes)
+		if (hasprefix(candidate, prefix))
+			candidate.erase(0, prefix.size());
 
 	mCandidate = std::move(candidate);
-	mMid = std::move(mid);
+}
+
+Candidate::Candidate(string candidate, string mid)
+    : Candidate(std::move(candidate)) {
+    if(!mid.empty())
+		mMid.emplace(std::move(mid));
+}
+
+void Candidate::hintMid(string mid) {
+	if (!mMid)
+		mMid.emplace(std::move(mid));
 }
 
 bool Candidate::resolve(ResolveMode mode) {
-	using TypeMap_t = std::unordered_map<string, Type>;	
+	using TypeMap_t = std::unordered_map<string, Type>;
 	using TcpTypeMap_t = std::unordered_map<string, TransportType>;
 
 	static const TypeMap_t TypeMap = {{"host", Type::Host},
@@ -79,12 +88,11 @@ bool Candidate::resolve(ResolveMode mode) {
 	if (mFamily != Family::Unresolved)
 		return true;
 
-	if(mCandidate.empty())
+	if (mCandidate.empty())
 		throw std::logic_error("Candidate is empty");
 
 	PLOG_VERBOSE << "Resolving candidate (mode="
-				 << (mode == ResolveMode::Simple ? "simple" : "lookup")
-				 << "): " << mCandidate;
+	             << (mode == ResolveMode::Simple ? "simple" : "lookup") << "): " << mCandidate;
 
 	// See RFC 8445 for format
 	std::istringstream iss(mCandidate);
@@ -100,17 +108,16 @@ bool Candidate::resolve(ResolveMode mode) {
 			mType = it->second;
 		else
 			mType = Type::Unknown;
-		
+
 		if (transport == "UDP" || transport == "udp") {
 			mTransportType = TransportType::Udp;
-		}
-		else if (transport == "TCP" || transport == "tcp") {
+		} else if (transport == "TCP" || transport == "tcp") {
 			std::istringstream iss(left);
 			string tcptype_, tcptype;
-			if(iss >> tcptype_ >> tcptype && tcptype_ == "tcptype") {
+			if (iss >> tcptype_ >> tcptype && tcptype_ == "tcptype") {
 				if (auto it = TcpTypeMap.find(tcptype); it != TcpTypeMap.end())
 					mTransportType = it->second;
-				else 
+				else
 					mTransportType = TransportType::TcpUnknown;
 
 			} else {
@@ -127,8 +134,7 @@ bool Candidate::resolve(ResolveMode mode) {
 		if (mTransportType == TransportType::Udp) {
 			hints.ai_socktype = SOCK_DGRAM;
 			hints.ai_protocol = IPPROTO_UDP;
-		}
-		else if (mTransportType != TransportType::Unknown) {
+		} else if (mTransportType != TransportType::Unknown) {
 			hints.ai_socktype = SOCK_STREAM;
 			hints.ai_protocol = IPPROTO_TCP;
 		}
@@ -146,11 +152,11 @@ bool Candidate::resolve(ResolveMode mode) {
 					if (getnameinfo(p->ai_addr, socklen_t(p->ai_addrlen), nodebuffer,
 					                MAX_NUMERICNODE_LEN, servbuffer, MAX_NUMERICSERV_LEN,
 					                NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
-						
+
 						mAddress = nodebuffer;
 						mPort = uint16_t(std::stoul(servbuffer));
 						mFamily = p->ai_family == AF_INET6 ? Family::Ipv6 : Family::Ipv4;
-						
+
 						const char sp{' '};
 						std::ostringstream oss;
 						oss << foundation << sp << component << sp << transport << sp << priority;
@@ -173,7 +179,7 @@ bool Candidate::resolve(ResolveMode mode) {
 
 string Candidate::candidate() const { return "candidate:" + mCandidate; }
 
-string Candidate::mid() const { return mMid; }
+string Candidate::mid() const { return mMid.value_or("0"); }
 
 Candidate::operator string() const {
 	std::ostringstream line;

+ 1 - 1
src/capi.cpp

@@ -528,7 +528,7 @@ int rtcSetRemoteDescription(int pc, const char *sdp, const char *type) {
 		if (!sdp)
 			throw std::invalid_argument("Unexpected null pointer for remote description");
 
-		peerConnection->setRemoteDescription({string(sdp), type ? string(type) : ""});
+		peerConnection->setRemoteDescription({string(sdp), type ? string(type) : "" });
 	});
 }
 

+ 76 - 51
src/description.cpp

@@ -66,11 +66,6 @@ template <typename T> T to_integer(string_view s) {
 
 namespace rtc {
 
-Description::Description(const string &sdp, const string &typeString)
-    : Description(sdp, stringToType(typeString)) {}
-
-Description::Description(const string &sdp, Type type) : Description(sdp, type, Role::ActPass) {}
-
 Description::Description(const string &sdp, Type type, Role role)
     : mType(Type::Unspec), mRole(role) {
 	hintType(type);
@@ -141,6 +136,10 @@ Description::Description(const string &sdp, Type type, Role role)
 	}
 }
 
+Description::Description(const string &sdp, string typeString)
+    : Description(sdp, !typeString.empty() ? stringToType(typeString) : Type::Unspec, Role::ActPass) {
+}
+
 Description::Type Description::type() const { return mType; }
 
 string Description::typeString() const { return typeToString(mType); }
@@ -173,12 +172,15 @@ void Description::setFingerprint(string fingerprint) {
 }
 
 void Description::addCandidate(Candidate candidate) {
+	candidate.hintMid(bundleMid());
 	mCandidates.emplace_back(std::move(candidate));
 }
 
 void Description::addCandidates(std::vector<Candidate> candidates) {
-	for(auto candidate : candidates)
+	for(Candidate candidate : candidates) {
+		candidate.hintMid(bundleMid());
 		mCandidates.emplace_back(std::move(candidate));
+	}
 }
 
 void Description::endCandidates() { mEnded = true; }
@@ -469,8 +471,10 @@ string Description::Entry::generateSdpLines(string_view eol) const {
 		break;
 	}
 
-	for (const auto &attr : mAttributes)
-		sdp << "a=" << attr << eol;
+	for (const auto &attr : mAttributes) {
+	    if (attr.find("extmap") == std::string::npos && attr.find("rtcp-rsize") == std::string::npos)
+            sdp << "a=" << attr << eol;
+    }
 
 	return sdp.str();
 }
@@ -499,6 +503,7 @@ void Description::Entry::parseSdpLine(string_view line) {
 
 void Description::Media::addSSRC(uint32_t ssrc, std::string name) {
     mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + name);
+    mSsrcs.emplace_back(ssrc);
 }
 
 void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, std::string name) {
@@ -517,11 +522,7 @@ void Description::Media::addSSRC(uint32_t ssrc) {
 }
 
 bool Description::Media::hasSSRC(uint32_t ssrc) {
-    for (auto &val : mAttributes) {
-        if (val.find("ssrc:" + std::to_string(ssrc)) != std::string ::npos)
-            return true;
-    }
-    return false;
+    return std::find(mSsrcs.begin(), mSsrcs.end(), ssrc) != mSsrcs.end();
 }
 
 Description::Application::Application(string mid)
@@ -672,8 +673,9 @@ void Description::Media::removeFormat(const string &fmt) {
 
 void Description::Video::addVideoCodec(int payloadType, const string &codec) {
 	RTPMap map(std::to_string(payloadType) + ' ' + codec + "/90000");
-        map.addFB("nack");
-	    map.addFB("nack pli");
+    map.addFB("nack");
+    map.addFB("nack pli");
+//    map.addFB("nack fir");
 	map.addFB("goog-remb");
 	if (codec == "H264") {
 		// Use Constrained Baseline profile Level 4.2 (necessary for Firefox)
@@ -685,7 +687,8 @@ void Description::Video::addVideoCodec(int payloadType, const string &codec) {
 		{
 			RTPMap map(std::to_string(payloadType+1) + ' ' + codec + "/90000");
 			map.addFB("nack");
-			    map.addFB("nack pli");
+            map.addFB("nack pli");
+//            map.addFB("nack fir");
 			map.addFB("goog-remb");
 			addRTPMap(map);
 			}
@@ -745,8 +748,10 @@ string Description::Media::generateSdpLines(string_view eol) const {
 			sdp << '/' << map.encParams;
 		sdp << eol;
 
-		for (const auto &val : map.rtcpFbs)
-			sdp << "a=rtcp-fb:" << map.pt << ' ' << val << eol;
+		for (const auto &val : map.rtcpFbs) {
+		    if (val != "transport-cc" )
+                sdp << "a=rtcp-fb:" << map.pt << ' ' << val << eol;
+        }
 		for (const auto &val : map.fmtps)
 			sdp << "a=fmtp:" << map.pt << ' ' << val << eol;
 	}
@@ -760,29 +765,32 @@ void Description::Media::parseSdpLine(string_view line) {
 		auto [key, value] = parse_pair(attr);
 
 		if (key == "rtpmap") {
-			Description::Media::RTPMap map(value);
-			int pt = map.pt;
-			mRtpMap.emplace(pt, std::move(map));
+		    auto pt = Description::Media::RTPMap::parsePT(value);
+            auto it = mRtpMap.find(pt);
+            if (it == mRtpMap.end()) {
+                it = mRtpMap.insert(std::make_pair(pt, Description::Media::RTPMap(value))).first;
+            }else {
+                it->second.setMLine(value);
+            }
 		} else if (key == "rtcp-fb") {
 			size_t p = value.find(' ');
 			int pt = to_integer<int>(value.substr(0, p));
 			auto it = mRtpMap.find(pt);
 			if (it == mRtpMap.end()) {
-				PLOG_WARNING << "rtcp-fb applied before the corresponding rtpmap, ignoring";
-			} else {
-				it->second.rtcpFbs.emplace_back(value.substr(p + 1));
-			}
+			    it = mRtpMap.insert(std::make_pair(pt, Description::Media::RTPMap())).first;
+            }
+            it->second.rtcpFbs.emplace_back(value.substr(p + 1));
 		} else if (key == "fmtp") {
 			size_t p = value.find(' ');
 			int pt = to_integer<int>(value.substr(0, p));
 			auto it = mRtpMap.find(pt);
-			if (it == mRtpMap.end()) {
-				PLOG_WARNING << "fmtp applied before the corresponding rtpmap, ignoring";
-			} else {
-				it->second.fmtps.emplace_back(value.substr(p + 1));
-			}
+			if (it == mRtpMap.end())
+                it = mRtpMap.insert(std::make_pair(pt, Description::Media::RTPMap())).first;
+            it->second.fmtps.emplace_back(value.substr(p + 1));
 		} else if (key == "rtcp-mux") {
-			// always added
+            // always added
+        }else if (key == "ssrc") {
+		    mSsrcs.emplace_back(std::stoul((std::string)value));
 		} else {
 			Entry::parseSdpLine(line);
 		}
@@ -808,28 +816,17 @@ std::vector<uint32_t> Description::Media::getSSRCs() {
     return vec;
 }
 
+std::map<int, Description::Media::RTPMap>::iterator Description::Media::beginMaps() {
+    return mRtpMap.begin();
+}
 
+std::map<int, Description::Media::RTPMap>::iterator Description::Media::endMaps() {
+    return mRtpMap.end();
+}
 
-Description::Media::RTPMap::RTPMap(string_view mline) {
-	size_t p = mline.find(' ');
-
-	this->pt = to_integer<int>(mline.substr(0, p));
-
-	string_view line = mline.substr(p + 1);
-	size_t spl = line.find('/');
-	this->format = line.substr(0, spl);
 
-	line = line.substr(spl + 1);
-	spl = line.find('/');
-	if (spl == string::npos) {
-		spl = line.find(' ');
-	}
-	if (spl == string::npos)
-		this->clockRate = to_integer<int>(line);
-	else {
-		this->clockRate = to_integer<int>(line.substr(0, spl));
-		this->encParams = line.substr(spl+1);
-	}
+Description::Media::RTPMap::RTPMap(string_view mline) {
+    setMLine(mline);
 }
 
 void Description::Media::RTPMap::removeFB(const string &str) {
@@ -844,6 +841,34 @@ void Description::Media::RTPMap::removeFB(const string &str) {
 
 void Description::Media::RTPMap::addFB(const string &str) { rtcpFbs.emplace_back(str); }
 
+int Description::Media::RTPMap::parsePT(string_view view) {
+    size_t p = view.find(' ');
+
+    return to_integer<int>(view.substr(0, p));
+}
+
+void Description::Media::RTPMap::setMLine(string_view mline) {
+    size_t p = mline.find(' ');
+
+    this->pt = to_integer<int>(mline.substr(0, p));
+
+    string_view line = mline.substr(p + 1);
+    size_t spl = line.find('/');
+    this->format = line.substr(0, spl);
+
+    line = line.substr(spl + 1);
+    spl = line.find('/');
+    if (spl == string::npos) {
+        spl = line.find(' ');
+    }
+    if (spl == string::npos)
+        this->clockRate = to_integer<int>(line);
+    else {
+        this->clockRate = to_integer<int>(line.substr(0, spl));
+        this->encParams = line.substr(spl+1);
+    }
+}
+
 Description::Audio::Audio(string mid, Direction dir)
     : Media("audio 9 UDP/TLS/RTP/SAVPF", std::move(mid), dir) {}
 
@@ -858,7 +883,7 @@ Description::Type Description::stringToType(const string &typeString) {
 	using TypeMap_t = std::unordered_map<string, Type>;
 	static const TypeMap_t TypeMap = {{"unspec", Type::Unspec},
 	                                  {"offer", Type::Offer},
-	                                  {"answer", Type::Pranswer},
+	                                  {"answer", Type::Answer},
 	                                  {"pranswer", Type::Pranswer},
 	                                  {"rollback", Type::Rollback}};
 	auto it = TypeMap.find(typeString);

+ 4 - 1
src/icetransport.cpp

@@ -145,7 +145,10 @@ Description IceTransport::getLocalDescription(Description::Type type) const {
 }
 
 void IceTransport::setRemoteDescription(const Description &description) {
-	mRole = description.role() == Description::Role::Active ? Description::Role::Passive
+    if (description.role() == Description::Role::ActPass)
+        mRole = Description::Role::Passive;
+    else
+	    mRole = description.role() == Description::Role::Active ? Description::Role::Passive
 	                                                        : Description::Role::Active;
 	mMid = description.bundleMid();
 	if (juice_set_remote_description(mAgent.get(),

+ 229 - 134
src/peerconnection.cpp

@@ -369,20 +369,24 @@ void PeerConnection::onSignalingStateChange(std::function<void(SignalingState st
 }
 
 std::shared_ptr<Track> PeerConnection::addTrack(Description::Media description) {
-
-	if (auto it = mTracks.find(description.mid()); it != mTracks.end())
-		if (auto track = it->second.lock())
-			return track;
-
 #if !RTC_ENABLE_MEDIA
 	if (mTracks.empty()) {
 		PLOG_WARNING << "Tracks will be inative (not compiled with SRTP support)";
 	}
 #endif
-	auto track = std::make_shared<Track>(std::move(description));
-	mTracks.emplace(std::make_pair(track->mid(), track));
 
-	// Renegotiation is needed for the new track
+	std::shared_ptr<Track> track;
+	if (auto it = mTracks.find(description.mid()); it != mTracks.end())
+		if (track = it->second.lock(); track)
+			track->setDescription(std::move(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
 	mNegotiationNeeded = true;
 
 	return track;
@@ -666,80 +670,82 @@ void PeerConnection::forwardMedia(message_ptr message) {
 
 	// Browsers like to compound their packets with a random SSRC.
 	// we have to do this monstrosity to distribute the report blocks
-    std::optional<string> mid;
+    std::optional<unsigned int> mediaLine;
     if (message->type == Message::Control) {
         unsigned int offset = 0;
         std::vector<SSRC> ssrcsFound;
         bool hasFound = false;
 
-        while ((sizeof(rtc::RTCP_HEADER) + offset) < message->size()) {
+        while ((sizeof(rtc::RTCP_HEADER) + offset) <= message->size()) {
             auto header = (rtc::RTCP_HEADER *) (message->data() + offset);
             if (header->lengthInBytes() > message->size() - offset) {
                 PLOG_WARNING << "Packet was truncated";
                 break;
-            } else {
-                if (header->payloadType() == 205 || header->payloadType() == 206) {
-                     auto rtcpfb = (RTCP_FB_HEADER*) header;
-                     auto ssrc = rtcpfb->getPacketSenderSSRC();
-                    mid = getMidFromSSRC(ssrc);
-                    if (mid.has_value() && std::find(ssrcsFound.begin(), ssrcsFound.end(), ssrc) == ssrcsFound.end()) {
+            }
+            offset += header->lengthInBytes();
+            if (header->payloadType() == 205 || header->payloadType() == 206) {
+                auto rtcpfb = (RTCP_FB_HEADER *) header;
+                auto ssrc = rtcpfb->getPacketSenderSSRC();
+                if (std::find(ssrcsFound.begin(), ssrcsFound.end(), ssrc) == ssrcsFound.end()) {
+                    mediaLine = getMLineFromSSRC(ssrc);
+                    if (mediaLine.has_value()) {
                         hasFound = true;
                         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);
-                            }
+                        if (auto track = mTrackLines[*mediaLine].lock()) {
+                            track->incoming(message);
                         }
                         ssrcsFound.emplace_back(ssrc);
                     }
+                }
 
-                    ssrc = rtcpfb->getMediaSourceSSRC();
-                    mid = getMidFromSSRC(ssrc);
-                    if (mid.has_value() && std::find(ssrcsFound.begin(), ssrcsFound.end(), ssrc) == ssrcsFound.end()) {
+                ssrc = rtcpfb->getMediaSourceSSRC();
+                if (std::find(ssrcsFound.begin(), ssrcsFound.end(), ssrc) == ssrcsFound.end()) {
+                    mediaLine = getMLineFromSSRC(ssrc);
+                    if (mediaLine.has_value()) {
                         hasFound = true;
                         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);
-                            }
+                        if (auto track = mTrackLines[*mediaLine].lock()) {
+                            track->incoming(message);
                         }
                         ssrcsFound.emplace_back(ssrc);
                     }
-                }else if (header->payloadType() == 200 || header->payloadType() == 201) {
-                    auto rtcpsr = (RTCP_SR*) header;
-                    auto ssrc = rtcpsr->senderSSRC();
-                    mid = getMidFromSSRC(ssrc);
-                    if (mid.has_value() && std::find(ssrcsFound.begin(), ssrcsFound.end(), ssrc) == ssrcsFound.end()) {
+                }
+            }else if (header->payloadType() == 200 || header->payloadType() == 201) {
+                auto rtcpsr = (RTCP_SR *) header;
+                auto ssrc = rtcpsr->senderSSRC();
+                if (std::find(ssrcsFound.begin(), ssrcsFound.end(), ssrc) == ssrcsFound.end()) {
+                    mediaLine = getMLineFromSSRC(ssrc);
+                    if (mediaLine.has_value()) {
                         hasFound = true;
                         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);
-                            }
+                        if (auto track = mTrackLines[*mediaLine].lock()) {
+                            track->incoming(message);
                         }
                         ssrcsFound.emplace_back(ssrc);
                     }
-                    for (int i = 0; i < rtcpsr->header.reportCount(); i++) {
-                        auto block = rtcpsr->getReportBlock(i);
-                        ssrc = block->getSSRC();
-                        mid = getMidFromSSRC(ssrc);
-                        if (mid.has_value() && std::find(ssrcsFound.begin(), ssrcsFound.end(), ssrc) == ssrcsFound.end()) {
+                }
+                for (int i = 0; i < rtcpsr->header.reportCount(); i++) {
+                    auto block = rtcpsr->getReportBlock(i);
+                    ssrc = block->getSSRC();
+                    if (std::find(ssrcsFound.begin(), ssrcsFound.end(), ssrc) == ssrcsFound.end()) {
+                        mediaLine = getMLineFromSSRC(ssrc);
+                        if (mediaLine.has_value()) {
                             hasFound = true;
                             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);
-                                }
+                            if (auto track = mTrackLines[*mediaLine].lock()) {
+                                track->incoming(message);
                             }
                             ssrcsFound.emplace_back(ssrc);
                         }
                     }
-                }else {
-		    // This warning is commonly thrown with SDES PT=202
-                    // PLOG_WARNING << "Unknown packet type: " << (int) header->payloadType();
+                }
+            } 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() << "";
                 }
             }
-            offset += header->lengthInBytes();
         }
 
         if (hasFound)
@@ -747,70 +753,118 @@ void PeerConnection::forwardMedia(message_ptr message) {
     }
 
     unsigned int ssrc = message->stream;
-    mid = getMidFromSSRC(ssrc);
+    mediaLine = getMLineFromSSRC(ssrc);
 
-	if (!mid) {
+	if (!mediaLine) {
 	    /* 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";
+	//	PLOG_WARNING << "Track not found for SSRC " << ssrc << ", dropping";
 		return;
 	}
 
 	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);
+    if (auto track = mTrackLines[*mediaLine].lock()) {
+        track->incoming(message);
+    }
+}
+
+std::optional<unsigned int> PeerConnection::getMLineFromSSRC(SSRC ssrc) {
+    if (auto it = mMLineFromSssrc.find(ssrc); it != mMLineFromSssrc.end()) {
+        return it->second;
+    }else {
+        {
+            std::lock_guard lock(mRemoteDescriptionMutex);
+            if (!mRemoteDescription)
+                return nullopt;
+            for (unsigned int i = 0; i < mRemoteDescription->mediaCount(); ++i) {
+                if (std::visit(
+                        rtc::overloaded{[&](Description::Application *) -> bool {
+                            return false;
+                        },
+                                        [&](Description::Media *media) -> bool {
+                                            return media->hasSSRC(ssrc);
+                                        }},
+                        mRemoteDescription->media(i))) {
+
+                    mMLineFromSssrc.emplace(ssrc, i);
+                    return i;
+                }
+            }
+        }
+        {
+            std::lock_guard lock(mLocalDescriptionMutex);
+            if (!mLocalDescription)
+                return nullopt;
+            for (unsigned int i = 0; i < mLocalDescription->mediaCount(); ++i) {
+                if (std::visit(
+                        rtc::overloaded{[&](Description::Application *) -> bool {
+                            return false;
+                        },
+                                        [&](Description::Media *media) -> bool {
+                                            return media->hasSSRC(ssrc);
+                                        }},
+                        mLocalDescription->media(i))) {
+
+                    mMLineFromSssrc.emplace(ssrc, i);
+                    return i;
+                }
+            }
+        }
+    }
+    return std::nullopt;
 }
 
 std::optional<std::string> PeerConnection::getMidFromSSRC(SSRC ssrc) {
     if (auto it = mMidFromSssrc.find(ssrc); it != mMidFromSssrc.end()) {
         return it->second;
     } else {
-        std::lock_guard lockA(mRemoteDescriptionMutex);
-        if (mRemoteDescription) {
-
-        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->hasSSRC(ssrc)
-                                               ? std::make_optional(media->mid())
-                                               : nullopt;
-                                    }},
-                    mRemoteDescription->media(i))) {
-
-                mMidFromSssrc.emplace(ssrc, *found);
-                return *found;
+        {
+            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->hasSSRC(ssrc)
+                                                   ? std::make_optional(media->mid())
+                                                   : nullopt;
+                                        }},
+                        mRemoteDescription->media(i))) {
+
+                    mMidFromSssrc.emplace(ssrc, *found);
+                    return *found;
+                }
             }
         }
-	}
-
-        std::lock_guard lockB(mLocalDescriptionMutex);
-        if (mLocalDescription) {
-        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))) {
-
-                mMidFromSssrc.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))) {
+
+                    mMidFromSssrc.emplace(ssrc, *found);
+                    return *found;
+                }
             }
         }
 	}
-    }
     return nullopt;
 }
 
@@ -900,7 +954,8 @@ 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);
 	}
 }
 
@@ -951,13 +1006,28 @@ void PeerConnection::validateRemoteDescription(const Description &description) {
 }
 
 void PeerConnection::processLocalDescription(Description description) {
+
 	if (auto remote = remoteDescription()) {
 		// Reciprocate remote description
-		for (unsigned int i = 0; i < remote->mediaCount(); ++i)
+		for (size_t i = 0; i < remote->mediaCount(); ++i)
 			std::visit( // reciprocate each media
 			    rtc::overloaded{
-			        [&](Description::Application *app) {
-				        auto reciprocated = app->reciprocate();
+			        [&](Description::Application *remoteApp) {
+				        std::shared_lock lock(mDataChannelsMutex);
+				        if (!mDataChannels.empty()) {
+					        // Prefer local description
+					        Description::Application app(remoteApp->mid());
+					        app.setSctpPort(DEFAULT_SCTP_PORT);
+					        app.setMaxMessageSize(LOCAL_MAX_MESSAGE_SIZE);
+
+					        PLOG_DEBUG << "Adding application to local description, mid=\""
+					                   << app.mid() << "\"";
+
+					        description.addMedia(std::move(app));
+					        return;
+				        }
+
+				        auto reciprocated = remoteApp->reciprocate();
 				        reciprocated.hintSctpPort(DEFAULT_SCTP_PORT);
 				        reciprocated.setMaxMessageSize(LOCAL_MAX_MESSAGE_SIZE);
 
@@ -966,24 +1036,52 @@ void PeerConnection::processLocalDescription(Description description) {
 
 				        description.addMedia(std::move(reciprocated));
 			        },
-			        [&](Description::Media *media) {
-				        auto reciprocated = media->reciprocate();
+			        [&](Description::Media *remoteMedia) {
+				        std::shared_lock lock(mTracksMutex);
+				        if (auto it = mTracks.find(remoteMedia->mid()); it != mTracks.end()) {
+					        // Prefer local description
+					        if (auto track = it->second.lock()) {
+						        auto media = track->description();
+#if !RTC_ENABLE_MEDIA
+						        // No media support, mark as inactive
+						        media.setDirection(Description::Direction::Inactive);
+#endif
+						        PLOG_DEBUG
+						            << "Adding media to local description, mid=\"" << media.mid()
+						            << "\", active=" << std::boolalpha
+						            << (media.direction() != Description::Direction::Inactive);
+
+						        description.addMedia(std::move(media));
+					        } else {
+						        auto reciprocated = remoteMedia->reciprocate();
+						        reciprocated.setDirection(Description::Direction::Inactive);
+
+						        PLOG_DEBUG << "Adding inactive media to local description, mid=\""
+						                   << reciprocated.mid() << "\"";
+
+						        description.addMedia(std::move(reciprocated));
+					        }
+					        return;
+				        }
+				        lock.unlock(); // we are going to call incomingTrack()
+
+				        auto reciprocated = remoteMedia->reciprocate();
 #if !RTC_ENABLE_MEDIA
-				        // No media support, mark as inactive
-				        reciprocated.setDirection(Description::Direction::Inactive);
+                                    // No media support, mark as inactive
+                                    reciprocated.setDirection(Description::Direction::Inactive);
 #endif
-				        incomingTrack(reciprocated);
+                                    incomingTrack(reciprocated);
 
-				        PLOG_DEBUG
-				            << "Reciprocating media in local description, mid=\""
-				            << reciprocated.mid() << "\", active=" << std::boolalpha
-				            << (reciprocated.direction() != Description::Direction::Inactive);
+                                    PLOG_DEBUG
+                                                << "Reciprocating media in local description, mid=\""
+                                                << reciprocated.mid() << "\", active=" << std::boolalpha
+                                                << (reciprocated.direction() != Description::Direction::Inactive);
 
-				        description.addMedia(std::move(reciprocated));
-			        },
-			    },
-			    remote->media(i));
-	}
+                                    description.addMedia(std::move(reciprocated));
+                                },
+                        },
+                        remote->media(i));
+        }
 
 	if (description.type() == Description::Type::Offer) {
 		// This is an offer, add locally created data channels and tracks
@@ -1005,23 +1103,23 @@ 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();
+                auto media = track->description();
 #if !RTC_ENABLE_MEDIA
-				// No media support, mark as inactive
-				media.setDirection(Description::Direction::Inactive);
+                // No media support, mark as inactive
+                media.setDirection(Description::Direction::Inactive);
 #endif
-				PLOG_DEBUG << "Adding media to local description, mid=\"" << media.mid()
-				           << "\", active=" << std::boolalpha
-				           << (media.direction() != Description::Direction::Inactive);
+                PLOG_DEBUG << "Adding media to local description, mid=\"" << media.mid()
+                           << "\", active=" << std::boolalpha
+                           << (media.direction() != Description::Direction::Inactive);
 
-				description.addMedia(std::move(media));
-			}
-		}
+                description.addMedia(std::move(media));
+            }
+        }
 	}
 
 	// Set local fingerprint (wait for certificate if necessary)
@@ -1089,9 +1187,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);
@@ -1107,13 +1208,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) {
@@ -1156,7 +1251,7 @@ bool PeerConnection::changeState(State state) {
 bool PeerConnection::changeGatheringState(GatheringState state) {
 	if (mGatheringState.exchange(state) == state)
 		return false;
-	
+
 	std::ostringstream s;
 	s << state;
 	PLOG_INFO << "Changed gathering state to " << s.str();
@@ -1165,9 +1260,9 @@ bool PeerConnection::changeGatheringState(GatheringState state) {
 }
 
 bool PeerConnection::changeSignalingState(SignalingState state) {
-	if (mSignalingState.exchange(state) == state) 
+	if (mSignalingState.exchange(state) == state)
 		return false;
-	
+
 	std::ostringstream s;
 	s << state;
 	PLOG_INFO << "Changed signaling state to " << s.str();

+ 6 - 2
src/track.cpp

@@ -32,8 +32,12 @@ string Track::mid() const { return mMediaDescription.mid(); }
 
 Description::Media Track::description() const { return mMediaDescription; }
 
-void Track::replaceSSRC(SSRC oldSSRC, SSRC ssrc, std::string cname) {
-    mMediaDescription.replaceSSRC(oldSSRC, ssrc, cname);
+
+void Track::setDescription(Description::Media description) {
+	if(description.mid() != mMediaDescription.mid())
+		throw std::logic_error("Media description mid does not match track mid");
+
+	mMediaDescription = std::move(description);
 }
 
 void Track::close() {