Browse Source

Added RTCP Sessions; Added copy/paste example demo to use in the browser

Staz M 5 years ago
parent
commit
07abd640fd

+ 1 - 0
CMakeLists.txt

@@ -265,6 +265,7 @@ if(NOT NO_EXAMPLES)
 	set(JSON_BuildTests OFF CACHE INTERNAL "")
 	set(JSON_BuildTests OFF CACHE INTERNAL "")
 	add_subdirectory(deps/json)
 	add_subdirectory(deps/json)
 	add_subdirectory(examples/client)
 	add_subdirectory(examples/client)
+	add_subdirectory(examples/media)
 	add_subdirectory(examples/copy-paste)
 	add_subdirectory(examples/copy-paste)
 	add_subdirectory(examples/copy-paste-capi)
 	add_subdirectory(examples/copy-paste-capi)
 endif()
 endif()

+ 2 - 0
examples/README.md

@@ -7,6 +7,8 @@ This directory contains different WebRTC clients and compatible WebSocket + JSON
 - [signaling-server-python](signaling-server-python) contains a similar signaling server in Python
 - [signaling-server-python](signaling-server-python) contains a similar signaling server in Python
 - [signaling-server-rust](signaling-server-rust) contains a similar signaling server in Rust (see [lerouxrgd/datachannel-rs](https://github.com/lerouxrgd/datachannel-rs) for Rust wrappers)
 - [signaling-server-rust](signaling-server-rust) contains a similar signaling server in Rust (see [lerouxrgd/datachannel-rs](https://github.com/lerouxrgd/datachannel-rs) for Rust wrappers)
 
 
+- [media](media) is a copy/paste demo to send the webcam from your browser into gstreamer.
+
 Additionally, it contains two debugging tools for libdatachannel with copy-pasting as signaling:
 Additionally, it contains two debugging tools for libdatachannel with copy-pasting as signaling:
 - [copy-paste](copy-paste) using the C++ API
 - [copy-paste](copy-paste) using the C++ API
 - [copy-paste-capi](copy-paste-capi) using the C API
 - [copy-paste-capi](copy-paste-capi) using the C API

+ 14 - 0
examples/media/CMakeLists.txt

@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 3.7)
+
+add_executable(datachannel-media main.cpp)
+set_target_properties(datachannel-media PROPERTIES
+        CXX_STANDARD 17
+        OUTPUT_NAME media)
+
+if(WIN32)
+    target_link_libraries(datachannel-media datachannel-static) # DLL exports only the C API
+else()
+    target_link_libraries(datachannel-media datachannel)
+endif()
+
+target_link_libraries(datachannel-media datachannel nlohmann_json)

+ 19 - 0
examples/media/README.md

@@ -0,0 +1,19 @@
+# Example Webcam from Browser to Port 5000
+This is an example copy/paste demo to send your webcam from your browser and out port 5000 through the demo application.
+
+## How to use
+Open main.html in your browser (you must open it either as HTTPS or as a domain of http://localhost).
+
+Start the application and copy it's offer into the text box of the web page.
+
+Copy the answer of the webpage back into the application.
+
+You will now see RTP traffic on `localhost:5000` of the computer that the application is running on.
+
+Use the following gstreamer demo pipeline to display the traffic 
+(you might need to wave your hand in front of your camera to force an I-frame). 
+
+`gst-launch-1.0 udpsrc address=127.0.0.1 port=5000 caps="application/x-rtp" ! queue ! rtph264depay ! video/x-h264,stream-format=byte-stream ! queue ! avdec_h264 ! queue ! autovideosink`
+
+## Troubleshooting
+Use chrome.

+ 68 - 0
examples/media/main.cpp

@@ -0,0 +1,68 @@
+#include <iostream>
+#include <memory>
+#include <rtc/log.hpp>
+#include <rtc/rtc.hpp>
+#include <rtc/rtp.hpp>
+
+#include <nlohmann/json.hpp>
+#include <utility>
+#include <arpa/inet.h>
+
+using nlohmann::json;
+
+int main() {
+    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->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;
+
+    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(3000000); // 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 offer;
+    rtc::Description::Media &m = offer.addVideoMedia(rtc::Description::RECV_ONLY);
+    m.addH264Codec(96);
+    pc->setLocalDescription(offer);
+    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;
+}

+ 56 - 0
examples/media/main.html

@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Title</title>
+</head>
+<body>
+
+<p>Please enter the offer provided to you by the application: </p>
+<textarea cols="50" rows="50"></textarea>
+<button>Submit</button>
+
+<script>
+    document.querySelector('button').addEventListener('click',  async () => {
+        let offer = JSON.parse(document.querySelector('textarea').value);
+        rtc = new RTCPeerConnection({
+            // Requirement of libdatachannel
+            bundlePolicy: "max-bundle",
+        });
+
+        rtc.onicegatheringstatechange = (state) => {
+            if (rtc.iceGatheringState === 'complete') {
+                // We only want to provide an answer once all of our candidates have been added to the SDP.
+                let answer = rtc.localDescription;
+                document.querySelector('textarea').value = JSON.stringify({"type": answer.type, sdp: answer.sdp});
+                document.querySelector('p').value = 'Please paste the answer in the application.';
+                alert('Please paste the answer in the application.');
+            }
+        }
+        await rtc.setRemoteDescription(offer);
+
+        let media = await navigator.mediaDevices.getUserMedia({
+            video: {
+                width: 1280,
+                height: 720
+            }
+        });
+        media.getTracks().forEach(track => rtc.addTrack(track, media));
+        let answer= await  rtc.createAnswer();
+        await rtc.setLocalDescription(answer);
+
+        // For some reason, (at least) chrome requires for you to manually add the candidates.
+        offer.sdp.split("\n").forEach(line => {
+            if (line.startsWith("a=candidate")) {
+                let cand = line.substring(2);
+                rtc.addIceCandidate({
+                    sdpMid: "video",
+                    candidate: cand.trim()
+                });
+            }
+        });
+    })
+</script>
+
+</body>
+</html>

+ 15 - 34
include/rtc/description.hpp

@@ -32,17 +32,20 @@ namespace rtc {
 
 
 class Description {
 class Description {
 public:
 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 class Role { ActPass = 0, Passive = 1, Active = 2 };
     enum Direction {
     enum Direction {
         SEND_ONLY,
         SEND_ONLY,
         RECV_ONLY,
         RECV_ONLY,
-        BOTH
+        BOTH,
+        UNKNOWN
     };
     };
 
 
     Description(const string &sdp, const string &typeString = "");
     Description(const string &sdp, const string &typeString = "");
 	Description(const string &sdp, Type type);
 	Description(const string &sdp, Type type);
 	Description(const string &sdp, Type type, Role role);
 	Description(const string &sdp, Type type, Role role);
+    Description(): Description(""){};
 
 
 	Type type() const;
 	Type type() const;
 	string typeString() const;
 	string typeString() const;
@@ -100,52 +103,30 @@ public:
             std::vector<string> rtcpFbs;
             std::vector<string> rtcpFbs;
             std::vector<string> fmtps;
             std::vector<string> fmtps;
 
 
-            void removeFB(const char *string);
-
+            void removeFB(const string& string);
+            void addFB(const string& string);
         };
         };
 
 
         std::unordered_map<int, RTPMap> rtpMap;
         std::unordered_map<int, RTPMap> rtpMap;
 
 
         Media::RTPMap& getFormat(int fmt);
         Media::RTPMap& getFormat(int fmt);
         Media::RTPMap& getFormat(const string& fmt);
         Media::RTPMap& getFormat(const string& fmt);
-
-        Direction getDirection() {
-            for (auto attr : attributes) {
-                if (attr == "sendrecv")
-                    return Direction::BOTH;
-                if (attr == "recvonly")
-                    return Direction::RECV_ONLY;
-                if (attr == "sendonly")
-                    return Direction::SEND_ONLY;
-            }
-        }
-
-        void setDirection(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::BOTH)
-                attributes.emplace(attributes.begin(), "sendrecv");
-            else if (dir == Direction::RECV_ONLY)
-                attributes.emplace(attributes.begin(), "recvonly");
-            if (dir == Direction::SEND_ONLY)
-                attributes.emplace(attributes.begin(), "sendonly");
-        }
-
         void removeFormat(const string &fmt);
         void removeFormat(const string &fmt);
 
 
-        void addH264Codec(int pt);
+        Direction getDirection();
+        void setDirection(Direction dir);
+
+        void addVideoCodec(int payloadType, const string& codec);
+        void addH264Codec(int payloadType);
+        void addVP8Codec(int payloadType);
+        void addVP9Codec(int payloadType);
     };
     };
 
 
     std::_Rb_tree_iterator<std::pair<const int, Media>> getMedia(int mLine);
     std::_Rb_tree_iterator<std::pair<const int, Media>> getMedia(int mLine);
 
 
     Media & addAudioMedia();
     Media & addAudioMedia();
 
 
-    Media &addVideoMedia(bool direction);
+    Media &addVideoMedia(Direction direction=Direction::RECV_ONLY);
 
 
 private:
 private:
 	Type mType;
 	Type mType;

+ 2 - 1
include/rtc/peerconnection.hpp

@@ -103,7 +103,8 @@ public:
 	// Media
 	// Media
 	bool hasMedia() const;
 	bool hasMedia() const;
 	void sendMedia(const binary &packet);
 	void sendMedia(const binary &packet);
-	void sendMedia(const byte *packet, size_t size);
+    void sendMedia(const byte *packet, size_t size);
+    void sendMedia(rtc::message_ptr ptr);
 
 
 	void onMedia(const std::function<void(rtc::message_ptr)>& callback);
 	void onMedia(const std::function<void(rtc::message_ptr)>& callback);
 
 

+ 422 - 134
include/rtc/rtp.hpp

@@ -1,42 +1,116 @@
 #ifndef RTC_RTPL_H
 #ifndef RTC_RTPL_H
 #define RTC_RTPL_H
 #define RTC_RTPL_H
 
 
+#include <cmath>
+
+#include <utility>
+#include <netinet/in.h>
+
+typedef uint32_t SSRC;
+
 struct RTCP_ReportBlock {
 struct RTCP_ReportBlock {
-    uint32_t ssrc;
+private:
+    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;
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+    uint32_t highestSeqNo:16;
+    uint32_t seqNoCycles:16;
+#endif
 
 
-    uint32_t fractionLostAndCumulitveNumberOfPacketsLost;
-    uint32_t highestSeqNoReceived;
     uint32_t arrivalJitter;
     uint32_t arrivalJitter;
     uint32_t lastReport;
     uint32_t lastReport;
     uint32_t delaySinceLastReport;
     uint32_t delaySinceLastReport;
 
 
+public:
     void print() {
     void print() {
-//        std::cout << " +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-\n";
-//        std::cout << "|" << ssrc << "|\n";
-//        std::cout << "|" << fractionLost << "|" << packetsLost << "|\n";
-//        std::cout << "|" << ntohl(highestSeqNoReceived) << "|\n";
-//        std::cout << "|" << ntohl(arrivalJitter) << "|\n";
-//        std::cout << "|" << ntohl(lastReport) << "|\n";
-//        std::cout << "|" << ntohl(delaySinceLastReport) << "|\n";
-//        std::cout << " +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-" << std::endl;
         std::cout <<
         std::cout <<
             " ssrc:" << ntohl(ssrc) <<
             " ssrc:" << ntohl(ssrc) <<
-//            " fractionLost: " << (uint16_t) fractionLost <<
-//            " packetsLost:" << ntohs(packetsLost) <<
-            "flcnpl: " << fractionLostAndCumulitveNumberOfPacketsLost <<
-            " highestSeqNo:" << ntohl(highestSeqNoReceived) <<
-            " jitter:" << ntohl(arrivalJitter) <<
-            " lastSR:" << ntohl(lastReport) <<
-            " lastSRDelay:" << ntohl(delaySinceLastReport) << std::endl;
+            // TODO Implement these reports
+//            " fractionLost: " << fractionLost <<
+//            " packetsLost: " << packetsLost <<
+            " highestSeqNo:" <<getHighestSeqNo() <<
+            " seqNoCycles:" << getSeqCycleCount() <<
+            " jitter:" << getJitter() <<
+            " lastSR:" << getNTPOfSR() <<
+            " lastSRDelay:" << getDelaySinceSR();
     }
     }
-};
 
 
-struct RTCP_HEADER {
+    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);
+    }
 };
 };
 
 
-struct RTCP_SR {
 
 
+struct RTCP_HEADER {
+private:
 #if __BYTE_ORDER == __BIG_ENDIAN
 #if __BYTE_ORDER == __BIG_ENDIAN
     uint16_t version:2;
     uint16_t version:2;
 	uint16_t padding:1;
 	uint16_t padding:1;
@@ -48,9 +122,55 @@ struct RTCP_SR {
     uint16_t version:2;
     uint16_t version:2;
     uint16_t payloadType:8;
     uint16_t payloadType:8;
 #endif
 #endif
-
     uint16_t length;
     uint16_t length;
-    uint32_t ssrc;
+
+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();
+    }
+};
+
+struct RTCP_SR {
+private:
+    RTCP_HEADER header;
+
+    SSRC senderSSRC;
 
 
     uint64_t ntpTimestamp;
     uint64_t ntpTimestamp;
     uint32_t rtpTimestamp;
     uint32_t rtpTimestamp;
@@ -59,97 +179,112 @@ struct RTCP_SR {
     uint32_t octetCount;
     uint32_t octetCount;
 
 
     RTCP_ReportBlock reportBlocks;
     RTCP_ReportBlock reportBlocks;
-
-    RTCP_ReportBlock* getReportBlock(int num) {
-        return &reportBlocks+num;
-    }
-
-    unsigned int getTotalSize() {
-        return offsetof(RTCP_SR, reportBlocks) + sizeof(RTCP_ReportBlock)*reportCount;
-    }
+public:
 
 
     void print() {
     void print() {
+        std::cout << "SR ";
+        header.print();
         std::cout <<
         std::cout <<
-              "version:" << (uint16_t) version <<
-              " padding:" << (padding ? "T" : "F") <<
-              " reportCount: " << (uint16_t) reportCount <<
-              " payloadType:" << (uint16_t) payloadType <<
-              " totalLength:" << ntohs(length) <<
-              " SSRC:" << ntohl(ssrc) <<
+              " SSRC:" << ntohl(senderSSRC) <<
               " NTP TS: " << ntpTimestamp << // TODO This needs to be convereted from network-endian
               " NTP TS: " << ntpTimestamp << // TODO This needs to be convereted from network-endian
               " RTP TS: " << ntohl(rtpTimestamp) <<
               " RTP TS: " << ntohl(rtpTimestamp) <<
               " packetCount: " << ntohl(packetCount) <<
               " packetCount: " << ntohl(packetCount) <<
-              " octetCount: " << ntohl(octetCount)
-        << std::endl;
-            for (int i =0;i < reportCount; i++)
+              " octetCount: " << ntohl(octetCount) << "\n";
+//        << std::endl;
+            for (int i =0;i < header.getReportCount(); i++) {
                 getReportBlock(i)->print();
                 getReportBlock(i)->print();
+                std::cout << "\n";
+            }
+    }
 
 
-//        // 4 bytes
-//        std::cout
-//        << "|" << version << " |" << (padding ? "P" : " ")
-//        << "|" << reportCount << "|" << payloadType << "|" << ntohs(length) << "|\n";
-//
-//        // 4 bytes
-//        std::cout << "|" << ntohl(ssrc) << "|\n";
-//
-//        std::cout << "|MATH IS HARD|\n";
-//        std::cout << "|MATH IS HARD|\n";
-//
-//        std::cout << "|" << packetCount << "|\n";
-//        std::cout << "|" << senderCount << "|\n";
-//
-//        for (int i =0;i < reportCount; i++)
-//            getReportBlock(i)->print();
-//
-//        std::cout << " +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-\n";
-//        std::cout << std::endl;
+
+    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;
     }
     }
-};
 
 
-struct RTCP_RR {
+    RTCP_ReportBlock* getReportBlock(int num) {
+        return &reportBlocks+num;
+    }
 
 
-#if __BYTE_ORDER == __BIG_ENDIAN
-    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;
-#endif
+    [[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());
+    }
 
 
-    uint16_t length;
-    uint32_t ssrc;
+    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_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) {
     RTCP_ReportBlock* getReportBlock(int num) {
         return &reportBlocks+num;
         return &reportBlocks+num;
     }
     }
 
 
-    unsigned int getTotalSize() const {
-        return offsetof(RTCP_RR, reportBlocks) + sizeof(RTCP_ReportBlock)*reportCount;
+    inline RTCP_HEADER& getHeader() {
+        return header;
     }
     }
-    void print() {
-        std::cout <<
-                  "version:" << (uint16_t) version <<
-                  " padding:" << (padding ? "T" : "F") <<
-                  " reportCount: " << (uint16_t) reportCount <<
-                  " payloadType:" << (uint16_t) payloadType <<
-                  " totalLength:" << ntohs(length) <<
-                  " SSRC:" << ntohl(ssrc)
-                  << std::endl;
-        for (int i =0;i < reportCount; i++)
-            getReportBlock(i)->print();
+
+    inline SSRC getSenderSSRC() const {
+        return ntohl(senderSSRC);
     }
     }
 
 
-    void setLength() {
-//        std::cout << "TOTAL SIZE" << getTotalSize() << std::endl;
-//        PLOG_DEBUG << "TOTAL SIZE " << getTotalSize()/4 << std::endl;
-//        this->length = htons((getTotalSize()/4)+1);
-        this->length = htons((sizeof(RTCP_ReportBlock)/4)+1);
+    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);
     }
     }
 };
 };
 
 
@@ -172,61 +307,214 @@ struct RTP
 #endif
 #endif
     uint16_t seqNumber;
     uint16_t seqNumber;
     uint32_t timestamp;
     uint32_t timestamp;
-    uint32_t ssrc;
-    uint32_t csrc[16];
-};
+    SSRC ssrc;
+    SSRC csrc[16];
 
 
-struct RTPC_TWCC {
-#if __BYTE_ORDER == __BIG_ENDIAN
-    uint16_t version:2;
-	uint16_t padding:1;
-	uint16_t format:5;
-	uint16_t payloadType:8;
-#elif __BYTE_ORDER == __LITTLE_ENDIAN
-    uint16_t format:5;
-    uint16_t padding:1;
-    uint16_t version:2;
-    uint16_t payloadType:8;
-#endif
+    inline uint32_t getSeqNo() const {
+        return ntohs(seqNumber);
+    }
 
 
-    uint16_t length;
-    uint32_t senderSSRC;
-    uint32_t mediaSSRC;
-    uint16_t baseSeqNo;
-    uint16_t packetCount;
-    uint16_t refTime;
-    uint16_t twccSSRC;
+    inline uint32_t getTS() const{
+        return ntohl(timestamp);
+    }
 };
 };
 
 
  struct RTCP_REMB
  struct RTCP_REMB
 {
 {
+     RTCP_HEADER header;
 
 
-#if __BYTE_ORDER == __BIG_ENDIAN
-    uint16_t version:2;
-	uint16_t padding:1;
-	uint16_t format:5;
-	uint16_t payloadType:8;
-#elif __BYTE_ORDER == __LITTLE_ENDIAN
-    uint16_t format:5;
-    uint16_t padding:1;
-    uint16_t version:2;
-    uint16_t payloadType:8;
-#endif
-    uint16_t length;
-
-    uint32_t senderSSRC;
-    uint32_t mediaSourceSSRC;
+    SSRC senderSSRC;
+    SSRC mediaSourceSSRC;
 
 
     /*! \brief Unique identifier ('R' 'E' 'M' 'B') */
     /*! \brief Unique identifier ('R' 'E' 'M' 'B') */
-    char id[4] = {'R', 'E', 'M', 'B'};
+    char id[4];
+
     /*! \brief Num SSRC, Br Exp, Br Mantissa (bit mask) */
     /*! \brief Num SSRC, Br Exp, Br Mantissa (bit mask) */
     uint32_t bitrate;
     uint32_t bitrate;
 
 
-    /*! \brief SSRC feedback (we expect at max three SSRCs in there) */
-    uint32_t ssrc[3];
+    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;
+
+    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);
+            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;
 
 
-    int getTotalSize() {
-        return offsetof(RTCP_REMB, ssrc) + 4;
+//        sendQueue.emplace_back(msg);
+         txCB(msg);
+//        std::cout << "end tx" << std::endl;
     }
     }
 };
 };
 #endif //RTC_RTPL_H
 #endif //RTC_RTPL_H

+ 54 - 38
src/description.cpp

@@ -388,7 +388,7 @@ Description::Media::RTPMap& Description::Media::getFormat(int fmt) {
     return it->second;
     return it->second;
 }
 }
 
 
-Description::Media::RTPMap &Description::Media::getFormat(const string& fmt) {
+Description::Media::RTPMap& Description::Media::getFormat(const string& fmt) {
     for (auto &[key, val] : this->rtpMap) {
     for (auto &[key, val] : this->rtpMap) {
         if (val.format == fmt)
         if (val.format == fmt)
             return val;
             return val;
@@ -434,15 +434,52 @@ void Description::Media::removeFormat(const string &fmt) {
     }
     }
 }
 }
 
 
-void Description::Media::addH264Codec(int pt) {
-    RTPMap map(std::to_string(pt) + " H264/90000");
-    map.rtcpFbs.emplace_back("nack");
+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));
     this->rtpMap.insert(std::pair<int, RTPMap>(map.pt, map));
+}
+
+void Description::Media::addH264Codec(int pt) {
+    addVideoCodec(pt, "H264");
+}
 
 
-    RTPMap rtxMap(std::to_string(pt+1) + " rtx/90000");
-    rtxMap.fmtps.emplace_back("apt=" + std::to_string(pt));
-    this->rtpMap.insert(std::pair<int, RTPMap>(rtxMap.pt, rtxMap));
-};
+void Description::Media::addVP8Codec(int payloadType) {
+    addVideoCodec(payloadType, "VP8");
+}
+
+void Description::Media::addVP9Codec(int payloadType) {
+    addVideoCodec(payloadType, "VP9");
+}
+
+Description::Direction Description::Media::getDirection() {
+    for (auto attr : attributes) {
+        if (attr == "sendrecv")
+            return Direction::BOTH;
+        if (attr == "recvonly")
+            return Direction::RECV_ONLY;
+        if (attr == "sendonly")
+            return Direction::SEND_ONLY;
+    }
+    return Direction::UNKNOWN;
+}
+
+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::BOTH)
+        attributes.emplace(attributes.begin(), "sendrecv");
+    else if (dir == Direction::RECV_ONLY)
+        attributes.emplace(attributes.begin(), "recvonly");
+    if (dir == Direction::SEND_ONLY)
+        attributes.emplace(attributes.begin(), "sendonly");
+}
 
 
 Description::Media::RTPMap::RTPMap(const string &mline) {
 Description::Media::RTPMap::RTPMap(const string &mline) {
     size_t p = mline.find(' ');
     size_t p = mline.find(' ');
@@ -466,7 +503,7 @@ Description::Media::RTPMap::RTPMap(const string &mline) {
     }
     }
 }
 }
 
 
-    void Description::Media::RTPMap::removeFB(const char *string) {
+void Description::Media::RTPMap::removeFB(const string& string) {
         auto it = rtcpFbs.begin();
         auto it = rtcpFbs.begin();
         while (it != rtcpFbs.end()) {
         while (it != rtcpFbs.end()) {
             if (it->find(string) != std::string::npos) {
             if (it->find(string) != std::string::npos) {
@@ -476,7 +513,11 @@ Description::Media::RTPMap::RTPMap(const string &mline) {
         }
         }
     }
     }
 
 
-    Description::Type Description::stringToType(const string &typeString) {
+void Description::Media::RTPMap::addFB(const string& string) {
+    rtcpFbs.emplace_back(string);
+}
+
+Description::Type Description::stringToType(const string &typeString) {
 	if (typeString == "offer")
 	if (typeString == "offer")
 		return Type::Offer;
 		return Type::Offer;
 	else if (typeString == "answer")
 	else if (typeString == "answer")
@@ -521,43 +562,18 @@ rtc::Description::Media& Description::addAudioMedia() {
     return this->mMedia.at(mMedia.size()-1);
     return this->mMedia.at(mMedia.size()-1);
 }
 }
 
 
-Description::Media &Description::addVideoMedia(bool direction) {
+Description::Media& Description::addVideoMedia(Description::Direction direction) {
     rtc::Description::Media media("video 9 UDP/TLS/RTP/SAVPF");
     rtc::Description::Media media("video 9 UDP/TLS/RTP/SAVPF");
     media.mid = "video";
     media.mid = "video";
-
-    /*
-     *
-a=msid:janus janusv0
-a=ssrc:3730453306 cname:janus
-a=ssrc:3730453306 msid:janus janusv0
-a=ssrc:3730453306 mslabel:janus
-a=ssrc:3730453306 label:janusv0
-a=ssrc:2731410783 cname:janus
-a=ssrc:2731410783 msid:janus janusv0
-a=ssrc:2731410783 mslabel:janus
-a=ssrc:2731410783 label:janusv0
-     */
-
     media.attributes.emplace_back("rtcp-mux");
     media.attributes.emplace_back("rtcp-mux");
 
 
-//    media.attributesl.emplace_back("ssrc-group:FID 1 2");
-//    media.attributesl.emplace_back("msid:janus janusv0");
-//
-//    media.attributesl.emplace_back("ssrc:1 cname:janus");
-//    media.attributesl.emplace_back("ssrc:1 msid:janus janusv0");
-//    media.attributesl.emplace_back("ssrc:1 mslabel:janus");
-//    media.attributesl.emplace_back("ssrc:1 label:janusv0");
-//
-//    media.attributesl.emplace_back("ssrc:2 cname:janus");
-//    media.attributesl.emplace_back("ssrc:2 msid:janus janusv0");
-//    media.attributesl.emplace_back("ssrc:2 mslabel:janus");
-//    media.attributesl.emplace_back("ssrc:2 label:janusv0");
+    media.setDirection(direction);
 
 
     this->mMedia.insert(std::pair<int, Media>(this->mMedia.size(), media));
     this->mMedia.insert(std::pair<int, Media>(this->mMedia.size(), media));
     return this->mMedia.at(mMedia.size()-1);
     return this->mMedia.at(mMedia.size()-1);
 }
 }
 
 
-    std::ostream &operator<<(std::ostream &out, const rtc::Description &description) {
+std::ostream &operator<<(std::ostream &out, const rtc::Description &description) {
 	return out << std::string(description);
 	return out << std::string(description);
 }
 }
 
 

+ 4 - 0
src/peerconnection.cpp

@@ -245,6 +245,10 @@ void PeerConnection::sendMedia(const byte *packet, size_t size) {
 	outgoingMedia(make_message(packet, packet + size, Message::Binary));
 	outgoingMedia(make_message(packet, packet + size, Message::Binary));
 }
 }
 
 
+void PeerConnection::sendMedia(message_ptr ptr) {
+    outgoingMedia(std::move(ptr));
+}
+
 void PeerConnection::onMedia(const std::function<void(rtc::message_ptr)>& callback) {
 void PeerConnection::onMedia(const std::function<void(rtc::message_ptr)>& callback) {
 	mMediaCallback = callback;
 	mMediaCallback = callback;
 }
 }