Browse Source

Refactored media API into track API

Paul-Louis Ageneau 5 years ago
parent
commit
d90a5d628f

+ 48 - 51
examples/media/main.cpp

@@ -11,60 +11,57 @@
 using nlohmann::json;
 
 int main() {
-    rtc::InitLogger(rtc::LogLevel::Debug);
-    auto pc = std::make_shared<rtc::PeerConnection>();
+	rtc::InitLogger(rtc::LogLevel::Debug);
+	auto pc = std::make_shared<rtc::PeerConnection>();
 
-    pc->onStateChange([](rtc::PeerConnection::State state) { std::cout << "State: " << state << std::endl; });
+	pc->onStateChange(
+	    [](rtc::PeerConnection::State state) { std::cout << "State: " << state << std::endl; });
 
-    pc->onGatheringStateChange( [pc](rtc::PeerConnection::GatheringState state) {
-        std::cout << "Gathering State: " << state << std::endl;
-        if (state == rtc::PeerConnection::GatheringState::Complete) {
-            auto description = pc->localDescription();
-            json message = {
-                    {"type", description->typeString()}, {"sdp", std::string(description.value())}
-            };
-            std::cout << message << std::endl;
-        }
-    });
+	pc->onGatheringStateChange([pc](rtc::PeerConnection::GatheringState state) {
+		std::cout << "Gathering State: " << state << std::endl;
+		if (state == rtc::PeerConnection::GatheringState::Complete) {
+			auto description = pc->localDescription();
+			json message = {{"type", description->typeString()},
+			                {"sdp", std::string(description.value())}};
+			std::cout << message << std::endl;
+		}
+	});
 
-    int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
-    sockaddr_in addr;
-    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
-    addr.sin_port = htons(5000);
-    addr.sin_family = AF_INET;
+	int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+	sockaddr_in addr;
+	addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+	addr.sin_port = htons(5000);
+	addr.sin_family = AF_INET;
 
-    RTCPSession session;
-    session.setOutgoingCallback([&pc](const rtc::message_ptr& ptr) {
-        // There is a chance we could send an RTCP packet before we connect (i.e. REMB)
-        if (pc->state() == rtc::PeerConnection::State::Connected) {
-            pc->sendMedia(ptr);
-        }
-    });
-//    session.requestBitrate(4000000); // Request 3Mbps (Browsers do not encode more than 2.5MBps from a webcam)
-    pc->onMedia([&session, &sock_fd, &addr](const rtc::message_ptr& ptr) {
-        auto resp = session.onData(ptr);
-        if (resp.has_value()) {
-            // This is an RTP packet
-            sendto(sock_fd, resp.value()->data(), resp.value()->size(), 0, (const struct sockaddr*) &addr, sizeof(addr));
-        }
-    });
+	rtc::Description::VideoMedia media(rtc::Description::RecvOnly);
+	media.addH264Codec(96);
+	media.setBitrate(
+	    3000); // Request 3Mbps (Browsers do not encode more than 2.5MBps from a webcam)
 
-    rtc::Description offer;
-    rtc::Description::Media &m = offer.addVideoMedia(rtc::Description::RecvOnly);
-    m.addH264Codec(96);
-    m.setBitrate(3000); // Request 3Mbps (Browsers do not encode more than 2.5MBps from a webcam)
-//    m.setBitrate(5000000);
-    pc->setLocalDescription(offer);
-    auto dc = pc->createDataChannel("test");
+	auto track = pc->createTrack(media);
+	auto dc = pc->createDataChannel("test");
 
-    std::cout << "Expect RTP video traffic on localhost:5000" << std::endl;
-    std::cout << "Please copy/paste the answer provided by the browser: " << std::endl;
-    std::string sdp;
-    std::getline (std::cin,sdp);
-    std::cout << "Got answer" << sdp << std::endl;
-    json j = json::parse(sdp);
-    rtc::Description answer(j["sdp"].get<std::string>(), j["type"].get<std::string>());
-    pc->setRemoteDescription(answer, answer);
-    std::cout << "Press any key to exit." << std::endl;
-    std::cin >> sdp;
-}
+	auto session = std::make_shared<rtc::RtcpSession>();
+	track->setRtcpHandler(session);
+
+	track->onMessage(
+	    [&session, &sock_fd, &addr](rtc::binary message) {
+		    // This is an RTP packet
+		    sendto(sock_fd, message.data(), message.size(), 0,
+		           reinterpret_cast<const struct sockaddr *>(&addr), sizeof(addr));
+	    },
+	    nullptr);
+
+	pc->setLocalDescription();
+
+	std::cout << "Expect RTP video traffic on localhost:5000" << std::endl;
+	std::cout << "Please copy/paste the answer provided by the browser: " << std::endl;
+	std::string sdp;
+	std::getline(std::cin, sdp);
+	std::cout << "Got answer" << sdp << std::endl;
+	json j = json::parse(sdp);
+	rtc::Description answer(j["sdp"].get<std::string>(), j["type"].get<std::string>());
+	pc->setRemoteDescription(answer);
+	std::cout << "Press any key to exit." << std::endl;
+	std::cin >> sdp;
+}

+ 66 - 59
include/rtc/description.hpp

@@ -32,20 +32,13 @@ namespace rtc {
 
 class Description {
 public:
-
-    enum class Type { Unspec = 0, Offer = 1, Answer = 2 };
+	enum class Type { Unspec = 0, Offer = 1, Answer = 2 };
 	enum class Role { ActPass = 0, Passive = 1, Active = 2 };
-    enum Direction {
-        SendOnly,
-        RecvOnly,
-        SendRecv,
-        Unknown
-    };
-
-    Description(const string &sdp, const string &typeString = "");
+	enum Direction { SendOnly, RecvOnly, SendRecv, Unknown };
+
+	Description(const string &sdp, const string &typeString = "");
 	Description(const string &sdp, Type type);
 	Description(const string &sdp, Type type, Role role);
-    Description(): Description(""){};
 
 	Type type() const;
 	string typeString() const;
@@ -68,68 +61,76 @@ public:
 	void endCandidates();
 	std::vector<Candidate> extractCandidates();
 
-	bool hasMedia() const;
-	void addMedia(const Description &source);
-
 	operator string() const;
-	string generateSdp(const string &eol) const;
-	string generateDataSdp(const string &eol) const;
+	string generateSdp(string_view eol) const;
+	string generateDataSdp(string_view eol) const;
+
+	// Media (non-data)
+	class Media {
+	public:
+		Media(string mline, Direction dir = Direction::Unknown, string mid = "media");
 
-	// Data
-    struct Data {
-        string mid;
-        std::optional<uint16_t> sctpPort;
-        std::optional<size_t> maxMessageSize;
-    };
+		string type() const { return mType; }
+		string description() const { return mDescription; }
+		string mid() const { return mMid; }
 
-    // Media (non-data)
-    struct Media {
-        Media(const string &lines);
-        string type;
-        string description;
-        string mid;
-        std::vector<string> attributes;
-        std::vector<string> attributesl;
-        int bAS = -1;
+		void removeFormat(const string &fmt);
 
-        struct RTPMap {
-            RTPMap(const string &mLine);
+		Direction getDirection();
+		void setDirection(Direction dir);
 
-            int pt;
-            string format;
-            int clockRate;
-            string encParams;
+		void addVideoCodec(int payloadType, const string &codec);
+		void addH264Codec(int payloadType);
+		void addVP8Codec(int payloadType);
+		void addVP9Codec(int payloadType);
 
-            std::vector<string> rtcpFbs;
-            std::vector<string> fmtps;
+		void setBitrate(int bitrate);
+		int getBitrate() const;
 
-            void removeFB(const string& string);
-            void addFB(const string& string);
-        };
+		void parseSdpLine(string line);
+		string generateSdp(string_view eol) const;
 
-        std::unordered_map<int, RTPMap> rtpMap;
+	private:
+		string mType;
+		string mDescription;
+		string mMid;
+		std::vector<string> mAttributes;
+		std::vector<string> mAttributesl;
+		int mBas = -1;
 
-        Media::RTPMap& getFormat(int fmt);
-        Media::RTPMap& getFormat(const string& fmt);
-        void removeFormat(const string &fmt);
+		struct RTPMap {
+			RTPMap(const string &mLine);
 
-        Direction getDirection();
-        void setDirection(Direction dir);
+			void removeFB(const string &string);
+			void addFB(const string &string);
 
-        void addVideoCodec(int payloadType, const string& codec);
-        void addH264Codec(int payloadType);
-        void addVP8Codec(int payloadType);
-        void addVP9Codec(int payloadType);
+			int pt;
+			string format;
+			int clockRate;
+			string encParams;
 
-        void setBitrate(int bitrate);
-        int getBitrate() const;
-    };
+			std::vector<string> rtcpFbs;
+			std::vector<string> fmtps;
+		};
 
-    std::_Rb_tree_iterator<std::pair<const int, Media>> getMedia(int mLine);
+		Media::RTPMap &getFormat(int fmt);
+		Media::RTPMap &getFormat(const string &fmt);
 
-    Media & addAudioMedia();
+		std::unordered_map<int, RTPMap> mRtpMap;
+	};
 
-    Media &addVideoMedia(Direction direction=Direction::RecvOnly);
+	class AudioMedia : public Media {
+	public:
+		AudioMedia(Direction dir, string mid = "audio");
+	};
+
+	class VideoMedia : public Media {
+	public:
+		VideoMedia(Direction dir, string mid = "video");
+	};
+
+	bool hasMedia() const;
+	void addMedia(Media media);
 
 private:
 	Type mType;
@@ -138,8 +139,14 @@ private:
 	string mIceUfrag, mIcePwd;
 	std::optional<string> mFingerprint;
 
+	struct Data {
+		string mid;
+		std::optional<uint16_t> sctpPort;
+		std::optional<size_t> maxMessageSize;
+	};
+
 	Data mData;
-  
+
 	std::map<int, Media> mMedia; // by m-line index
 
 	// Candidates

+ 2 - 0
include/rtc/include.hpp

@@ -41,12 +41,14 @@
 #include <mutex>
 #include <optional>
 #include <string>
+#include <string_view>
 #include <vector>
 
 namespace rtc {
 
 using std::byte;
 using std::string;
+using std::string_view;
 using binary = std::vector<byte>;
 
 using std::nullopt;

+ 2 - 3
include/rtc/peerconnection.hpp

@@ -81,9 +81,8 @@ public:
 	std::optional<string> localAddress() const;
 	std::optional<string> remoteAddress() const;
 
-	void setLocalDescription(std::optional<Description> mediaDescription = nullopt);
-	void setRemoteDescription(Description description,
-	                          std::optional<Description> mediaDescription = nullopt);
+	void setLocalDescription();
+	void setRemoteDescription(Description description);
 	void addRemoteCandidate(Candidate candidate);
 
 	std::shared_ptr<DataChannel> createDataChannel(string label, string protocol = "",

+ 418 - 450
include/rtc/rtp.hpp

@@ -1,521 +1,489 @@
+/**
+ * Copyright (c) 2020 Staz M
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
 #ifndef RTC_RTPL_H
 #define RTC_RTPL_H
 
+#include "include.hpp"
+#include "log.hpp"
+#include "message.hpp"
+
 #include <cmath>
+#include <functional>
+#include <iostream>
 
 #include <utility>
 #include <netinet/in.h>
 
+namespace rtc {
+
 typedef uint32_t SSRC;
 
 struct RTCP_ReportBlock {
 private:
-    SSRC ssrc;
-
-    /** fraction lost is 8 bits; packets lost is 24 bits */
-    uint32_t fractionLostAndPacketsLost;
+	SSRC ssrc;
 
+	/** fraction lost is 8 bits; packets lost is 24 bits */
+	uint32_t fractionLostAndPacketsLost;
 
 #if __BYTE_ORDER == __BIG_ENDIAN
-    uint32_t seqNoCycles:16;
-    uint32_t highestSeqNo:16;
+	uint32_t seqNoCycles : 16;
+	uint32_t highestSeqNo : 16;
 #elif __BYTE_ORDER == __LITTLE_ENDIAN
-    uint32_t highestSeqNo:16;
-    uint32_t seqNoCycles:16;
+	uint32_t highestSeqNo : 16;
+	uint32_t seqNoCycles : 16;
 #endif
 
-    uint32_t arrivalJitter;
-    uint32_t lastReport;
-    uint32_t delaySinceLastReport;
+	uint32_t arrivalJitter;
+	uint32_t lastReport;
+	uint32_t delaySinceLastReport;
 
 public:
-    void print() {
-        std::cout <<
-            " ssrc:" << ntohl(ssrc) <<
-            // TODO Implement these reports
-//            " fractionLost: " << fractionLost <<
-//            " packetsLost: " << packetsLost <<
-            " highestSeqNo:" <<getHighestSeqNo() <<
-            " seqNoCycles:" << getSeqCycleCount() <<
-            " jitter:" << getJitter() <<
-            " lastSR:" << getNTPOfSR() <<
-            " lastSRDelay:" << getDelaySinceSR();
-    }
-
-    void preparePacket(SSRC ssrc, unsigned int packetsLost, unsigned int totalPackets, uint16_t highestSeqNo, uint16_t seqNoCycles, uint32_t jitter, uint64_t lastSR_NTP, uint64_t lastSR_DELAY) {
-        setSeqNo(highestSeqNo, seqNoCycles);
-        setJitter(jitter);
-        setSSRC(ssrc);
-
-        // Middle 32 bits of NTP Timestamp
-//        this->lastReport = lastSR_NTP >> 16u;
-        setNTPOfSR(lastSR_NTP);
-        setDelaySinceSR(lastSR_DELAY);
-
-        // The delay, expressed in units of 1/65536 seconds
-//        this->delaySinceLastReport = lastSR_DELAY;
-    }
-
-    void inline setSSRC(SSRC ssrc) {
-        this->ssrc = htonl(ssrc);
-    }
-    SSRC inline getSSRC() const {return ntohl(ssrc);}
-
-    void inline setPacketsLost(unsigned int packetsLost, unsigned int totalPackets) {
-        // TODO Implement loss percentages.
-        this->fractionLostAndPacketsLost = 0;
-    }
-    unsigned int inline getLossPercentage() const {
-        // TODO Implement loss percentages.
-        return 0;
-    }
-    unsigned int inline getPacketLostCount() const {
-        // TODO Implement total packets lost.
-        return 0;
-    }
-
-    void inline setSeqNo(uint16_t highestSeqNo, uint16_t seqNoCycles) {
-        this->highestSeqNo = htons(highestSeqNo);
-        this->seqNoCycles = htons(seqNoCycles);
-    }
-
-    uint16_t inline getHighestSeqNo() const {
-        return ntohs(this->highestSeqNo);
-    }
-    uint16_t inline getSeqCycleCount() const {
-        return ntohs(this->seqNoCycles);
-    }
-
-    uint32_t inline getJitter() const {
-        return ntohl(arrivalJitter);
-    }
-    void inline setJitter(uint32_t jitter) {
-        this->arrivalJitter = htonl(jitter);
-    }
-
-    void inline setNTPOfSR(uint32_t ntp) {
-        lastReport = htonl(ntp >> 16u);
-    }
-    inline uint32_t getNTPOfSR() const  {
-        return ntohl(lastReport) << 16u;
-    }
-    inline void setDelaySinceSR(uint32_t sr) {
-        // The delay, expressed in units of 1/65536 seconds
-        delaySinceLastReport = htonl(sr);
-    }
-    inline uint32_t getDelaySinceSR() const {
-        return ntohl(delaySinceLastReport);
-    }
+	void print() {
+		std::cout << " ssrc:" << ntohl(ssrc) <<
+		    // TODO Implement these reports
+		    //			  " fractionLost: " << fractionLost <<
+		    //			  " packetsLost: " << packetsLost <<
+		    " highestSeqNo:" << getHighestSeqNo() << " seqNoCycles:" << getSeqCycleCount()
+		          << " jitter:" << getJitter() << " lastSR:" << getNTPOfSR()
+		          << " lastSRDelay:" << getDelaySinceSR();
+	}
+
+	void preparePacket(SSRC ssrc, [[maybe_unused]] unsigned int packetsLost,
+	                   [[maybe_unused]] unsigned int totalPackets, uint16_t highestSeqNo,
+	                   uint16_t seqNoCycles, uint32_t jitter, uint64_t lastSR_NTP,
+	                   uint64_t lastSR_DELAY) {
+		setSeqNo(highestSeqNo, seqNoCycles);
+		setJitter(jitter);
+		setSSRC(ssrc);
+
+		// Middle 32 bits of NTP Timestamp
+		//		  this->lastReport = lastSR_NTP >> 16u;
+		setNTPOfSR(lastSR_NTP);
+		setDelaySinceSR(lastSR_DELAY);
+
+		// The delay, expressed in units of 1/65536 seconds
+		//		  this->delaySinceLastReport = lastSR_DELAY;
+	}
+
+	void inline setSSRC(SSRC ssrc) { this->ssrc = htonl(ssrc); }
+	SSRC inline getSSRC() const { return ntohl(ssrc); }
+
+	void inline setPacketsLost([[maybe_unused]] unsigned int packetsLost,
+	                           [[maybe_unused]] unsigned int totalPackets) {
+		// TODO Implement loss percentages.
+		this->fractionLostAndPacketsLost = 0;
+	}
+	unsigned int inline getLossPercentage() const {
+		// TODO Implement loss percentages.
+		return 0;
+	}
+	unsigned int inline getPacketLostCount() const {
+		// TODO Implement total packets lost.
+		return 0;
+	}
+
+	void inline setSeqNo(uint16_t highestSeqNo, uint16_t seqNoCycles) {
+		this->highestSeqNo = htons(highestSeqNo);
+		this->seqNoCycles = htons(seqNoCycles);
+	}
+
+	uint16_t inline getHighestSeqNo() const { return ntohs(this->highestSeqNo); }
+	uint16_t inline getSeqCycleCount() const { return ntohs(this->seqNoCycles); }
+
+	uint32_t inline getJitter() const { return ntohl(arrivalJitter); }
+	void inline setJitter(uint32_t jitter) { this->arrivalJitter = htonl(jitter); }
+
+	void inline setNTPOfSR(uint32_t ntp) { lastReport = htonl(ntp >> 16u); }
+	inline uint32_t getNTPOfSR() const { return ntohl(lastReport) << 16u; }
+	inline void setDelaySinceSR(uint32_t sr) {
+		// The delay, expressed in units of 1/65536 seconds
+		delaySinceLastReport = htonl(sr);
+	}
+	inline uint32_t getDelaySinceSR() const { return ntohl(delaySinceLastReport); }
 };
 
 
 struct RTCP_HEADER {
 private:
 #if __BYTE_ORDER == __BIG_ENDIAN
-    uint16_t version:2;
+	uint16_t version : 2;
 	uint16_t padding:1;
 	uint16_t rc:5;
 	uint16_t payloadType:8;
 #elif __BYTE_ORDER == __LITTLE_ENDIAN
-    uint16_t reportCount:5;
-    uint16_t padding:1;
-    uint16_t version:2;
-    uint16_t payloadType:8;
+	uint16_t reportCount : 5;
+	uint16_t padding : 1;
+	uint16_t version : 2;
+	uint16_t payloadType : 8;
 #endif
-    uint16_t length;
+	uint16_t length;
 
 public:
-    void prepareHeader(uint8_t payloadType, unsigned int reportCount, uint16_t length) {
-        version = 2;
-        padding = false;
-        this->payloadType = payloadType;
-        this->reportCount = reportCount;
-        setLength(length);
-    }
-
-    inline uint8_t getPayloadType() const {
-        return payloadType;
-    }
-    inline void setPayloadType(uint8_t payloadType) {
-        this->payloadType = payloadType;
-    }
-
-    inline uint8_t getReportCount() const {
-        return reportCount;
-    }
-
-    inline void setReportCount(uint8_t reportCount) {
-        this->reportCount = reportCount;
-    }
-
-    inline uint16_t getLength() const {
-        return ntohs(length);
-    }
-
-    inline void setLength(uint16_t length) {
-        this->length = htons(length);
-    }
-
-    void print() {
-        std::cout <<
-        "version:" << (uint16_t) version <<
-                   " padding:" << (padding ? "T" : "F") <<
-                   " reportCount: " <<(uint16_t) getReportCount() <<
-                   " payloadType:" << (uint16_t)getPayloadType() <<
-                   " length: " << getLength();
-    }
+	void prepareHeader(uint8_t payloadType, unsigned int reportCount, uint16_t length) {
+		version = 2;
+		padding = false;
+		this->payloadType = payloadType;
+		this->reportCount = reportCount;
+		setLength(length);
+	}
+
+	inline uint8_t getPayloadType() const { return payloadType; }
+	inline void setPayloadType(uint8_t payloadType) { this->payloadType = payloadType; }
+
+	inline uint8_t getReportCount() const { return reportCount; }
+	inline void setReportCount(uint8_t reportCount) { this->reportCount = reportCount; }
+
+	inline uint16_t getLength() const { return ntohs(length); }
+	inline void setLength(uint16_t length) { this->length = htons(length); }
+
+	void print() {
+		std::cout << "version:" << (uint16_t)version << " padding:" << (padding ? "T" : "F")
+		          << " reportCount: " << (uint16_t)getReportCount()
+		          << " payloadType:" << (uint16_t)getPayloadType() << " length: " << getLength();
+	}
 };
 
 struct RTCP_SR {
 private:
-    RTCP_HEADER header;
+	RTCP_HEADER header;
 
-    SSRC senderSSRC;
+	SSRC senderSSRC;
 
-    uint64_t ntpTimestamp;
-    uint32_t rtpTimestamp;
+	uint64_t ntpTimestamp;
+	uint32_t rtpTimestamp;
 
-    uint32_t packetCount;
-    uint32_t octetCount;
+	uint32_t packetCount;
+	uint32_t octetCount;
 
-    RTCP_ReportBlock reportBlocks;
-public:
+	RTCP_ReportBlock reportBlocks;
 
-    void print() {
-        std::cout << "SR ";
-        header.print();
-        std::cout <<
-              " SSRC:" << ntohl(senderSSRC) <<
-              " NTP TS: " << ntpTimestamp << // TODO This needs to be convereted from network-endian
-              " RTP TS: " << ntohl(rtpTimestamp) <<
-              " packetCount: " << ntohl(packetCount) <<
-              " octetCount: " << ntohl(octetCount) << "\n";
-//        << std::endl;
-            for (int i =0;i < header.getReportCount(); i++) {
-                getReportBlock(i)->print();
-                std::cout << "\n";
-            }
-    }
-
-
-    inline void preparePacket(SSRC senderSSRC, uint8_t reportCount) {
-        unsigned int length = ( (offsetof(RTCP_SR, reportBlocks) + reportCount*sizeof(RTCP_ReportBlock))/4 )-1;
-        header.prepareHeader(200, reportCount, length);
-        this->senderSSRC = senderSSRC;
-    }
-
-    RTCP_ReportBlock* getReportBlock(int num) {
-        return &reportBlocks+num;
-    }
-
-    [[nodiscard]] unsigned int getSize() const {
-        // "length" in packet is one less than the number of 32 bit words in the packet.
-        return sizeof(uint32_t) * (1+header.getLength());
-    }
-
-    inline uint32_t getRTPTS() const{
-        return ntohl(rtpTimestamp);
-    }
-    inline uint32_t getNTPTS() const{
-        return ntohl(ntpTimestamp);
-    }
-
-    inline void setRTPTS(uint32_t ts) {
-        this->rtpTimestamp = htons(ts);
-    }
-    inline void setNTPTS(uint32_t ts) {
-        this->ntpTimestamp = htons(ts);
-    }
+public:
+	void print() {
+		std::cout << "SR ";
+		header.print();
+		std::cout << " SSRC:" << ntohl(senderSSRC) << " NTP TS: " << ntpTimestamp
+		          << // TODO This needs to be convereted from network-endian
+		    " RTP TS: " << ntohl(rtpTimestamp) << " packetCount: " << ntohl(packetCount)
+		          << " octetCount: " << ntohl(octetCount) << "\n";
+
+		for (int i = 0; i < header.getReportCount(); i++) {
+			getReportBlock(i)->print();
+			std::cout << "\n";
+		}
+	}
+
+	inline void preparePacket(SSRC senderSSRC, uint8_t reportCount) {
+		unsigned int length =
+		    ((offsetof(RTCP_SR, reportBlocks) + reportCount * sizeof(RTCP_ReportBlock)) / 4) - 1;
+		header.prepareHeader(200, reportCount, length);
+		this->senderSSRC = senderSSRC;
+	}
+
+	RTCP_ReportBlock *getReportBlock(int num) { return &reportBlocks + num; }
+
+	[[nodiscard]] unsigned int getSize() const {
+		// "length" in packet is one less than the number of 32 bit words in the packet.
+		return sizeof(uint32_t) * (1 + header.getLength());
+	}
+
+	inline uint32_t getRTPTS() const { return ntohl(rtpTimestamp); }
+	inline uint32_t getNTPTS() const { return ntohl(ntpTimestamp); }
+
+	inline void setRTPTS(uint32_t ts) { this->rtpTimestamp = htons(ts); }
+	inline void setNTPTS(uint32_t ts) { this->ntpTimestamp = htons(ts); }
 };
 
 struct RTCP_RR {
 private:
-    RTCP_HEADER header;
-    SSRC senderSSRC;
-    RTCP_ReportBlock reportBlocks;
+	RTCP_HEADER header;
+	SSRC senderSSRC;
+	RTCP_ReportBlock reportBlocks;
 
 public:
-    void print() {
-        std::cout << "RR ";
-        header.print();
-        std::cout <<
-//                  "version:" << (uint16_t) version <<
-//                  " padding:" << (padding ? "T" : "F") <<
-//                  " reportCount: " << (uint16_t) reportCount <<
-//                  " payloadType:" << (uint16_t) payloadType <<
-//                  " totalLength:" << ntohs(length) <<
-                  " SSRC:" << ntohl(senderSSRC) << "\n";
-//                  << std::endl;
-        for (int i =0;i < header.getReportCount(); i++) {
-            getReportBlock(i)->print();
-            std::cout << "\n";
-        }
-    }
-    RTCP_ReportBlock* getReportBlock(int num) {
-        return &reportBlocks+num;
-    }
-
-    inline RTCP_HEADER& getHeader() {
-        return header;
-    }
-
-    inline SSRC getSenderSSRC() const {
-        return ntohl(senderSSRC);
-    }
-
-    inline void setSenderSSRC(SSRC ssrc) {
-        this->senderSSRC = ssrc;
-    }
-
-    [[nodiscard]] inline unsigned int getSize() const {
-        // "length" in packet is one less than the number of 32 bit words in the packet.
-        return sizeof(uint32_t) * (1+header.getLength());
-    }
-
-    inline void preparePacket(SSRC senderSSRC, uint8_t reportCount) {
-//        version = 2;
-//        padding = false;
-//        this->reportCount = reportCount;
-//        payloadType = 201;
-//        // "length" in packet is one less than the number of 32 bit words in the packet.
-        unsigned int length = ( (offsetof(RTCP_RR, reportBlocks) + reportCount*sizeof(RTCP_ReportBlock))/4 )-1;
-        header.prepareHeader(201, reportCount, length);
-        this->senderSSRC = htonl(senderSSRC);
-    }
-
-    static unsigned inline int sizeWithReportBlocks(int reportCount) {
-        return offsetof(RTCP_RR, reportBlocks) + reportCount*sizeof(RTCP_ReportBlock);
-    }
+	void print() {
+		std::cout << "RR ";
+		header.print();
+		std::cout <<
+		    //					"version:" << (uint16_t) version <<
+		    //					" padding:" << (padding ? "T" : "F") <<
+		    //					" reportCount: " << (uint16_t) reportCount <<
+		    //					" payloadType:" << (uint16_t) payloadType <<
+		    //					" totalLength:" << ntohs(length) <<
+		    " SSRC:" << ntohl(senderSSRC) << "\n";
+
+		for (int i = 0; i < header.getReportCount(); i++) {
+			getReportBlock(i)->print();
+			std::cout << "\n";
+		}
+	}
+	RTCP_ReportBlock *getReportBlock(int num) { return &reportBlocks + num; }
+
+	inline RTCP_HEADER &getHeader() { return header; }
+
+	inline SSRC getSenderSSRC() const { return ntohl(senderSSRC); }
+
+	inline void setSenderSSRC(SSRC ssrc) { this->senderSSRC = ssrc; }
+
+	[[nodiscard]] inline unsigned int getSize() const {
+		// "length" in packet is one less than the number of 32 bit words in the packet.
+		return sizeof(uint32_t) * (1 + header.getLength());
+	}
+
+	inline void preparePacket(SSRC senderSSRC, uint8_t reportCount) {
+		//		  version = 2;
+		//		  padding = false;
+		//		  this->reportCount = reportCount;
+		//		  payloadType = 201;
+		//		  // "length" in packet is one less than the number of 32 bit words in the packet.
+		unsigned int length =
+		    ((offsetof(RTCP_RR, reportBlocks) + reportCount * sizeof(RTCP_ReportBlock)) / 4) - 1;
+		header.prepareHeader(201, reportCount, length);
+		this->senderSSRC = htonl(senderSSRC);
+	}
+
+	static unsigned inline int sizeWithReportBlocks(int reportCount) {
+		return offsetof(RTCP_RR, reportBlocks) + reportCount * sizeof(RTCP_ReportBlock);
+	}
 };
 
 struct RTP
 {
 #if __BYTE_ORDER == __BIG_ENDIAN
-    uint16_t version:2;
+	uint16_t version : 2;
 	uint16_t padding:1;
 	uint16_t extension:1;
 	uint16_t csrcCount:4;
 	uint16_t markerBit:1;
 	uint16_t payloadType:7;
 #elif __BYTE_ORDER == __LITTLE_ENDIAN
-    uint16_t csrCcount:4;
-    uint16_t extension:1;
-    uint16_t padding:1;
-    uint16_t version:2;
-    uint16_t payloadType:7;
-    uint16_t markerBit:1;
+	uint16_t csrCcount : 4;
+	uint16_t extension : 1;
+	uint16_t padding : 1;
+	uint16_t version : 2;
+	uint16_t payloadType : 7;
+	uint16_t markerBit : 1;
 #endif
-    uint16_t seqNumber;
-    uint32_t timestamp;
-    SSRC ssrc;
-    SSRC csrc[16];
-
-    inline uint32_t getSeqNo() const {
-        return ntohs(seqNumber);
-    }
-
-    inline uint32_t getTS() const{
-        return ntohl(timestamp);
-    }
+	uint16_t seqNumber;
+	uint32_t timestamp;
+	SSRC ssrc;
+	SSRC csrc[16];
+
+	inline uint32_t getSeqNo() const { return ntohs(seqNumber); }
+	inline uint32_t getTS() const { return ntohl(timestamp); }
 };
 
- struct RTCP_REMB
-{
-     RTCP_HEADER header;
-
-    SSRC senderSSRC;
-    SSRC mediaSourceSSRC;
-
-    /*! \brief Unique identifier ('R' 'E' 'M' 'B') */
-    char id[4];
-
-    /*! \brief Num SSRC, Br Exp, Br Mantissa (bit mask) */
-    uint32_t bitrate;
-
-    SSRC ssrc[1];
-
-    [[nodiscard]] unsigned int getSize() const {
-        // "length" in packet is one less than the number of 32 bit words in the packet.
-        return sizeof(uint32_t) * (1+header.getLength());
-    }
-
-    void preparePacket(SSRC senderSSRC, unsigned int numSSRC, unsigned int bitrate) {
-//        version = 2;
-//        format = 15;
-//        padding = false;
-//        payloadType = 206;
-
-        // Report Count becomes the format here.
-        header.prepareHeader(206, 15, 0);
-
-        // Always zero.
-        mediaSourceSSRC = 0;
-
-        this->senderSSRC = htonl(senderSSRC);
-        id[0] = 'R';
-        id[1] = 'E';
-        id[2] = 'M';
-        id[3] = 'B';
-
-        setBitrate(numSSRC, bitrate);
-    }
-
-    void setBitrate(unsigned int numSSRC, unsigned int bitrate) {
-        unsigned int exp = 0;
-        while (bitrate > pow(2,18)-1) {
-            exp++;
-            bitrate /= 2;
-        }
-
-        // "length" in packet is one less than the number of 32 bit words in the packet.
-        header.setLength((offsetof(RTCP_REMB, ssrc)/4)-1+numSSRC);
-
-        this->bitrate = htonl(
-                (numSSRC << (32u-8u)) | (exp << (32u-8u-6u)) | bitrate
-        );
-    }
-
-    //TODO Make this work
-//    uint64_t getBitrate() const{
-//        uint32_t ntohed = ntohl(this->bitrate);
-//        uint64_t bitrate = ntohed & (unsigned int)(pow(2, 18)-1);
-//        unsigned int exp = ntohed & ((unsigned int)( (pow(2, 6)-1)) << (32u-8u-6u));
-//        return bitrate * pow(2,exp);
-//    }
-//
-//    uint8_t getNumSSRCS() const {
-//        return ntohl(this->bitrate) & (((unsigned int) pow(2,8)-1) << (32u-8u));
-//    }
-
-    void print() {
-        std::cout << "REMB ";
-        header.print();
-        std::cout << " SSRC:" << ntohl(senderSSRC);
-    }
-
-    void setSSRC(uint8_t iterator, SSRC ssrc) {
-        this->ssrc[iterator] = htonl(ssrc);
-    }
-
-    static unsigned int sizeWithSSRCs(int numSSRC) {
-        return (offsetof(RTCP_REMB, ssrc))+sizeof(SSRC)*numSSRC;
-    }
+struct RTCP_REMB {
+	RTCP_HEADER header;
+
+	SSRC senderSSRC;
+	SSRC mediaSourceSSRC;
+
+	/*! \brief Unique identifier ('R' 'E' 'M' 'B') */
+	char id[4];
+
+	/*! \brief Num SSRC, Br Exp, Br Mantissa (bit mask) */
+	uint32_t bitrate;
+
+	SSRC ssrc[1];
+
+	[[nodiscard]] unsigned int getSize() const {
+		// "length" in packet is one less than the number of 32 bit words in the packet.
+		return sizeof(uint32_t) * (1 + header.getLength());
+	}
+
+	void preparePacket(SSRC senderSSRC, unsigned int numSSRC, unsigned int bitrate) {
+		//		  version = 2;
+		//		  format = 15;
+		//		  padding = false;
+		//		  payloadType = 206;
+
+		// Report Count becomes the format here.
+		header.prepareHeader(206, 15, 0);
+
+		// Always zero.
+		mediaSourceSSRC = 0;
+
+		this->senderSSRC = htonl(senderSSRC);
+		id[0] = 'R';
+		id[1] = 'E';
+		id[2] = 'M';
+		id[3] = 'B';
+
+		setBitrate(numSSRC, bitrate);
+	}
+
+	void setBitrate(unsigned int numSSRC, unsigned int bitrate) {
+		unsigned int exp = 0;
+		while (bitrate > pow(2, 18) - 1) {
+			exp++;
+			bitrate /= 2;
+		}
+
+		// "length" in packet is one less than the number of 32 bit words in the packet.
+		header.setLength((offsetof(RTCP_REMB, ssrc) / 4) - 1 + numSSRC);
+
+		this->bitrate = htonl((numSSRC << (32u - 8u)) | (exp << (32u - 8u - 6u)) | bitrate);
+	}
+
+	// TODO Make this work
+	//	  uint64_t getBitrate() const{
+	//		  uint32_t ntohed = ntohl(this->bitrate);
+	//		  uint64_t bitrate = ntohed & (unsigned int)(pow(2, 18)-1);
+	//		  unsigned int exp = ntohed & ((unsigned int)( (pow(2, 6)-1)) << (32u-8u-6u));
+	//		  return bitrate * pow(2,exp);
+	//	  }
+	//
+	//	  uint8_t getNumSSRCS() const {
+	//		  return ntohl(this->bitrate) & (((unsigned int) pow(2,8)-1) << (32u-8u));
+	//	  }
+
+	void print() {
+		std::cout << "REMB ";
+		header.print();
+		std::cout << " SSRC:" << ntohl(senderSSRC);
+	}
+
+	void setSSRC(uint8_t iterator, SSRC ssrc) { this->ssrc[iterator] = htonl(ssrc); }
+
+	static unsigned int sizeWithSSRCs(int numSSRC) {
+		return (offsetof(RTCP_REMB, ssrc)) + sizeof(SSRC) * numSSRC;
+	}
 };
 
-class RTCPSession {
-private:
-    std::function<void(RTP)> onPacketCB;
-    unsigned int requestedBitrate = 0;
-//    synchronized_callback<const rtc::message_ptr&> txCB;
-    std::function<void(rtc::message_ptr)> txCB;
-    SSRC ssrc;
-    uint32_t greatestSeqNo = 0, greatestTS;
-    uint64_t syncRTPTS, syncNTPTS;
+class RtcpHandler {
+public:
+	virtual void onOutgoing(std::function<void(rtc::message_ptr)> cb) = 0;
+	virtual std::optional<rtc::message_ptr> incoming(rtc::message_ptr ptr) = 0;
+};
 
-    unsigned int rotationCount = 0;
+class RtcpSession : public RtcpHandler {
+private:
+	std::function<void(RTP)> onPacketCB;
+	unsigned int requestedBitrate = 0;
+	synchronized_callback<rtc::message_ptr> txCB;
+	SSRC ssrc = 0;
+	uint32_t greatestSeqNo = 0, greatestTS;
+	uint64_t syncRTPTS, syncNTPTS;
 
+	unsigned int rotationCount = 0;
 
 public:
-    std::vector<rtc::message_ptr> sendQueue;
-//    RTCPSession(  callback): txCB(std::move(callback)) {}
-//    RTCPSession(const std::function<void(rtc::message_ptr)>& callback, SSRC ssrc) : ssrc(ssrc) {}
-
-    void setOutgoingCallback(const std::function<void(rtc::message_ptr)>& callback) {
-        txCB = callback;
-    }
-
-    std::optional<rtc::message_ptr> onData(rtc::message_ptr ptr) {
-        if (ptr->type == rtc::Message::Type::Binary) {
-            RTP* rtp = (RTP*) ptr->data();
-
-            // https://tools.ietf.org/html/rfc3550#appendix-A.1
-            if (rtp->version != 2) {
-                PLOG_WARNING << "RTP packet is not version 2";
-
-                return std::nullopt;
-            }
-            if (rtp->payloadType == 201 || rtp->payloadType == 200) {
-                PLOG_WARNING << "RTP packet has a payload type indicating RR/SR";
-
-                return std::nullopt;
-            }
-
-            // TODO Implement the padding bit
-            if (rtp->padding) {
-                PLOG_WARNING << "Padding processing not implemented";
-            }
-
-            ssrc = ntohl(rtp->ssrc);
-
-            uint32_t seqNo = rtp->getSeqNo();
-            uint32_t rtpTS = rtp->getTS();
-
-            if (greatestSeqNo < seqNo)
-                greatestSeqNo = seqNo;
-
-            return ptr;
-        }
-//        return std::nullopt;
-        assert(ptr->type == rtc::Message::Type::Control);
-        auto rr = (RTCP_RR*) ptr->data();
-        if (rr->getHeader().getPayloadType() == 201) {
-            // RR
-            ssrc = rr->getSenderSSRC();
-            rr->print();
-            std::cout << std::endl;
-        }else if (rr->getHeader().getPayloadType() == 200){
-            // SR
-            ssrc = rr->getSenderSSRC();
-            auto sr = (RTCP_SR*) ptr->data();
-            syncRTPTS = sr->getRTPTS();
-            syncNTPTS = sr->getNTPTS();
-            sr->print();
-            std::cout << std::endl;
-
-            // TODO For the time being, we will send RR's/REMB's when we get an SR
-            pushRR(0);
-            if (requestedBitrate > 0)
-                pushREMB(requestedBitrate);
-        }
-        return std::nullopt;
-    }
-
-    void requestBitrate(unsigned int newBitrate) {
-        this->requestedBitrate = newBitrate;
-
-        PLOG_DEBUG << "[GOOG-REMB] Requesting bitrate: " << newBitrate << std::endl;
-        pushREMB(newBitrate);
-    }
+	void onOutgoing(std::function<void(rtc::message_ptr)> cb) override { txCB = cb; }
+
+	std::optional<rtc::message_ptr> incoming(rtc::message_ptr ptr) override {
+		if (ptr->type == rtc::Message::Type::Binary) {
+			RTP *rtp = (RTP *)ptr->data();
+
+			// https://tools.ietf.org/html/rfc3550#appendix-A.1
+			if (rtp->version != 2) {
+				PLOG_WARNING << "RTP packet is not version 2";
+
+				return std::nullopt;
+			}
+			if (rtp->payloadType == 201 || rtp->payloadType == 200) {
+				PLOG_WARNING << "RTP packet has a payload type indicating RR/SR";
+
+				return std::nullopt;
+			}
+
+			// TODO Implement the padding bit
+			if (rtp->padding) {
+				PLOG_WARNING << "Padding processing not implemented";
+			}
+
+			ssrc = ntohl(rtp->ssrc);
+
+			uint32_t seqNo = rtp->getSeqNo();
+			// uint32_t rtpTS = rtp->getTS();
+
+			if (greatestSeqNo < seqNo)
+				greatestSeqNo = seqNo;
+
+			return ptr;
+		}
+
+		assert(ptr->type == rtc::Message::Type::Control);
+		auto rr = (RTCP_RR *)ptr->data();
+		if (rr->getHeader().getPayloadType() == 201) {
+			// RR
+			ssrc = rr->getSenderSSRC();
+			rr->print();
+			std::cout << std::endl;
+		} else if (rr->getHeader().getPayloadType() == 200) {
+			// SR
+			ssrc = rr->getSenderSSRC();
+			auto sr = (RTCP_SR *)ptr->data();
+			syncRTPTS = sr->getRTPTS();
+			syncNTPTS = sr->getNTPTS();
+			sr->print();
+			std::cout << std::endl;
+
+			// TODO For the time being, we will send RR's/REMB's when we get an SR
+			pushRR(0);
+			if (requestedBitrate > 0)
+				pushREMB(requestedBitrate);
+		}
+		return std::nullopt;
+	}
+
+	void requestBitrate(unsigned int newBitrate) {
+		this->requestedBitrate = newBitrate;
+
+		PLOG_DEBUG << "[GOOG-REMB] Requesting bitrate: " << newBitrate << std::endl;
+		pushREMB(newBitrate);
+	}
 
 private:
-    void pushREMB(unsigned int bitrate) {
-        rtc::message_ptr msg = rtc::make_message(RTCP_REMB::sizeWithSSRCs(1), rtc::Message::Type::Control);
-        auto remb = (RTCP_REMB*) msg->data();
-        remb->preparePacket(ssrc, 1, bitrate);
-        remb->setSSRC(0, ssrc);
-        remb->print();
-        std::cout << std::endl;
-
-//        std::cout << "begin tx" << std::endl;
-        txCB(msg);
-//        sendQueue.emplace_back(msg);
-//        std::cout << "end tx" << std::endl;
-    }
-
-    void pushRR(unsigned int lastSR_delay) {
-//        std::cout << "size " << RTCP_RR::sizeWithReportBlocks(1) << std::endl;
-        auto msg = rtc::make_message(RTCP_RR::sizeWithReportBlocks(1), rtc::Message::Type::Control);
-        auto rr = (RTCP_RR*) msg->data();
-        rr->preparePacket(ssrc, 1);
-        rr->getReportBlock(0)->preparePacket(ssrc, 0, 0, greatestSeqNo, 0, 0, syncNTPTS, lastSR_delay);
-        rr->print();
-        std::cout << std::endl;
-
-//        std::cout << "begin tx" << std::endl;
-
-//        sendQueue.emplace_back(msg);
-         txCB(msg);
-//        std::cout << "end tx" << std::endl;
-    }
+	void pushREMB(unsigned int bitrate) {
+		rtc::message_ptr msg =
+		    rtc::make_message(RTCP_REMB::sizeWithSSRCs(1), rtc::Message::Type::Control);
+		auto remb = (RTCP_REMB *)msg->data();
+		remb->preparePacket(ssrc, 1, bitrate);
+		remb->setSSRC(0, ssrc);
+		remb->print();
+		std::cout << std::endl;
+
+		tx(msg);
+	}
+
+	void pushRR(unsigned int lastSR_delay) {
+		//		  std::cout << "size " << RTCP_RR::sizeWithReportBlocks(1) << std::endl;
+		auto msg = rtc::make_message(RTCP_RR::sizeWithReportBlocks(1), rtc::Message::Type::Control);
+		auto rr = (RTCP_RR *)msg->data();
+		rr->preparePacket(ssrc, 1);
+		rr->getReportBlock(0)->preparePacket(ssrc, 0, 0, greatestSeqNo, 0, 0, syncNTPTS,
+		                                     lastSR_delay);
+		rr->print();
+		std::cout << std::endl;
+
+		tx(msg);
+	}
+
+	void tx(message_ptr msg) {
+		try {
+			txCB(msg);
+		} catch (const std::exception &e) {
+			LOG_DEBUG << "RTCP tx failed: " << e.what();
+		}
+	}
 };
-#endif //RTC_RTPL_H
+
+} // namespace rtc
+
+#endif // RTC_RTPL_H

+ 5 - 0
include/rtc/track.hpp

@@ -24,6 +24,7 @@
 #include "include.hpp"
 #include "message.hpp"
 #include "queue.hpp"
+#include "rtp.hpp"
 
 #include <atomic>
 #include <variant>
@@ -54,6 +55,9 @@ public:
 	size_t availableAmount() const override;
 	std::optional<message_variant> receive() override;
 
+	// RTCP handler
+	void setRtcpHandler(std::shared_ptr<RtcpHandler> handler);
+
 private:
 #if RTC_ENABLE_MEDIA
 	void open(std::shared_ptr<DtlsSrtpTransport> transport);
@@ -67,6 +71,7 @@ private:
 	std::atomic<bool> mIsClosed = false;
 
 	Queue<message_ptr> mRecvQueue;
+	std::shared_ptr<RtcpHandler> mRtcpHandler;
 
 	friend class PeerConnection;
 };

+ 224 - 220
src/description.cpp

@@ -73,9 +73,9 @@ Description::Description(const string &sdp, Type type, Role role)
 		// Media description line (aka m-line)
 		if (finished || match_prefix(line, "m=")) {
 			if (currentMedia) {
-				if (!currentMedia->mid.empty()) {
-					if (currentMedia->type == "application")
-						mData.mid = currentMedia->mid;
+				if (!currentMedia->mid().empty()) {
+					if (currentMedia->type() == "application")
+						mData.mid = currentMedia->mid();
 					else
 						mMedia.emplace(mlineIndex, std::move(*currentMedia));
 
@@ -102,7 +102,7 @@ Description::Description(const string &sdp, Type type, Role role)
 
 			if (key == "mid") {
 				if (currentMedia)
-					currentMedia->mid = value;
+					currentMedia->mid() = value;
 
 			} else if (key == "setup") {
 				if (value == "active")
@@ -130,40 +130,15 @@ Description::Description(const string &sdp, Type type, Role role)
 			} else if (key == "max-message-size") {
 				mData.maxMessageSize = size_t(std::stoul(value));
 			} else if (key == "candidate") {
-				addCandidate(Candidate(attr, currentMedia ? currentMedia->mid : mData.mid));
+				addCandidate(Candidate(attr, currentMedia ? currentMedia->mid() : mData.mid));
 			} else if (key == "end-of-candidates") {
-                mEnded = true;
+				mEnded = true;
 			} else if (currentMedia) {
-			    if (key == "rtpmap") {
-                    Description::Media::RTPMap map(value);
-                    currentMedia->rtpMap.insert(std::pair<int, Description::Media::RTPMap>(map.pt, map));
-                }else if (key == "rtcp-fb") {
-                    size_t p = value.find(' ');
-                    int pt = std::stoi(value.substr(0, p));
-                    auto it = currentMedia->rtpMap.find(pt);
-                    if (it == currentMedia->rtpMap.end()) {
-                        PLOG_WARNING << "rtcp-fb applied before it's rtpmap. Ignoring";
-                    } else
-                        it->second.rtcpFbs.emplace_back(value.substr(p + 1));
-                }else if (key == "fmtp") {
-                    size_t p = value.find(' ');
-                    int pt = std::stoi(value.substr(0, p));
-                    auto it = currentMedia->rtpMap.find(pt);
-                    if (it == currentMedia->rtpMap.end()) {
-                        PLOG_WARNING << "fmtp applied before it's rtpmap. Ignoring";
-                    } else
-                        it->second.fmtps.emplace_back(value.substr(p + 1));
-
-                } else if (key == "b") {
-
-                }else
-				    currentMedia->attributes.emplace_back(line.substr(2));
-			    std::cout << key << std::endl;
+				currentMedia->parseSdpLine(std::move(line));
 			}
-		}else if (match_prefix(line, "b=AS")) {
-            currentMedia->bAS = std::stoi(line.substr(line.find(':')+1));
+		} else if (currentMedia) {
+			currentMedia->parseSdpLine(std::move(line));
 		}
-
 	} while (!finished);
 }
 
@@ -180,7 +155,7 @@ string Description::dataMid() const { return mData.mid; }
 string Description::bundleMid() const {
 	// Get the mid of the first media
 	if (auto it = mMedia.find(0); it != mMedia.end())
-		return it->second.mid;
+		return it->second.mid();
 	else
 		return mData.mid;
 }
@@ -226,14 +201,11 @@ std::vector<Candidate> Description::extractCandidates() {
 
 bool Description::hasMedia() const { return !mMedia.empty(); }
 
-void Description::addMedia(const Description &source) {
-	for (auto p : source.mMedia)
-		mMedia.emplace(p);
-}
+void Description::addMedia(Media media) { mMedia.emplace(mMedia.size(), std::move(media)); }
 
 Description::operator string() const { return generateSdp("\r\n"); }
 
-string Description::generateSdp(const string &eol) const {
+string Description::generateSdp(string_view eol) const {
 	std::ostringstream sdp;
 
 	// Header
@@ -246,11 +218,12 @@ string Description::generateSdp(const string &eol) const {
 	// see Negotiating Media Multiplexing Using the Session Description Protocol
 	// https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-54
 	sdp << "a=group:BUNDLE";
-	for (int i = 0; i < int(mMedia.size() + 1); ++i)
+	for (int i = 0; i < int(mMedia.size() + 1); ++i) {
 		if (auto it = mMedia.find(i); it != mMedia.end())
-			sdp << ' ' << it->second.mid;
+			sdp << ' ' << it->second.mid();
 		else
 			sdp << ' ' << mData.mid;
+	}
 	sdp << eol;
 
 	// Non-data media
@@ -258,7 +231,7 @@ string Description::generateSdp(const string &eol) const {
 		// Lip-sync
 		sdp << "a=group:LS";
 		for (const auto &p : mMedia)
-			sdp << " " << p.second.mid;
+			sdp << " " << p.second.mid();
 		sdp << eol;
 	}
 
@@ -277,36 +250,7 @@ string Description::generateSdp(const string &eol) const {
 	// Media descriptions and attributes
 	for (int i = 0; i < int(mMedia.size() + 1); ++i) {
 		if (auto it = mMedia.find(i); it != mMedia.end()) {
-			// Non-data media
-			const auto &media = it->second;
-			sdp << "m=" << media.type << ' ' << 0 << ' ' << media.description;
-
-            for (const auto& [key, _] : media.rtpMap)
-                sdp << " " << key;
-
-            sdp << eol;
-			sdp << "c=IN IP4 0.0.0.0" << eol;
-            if (media.bAS > -1)
-                sdp << "b=AS:" << media.bAS << eol;
-			sdp << "a=bundle-only" << eol;
-			sdp << "a=mid:" << media.mid << eol;
-			for (const auto &attr : media.attributes)
-				sdp << "a=" << attr << eol;
-			for (const auto& [_, map] : media.rtpMap) {
-			    // Create the a=rtpmap
-			    sdp << "a=rtpmap:" << map.pt << " " << map.format << "/" << map.clockRate;
-			    if (!map.encParams.empty())
-			        sdp << "/" << map.encParams;
-			    sdp << eol;
-
-
-                for (const auto &val : map.rtcpFbs)
-                    sdp << "a=rtcp-fb:" << map.pt << " " << val << eol;
-                for (const auto &val : map.fmtps)
-                    sdp << "a=fmtp:" << map.pt << " " << val << eol;
-			}
-            for (const auto &attr : media.attributesl)
-                sdp << "a=" << attr << eol;
+			sdp << it->second.generateSdp(eol);
 		} else {
 			// Data
 			const string description = "UDP/DTLS/SCTP webrtc-datachannel";
@@ -333,7 +277,7 @@ string Description::generateSdp(const string &eol) const {
 	return sdp.str();
 }
 
-string Description::generateDataSdp(const string &eol) const {
+string Description::generateDataSdp(string_view eol) const {
 	std::ostringstream sdp;
 
 	// Header
@@ -372,158 +316,243 @@ string Description::generateDataSdp(const string &eol) const {
 	return sdp.str();
 }
 
-Description::Media::Media(const string &mline) {
+Description::Media::Media(string mline, Direction dir, string mid) {
 	size_t p = mline.find(' ');
-	this->type = mline.substr(0, p);
+	mType = mline.substr(0, p);
 	if (p != string::npos)
-		if (size_t q = mline.find(' ', p + 1); q != string::npos) {
-            this->description = mline.substr(q + 1, mline.find(' ', q+1)-q-2);
-        }
+		if (size_t q = mline.find(' ', p + 1); q != string::npos)
+			mDescription = mline.substr(q + 1, mline.find(' ', q + 1) - q - 2);
+
+	mMid = mid;
+	mAttributes.emplace_back("rtcp-mux");
+
+	setDirection(dir);
 }
 
-Description::Media::RTPMap& Description::Media::getFormat(int fmt) {
-    auto it = this->rtpMap.find(fmt);
-    if (it == this->rtpMap.end())
-        throw std::invalid_argument("mLineIndex is out of bounds");
-    return it->second;
+Description::Media::RTPMap &Description::Media::getFormat(int fmt) {
+	auto it = mRtpMap.find(fmt);
+	if (it != mRtpMap.end())
+		return it->second;
+
+	throw std::invalid_argument("mLineIndex is out of bounds");
 }
 
-Description::Media::RTPMap& Description::Media::getFormat(const string& fmt) {
-    for (auto &[key, val] : this->rtpMap) {
-        if (val.format == fmt)
-            return val;
-    }
-    throw std::invalid_argument("format was not found");
+Description::Media::RTPMap &Description::Media::getFormat(const string &fmt) {
+	for (auto it = mRtpMap.begin(); it != mRtpMap.end(); ++it)
+		if (it->second.format == fmt)
+			return it->second;
+
+	throw std::invalid_argument("format was not found");
 }
 
 void Description::Media::removeFormat(const string &fmt) {
-    auto it = this->rtpMap.begin();
-    std::vector<int> remed;
-
-    // Remove the actual formats
-    while (it != this->rtpMap.end()) {
-        if (it->second.format == fmt) {
-            remed.emplace_back(it->first);
-            it = this->rtpMap.erase(it);
-        } else
-            it++;
-    }
-
-    // Remove any other rtpmaps that depend on the formats we just removed
-    it = this->rtpMap.begin();
-    while (it != this->rtpMap.end()) {
-        auto it2 = it->second.fmtps.begin();
-        bool rem = false;
-        while (it2 != it->second.fmtps.end()) {
-            if (it2->find("apt=") == 0) {
-                for (auto remid : remed) {
-                    if (it2->find(std::to_string(remid)) != string::npos) {
-                        std::cout << *it2 << " " << remid << std::endl;
-                        it = this->rtpMap.erase(it);
-                        rem = true;
-                        break;
-                    }
-                }
-                break;
-            }
-            it2++;
-        }
-        if (!rem)
-
-            it++;
-    }
+	auto it = mRtpMap.begin();
+	std::vector<int> remed;
+
+	// Remove the actual formats
+	while (it != mRtpMap.end()) {
+		if (it->second.format == fmt) {
+			remed.emplace_back(it->first);
+			it = mRtpMap.erase(it);
+		} else {
+			it++;
+		}
+	}
+
+	// Remove any other rtpmaps that depend on the formats we just removed
+	it = mRtpMap.begin();
+	while (it != mRtpMap.end()) {
+		auto it2 = it->second.fmtps.begin();
+		bool rem = false;
+		while (it2 != it->second.fmtps.end()) {
+			if (it2->find("apt=") == 0) {
+				for (auto remid : remed) {
+					if (it2->find(std::to_string(remid)) != string::npos) {
+						std::cout << *it2 << " " << remid << std::endl;
+						it = mRtpMap.erase(it);
+						rem = true;
+						break;
+					}
+				}
+				break;
+			}
+			it2++;
+		}
+		if (!rem)
+			it++;
+	}
 }
 
 void Description::Media::addVideoCodec(int payloadType, const string &codec) {
-    RTPMap map(std::to_string(payloadType) + " " + codec + "/90000");
-    map.addFB("nack");
-    map.addFB("goog-remb");
-    this->rtpMap.insert(std::pair<int, RTPMap>(map.pt, map));
+	RTPMap map(std::to_string(payloadType) + " " + codec + "/90000");
+	map.addFB("nack");
+	map.addFB("goog-remb");
+	mRtpMap.emplace(map.pt, map);
 }
 
-void Description::Media::addH264Codec(int pt) {
-    addVideoCodec(pt, "H264");
-}
+void Description::Media::addH264Codec(int pt) { addVideoCodec(pt, "H264"); }
 
-void Description::Media::addVP8Codec(int payloadType) {
-    addVideoCodec(payloadType, "VP8");
-}
+void Description::Media::addVP8Codec(int payloadType) { addVideoCodec(payloadType, "VP8"); }
 
-void Description::Media::addVP9Codec(int payloadType) {
-    addVideoCodec(payloadType, "VP9");
-}
+void Description::Media::addVP9Codec(int payloadType) { addVideoCodec(payloadType, "VP9"); }
 
 Description::Direction Description::Media::getDirection() {
-    for (auto attr : attributes) {
-        if (attr == "sendrecv")
-            return Direction::SendRecv;
-        if (attr == "recvonly")
-            return Direction::RecvOnly;
-        if (attr == "sendonly")
-            return Direction::SendOnly;
-    }
-    return Direction::Unknown;
+	for (auto attr : mAttributes) {
+		if (attr == "sendrecv")
+			return Direction::SendRecv;
+		if (attr == "recvonly")
+			return Direction::RecvOnly;
+		if (attr == "sendonly")
+			return Direction::SendOnly;
+	}
+	return Direction::Unknown;
 }
 
-void Description::Media::setBitrate(int bitrate) {
-    this->bAS = bitrate;
+void Description::Media::setBitrate(int bitrate) { mBas = bitrate; }
+
+int Description::Media::getBitrate() const { return mBas; }
+
+void Description::Media::setDirection(Description::Direction dir) {
+	auto it = mAttributes.begin();
+	while (it != mAttributes.end()) {
+		if (*it == "sendrecv" || *it == "sendonly" || *it == "recvonly")
+			it = mAttributes.erase(it);
+		else
+			it++;
+	}
+	if (dir == Direction::SendRecv)
+		mAttributes.emplace(mAttributes.begin(), "sendrecv");
+	else if (dir == Direction::RecvOnly)
+		mAttributes.emplace(mAttributes.begin(), "recvonly");
+	if (dir == Direction::SendOnly)
+		mAttributes.emplace(mAttributes.begin(), "sendonly");
 }
-int Description::Media::getBitrate() const {
-    return this->bAS;
+
+string Description::Media::generateSdp(string_view eol) const {
+	std::ostringstream sdp;
+
+	sdp << "m=" << mType << ' ' << 0 << ' ' << mDescription;
+
+	for (auto it = mRtpMap.begin(); it != mRtpMap.end(); ++it)
+		sdp << ' ' << it->first;
+
+	sdp << eol;
+	sdp << "c=IN IP4 0.0.0.0" << eol;
+	if (mBas > -1)
+		sdp << "b=AS:" << mBas << eol;
+
+	sdp << "a=bundle-only" << eol;
+	sdp << "a=mid:" << mMid << eol;
+
+	for (const auto &attr : mAttributes)
+		sdp << "a=" << attr << eol;
+
+	for (auto it = mRtpMap.begin(); it != mRtpMap.end(); ++it) {
+		auto &map = it->second;
+
+		// Create the a=rtpmap
+		sdp << "a=rtpmap:" << map.pt << " " << map.format << "/" << map.clockRate;
+		if (!map.encParams.empty())
+			sdp << "/" << map.encParams;
+		sdp << eol;
+
+		for (const auto &val : map.rtcpFbs)
+			sdp << "a=rtcp-fb:" << map.pt << " " << val << eol;
+		for (const auto &val : map.fmtps)
+			sdp << "a=fmtp:" << map.pt << " " << val << eol;
+	}
+
+	for (const auto &attr : mAttributesl)
+		sdp << "a=" << attr << eol;
+
+	return sdp.str();
 }
 
-void Description::Media::setDirection(Description::Direction dir) {
-    auto it = attributes.begin();
-    while (it != attributes.end()) {
-        if (*it == "sendrecv" || *it == "sendonly" || *it == "recvonly")
-            it = attributes.erase(it);
-        else
-            it++;
-    }
-    if (dir == Direction::SendRecv)
-        attributes.emplace(attributes.begin(), "sendrecv");
-    else if (dir == Direction::RecvOnly)
-        attributes.emplace(attributes.begin(), "recvonly");
-    if (dir == Direction::SendOnly)
-        attributes.emplace(attributes.begin(), "sendonly");
+void Description::Media::parseSdpLine(string line) {
+	if (match_prefix(line, "a=")) {
+		string attr = line.substr(2);
+
+		string key, value;
+		if (size_t separator = attr.find(':'); separator != string::npos) {
+			key = attr.substr(0, separator);
+			value = attr.substr(separator + 1);
+		} else {
+			key = attr;
+		}
+
+		if (key == "mid") {
+			mMid = value;
+		} else if (key == "rtpmap") {
+			Description::Media::RTPMap map(value);
+			mRtpMap.emplace(map.pt, map);
+		} else if (key == "rtcp-fb") {
+			size_t p = value.find(' ');
+			int pt = std::stoi(value.substr(0, p));
+			auto it = mRtpMap.find(pt);
+			if (it == mRtpMap.end()) {
+				PLOG_WARNING << "rtcp-fb applied before it's rtpmap. Ignoring";
+			} else
+				it->second.rtcpFbs.emplace_back(value.substr(p + 1));
+		} else if (key == "fmtp") {
+			size_t p = value.find(' ');
+			int pt = std::stoi(value.substr(0, p));
+			auto it = mRtpMap.find(pt);
+			if (it == mRtpMap.end()) {
+				PLOG_WARNING << "fmtp applied before it's rtpmap. Ignoring";
+			} else {
+				it->second.fmtps.emplace_back(value.substr(p + 1));
+			}
+		} else if (key == "b") {
+			// TODO
+		} else {
+			mAttributes.emplace_back(line.substr(2));
+		}
+	} else if (match_prefix(line, "b=AS")) {
+		mBas = std::stoi(line.substr(line.find(':') + 1));
+	}
 }
 
 Description::Media::RTPMap::RTPMap(const string &mline) {
-    size_t p = mline.find(' ');
-
-    this->pt = std::stoi(mline.substr(0, p));
-
-    auto 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 = std::stoi(line);
-    else {
-        this->clockRate = std::stoi(line.substr(0, spl));
-        this->encParams = line.substr(spl);
-    }
+	size_t p = mline.find(' ');
+
+	this->pt = std::stoi(mline.substr(0, p));
+
+	auto 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 = std::stoi(line);
+	else {
+		this->clockRate = std::stoi(line.substr(0, spl));
+		this->encParams = line.substr(spl);
+	}
 }
 
 void Description::Media::RTPMap::removeFB(const string& string) {
-        auto it = rtcpFbs.begin();
-        while (it != rtcpFbs.end()) {
-            if (it->find(string) != std::string::npos) {
-                it = rtcpFbs.erase(it);
-            } else
-                it++;
-        }
-    }
-
-void Description::Media::RTPMap::addFB(const string& string) {
-    rtcpFbs.emplace_back(string);
+	auto it = rtcpFbs.begin();
+	while (it != rtcpFbs.end()) {
+		if (it->find(string) != std::string::npos) {
+			it = rtcpFbs.erase(it);
+		} else
+			it++;
+	}
 }
 
+void Description::Media::RTPMap::addFB(const string &string) { rtcpFbs.emplace_back(string); }
+
+Description::AudioMedia::AudioMedia(Direction dir, string mid)
+    : Media("audio 9 UDP/TLS/RTP/SAVPF", dir, std::move(mid)) {
+}
+
+Description::VideoMedia::VideoMedia(Direction dir, string mid)
+    : Media("video 9 UDP/TLS/RTP/SAVPF", dir, std::move(mid)) {}
+
 Description::Type Description::stringToType(const string &typeString) {
 	if (typeString == "offer")
 		return Type::Offer;
@@ -555,31 +584,6 @@ string Description::roleToString(Role role) {
 	}
 }
 
-std::_Rb_tree_iterator<std::pair<const int, Description::Media>> Description::getMedia(int mLine) {
-    return this->mMedia.find(mLine);
-}
-
-rtc::Description::Media& Description::addAudioMedia() {
-    rtc::Description::Media media("audio 9 UDP/TLS/RTP/SAVPF");
-    media.mid = "audio";
-
-    media.attributes.emplace_back("rtcp-mux");
-
-    this->mMedia.insert(std::pair<int, Media>(this->mMedia.size(), media));
-    return this->mMedia.at(mMedia.size()-1);
-}
-
-Description::Media& Description::addVideoMedia(Description::Direction direction) {
-    rtc::Description::Media media("video 9 UDP/TLS/RTP/SAVPF");
-    media.mid = "video";
-    media.attributes.emplace_back("rtcp-mux");
-
-    media.setDirection(direction);
-
-    this->mMedia.insert(std::pair<int, Media>(this->mMedia.size(), media));
-    return this->mMedia.at(mMedia.size()-1);
-}
-
 } // namespace rtc
 
 std::ostream &operator<<(std::ostream &out, const rtc::Description &description) {

+ 8 - 7
src/peerconnection.cpp

@@ -81,7 +81,7 @@ std::optional<Description> PeerConnection::remoteDescription() const {
 	return mRemoteDescription;
 }
 
-void PeerConnection::setLocalDescription(std::optional<Description> mediaDescription) {
+void PeerConnection::setLocalDescription() {
 	PLOG_VERBOSE << "Setting local description";
 
 	if (std::atomic_load(&mIceTransport))
@@ -92,14 +92,11 @@ void PeerConnection::setLocalDescription(std::optional<Description> mediaDescrip
 	// See https://tools.ietf.org/html/rfc5763#section-5
 	auto iceTransport = initIceTransport(Description::Role::ActPass);
 	Description localDescription = iceTransport->getLocalDescription(Description::Type::Offer);
-	if (mediaDescription)
-		localDescription.addMedia(*mediaDescription);
 	processLocalDescription(localDescription);
 	iceTransport->gatherLocalCandidates();
 }
 
-void PeerConnection::setRemoteDescription(Description description,
-                                          std::optional<Description> mediaDescription) {
+void PeerConnection::setRemoteDescription(Description description) {
 	PLOG_VERBOSE << "Setting remote description: " << string(description);
 
 	if (!description.fingerprint())
@@ -122,8 +119,6 @@ void PeerConnection::setRemoteDescription(Description description,
 	if (type == Description::Type::Offer) {
 		// This is an offer and we are the answerer.
 		Description localDescription = iceTransport->getLocalDescription(Description::Type::Answer);
-		if (mediaDescription)
-			localDescription.addMedia(*mediaDescription);
 		processLocalDescription(localDescription);
 		iceTransport->gatherLocalCandidates();
 	} else {
@@ -625,9 +620,15 @@ void PeerConnection::processLocalDescription(Description description) {
 	    remoteSctpPort = remote->sctpPort();
 	}
 
+	// Set the same data mid as remote
 	if (remoteDataMid)
 		description.setDataMid(*remoteDataMid);
 
+	// Set the media
+	for (auto it = mTracks.begin(); it != mTracks.end(); ++it)
+		if (auto track = it->second.lock())
+			description.addMedia(track->description());
+
 	description.setSctpPort(remoteSctpPort.value_or(DEFAULT_SCTP_PORT));
 	description.setMaxMessageSize(LOCAL_MAX_MESSAGE_SIZE);
 	description.setFingerprint(mCertificate.get()->fingerprint()); // wait for certificate

+ 27 - 1
src/track.cpp

@@ -28,13 +28,14 @@ using std::weak_ptr;
 Track::Track(Description::Media description)
     : mMediaDescription(std::move(description)), mRecvQueue(RECV_QUEUE_LIMIT, message_size_func) {}
 
-string Track::mid() const { return mMediaDescription.mid; }
+string Track::mid() const { return mMediaDescription.mid(); }
 
 Description::Media Track::description() const { return mMediaDescription; }
 
 void Track::close() {
 	mIsClosed = true;
 	resetCallbacks();
+	setRtcpHandler(nullptr);
 }
 
 bool Track::send(message_variant data) { return outgoing(make_message(std::move(data))); }
@@ -95,6 +96,14 @@ void Track::incoming(message_ptr message) {
 	if (!message)
 		return;
 
+	if (mRtcpHandler) {
+		auto opt = mRtcpHandler->incoming(message);
+		if (!opt)
+			return;
+
+		message = *opt;
+	}
+
 	// Tail drop if queue is full
 	if (mRecvQueue.full())
 		return;
@@ -103,5 +112,22 @@ void Track::incoming(message_ptr message) {
 	triggerAvailable(mRecvQueue.size());
 }
 
+void Track::setRtcpHandler(std::shared_ptr<RtcpHandler> handler) {
+	if (mRtcpHandler)
+		mRtcpHandler->onOutgoing(nullptr);
+
+	mRtcpHandler = std::move(handler);
+	if (mRtcpHandler) {
+		mRtcpHandler->onOutgoing([this]([[maybe_unused]] message_ptr message) {
+#if RTC_ENABLE_MEDIA
+			if (auto transport = mDtlsSrtpTransport.lock())
+				transport->sendMedia(message);
+#else
+			PLOG_WARNING << "Ignoring RTCP send (not compiled with SRTP support)";
+#endif
+		});
+	}
+}
+
 } // namespace rtc