浏览代码

Add h264 and opus streaming support to libdatachannel

Filip Klembara 4 年之前
父节点
当前提交
1d27f5b876

+ 16 - 0
CMakeLists.txt

@@ -64,6 +64,14 @@ set(LIBDATACHANNEL_SOURCES
 	${CMAKE_CURRENT_SOURCE_DIR}/src/track.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/processor.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/capi.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizationconfig.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpsenderreportable.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizer.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/opusrtppacketizer.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/opuspacketizationhandler.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/h264rtppacketizer.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/nalunit.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/h264packetizationhandler.cpp
 )
 
 set(LIBDATACHANNEL_WEBSOCKET_SOURCES
@@ -95,6 +103,14 @@ set(LIBDATACHANNEL_HEADERS
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtp.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/track.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/websocket.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtppacketizationconfig.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpsenderreportable.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtppacketizer.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/opusrtppacketizer.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/opuspacketizationhandler.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h264rtppacketizer.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/nalunit.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h264packetizationhandler.hpp
 )
 
 set(TESTS_SOURCES

+ 72 - 0
include/rtc/h264packetizationhandler.hpp

@@ -0,0 +1,72 @@
+/*
+ * libdatachannel client example
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef H264PacketizationHandler_hpp
+#define H264PacketizationHandler_hpp
+
+#include "rtcp.hpp"
+#include "h264rtppacketizer.hpp"
+#include "rtcpsenderreportable.hpp"
+#include "nalunit.hpp"
+
+#if RTC_ENABLE_MEDIA
+
+namespace rtc {
+
+/// Handler for H264 packetization
+class H264PacketizationHandler: public RtcpHandler, public RTCPSenderReportable {
+    /// RTP packetizer for H264
+    const std::shared_ptr<H264RTPPacketizer> packetizer;
+
+    const uint16_t maximumFragmentSize;
+
+    std::shared_ptr<NalUnits> splitMessage(message_ptr message);
+public:
+    /// Nalunit separator
+    enum class Separator {
+        LongStartSequence,  // 0x00, 0x00, 0x00, 0x01
+        ShortStartSequence, // 0x00, 0x00, 0x01
+        StartSequence,      // LongStartSequence or ShortStartSequence
+        Length              // first 4 bytes is nal unit length
+    };
+
+    /// Construct handler for H264 packetization.
+    /// @param separator Nal units separator
+    /// @param packetizer RTP packetizer for h264
+    H264PacketizationHandler(Separator separator, std::shared_ptr<H264RTPPacketizer> packetizer, uint16_t maximumFragmentSize = NalUnits::defaultMaximumFragmentSize);
+
+    /// Returns message unchanged
+    /// @param ptr message
+    message_ptr incoming(message_ptr ptr) override;
+
+    /// Returns packetized message if message type is binary
+    /// @note NAL units in `ptr` message must be separated by `separator` given in constructor
+    /// @note If message generates multiple rtp packets, all but last are send using `outgoingCallback`. It is your responsibility to send last packet.
+    /// @param ptr message containing all NAL units for current timestamp (one sample)
+    /// @return last packet
+    message_ptr outgoing(message_ptr ptr) override;
+private:
+    /// Separator
+    const Separator separator;
+};
+
+} // namespace
+
+#endif /* RTC_ENABLE_MEDIA */
+
+#endif /* H264PacketizationHandler_hpp */

+ 45 - 0
include/rtc/h264rtppacketizer.hpp

@@ -0,0 +1,45 @@
+/*
+ * libdatachannel streamer example
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef H264RTPPacketizer_hpp
+#define H264RTPPacketizer_hpp
+
+#include "rtppacketizer.hpp"
+
+#if RTC_ENABLE_MEDIA
+
+namespace rtc {
+
+/// RTP packetization of h264 payload
+class H264RTPPacketizer: public rtc::RTPPacketizer {
+
+public:
+    /// Default clock rate for H264 in RTP
+    static const auto defaultClockRate = 90 * 1000;
+
+    /// Constructs h264 payload packetizer with given RTP configuration.
+    /// @note RTP configuration is used in packetization process which may change some configuration properties such as sequence number.
+    /// @param rtpConfig  RTP configuration
+    H264RTPPacketizer(std::shared_ptr<rtc::RTPPacketizationConfig> rtpConfig);
+};
+
+} // namespace
+
+#endif /* RTC_ENABLE_MEDIA */
+
+#endif /* H264RTPPacketizer_hpp */

+ 155 - 0
include/rtc/nalunit.hpp

@@ -0,0 +1,155 @@
+/*
+ * libdatachannel streamer example
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NalUnit_hpp
+#define NalUnit_hpp
+
+#include "include.hpp"
+
+#if RTC_ENABLE_MEDIA
+
+namespace rtc {
+
+#pragma pack(push, 1)
+
+/// Nalu header
+struct NalUnitHeader {
+    bool forbiddenBit() { return _first >> 7; }
+    uint8_t nri() { return _first >> 5 & 0x03; }
+    uint8_t unitType() { return _first & 0x1F; }
+
+    void setForbiddenBit(bool isSet) { _first = (_first & 0x7F) | (isSet << 7); }
+    void setNRI(uint8_t nri) { _first = (_first & 0x9F) | ((nri & 0x03) << 5); }
+    void setUnitType(uint8_t type) { _first = (_first &0xE0) | (type & 0x1F); }
+private:
+    uint8_t _first = 0;
+};
+
+/// Nalu fragment header
+struct NalUnitFragmentHeader {
+    bool isStart() { return _first >> 7; }
+    bool reservedBit6() { return (_first >> 6) & 0x01; }
+    bool isEnd() { return (_first >> 5) & 0x01; }
+    uint8_t unitType() { return _first & 0x1F; }
+
+    void setStart(bool isSet) { _first = (_first & 0x7F) | (isSet << 7); }
+    void setEnd(bool isSet) { _first = (_first & 0xDF) | (isSet << 6); }
+    void setReservedBit6(bool isSet) { _first = (_first & 0xBF) | (isSet << 5); }
+    void setUnitType(uint8_t type) { _first = (_first &0xE0) | (type & 0x1F); }
+private:
+    uint8_t _first = 0;
+};
+
+#pragma pack(pop)
+
+/// Nal unit
+struct NalUnit: rtc::binary {
+    NalUnit(const NalUnit &unit) = default;
+    NalUnit(size_t size, bool includingHeader = true): rtc::binary(size + (includingHeader ? 0 : 1)) { }
+
+    template <typename Iterator>
+    NalUnit(Iterator begin_, Iterator end_): rtc::binary(begin_, end_) { }
+
+    NalUnit(rtc::binary &&data) : rtc::binary(std::move(data)) { }
+
+    bool forbiddenBit() { return header()->forbiddenBit(); }
+    uint8_t nri() { return header()->nri(); }
+    uint8_t unitType() { return header()->unitType(); }
+    rtc::binary payload() {
+        assert(size() >= 1);
+        return {begin() + 1, end()};
+    }
+
+    void setForbiddenBit(bool isSet) { header()->setForbiddenBit(isSet); }
+    void setNRI(uint8_t nri) { header()->setNRI(nri); }
+    void setUnitType(uint8_t type) { header()->setUnitType(type); }
+    void setPayload(rtc::binary payload) {
+        assert(size() >= 1);
+        erase(begin() + 1, end());
+        insert(end(), payload.begin(), payload.end());
+    }
+
+protected:
+    NalUnitHeader * header() {
+        assert(size() >= 1);
+        return (NalUnitHeader *) data();
+    }
+};
+
+/// Nal unit fragment A
+struct NalUnitFragmentA: NalUnit {
+    enum class FragmentType {
+        Start,
+        Middle,
+        End
+    };
+
+    NalUnitFragmentA(FragmentType type, bool forbiddenBit, uint8_t nri, uint8_t unitType, rtc::binary data);
+
+    static std::vector<NalUnitFragmentA> fragmentsFrom(NalUnit nalu, uint16_t maximumFragmentSize);
+
+    uint8_t unitType() { return fragmentHeader()->unitType(); }
+
+    rtc::binary payload() {
+        assert(size() >= 2);
+        return {begin() + 2, end()};
+    }
+
+    FragmentType type() {
+        if(fragmentHeader()->isStart()) {
+            return FragmentType::Start;
+        } else if(fragmentHeader()->isEnd()) {
+            return FragmentType::End;
+        } else {
+            return FragmentType::Middle;
+        }
+    }
+
+    void setUnitType(uint8_t type) { fragmentHeader()->setUnitType(type); }
+
+    void setPayload(rtc::binary payload) {
+        assert(size() >= 2);
+        erase(begin() + 2, end());
+        insert(end(), payload.begin(), payload.end());
+    }
+
+    void setFragmentType(FragmentType type);
+
+protected:
+    NalUnitHeader * fragmentIndicator() {
+        return (NalUnitHeader *) data();
+    }
+
+    NalUnitFragmentHeader * fragmentHeader() {
+        return (NalUnitFragmentHeader *) fragmentIndicator() + 1;
+    }
+
+    const uint8_t nal_type_fu_A = 28;
+};
+
+class NalUnits: public std::vector<NalUnit> {
+public:
+    static const uint16_t defaultMaximumFragmentSize = 1100;
+    std::vector<rtc::binary> generateFragments(uint16_t maximumFragmentSize = NalUnits::defaultMaximumFragmentSize);
+};
+
+} // namespace
+
+#endif /* RTC_ENABLE_MEDIA */
+
+#endif /* NalUnit_hpp */

+ 52 - 0
include/rtc/opuspacketizationhandler.hpp

@@ -0,0 +1,52 @@
+/*
+ * libdatachannel client example
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef OpusPacketizationHandler_hpp
+#define OpusPacketizationHandler_hpp
+
+#include "rtcpsenderreportable.hpp"
+#include "opusrtppacketizer.hpp"
+#include "rtcp.hpp"
+
+#if RTC_ENABLE_MEDIA
+
+namespace rtc {
+
+/// Handler for opus packetization
+class OpusPacketizationHandler: public RtcpHandler, public RTCPSenderReportable {
+    /// RTP packetizer for opus
+    const std::shared_ptr<OpusRTPPacketizer> packetizer;
+
+public:
+    /// Construct handler for opus packetization.
+    /// @param packetizer RTP packetizer for opus
+    OpusPacketizationHandler(std::shared_ptr<OpusRTPPacketizer> packetizer);
+
+    /// Returns message unchanged
+    /// @param ptr message
+    message_ptr incoming(message_ptr ptr) override;
+    /// Returns packetized message if message type is binary
+    /// @param ptr message
+    message_ptr outgoing(message_ptr ptr) override;
+};
+
+}   // namespace
+
+#endif /* RTC_ENABLE_MEDIA */
+
+#endif /* OpusPacketizationHandler_hpp */

+ 51 - 0
include/rtc/opusrtppacketizer.hpp

@@ -0,0 +1,51 @@
+/*
+ * libdatachannel streamer example
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef OpusRTPPacketizer_hpp
+#define OpusRTPPacketizer_hpp
+
+#include "rtppacketizer.hpp"
+
+#if RTC_ENABLE_MEDIA
+
+namespace rtc {
+
+/// RTP packetizer for opus
+class OpusRTPPacketizer: public rtc::RTPPacketizer {
+
+public:
+    /// default clock rate used in opus RTP communication
+    static const uint32_t defaultClockRate = 48 * 1000;
+
+    /// Constructs opus packetizer with given RTP configuration.
+    /// @note RTP configuration is used in packetization process which may change some configuration properties such as sequence number.
+    /// @param rtpConfig  RTP configuration
+    OpusRTPPacketizer(std::shared_ptr<rtc::RTPPacketizationConfig> rtpConfig);
+
+    /// Creates RTP packet for given payload based on `rtpConfig`.
+    /// @note This function increase sequence number after packetization.
+    /// @param payload RTP payload
+    /// @param setMark This needs to be `false` for all RTP packets with opus payload
+    rtc::message_ptr packetize(rtc::binary payload, bool setMark) override;
+};
+
+} // namespace
+
+#endif /* RTC_ENABLE_MEDIA */
+
+#endif /* OpusRTPPacketizer_hpp */

+ 8 - 0
include/rtc/rtc.hpp

@@ -25,5 +25,13 @@
 #include "peerconnection.hpp"
 #include "websocket.hpp"
 
+#if RTC_ENABLE_MEDIA
+
+// opus/h264 streaming
+#include "opuspacketizationhandler.hpp"
+#include "h264packetizationhandler.hpp"
+
+#endif /* RTC_ENABLE_MEDIA */
+
 // C API
 #include "rtc.h"

+ 87 - 0
include/rtc/rtcpsenderreportable.hpp

@@ -0,0 +1,87 @@
+/*
+ * libdatachannel streamer example
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RTCPSenderReporter_hpp
+#define RTCPSenderReporter_hpp
+
+#include "message.hpp"
+#include "rtppacketizationconfig.hpp"
+
+#if RTC_ENABLE_MEDIA
+
+namespace rtc {
+
+/// Class for sending RTCP SR
+class RTCPSenderReportable {
+    bool needsToReport = false;
+
+    uint32_t packetCount = 0;
+    uint32_t payloadOctets = 0;
+    double timeOffset = 0;
+
+    uint32_t _previousReportedTimestamp = 0;
+
+    void addToReport(RTP * rtp, uint32_t rtpSize);
+    message_ptr getSenderReport(uint32_t timestamp);
+protected:
+    /// Outgoing callback for sender reports
+    synchronized_callback<message_ptr> senderReportOutgoingCallback;
+public:
+    static uint64_t secondsToNTP(double seconds);
+
+    /// Timestamp of previous sender report
+    const uint32_t & previousReportedTimestamp = _previousReportedTimestamp;
+
+    /// RTP configuration
+    const std::shared_ptr<RTPPacketizationConfig> rtpConfig;
+
+    RTCPSenderReportable(std::shared_ptr<RTPPacketizationConfig> rtpConfig);
+
+    /// Set `needsToReport` flag. Sender report will be sent before next RTP packet with same timestamp.
+    void setNeedsToReport();
+
+    /// Set offset to compute NTS for RTCP SR packets. Offset represents relation between real start time and timestamp of the stream in RTP packets
+    /// @note `time_offset = rtpConfig->startTime_s - rtpConfig->timestampToSeconds(rtpConfig->timestamp)`
+    void startRecording();
+
+    /// Send RTCP SR with given timestamp
+    /// @param timestamp timestamp of the RTCP SR
+    void sendReport(uint32_t timestamp);
+    
+protected:
+    /// Calls given block with function for statistics. Sends RTCP SR packet with current timestamp before `block` call if `needs_to_report` flag is true.
+    /// @param block Block of code to run. This block has function for rtp stats recording.
+    template <typename T>
+    T withStatsRecording(std::function<T (std::function<void (message_ptr)>)> block) {
+        if (needsToReport) {
+            sendReport(rtpConfig->timestamp);
+            needsToReport = false;
+        }
+        auto result = block([this](message_ptr _rtp) {
+            auto rtp = reinterpret_cast<RTP *>(_rtp->data());
+            this->addToReport(rtp, _rtp->size());
+        });
+        return result;
+    }
+};
+
+} // namespace
+
+#endif /* RTC_ENABLE_MEDIA */
+
+#endif /* RTCPSenderReporter_hpp */

+ 91 - 0
include/rtc/rtppacketizationconfig.hpp

@@ -0,0 +1,91 @@
+/*
+ * libdatachannel streamer example
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RTPPacketizationConfig_hpp
+#define RTPPacketizationConfig_hpp
+
+#include "rtp.hpp"
+
+#if RTC_ENABLE_MEDIA
+
+namespace rtc {
+
+/// RTP configuration used in packetization process
+struct RTPPacketizationConfig {
+private:
+    uint32_t _startTimestamp = 0;
+    double _startTime_s = 0;
+    RTPPacketizationConfig(const RTPPacketizationConfig&) = delete;
+public:
+    const SSRC ssrc;
+    const std::string cname;
+    const uint8_t payloadType;
+    const uint32_t clockRate;
+    const double & startTime_s = _startTime_s;
+    const uint32_t & startTimestamp = _startTimestamp;
+
+    /// current sequence number
+    uint16_t sequenceNumber;
+    /// current timestamp
+    uint32_t timestamp;
+
+    enum class EpochStart: unsigned long long {
+        T1970 = 2208988800, // number of seconds between 1970 and 1900
+        T1900 = 0
+    };
+
+    /// Creates relation between time and timestamp mapping given start time and start timestamp
+    /// @param startTime_s Start time of the stream
+    /// @param epochStart Type of used epoch
+    /// @param startTimestamp Corresponding timestamp for given start time (current timestamp will be used if value is nullopt)
+    void setStartTime(double startTime_s, EpochStart epochStart, std::optional<uint32_t> startTimestamp = std::nullopt);
+
+
+    /// Construct RTP configuration used in packetization process
+    /// @param ssrc SSRC of source
+    /// @param cname CNAME of source
+    /// @param payloadType Payload type of source
+    /// @param clockRate Clock rate of source used in timestamps
+    /// @param sequenceNumber Initial sequence number of RTP packets (random number is choosed if nullopt)
+    /// @param timestamp Initial timastamp of RTP packets (random number is choosed if nullopt)
+    RTPPacketizationConfig(SSRC ssrc, std::string cname, uint8_t payloadType, uint32_t clockRate, std::optional<uint16_t> sequenceNumber = std::nullopt, std::optional<uint32_t> timestamp = std::nullopt);
+
+    /// Convert timestamp to seconds
+    /// @param timestamp Timestamp
+    /// @param clockRate Clock rate for timestamp calculation
+    static double getSecondsFromTimestamp(uint32_t timestamp, uint32_t clockRate);
+
+    /// Convert timestamp to seconds
+    /// @param timestamp Timestamp
+    double timestampToSeconds(uint32_t timestamp);
+
+    /// Convert seconds to timestamp
+    /// @param seconds Number of seconds
+    /// @param clockRate Clock rate for timestamp calculation
+    static uint32_t getTimestampFromSeconds(double seconds, uint32_t clockRate);
+
+    /// Convert seconds to timestamp
+    /// @param seconds Number of seconds
+    uint32_t secondsToTimestamp(double seconds);
+};
+
+} // namespace
+
+#endif /* RTC_ENABLE_MEDIA */
+
+#endif /* RTPPacketizationConfig_hpp */

+ 52 - 0
include/rtc/rtppacketizer.hpp

@@ -0,0 +1,52 @@
+/*
+ * libdatachannel streamer example
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RTPPacketizer_hpp
+#define RTPPacketizer_hpp
+
+#include "rtppacketizationconfig.hpp"
+#include "message.hpp"
+
+#if RTC_ENABLE_MEDIA
+
+namespace rtc {
+
+/// Class responsizble for rtp packetization
+class RTPPacketizer {
+    static const auto rtpHeaderSize = 12;
+public:
+    // rtp configuration
+    const std::shared_ptr<RTPPacketizationConfig> rtpConfig;
+
+    /// Constructs packetizer with given RTP configuration.
+    /// @note RTP configuration is used in packetization process which may change some configuration properties such as sequence number.
+    /// @param rtpConfig  RTP configuration
+    RTPPacketizer(std::shared_ptr<RTPPacketizationConfig> rtpConfig);
+
+    /// Creates RTP packet for given payload based on `rtpConfig`.
+    /// @note This function increase sequence number after packetization.
+    /// @param payload RTP payload
+    /// @param setMark Set marker flag in RTP packet if true
+    virtual message_ptr packetize(binary payload, bool setMark);
+};
+
+}   // namespace
+
+#endif /* RTC_ENABLE_MEDIA */
+
+#endif /* RTPPacketizer_hpp */

+ 164 - 0
src/h264packetizationhandler.cpp

@@ -0,0 +1,164 @@
+/*
+ * libdatachannel client example
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "h264packetizationhandler.hpp"
+
+#if RTC_ENABLE_MEDIA
+
+using namespace rtc;
+using namespace std;
+
+typedef enum {
+    NUSM_noMatch,
+    NUSM_firstZero,
+    NUSM_secondZero,
+    NUSM_thirdZero,
+    NUSM_shortMatch,
+    NUSM_longMatch
+} NalUnitStartSequenceMatch;
+
+NalUnitStartSequenceMatch StartSequenceMatchSucc(NalUnitStartSequenceMatch match, byte _byte, H264PacketizationHandler::Separator separator) {
+    assert(separator != H264PacketizationHandler::Separator::Length);
+    auto byte = (uint8_t) _byte;
+    auto detectShort = separator == H264PacketizationHandler::Separator::ShortStartSequence || separator == H264PacketizationHandler::Separator::StartSequence;
+    auto detectLong = separator == H264PacketizationHandler::Separator::LongStartSequence || separator == H264PacketizationHandler::Separator::StartSequence;
+    switch (match) {
+        case NUSM_noMatch:
+            if (byte == 0x00) {
+                return NUSM_firstZero;
+            }
+            break;
+        case NUSM_firstZero:
+            if (byte == 0x00) {
+                return NUSM_secondZero;
+            }
+            break;
+        case NUSM_secondZero:
+            if (byte == 0x00 && detectLong) {
+                return NUSM_thirdZero;
+            } else if (byte == 0x01 && detectShort) {
+                return NUSM_shortMatch;
+            }
+            break;
+        case NUSM_thirdZero:
+            if (byte == 0x01 && detectLong) {
+                return NUSM_longMatch;
+            }
+            break;
+        case NUSM_shortMatch:
+            return NUSM_shortMatch;
+        case NUSM_longMatch:
+            return NUSM_longMatch;
+    }
+    return NUSM_noMatch;
+}
+
+message_ptr H264PacketizationHandler::incoming(message_ptr ptr) {
+    return ptr;
+}
+
+shared_ptr<NalUnits> H264PacketizationHandler::splitMessage(rtc::message_ptr message) {
+    auto nalus = make_shared<NalUnits>();
+    if (separator == Separator::Length) {
+        unsigned long long index = 0;
+        while (index < message->size()) {
+            assert(index + 4 < message->size());
+            if (index + 4 >= message->size()) {
+                LOG_WARNING << "Invalid NAL Unit data (incomplete length), ignoring!";
+                break;
+            }
+            auto lengthPtr = (uint32_t *) (message->data() + index);
+            uint32_t length = ntohl(*lengthPtr);
+            auto naluStartIndex = index + 4;
+            auto naluEndIndex = naluStartIndex + length;
+
+            assert(naluEndIndex <= message->size());
+            if (naluEndIndex > message->size()) {
+                LOG_WARNING << "Invalid NAL Unit data (incomplete unit), ignoring!";
+                break;
+            }
+            nalus->push_back(NalUnit(message->begin() + naluStartIndex, message->begin() + naluEndIndex));
+            index = naluEndIndex;
+        }
+    } else {
+        NalUnitStartSequenceMatch match = NUSM_noMatch;
+        unsigned long long index = 0;
+        while (index < message->size()) {
+            match = StartSequenceMatchSucc(match, (*message)[index++], separator);
+            if (match == NUSM_longMatch || match == NUSM_shortMatch) {
+                match = NUSM_noMatch;
+                break;
+            }
+        }
+        index++;
+        unsigned long long naluStartIndex = index;
+
+        while (index < message->size()) {
+            match = StartSequenceMatchSucc(match, (*message)[index], separator);
+            if (match == NUSM_longMatch || match == NUSM_shortMatch) {
+                auto sequenceLength = match == NUSM_longMatch ? 4 : 3;
+                unsigned long long naluEndIndex = index - sequenceLength;
+                match = NUSM_noMatch;
+                nalus->push_back(NalUnit(message->begin() + naluStartIndex, message->begin() + naluEndIndex + 1));
+                naluStartIndex = index + 1;
+            }
+            index++;
+        }
+        nalus->push_back(NalUnit(message->begin() + naluStartIndex, message->end()));
+    }
+    return nalus;
+}
+
+message_ptr H264PacketizationHandler::outgoing(message_ptr ptr) {
+    if (ptr->type == Message::Binary) {
+        auto nalus = splitMessage(ptr);
+        auto fragments = nalus->generateFragments(maximumFragmentSize);
+
+         auto lastPacket = withStatsRecording<message_ptr>([fragments, this](function<void (message_ptr)> addToReport) {
+             for(unsigned long long index = 0; index < fragments.size() - 1; index++) {
+                 auto packet = packetizer->packetize(fragments[index], false);
+
+                 addToReport(packet);
+
+                 outgoingCallback(std::move(packet));
+             }
+             // packet is last, marker must be set
+             auto lastPacket = packetizer->packetize(fragments[fragments.size() - 1], true);
+             addToReport(lastPacket);
+             return lastPacket;
+         });
+        return lastPacket;
+    }
+    return ptr;
+}
+
+H264PacketizationHandler::H264PacketizationHandler(Separator separator,
+                                                   std::shared_ptr<H264RTPPacketizer> packetizer,
+                                                   uint16_t maximumFragmentSize):
+        RtcpHandler(),
+        rtc::RTCPSenderReportable(packetizer->rtpConfig),
+        packetizer(packetizer),
+        maximumFragmentSize(maximumFragmentSize),
+        separator(separator) {
+
+    senderReportOutgoingCallback = [this](message_ptr msg) {
+        outgoingCallback(msg);
+    };
+}
+
+#endif /* RTC_ENABLE_MEDIA */

+ 28 - 0
src/h264rtppacketizer.cpp

@@ -0,0 +1,28 @@
+/*
+ * libdatachannel streamer example
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "h264rtppacketizer.hpp"
+
+#if RTC_ENABLE_MEDIA
+
+using namespace std;
+using namespace rtc;
+
+H264RTPPacketizer::H264RTPPacketizer(std::shared_ptr<RTPPacketizationConfig> rtpConfig): RTPPacketizer(rtpConfig) { }
+
+#endif /* RTC_ENABLE_MEDIA */

+ 103 - 0
src/nalunit.cpp

@@ -0,0 +1,103 @@
+/*
+ * libdatachannel streamer example
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "nalunit.hpp"
+
+#if RTC_ENABLE_MEDIA
+
+using namespace std;
+using namespace rtc;
+
+NalUnitFragmentA::NalUnitFragmentA(FragmentType type, bool forbiddenBit, uint8_t nri, uint8_t unitType, binary data): NalUnit(data.size() + 2) {
+    setForbiddenBit(forbiddenBit);
+    setNRI(nri);
+    fragmentIndicator()->setUnitType(NalUnitFragmentA::nal_type_fu_A);
+    setFragmentType(type);
+    setUnitType(unitType);
+    copy(data.begin(), data.end(), begin() + 2);
+}
+
+vector<NalUnitFragmentA> NalUnitFragmentA::fragmentsFrom(NalUnit nalu, uint16_t maximumFragmentSize) {
+    assert(nalu.size() > maximumFragmentSize);
+    if (nalu.size() <= maximumFragmentSize) {
+        // we need to change `maximum_fragment_size` to have at least two fragments
+        maximumFragmentSize = nalu.size() / 2;
+    }
+    auto fragments_count = ceil(double(nalu.size()) / maximumFragmentSize);
+    maximumFragmentSize = ceil(nalu.size() / fragments_count);
+
+    // 2 bytes for FU indicator and FU header
+    maximumFragmentSize -= 2;
+    auto f = nalu.forbiddenBit();
+    uint8_t nri = nalu.nri() & 0x03;
+    uint8_t naluType = nalu.unitType() & 0x1F;
+    auto payload = nalu.payload();
+    vector<NalUnitFragmentA> result{};
+    uint64_t offset = 0;
+    while (offset < payload.size()) {
+        vector<byte> fragmentData;
+        FragmentType fragmentType;
+        if (offset == 0) {
+            fragmentType = FragmentType::Start;
+        } else if (offset + maximumFragmentSize < payload.size()) {
+            fragmentType = FragmentType::Middle;
+        } else {
+            if (offset + maximumFragmentSize > payload.size()) {
+                maximumFragmentSize = payload.size() - offset;
+            }
+            fragmentType = FragmentType::End;
+        }
+        fragmentData = {payload.begin() + offset, payload.begin() + offset + maximumFragmentSize};
+        NalUnitFragmentA fragment{fragmentType, f, nri, naluType, fragmentData};
+        result.push_back(fragment);
+        offset += maximumFragmentSize;
+    }
+    return result;
+}
+
+void NalUnitFragmentA::setFragmentType(FragmentType type) {
+    fragmentHeader()->setReservedBit6(false);
+    switch (type) {
+        case FragmentType::Start:
+            fragmentHeader()->setStart(true);
+            fragmentHeader()->setEnd(false);
+            break;
+        case FragmentType::End:
+            fragmentHeader()->setStart(false);
+            fragmentHeader()->setEnd(true);
+            break;
+        default:
+            fragmentHeader()->setStart(false);
+            fragmentHeader()->setEnd(false);
+    }
+}
+
+vector<binary> NalUnits::generateFragments(uint16_t maximumFragmentSize) {
+    vector<binary> result{};
+    for (auto nalu: *this) {
+        if (nalu.size() > maximumFragmentSize) {
+            std::vector<NalUnitFragmentA> fragments = NalUnitFragmentA::fragmentsFrom(nalu, maximumFragmentSize);
+            result.insert(result.end(), fragments.begin(), fragments.end());
+        } else {
+            result.push_back(nalu);
+        }
+    }
+    return result;
+}
+
+#endif /* RTC_ENABLE_MEDIA */

+ 46 - 0
src/opuspacketizationhandler.cpp

@@ -0,0 +1,46 @@
+/*
+ * libdatachannel client example
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "opuspacketizationhandler.hpp"
+
+#if RTC_ENABLE_MEDIA
+
+using namespace rtc;
+
+OpusPacketizationHandler::OpusPacketizationHandler(std::shared_ptr<OpusRTPPacketizer> packetizer): RtcpHandler(), RTCPSenderReportable(packetizer->rtpConfig), packetizer(packetizer) {
+    senderReportOutgoingCallback = [this](message_ptr msg) {
+        outgoingCallback(msg);
+    };
+}
+
+message_ptr OpusPacketizationHandler::incoming(message_ptr ptr) {
+    return ptr;
+}
+
+message_ptr OpusPacketizationHandler::outgoing(message_ptr ptr) {
+    if (ptr->type == Message::Binary) {
+        return withStatsRecording<message_ptr>([this, ptr](std::function<void (message_ptr)> addToReport) {
+            auto rtp = packetizer->packetize(*ptr, false);
+            addToReport(rtp);
+            return rtp;
+        });
+    }
+    return ptr;
+}
+
+#endif /* RTC_ENABLE_MEDIA */

+ 33 - 0
src/opusrtppacketizer.cpp

@@ -0,0 +1,33 @@
+/*
+ * libdatachannel streamer example
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "opusrtppacketizer.hpp"
+
+#if RTC_ENABLE_MEDIA
+
+using namespace std;
+using namespace rtc;
+
+OpusRTPPacketizer::OpusRTPPacketizer(std::shared_ptr<RTPPacketizationConfig> rtpConfig): RTPPacketizer(rtpConfig) { }
+
+message_ptr OpusRTPPacketizer::packetize(binary payload, bool setMark) {
+    assert(!setMark);
+    return RTPPacketizer::packetize(payload, false);
+}
+
+#endif /* RTC_ENABLE_MEDIA */

+ 74 - 0
src/rtcpsenderreportable.cpp

@@ -0,0 +1,74 @@
+/*
+ * libdatachannel streamer example
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "rtcpsenderreportable.hpp"
+
+#if RTC_ENABLE_MEDIA
+
+using namespace rtc;
+using namespace std;
+
+void RTCPSenderReportable::startRecording() {
+    _previousReportedTimestamp = rtpConfig->timestamp;
+    timeOffset = rtpConfig->startTime_s - rtpConfig->timestampToSeconds(rtpConfig->timestamp);
+}
+
+void RTCPSenderReportable::sendReport(uint32_t timestamp) {
+    auto sr = getSenderReport(timestamp);
+    _previousReportedTimestamp = timestamp;
+    senderReportOutgoingCallback(move(sr));
+}
+
+void RTCPSenderReportable::addToReport(RTP * rtp, uint32_t rtpSize) {
+    packetCount += 1;
+    assert(!rtp->padding());
+    payloadOctets += rtpSize - rtp->getSize();
+}
+
+RTCPSenderReportable::RTCPSenderReportable(std::shared_ptr<RTPPacketizationConfig> rtpConfig): rtpConfig(rtpConfig) { }
+
+uint64_t RTCPSenderReportable::secondsToNTP(double seconds) {
+    return std::round(seconds * double(uint64_t(1) << 32));
+}
+
+void RTCPSenderReportable::setNeedsToReport() {
+    needsToReport = true;
+}
+
+message_ptr RTCPSenderReportable::getSenderReport(uint32_t timestamp) {
+    auto srSize = RTCP_SR::size(0);
+    auto msg = make_message(srSize + RTCP_SDES::size({uint8_t(rtpConfig->cname.size())}), Message::Type::Control);
+    auto sr = reinterpret_cast<RTCP_SR *>(msg->data());
+    auto timestamp_s = rtpConfig->timestampToSeconds(timestamp);
+    auto currentTime = timeOffset + timestamp_s;
+    sr->setNtpTimestamp(secondsToNTP(currentTime));
+    sr->setRtpTimestamp(timestamp);
+    sr->setPacketCount(packetCount);
+    sr->setOctetCount(payloadOctets);
+    sr->preparePacket(rtpConfig->ssrc, 0);
+
+    auto sdes = reinterpret_cast<RTCP_SDES *>(msg->data() + srSize);
+    auto chunk = sdes->getChunk(0);
+    chunk->setSSRC(rtpConfig->ssrc);
+    chunk->type = 1;
+    chunk->setText(rtpConfig->cname);
+    sdes->preparePacket(1);
+    return msg;
+}
+
+#endif /* RTC_ENABLE_MEDIA */

+ 73 - 0
src/rtppacketizationconfig.cpp

@@ -0,0 +1,73 @@
+/*
+ * libdatachannel streamer example
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "rtppacketizationconfig.hpp"
+
+#if RTC_ENABLE_MEDIA
+
+using namespace rtc;
+using namespace std;
+
+RTPPacketizationConfig::RTPPacketizationConfig(SSRC ssrc,
+                                               string cname,
+                                               uint8_t payloadType,
+                                               uint32_t clockRate,
+                                               optional<uint16_t> sequenceNumber,
+                                               optional<uint32_t> timestamp): ssrc(ssrc), cname(cname), payloadType(payloadType), clockRate(clockRate) {
+    assert(clockRate > 0);
+    srand((unsigned)time(NULL));
+    if (sequenceNumber.has_value()) {
+        this->sequenceNumber = sequenceNumber.value();
+    } else {
+        this->sequenceNumber = rand();
+    }
+    if (timestamp.has_value()) {
+        this->timestamp = timestamp.value();
+    } else {
+        this->timestamp = rand();
+    }
+    this->_startTimestamp = this->timestamp;
+}
+
+void RTPPacketizationConfig::setStartTime(double startTime_s, EpochStart epochStart, std::optional<uint32_t> startTimestamp) {
+    this->_startTime_s = startTime_s + static_cast<unsigned long long>(epochStart);
+    if (startTimestamp.has_value()) {
+        this->_startTimestamp = startTimestamp.value();
+        timestamp = this->startTimestamp;
+    } else {
+        this->_startTimestamp = timestamp;
+    }
+}
+
+double RTPPacketizationConfig::getSecondsFromTimestamp(uint32_t timestamp, uint32_t clockRate) {
+    return double(timestamp) / double(clockRate);
+}
+
+double RTPPacketizationConfig::timestampToSeconds(uint32_t timestamp) {
+    return RTPPacketizationConfig::getSecondsFromTimestamp(timestamp, clockRate);
+}
+
+uint32_t RTPPacketizationConfig::getTimestampFromSeconds(double seconds, uint32_t clockRate) {
+    return uint32_t(seconds * clockRate);
+}
+
+uint32_t RTPPacketizationConfig::secondsToTimestamp(double seconds) {
+    return RTPPacketizationConfig::getTimestampFromSeconds(seconds, clockRate);
+}
+
+#endif /* RTC_ENABLE_MEDIA */

+ 44 - 0
src/rtppacketizer.cpp

@@ -0,0 +1,44 @@
+/*
+ * libdatachannel streamer example
+ * Copyright (c) 2020 Filip Klembara (in2core)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "rtppacketizer.hpp"
+
+#if RTC_ENABLE_MEDIA
+
+using namespace std;
+using namespace rtc;
+
+RTPPacketizer::RTPPacketizer(shared_ptr<RTPPacketizationConfig> rtpConfig): rtpConfig(rtpConfig) { }
+
+message_ptr RTPPacketizer::packetize(binary payload, bool setMark) {
+    auto msg = make_message(rtpHeaderSize + payload.size());
+    auto * rtp = (RTP *)msg->data();
+    rtp->setPayloadType(rtpConfig->payloadType);
+    // increase sequence number
+    rtp->setSeqNumber(rtpConfig->sequenceNumber++);
+    rtp->setTimestamp(rtpConfig->timestamp);
+    rtp->setSsrc(rtpConfig->ssrc);
+    if (setMark) {
+        rtp->setMarker(true);
+    }
+    rtp->preparePacket();
+    copy(payload.begin(), payload.end(), msg->begin() + rtpHeaderSize);
+    return msg;
+}
+
+#endif /* RTC_ENABLE_MEDIA */