瀏覽代碼

Refactoring to add sendFrame()

Paul-Louis Ageneau 10 月之前
父節點
當前提交
4c3fb5a592

+ 1 - 1
examples/streamer/main.cpp

@@ -208,7 +208,7 @@ shared_ptr<ClientTrackData> addVideo(const shared_ptr<PeerConnection> pc, const
     video.addSSRC(ssrc, cname, msid, cname);
     auto track = pc->addTrack(video);
     // create RTP configuration
-    auto rtpConfig = make_shared<RtpPacketizationConfig>(ssrc, cname, payloadType, H264RtpPacketizer::defaultClockRate);
+    auto rtpConfig = make_shared<RtpPacketizationConfig>(ssrc, cname, payloadType, H264RtpPacketizer::ClockRate);
     // create packetizer
     auto packetizer = make_shared<H264RtpPacketizer>(NalUnit::Separator::Length, rtpConfig);
     // add RTCP SR handler

+ 11 - 10
include/rtc/av1rtppacketizer.hpp

@@ -20,8 +20,8 @@ namespace rtc {
 // RTP packetization of AV1 payload
 class RTC_CPP_EXPORT AV1RtpPacketizer final : public RtpPacketizer {
 public:
-	// Default clock rate for AV1 in RTP
-	inline static const uint32_t defaultClockRate = 90 * 1000;
+	inline static const uint32_t ClockRate = VideoClockRate;
+	[[deprecated("Use ClockRate")]] inline static const uint32_t defaultClockRate = ClockRate;
 
 	// Define how OBUs are seperated in a AV1 Sample
 	enum class Packetization {
@@ -33,17 +33,18 @@ public:
 	// @note RTP configuration is used in packetization process which may change some configuration
 	// properties such as sequence number.
 	AV1RtpPacketizer(Packetization packetization, shared_ptr<RtpPacketizationConfig> rtpConfig,
-	                 uint16_t maxFragmentSize = NalUnits::defaultMaximumFragmentSize);
-
-	void outgoing(message_vector &messages, const message_callback &send) override;
+	                 size_t maxFragmentSize = DefaultMaxFragmentSize);
 
 private:
-	shared_ptr<NalUnits> splitMessage(binary_ptr message);
-	std::vector<shared_ptr<binary>> packetizeObu(binary_ptr message, uint16_t maxFragmentSize);
+	static std::vector<binary> extractTemporalUnitObus(const binary &data);
+
+	std::vector<binary> fragment(binary data) override;
+	std::vector<binary> fragmentObu(const binary &data);
+
+	const Packetization mPacketization;
+	const size_t mMaxFragmentSize;
 
-	const uint16_t maxFragmentSize;
-	const Packetization packetization;
-	std::shared_ptr<binary> sequenceHeader;
+	std::unique_ptr<binary> mSequenceHeader;
 };
 
 // For backward compatibility, do not use

+ 0 - 1
include/rtc/common.hpp

@@ -67,7 +67,6 @@ using std::variant;
 using std::weak_ptr;
 
 using binary = std::vector<byte>;
-using binary_ptr = shared_ptr<binary>;
 using message_variant = variant<binary, string>;
 
 using std::int16_t;

+ 11 - 3
include/rtc/frameinfo.hpp

@@ -11,12 +11,20 @@
 
 #include "common.hpp"
 
+#include <chrono>
+
 namespace rtc {
 
 struct RTC_CPP_EXPORT FrameInfo {
-	FrameInfo(uint8_t payloadType, uint32_t timestamp) : payloadType(payloadType), timestamp(timestamp){};
-	uint8_t payloadType; // Indicates codec of the frame
-	uint32_t timestamp = 0; // RTP Timestamp
+	FrameInfo(uint32_t timestamp) : timestamp(timestamp) {};
+	template<typename Period = std::ratio<1>> FrameInfo(std::chrono::duration<double, Period> timestamp) : timestampSeconds(timestamp) {};
+
+	[[deprecated]] FrameInfo(uint8_t payloadType, uint32_t timestamp) : timestamp(timestamp), payloadType(payloadType) {};
+
+	uint32_t timestamp = 0;
+	uint8_t payloadType = 0;
+
+	optional<std::chrono::duration<double>> timestampSeconds;
 };
 
 } // namespace rtc

+ 2 - 0
include/rtc/h264rtpdepacketizer.hpp

@@ -27,6 +27,8 @@ class RTC_CPP_EXPORT H264RtpDepacketizer : public MediaHandler {
 public:
 	using Separator = NalUnit::Separator;
 
+	inline static const uint32_t ClockRate = 90 * 1000;
+
 	H264RtpDepacketizer(Separator separator = Separator::LongStartSequence);
 	virtual ~H264RtpDepacketizer() = default;
 

+ 8 - 9
include/rtc/h264rtppacketizer.hpp

@@ -22,8 +22,8 @@ class RTC_CPP_EXPORT H264RtpPacketizer final : public RtpPacketizer {
 public:
 	using Separator = NalUnit::Separator;
 
-	/// Default clock rate for H264 in RTP
-	inline static const uint32_t defaultClockRate = 90 * 1000;
+	inline static const uint32_t ClockRate = VideoClockRate;
+	[[deprecated("Use ClockRate")]] inline static const uint32_t defaultClockRate = ClockRate;
 
 	/// Constructs h264 payload packetizer with given RTP configuration.
 	/// @note RTP configuration is used in packetization process which may change some configuration
@@ -32,20 +32,19 @@ public:
 	/// @param rtpConfig RTP configuration
 	/// @param maxFragmentSize maximum size of one NALU fragment
 	H264RtpPacketizer(Separator separator, shared_ptr<RtpPacketizationConfig> rtpConfig,
-	                  uint16_t maxFragmentSize = NalUnits::defaultMaximumFragmentSize);
+	                  size_t maxFragmentSize = DefaultMaxFragmentSize);
 
 	// For backward compatibility, do not use
 	[[deprecated]] H264RtpPacketizer(
 	    shared_ptr<RtpPacketizationConfig> rtpConfig,
-	    uint16_t maxFragmentSize = NalUnits::defaultMaximumFragmentSize);
-
-	void outgoing(message_vector &messages, const message_callback &send) override;
+	    size_t maxFragmentSize = DefaultMaxFragmentSize);
 
 private:
-	shared_ptr<NalUnits> splitMessage(binary_ptr message);
+	std::vector<binary> fragment(binary data) override;
+	std::vector<NalUnit> splitFrame(const binary &frame);
 
-	const uint16_t maxFragmentSize;
-	const Separator separator;
+	const Separator mSeparator;
+	const size_t mMaxFragmentSize;
 };
 
 // For backward compatibility, do not use

+ 12 - 4
include/rtc/h265nalunit.hpp

@@ -15,6 +15,7 @@
 #include "nalunit.hpp"
 
 #include <cassert>
+#include <vector>
 
 namespace rtc {
 
@@ -72,8 +73,13 @@ struct RTC_CPP_EXPORT H265NalUnitFragmentHeader {
 
 #pragma pack(pop)
 
-/// Nal unit
+struct H265NalUnitFragment;
+
+/// NAL unit
 struct RTC_CPP_EXPORT H265NalUnit : NalUnit {
+	static std::vector<binary> GenerateFragments(const std::vector<H265NalUnit> &nalus,
+	                                             size_t maxFragmentSize);
+
 	H265NalUnit(const H265NalUnit &unit) = default;
 	H265NalUnit(size_t size, bool includingHeader = true)
 	    : NalUnit(size, includingHeader, NalUnit::Type::H265) {}
@@ -104,6 +110,8 @@ struct RTC_CPP_EXPORT H265NalUnit : NalUnit {
 		insert(end(), payload.begin(), payload.end());
 	}
 
+	std::vector<H265NalUnitFragment> generateFragments(size_t maxFragmentSize) const;
+
 protected:
 	const H265NalUnitHeader *header() const {
 		assert(size() >= H265_NAL_HEADER_SIZE);
@@ -116,9 +124,9 @@ protected:
 	}
 };
 
-/// Nal unit fragment A
+/// NAL unit fragment
 struct RTC_CPP_EXPORT H265NalUnitFragment : H265NalUnit {
-	static std::vector<shared_ptr<H265NalUnitFragment>> fragmentsFrom(shared_ptr<H265NalUnit> nalu,
+	[[deprecated]] static std::vector<shared_ptr<H265NalUnitFragment>> fragmentsFrom(shared_ptr<H265NalUnit> nalu,
 	                                                                  uint16_t maxFragmentSize);
 
 	enum class FragmentType { Start, Middle, End };
@@ -171,7 +179,7 @@ protected:
 	}
 };
 
-class RTC_CPP_EXPORT H265NalUnits : public std::vector<shared_ptr<H265NalUnit>> {
+class [[deprecated]] RTC_CPP_EXPORT H265NalUnits : public std::vector<shared_ptr<H265NalUnit>> {
 public:
 	static const uint16_t defaultMaximumFragmentSize =
 	    uint16_t(RTC_DEFAULT_MTU - 12 - 8 - 40); // SRTP/UDP/IPv6

+ 2 - 0
include/rtc/h265rtpdepacketizer.hpp

@@ -28,6 +28,8 @@ class RTC_CPP_EXPORT H265RtpDepacketizer : public MediaHandler {
 public:
 	using Separator = NalUnit::Separator;
 
+	inline static const uint32_t ClockRate = 90 * 1000;
+
 	H265RtpDepacketizer(Separator separator = Separator::LongStartSequence);
 	virtual ~H265RtpDepacketizer() = default;
 

+ 9 - 10
include/rtc/h265rtppacketizer.hpp

@@ -21,8 +21,8 @@ class RTC_CPP_EXPORT H265RtpPacketizer final : public RtpPacketizer {
 public:
 	using Separator = NalUnit::Separator;
 
-	// Default clock rate for H265 in RTP
-	inline static const uint32_t defaultClockRate = 90 * 1000;
+	inline static const uint32_t ClockRate = VideoClockRate;
+	[[deprecated("Use ClockRate")]] inline static const uint32_t defaultClockRate = ClockRate;
 
 	// Constructs h265 payload packetizer with given RTP configuration.
 	// @note RTP configuration is used in packetization process which may change some configuration
@@ -31,19 +31,18 @@ public:
 	// @param rtpConfig  RTP configuration
 	// @param maxFragmentSize maximum size of one NALU fragment
 	H265RtpPacketizer(Separator separator, shared_ptr<RtpPacketizationConfig> rtpConfig,
-	                  uint16_t maxFragmentSize = H265NalUnits::defaultMaximumFragmentSize);
+	                  size_t maxFragmentSize = DefaultMaxFragmentSize);
 
-	// for backward compatibility
+	// For backward compatibility, do not use
 	[[deprecated]] H265RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig,
-	                  uint16_t maxFragmentSize = H265NalUnits::defaultMaximumFragmentSize);
-
-	void outgoing(message_vector &messages, const message_callback &send) override;
+	                  size_t maxFragmentSize = DefaultMaxFragmentSize);
 
 private:
-	shared_ptr<H265NalUnits> splitMessage(binary_ptr message);
+	std::vector<binary> fragment(binary data) override;
+	std::vector<H265NalUnit> splitFrame(const binary &frame);
 
-	const uint16_t maxFragmentSize;
-	const NalUnit::Separator separator;
+	const NalUnit::Separator mSeparator;
+	const size_t mMaxFragmentSize;
 };
 
 // For backward compatibility, do not use

+ 20 - 4
include/rtc/message.hpp

@@ -46,11 +46,26 @@ inline size_t message_size_func(const message_ptr &m) {
 
 template <typename Iterator>
 message_ptr make_message(Iterator begin, Iterator end, Message::Type type = Message::Binary,
-                         unsigned int stream = 0, shared_ptr<Reliability> reliability = nullptr,
-                         shared_ptr<FrameInfo> frameInfo = nullptr) {
+                         unsigned int stream = 0, shared_ptr<Reliability> reliability = nullptr) {
 	auto message = std::make_shared<Message>(begin, end, type);
 	message->stream = stream;
 	message->reliability = reliability;
+	return message;
+}
+
+template <typename Iterator>
+message_ptr make_message(Iterator begin, Iterator end, shared_ptr<FrameInfo> frameInfo) {
+	auto message = std::make_shared<Message>(begin, end);
+	message->frameInfo = frameInfo;
+	return message;
+}
+
+// For backward compatibiity, do not use
+template <typename Iterator>
+[[deprecated]] message_ptr make_message(Iterator begin, Iterator end, Message::Type type,
+                         unsigned int stream, shared_ptr<FrameInfo> frameInfo) {
+	auto message = std::make_shared<Message>(begin, end, type);
+	message->stream = stream;
 	message->frameInfo = frameInfo;
 	return message;
 }
@@ -61,8 +76,9 @@ RTC_CPP_EXPORT message_ptr make_message(size_t size, Message::Type type = Messag
 
 RTC_CPP_EXPORT message_ptr make_message(binary &&data, Message::Type type = Message::Binary,
                                         unsigned int stream = 0,
-                                        shared_ptr<Reliability> reliability = nullptr,
-                                        shared_ptr<FrameInfo> frameInfo = nullptr);
+                                        shared_ptr<Reliability> reliability = nullptr);
+
+RTC_CPP_EXPORT message_ptr make_message(binary &&data, shared_ptr<FrameInfo> frameInfo);
 
 RTC_CPP_EXPORT message_ptr make_message(size_t size, message_ptr orig);
 

+ 27 - 57
include/rtc/nalunit.hpp

@@ -13,6 +13,7 @@
 
 #include "common.hpp"
 
+#include <vector>
 #include <cassert>
 
 namespace rtc {
@@ -61,15 +62,31 @@ enum NalUnitStartSequenceMatch {
 
 static const size_t H264_NAL_HEADER_SIZE = 1;
 static const size_t H265_NAL_HEADER_SIZE = 2;
-/// Nal unit
+
+struct NalUnitFragmentA;
+
+/// NAL unit
 struct RTC_CPP_EXPORT NalUnit : binary {
+	static std::vector<binary> GenerateFragments(const std::vector<NalUnit> &nalus,
+	                                             size_t maxFragmentSize);
+
+	enum class Separator {
+		Length = RTC_NAL_SEPARATOR_LENGTH, // first 4 bytes are NAL unit length
+		LongStartSequence = RTC_NAL_SEPARATOR_LONG_START_SEQUENCE,   // 0x00, 0x00, 0x00, 0x01
+		ShortStartSequence = RTC_NAL_SEPARATOR_SHORT_START_SEQUENCE, // 0x00, 0x00, 0x01
+		StartSequence = RTC_NAL_SEPARATOR_START_SEQUENCE, // LongStartSequence or ShortStartSequence
+	};
+
+	static NalUnitStartSequenceMatch StartSequenceMatchSucc(NalUnitStartSequenceMatch match,
+	                                                 std::byte _byte, Separator separator);
+
 	enum class Type { H264, H265 };
 
 	NalUnit(const NalUnit &unit) = default;
 	NalUnit(size_t size, bool includingHeader = true, Type type = Type::H264)
-	    : binary(size + (includingHeader
-	                         ? 0
-	                         : (type == Type::H264 ? H264_NAL_HEADER_SIZE : H265_NAL_HEADER_SIZE))) {}
+	    : binary(size + (includingHeader ? 0
+	                                     : (type == Type::H264 ? H264_NAL_HEADER_SIZE
+	                                                           : H265_NAL_HEADER_SIZE))) {}
 	NalUnit(binary &&data) : binary(std::move(data)) {}
 	NalUnit(Type type = Type::H264)
 	    : binary(type == Type::H264 ? H264_NAL_HEADER_SIZE : H265_NAL_HEADER_SIZE) {}
@@ -94,56 +111,7 @@ struct RTC_CPP_EXPORT NalUnit : binary {
 		insert(end(), payload.begin(), payload.end());
 	}
 
-	/// NAL unit separator
-	enum class Separator {
-		Length = RTC_NAL_SEPARATOR_LENGTH, // first 4 bytes are NAL unit length
-		LongStartSequence = RTC_NAL_SEPARATOR_LONG_START_SEQUENCE,   // 0x00, 0x00, 0x00, 0x01
-		ShortStartSequence = RTC_NAL_SEPARATOR_SHORT_START_SEQUENCE, // 0x00, 0x00, 0x01
-		StartSequence = RTC_NAL_SEPARATOR_START_SEQUENCE, // LongStartSequence or ShortStartSequence
-	};
-
-	static NalUnitStartSequenceMatch StartSequenceMatchSucc(NalUnitStartSequenceMatch match,
-	                                                        std::byte _byte, Separator separator) {
-		assert(separator != Separator::Length);
-		auto byte = (uint8_t)_byte;
-		auto detectShort =
-		    separator == Separator::ShortStartSequence || separator == Separator::StartSequence;
-		auto detectLong =
-		    separator == Separator::LongStartSequence || separator == 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 == 0x00 && detectShort) {
-				return NUSM_secondZero;
-			} else if (byte == 0x01 && detectShort) {
-				return NUSM_shortMatch;
-			}
-			break;
-		case NUSM_thirdZero:
-			if (byte == 0x00 && detectLong) {
-				return NUSM_thirdZero;
-			} else if (byte == 0x01 && detectLong) {
-				return NUSM_longMatch;
-			}
-			break;
-		case NUSM_shortMatch:
-			return NUSM_shortMatch;
-		case NUSM_longMatch:
-			return NUSM_longMatch;
-		}
-		return NUSM_noMatch;
-	}
+	std::vector<NalUnitFragmentA> generateFragments(size_t maxFragmentSize) const;
 
 protected:
 	const NalUnitHeader *header() const {
@@ -159,8 +127,9 @@ protected:
 
 /// Nal unit fragment A
 struct RTC_CPP_EXPORT NalUnitFragmentA : NalUnit {
-	static std::vector<shared_ptr<NalUnitFragmentA>> fragmentsFrom(shared_ptr<NalUnit> nalu,
-	                                                               uint16_t maxFragmentSize);
+	// For backward compatibility, do not use
+	[[deprecated]] static std::vector<shared_ptr<NalUnitFragmentA>>
+	fragmentsFrom(shared_ptr<NalUnit> nalu, uint16_t maxFragmentSize);
 
 	enum class FragmentType { Start, Middle, End };
 
@@ -212,7 +181,8 @@ protected:
 	}
 };
 
-class RTC_CPP_EXPORT NalUnits : public std::vector<shared_ptr<NalUnit>> {
+// For backward compatibility, do not use
+class [[deprecated]] RTC_CPP_EXPORT NalUnits : public std::vector<shared_ptr<NalUnit>> {
 public:
 	static const uint16_t defaultMaximumFragmentSize =
 	    uint16_t(RTC_DEFAULT_MTU - 12 - 8 - 40); // SRTP/UDP/IPv6

+ 3 - 3
include/rtc/rtc.h

@@ -434,9 +434,6 @@ RTC_C_EXPORT int rtcSetTrackRtpTimestamp(int id, uint32_t timestamp);
 // Get timestamp of last RTCP SR, result is written to timestamp
 RTC_C_EXPORT int rtcGetLastTrackSenderReportTimestamp(int id, uint32_t *timestamp);
 
-// Set NeedsToReport flag in RtcpSrReporter handler identified by given track id
-RTC_C_EXPORT int rtcSetNeedsToSendRtcpSr(int id);
-
 // Get all available payload types for given codec and stores them in buffer, does nothing if
 // buffer is NULL
 int rtcGetTrackPayloadTypesForCodec(int tr, const char *ccodec, int *buffer, int size);
@@ -454,6 +451,9 @@ int rtcGetSsrcsForType(const char *mediaType, const char *sdp, uint32_t *buffer,
 int rtcSetSsrcForType(const char *mediaType, const char *sdp, char *buffer, const int bufferSize,
                       rtcSsrcForTypeInit *init);
 
+// For backward compatibility, do not use
+RTC_C_EXPORT RTC_DEPRECATED int rtcSetNeedsToSendRtcpSr(int id);
+
 #endif // RTC_ENABLE_MEDIA
 
 #if RTC_ENABLE_WEBSOCKET

+ 4 - 4
include/rtc/rtcpnackresponder.hpp

@@ -33,8 +33,8 @@ private:
 
 		/// Packet storage element
 		struct RTC_CPP_EXPORT Element {
-			Element(binary_ptr packet, uint16_t sequenceNumber, shared_ptr<Element> next = nullptr);
-			const binary_ptr packet;
+			Element(message_ptr packet, uint16_t sequenceNumber, shared_ptr<Element> next = nullptr);
+			const message_ptr packet;
 			const uint16_t sequenceNumber;
 			/// Pointer to newer element
 			shared_ptr<Element> next = nullptr;
@@ -59,11 +59,11 @@ private:
 		Storage(size_t _maxSize);
 
 		/// Returns packet with given sequence number
-		optional<binary_ptr> get(uint16_t sequenceNumber);
+		message_ptr get(uint16_t sequenceNumber);
 
 		/// Stores packet
 		/// @param packet Packet
-		void store(binary_ptr packet);
+		void store(message_ptr packet);
 	};
 
 	const shared_ptr<Storage> mStorage;

+ 7 - 4
include/rtc/rtcpsrreporter.hpp

@@ -12,17 +12,20 @@
 #if RTC_ENABLE_MEDIA
 
 #include "mediahandler.hpp"
-#include "rtppacketizationconfig.hpp"
 #include "rtp.hpp"
+#include "rtppacketizationconfig.hpp"
+
+#include <chrono>
 
 namespace rtc {
 
 class RTC_CPP_EXPORT RtcpSrReporter final : public MediaHandler {
 public:
 	RtcpSrReporter(shared_ptr<RtpPacketizationConfig> rtpConfig);
+	~RtcpSrReporter();
 
 	uint32_t lastReportedTimestamp() const;
-	void setNeedsToReport();
+	[[deprecated]] void setNeedsToReport();
 
 	void outgoing(message_vector &messages, const message_callback &send) override;
 
@@ -30,13 +33,13 @@ public:
 	const shared_ptr<RtpPacketizationConfig> rtpConfig;
 
 private:
-	void addToReport(RtpHeader *rtp, uint32_t rtpSize);
+	void addToReport(RtpHeader *header, size_t size);
 	message_ptr getSenderReport(uint32_t timestamp);
 
 	uint32_t mPacketCount = 0;
 	uint32_t mPayloadOctets = 0;
 	uint32_t mLastReportedTimestamp = 0;
-	bool mNeedsToReport = false;
+	std::chrono::steady_clock::time_point mLastReportTime;
 };
 
 } // namespace rtc

+ 6 - 2
include/rtc/rtpdepacketizer.hpp

@@ -18,10 +18,14 @@ namespace rtc {
 
 class RTC_CPP_EXPORT RtpDepacketizer : public MediaHandler {
 public:
-	RtpDepacketizer() = default;
-	virtual ~RtpDepacketizer() = default;
+	RtpDepacketizer();
+	RtpDepacketizer(uint32_t clockRate);
+	virtual ~RtpDepacketizer();
 
 	virtual void incoming(message_vector &messages, const message_callback &send) override;
+
+private:
+	uint32_t mClockRate;
 };
 
 using OpusRtpDepacketizer = RtpDepacketizer;

+ 20 - 4
include/rtc/rtppacketizer.hpp

@@ -20,6 +20,12 @@ namespace rtc {
 /// RTP packetizer
 class RTC_CPP_EXPORT RtpPacketizer : public MediaHandler {
 public:
+	/// Default maximum fragment size (for video packetizers)
+	inline static const size_t DefaultMaxFragmentSize = RTC_DEFAULT_MAX_FRAGMENT_SIZE;
+
+	/// Clock rate for video in RTP
+	inline static const uint32_t VideoClockRate = 90 * 1000;
+
 	/// Constructs packetizer with given RTP configuration
 	/// @note RTP configuration is used in packetization process which may change some configuration
 	/// properties such as sequence number.
@@ -34,11 +40,19 @@ public:
 	const shared_ptr<RtpPacketizationConfig> rtpConfig;
 
 protected:
-	/// Creates RTP packet for given payload
-	/// @note This function increase sequence number after packetization.
+	/// Fragment data into payloads
+	/// Default implementation returns data as a single payload
+	/// @param message Input data
+	virtual std::vector<binary> fragment(binary data);
+
+	/// Creates an RTP packet for a payload
+	/// @note This function increases the sequence number.
 	/// @param payload RTP payload
-	/// @param setMark Set marker flag in RTP packet if true
-	virtual message_ptr packetize(shared_ptr<binary> payload, bool mark);
+	/// @param mark Set marker flag in RTP packet if true
+	virtual message_ptr packetize(const binary &payload, bool mark);
+
+	// For backward compatibility, do not use
+	[[deprecated]] virtual message_ptr packetize(shared_ptr<binary> payload, bool mark);
 
 private:
 	static const auto RtpHeaderSize = 12;
@@ -60,6 +74,8 @@ public:
 // Audio RTP packetizers
 using OpusRtpPacketizer = AudioRtpPacketizer<48000>;
 using AACRtpPacketizer = AudioRtpPacketizer<48000>;
+using PCMARtpPacketizer = AudioRtpPacketizer<8000>;
+using PCMURtpPacketizer = AudioRtpPacketizer<8000>;
 
 // Dummy wrapper for backward compatibility, do not use
 class RTC_CPP_EXPORT PacketizationHandler final : public MediaHandler {

+ 2 - 1
include/rtc/track.hpp

@@ -41,7 +41,8 @@ public:
 	bool isClosed(void) const override;
 	size_t maxMessageSize() const override;
 
-	void onFrame(std::function<void(binary data, FrameInfo frame)> callback);
+	void sendFrame(binary data, FrameInfo info);
+	void onFrame(std::function<void(binary data, FrameInfo info)> callback);
 
 	bool requestKeyframe();
 	bool requestBitrate(unsigned int bitrate);

+ 68 - 87
src/av1rtppacketizer.cpp

@@ -40,34 +40,39 @@ const auto oneByteLeb128Size = 1;
 const uint8_t sevenLsbBitmask = 0b01111111;
 const uint8_t msbBitmask = 0b10000000;
 
-std::vector<binary_ptr> extractTemporalUnitObus(binary_ptr message) {
-	std::vector<shared_ptr<binary>> obus{};
+AV1RtpPacketizer::AV1RtpPacketizer(Packetization packetization,
+                                   shared_ptr<RtpPacketizationConfig> rtpConfig,
+                                   size_t maxFragmentSize)
+    : RtpPacketizer(rtpConfig), mPacketization(packetization), mMaxFragmentSize(maxFragmentSize) {}
+
+std::vector<binary> AV1RtpPacketizer::extractTemporalUnitObus(const binary &data) {
+	std::vector<binary> obus;
 
-	if (message->size() <= 2 || (message->at(0) != obuTemporalUnitDelimiter.at(0)) ||
-	    (message->at(1) != obuTemporalUnitDelimiter.at(1))) {
-		return obus;
+	if (data.size() <= 2 || (data.at(0) != obuTemporalUnitDelimiter.at(0)) ||
+	    (data.at(1) != obuTemporalUnitDelimiter.at(1))) {
+		return {};
 	}
 
-	size_t messageIndex = 2;
-	while (messageIndex < message->size()) {
-		if ((message->at(messageIndex) & obuHasSizeMask) == byte(0)) {
+	size_t index = 2;
+	while (index < data.size()) {
+		if ((data.at(index) & obuHasSizeMask) == byte(0)) {
 			return obus;
 		}
 
-		if ((message->at(messageIndex) & obuHasExtensionMask) != byte(0)) {
-			messageIndex++;
+		if ((data.at(index) & obuHasExtensionMask) != byte(0)) {
+			index++;
 		}
 
 		// https://aomediacodec.github.io/av1-spec/#leb128
 		uint32_t obuLength = 0;
 		uint8_t leb128Size = 0;
 		while (leb128Size < 8) {
-			auto leb128Index = messageIndex + leb128Size + obuHeaderSize;
-			if (message->size() < leb128Index) {
+			auto leb128Index = index + leb128Size + obuHeaderSize;
+			if (data.size() < leb128Index) {
 				break;
 			}
 
-			auto leb128_byte = uint8_t(message->at(leb128Index));
+			auto leb128_byte = uint8_t(data.at(leb128Index));
 
 			obuLength |= ((leb128_byte & sevenLsbBitmask) << (leb128Size * 7));
 			leb128Size++;
@@ -77,16 +82,31 @@ std::vector<binary_ptr> extractTemporalUnitObus(binary_ptr message) {
 			}
 		}
 
-		obus.push_back(std::make_shared<binary>(message->begin() + messageIndex,
-		                                        message->begin() + messageIndex + obuHeaderSize +
-		                                            leb128Size + obuLength));
+		obus.emplace_back(data.begin() + index,
+		                  data.begin() + index + obuHeaderSize + leb128Size + obuLength);
 
-		messageIndex += obuHeaderSize + leb128Size + obuLength;
+		index += obuHeaderSize + leb128Size + obuLength;
 	}
 
 	return obus;
 }
 
+std::vector<binary> AV1RtpPacketizer::fragment(binary data) {
+	if (mPacketization == AV1RtpPacketizer::Packetization::TemporalUnit) {
+		std::vector<binary> result;
+		auto obus = extractTemporalUnitObus(data);
+		for (auto obu : obus) {
+			auto fragments = fragmentObu(obu);
+			result.reserve(result.size() + fragments.size());
+			for(auto &fragment : fragments)
+				fragments.push_back(std::move(fragment));
+		}
+		return result;
+	} else {
+		return fragmentObu(data);
+	}
+}
+
 /*
  *  0 1 2 3 4 5 6 7
  * +-+-+-+-+-+-+-+-+
@@ -118,110 +138,71 @@ std::vector<binary_ptr> extractTemporalUnitObus(binary_ptr message) {
  *
  **/
 
-std::vector<binary_ptr> AV1RtpPacketizer::packetizeObu(binary_ptr message,
-                                                       uint16_t maxFragmentSize) {
-
-	std::vector<shared_ptr<binary>> payloads{};
-	size_t messageIndex = 0;
+std::vector<binary> AV1RtpPacketizer::fragmentObu(const binary &data) {
+	std::vector<binary> payloads;
 
-	if (message->size() < 1) {
-		return payloads;
-	}
+	if (data.size() < 1)
+		return {};
 
 	// Cache sequence header and packetize with next OBU
-	auto frameType = (message->at(0) & obuFrameTypeMask) >> obuFrameTypeBitshift;
+	auto frameType = (data.at(0) & obuFrameTypeMask) >> obuFrameTypeBitshift;
 	if (frameType == obuFrameTypeSequenceHeader) {
-		sequenceHeader = std::make_shared<binary>(message->begin(), message->end());
-		return payloads;
+		mSequenceHeader = std::make_unique<binary>(data.begin(), data.end());
+		return {};
 	}
 
-	size_t messageRemaining = message->size();
-	while (messageRemaining > 0) {
-		auto obuCount = 1;
-		auto metadataSize = payloadHeaderSize;
+	size_t index = 0;
+	size_t remaining = data.size();
+	while (remaining > 0) {
+		size_t obuCount = 1;
+		size_t metadataSize = payloadHeaderSize;
 
-		if (sequenceHeader != nullptr) {
+		if (mSequenceHeader) {
 			obuCount++;
-			metadataSize += /* 1 byte leb128 */ 1 + int(sequenceHeader->size());
+			metadataSize += 1 + int(mSequenceHeader->size()); // 1 byte leb128
 		}
 
-		auto payload = std::make_shared<binary>(
-		    std::min(size_t(maxFragmentSize), messageRemaining + metadataSize));
-		auto payloadOffset = payloadHeaderSize;
+		binary payload(std::min(size_t(mMaxFragmentSize), remaining + metadataSize));
+		size_t payloadOffset = payloadHeaderSize;
 
-		payload->at(0) = byte(obuCount) << wBitshift;
+		payload.at(0) = byte(obuCount) << wBitshift;
 
 		// Packetize cached SequenceHeader
 		if (obuCount == 2) {
-			payload->at(0) ^= nMask;
-			payload->at(1) = byte(sequenceHeader->size() & sevenLsbBitmask);
+			payload.at(0) ^= nMask;
+			payload.at(1) = byte(mSequenceHeader->size() & sevenLsbBitmask);
 			payloadOffset += oneByteLeb128Size;
 
-			std::memcpy(payload->data() + payloadOffset, sequenceHeader->data(),
-			            sequenceHeader->size());
-			payloadOffset += int(sequenceHeader->size());
+			std::memcpy(payload.data() + payloadOffset, mSequenceHeader->data(),
+			            mSequenceHeader->size());
+			payloadOffset += int(mSequenceHeader->size());
 
-			sequenceHeader = nullptr;
+			mSequenceHeader = nullptr;
 		}
 
 		// Copy as much of OBU as possible into Payload
-		auto payloadRemaining = payload->size() - payloadOffset;
-		std::memcpy(payload->data() + payloadOffset, message->data() + messageIndex,
+		size_t payloadRemaining = payload.size() - payloadOffset;
+		std::memcpy(payload.data() + payloadOffset, data.data() + index,
 		            payloadRemaining);
-		messageRemaining -= payloadRemaining;
-		messageIndex += payloadRemaining;
+		remaining -= payloadRemaining;
+		index += payloadRemaining;
 
 		// Does this Fragment contain an OBU that started in a previous payload
 		if (payloads.size() > 0) {
-			payload->at(0) ^= zMask;
+			payload.at(0) ^= zMask;
 		}
 
 		// This OBU will be continued in next Payload
-		if (messageIndex < message->size()) {
-			payload->at(0) ^= yMask;
+		if (index < data.size()) {
+			payload.at(0) ^= yMask;
 		}
 
-		payloads.push_back(payload);
+		payloads.push_back(std::move(payload));
 	}
 
 	return payloads;
 }
 
-AV1RtpPacketizer::AV1RtpPacketizer(AV1RtpPacketizer::Packetization packetization,
-                                   shared_ptr<RtpPacketizationConfig> rtpConfig,
-                                   uint16_t maxFragmentSize)
-    : RtpPacketizer(rtpConfig), maxFragmentSize(maxFragmentSize),
-      packetization(packetization) {}
-
-void AV1RtpPacketizer::outgoing(message_vector &messages,
-                                [[maybe_unused]] const message_callback &send) {
-	message_vector result;
-	for (const auto &message : messages) {
-		std::vector<binary_ptr> obus;
-		if (packetization == AV1RtpPacketizer::Packetization::TemporalUnit) {
-			obus = extractTemporalUnitObus(message);
-		} else {
-			obus.push_back(message);
-		}
-
-		std::vector<binary_ptr> fragments;
-		for (auto obu : obus) {
-			auto p = packetizeObu(obu, maxFragmentSize);
-			fragments.insert(fragments.end(), p.begin(), p.end());
-		}
-
-		if (fragments.size() == 0)
-			continue;
-
-		for (size_t i = 0; i < fragments.size() - 1; i++)
-			result.push_back(packetize(fragments[i], false));
-
-		result.push_back(packetize(fragments[fragments.size() - 1], true));
-	}
-
-	messages.swap(result);
-}
-
 } // namespace rtc
 
 #endif /* RTC_ENABLE_MEDIA */

+ 5 - 8
src/capi.cpp

@@ -1414,14 +1414,6 @@ int rtcGetLastTrackSenderReportTimestamp(int id, uint32_t *timestamp) {
 	});
 }
 
-int rtcSetNeedsToSendRtcpSr(int id) {
-	return wrap([id] {
-		auto sender = getRtcpSrReporter(id);
-		sender->setNeedsToReport();
-		return RTC_ERR_SUCCESS;
-	});
-}
-
 int rtcGetTrackPayloadTypesForCodec(int tr, const char *ccodec, int *buffer, int size) {
 	return wrap([&] {
 		auto track = getTrack(tr);
@@ -1498,6 +1490,11 @@ int rtcSetSsrcForType(const char *mediaType, const char *sdp, char *buffer, cons
 	});
 }
 
+int rtcSetNeedsToSendRtcpSr(int) {
+	// Dummy
+	return RTC_ERR_SUCCESS;
+}
+
 #endif // RTC_ENABLE_MEDIA
 
 #if RTC_ENABLE_WEBSOCKET

+ 7 - 5
src/h264rtpdepacketizer.cpp

@@ -14,6 +14,7 @@
 #include "impl/internals.hpp"
 
 #include <algorithm>
+#include <chrono>
 
 namespace rtc {
 
@@ -43,9 +44,8 @@ void H264RtpDepacketizer::addSeparator(binary &accessUnit) {
 message_vector H264RtpDepacketizer::buildFrames(message_vector::iterator begin,
                                                 message_vector::iterator end, uint8_t payloadType,
                                                 uint32_t timestamp) {
-	message_vector out = {};
-	auto accessUnit = binary{};
-	auto frameInfo = std::make_shared<FrameInfo>(payloadType, timestamp);
+	message_vector out;
+	binary accessUnit;
 
 	for (auto it = begin; it != end; ++it) {
 		auto pkt = it->get();
@@ -109,8 +109,10 @@ message_vector H264RtpDepacketizer::buildFrames(message_vector::iterator begin,
 	}
 
 	if (!accessUnit.empty()) {
-		out.emplace_back(
-		    make_message(std::move(accessUnit), Message::Binary, 0, nullptr, frameInfo));
+		auto frameInfo = std::make_shared<FrameInfo>(timestamp);
+		frameInfo->timestampSeconds = std::chrono::duration<double>(double(timestamp) / double(ClockRate));
+		frameInfo->payloadType = payloadType;
+		out.emplace_back(make_message(std::move(accessUnit), frameInfo));
 	}
 
 	return out;

+ 35 - 49
src/h264rtppacketizer.cpp

@@ -23,37 +23,50 @@
 
 namespace rtc {
 
-shared_ptr<NalUnits> H264RtpPacketizer::splitMessage(binary_ptr message) {
-	auto nalus = std::make_shared<NalUnits>();
-	if (separator == Separator::Length) {
+H264RtpPacketizer::H264RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig,
+                                     size_t maxFragmentSize)
+    : RtpPacketizer(std::move(rtpConfig)), mSeparator(Separator::Length), mMaxFragmentSize(maxFragmentSize) {}
+
+H264RtpPacketizer::H264RtpPacketizer(Separator separator,
+                                     shared_ptr<RtpPacketizationConfig> rtpConfig,
+                                     size_t maxFragmentSize)
+    : RtpPacketizer(rtpConfig), mSeparator(separator), mMaxFragmentSize(maxFragmentSize) {}
+
+std::vector<binary> H264RtpPacketizer::fragment(binary data) {
+	return NalUnit::GenerateFragments(splitFrame(data), mMaxFragmentSize);
+}
+
+std::vector<NalUnit> H264RtpPacketizer::splitFrame(const binary &frame) {
+	std::vector<NalUnit> nalus;
+	if (mSeparator == Separator::Length) {
 		size_t index = 0;
-		while (index < message->size()) {
-			assert(index + 4 < message->size());
-			if (index + 4 >= message->size()) {
+		while (index < frame.size()) {
+			assert(index + 4 < frame.size());
+			if (index + 4 >= frame.size()) {
 				LOG_WARNING << "Invalid NAL Unit data (incomplete length), ignoring!";
 				break;
 			}
 			uint32_t length;
-			std::memcpy(&length, message->data() + index, sizeof(uint32_t));
+			std::memcpy(&length, frame.data() + index, sizeof(uint32_t));
 			length = ntohl(length);
 			auto naluStartIndex = index + 4;
 			auto naluEndIndex = naluStartIndex + length;
 
-			assert(naluEndIndex <= message->size());
-			if (naluEndIndex > message->size()) {
+			assert(naluEndIndex <= frame.size());
+			if (naluEndIndex > frame.size()) {
 				LOG_WARNING << "Invalid NAL Unit data (incomplete unit), ignoring!";
 				break;
 			}
-			auto begin = message->begin() + naluStartIndex;
-			auto end = message->begin() + naluEndIndex;
-			nalus->push_back(std::make_shared<NalUnit>(begin, end));
+			auto begin = frame.begin() + naluStartIndex;
+			auto end = frame.begin() + naluEndIndex;
+			nalus.emplace_back(begin, end);
 			index = naluEndIndex;
 		}
 	} else {
 		NalUnitStartSequenceMatch match = NUSM_noMatch;
 		size_t index = 0;
-		while (index < message->size()) {
-			match = NalUnit::StartSequenceMatchSucc(match, (*message)[index++], separator);
+		while (index < frame.size()) {
+			match = NalUnit::StartSequenceMatchSucc(match, frame[index++], mSeparator);
 			if (match == NUSM_longMatch || match == NUSM_shortMatch) {
 				match = NUSM_noMatch;
 				break;
@@ -62,53 +75,26 @@ shared_ptr<NalUnits> H264RtpPacketizer::splitMessage(binary_ptr message) {
 
 		size_t naluStartIndex = index;
 
-		while (index < message->size()) {
-			match = NalUnit::StartSequenceMatchSucc(match, (*message)[index], separator);
+		while (index < frame.size()) {
+			match = NalUnit::StartSequenceMatchSucc(match, frame[index], mSeparator);
 			if (match == NUSM_longMatch || match == NUSM_shortMatch) {
 				auto sequenceLength = match == NUSM_longMatch ? 4 : 3;
 				size_t naluEndIndex = index - sequenceLength;
 				match = NUSM_noMatch;
-				auto begin = message->begin() + naluStartIndex;
-				auto end = message->begin() + naluEndIndex + 1;
-				nalus->push_back(std::make_shared<NalUnit>(begin, end));
+				auto begin = frame.begin() + naluStartIndex;
+				auto end = frame.begin() + naluEndIndex + 1;
+				nalus.emplace_back(begin, end);
 				naluStartIndex = index + 1;
 			}
 			index++;
 		}
-		auto begin = message->begin() + naluStartIndex;
-		auto end = message->end();
-		nalus->push_back(std::make_shared<NalUnit>(begin, end));
+		auto begin = frame.begin() + naluStartIndex;
+		auto end = frame.end();
+		nalus.emplace_back(begin, end);
 	}
 	return nalus;
 }
 
-H264RtpPacketizer::H264RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig,
-                                     uint16_t maxFragmentSize)
-    : RtpPacketizer(std::move(rtpConfig)), maxFragmentSize(maxFragmentSize),
-      separator(Separator::Length) {}
-
-H264RtpPacketizer::H264RtpPacketizer(Separator separator,
-                                     shared_ptr<RtpPacketizationConfig> rtpConfig,
-                                     uint16_t maxFragmentSize)
-    : RtpPacketizer(rtpConfig), maxFragmentSize(maxFragmentSize), separator(separator) {}
-
-void H264RtpPacketizer::outgoing(message_vector &messages, [[maybe_unused]] const message_callback &send) {
-	message_vector result;
-	for(const auto &message : messages) {
-		auto nalus = splitMessage(message);
-		auto fragments = nalus->generateFragments(maxFragmentSize);
-		if (fragments.size() == 0)
-			continue;
-
-		for (size_t i = 0; i < fragments.size() - 1; i++)
-			result.push_back(packetize(fragments[i], false));
-
-		result.push_back(packetize(fragments[fragments.size() - 1], true));
-	}
-
-	messages.swap(result);
-}
-
 } // namespace rtc
 
 #endif /* RTC_ENABLE_MEDIA */

+ 61 - 35
src/h265nalunit.cpp

@@ -16,35 +16,39 @@
 
 namespace rtc {
 
-H265NalUnitFragment::H265NalUnitFragment(FragmentType type, bool forbiddenBit, uint8_t nuhLayerId,
-                                         uint8_t nuhTempIdPlus1, uint8_t unitType, binary data)
-    : H265NalUnit(data.size() + H265_NAL_HEADER_SIZE + H265_FU_HEADER_SIZE) {
-	setForbiddenBit(forbiddenBit);
-	setNuhLayerId(nuhLayerId);
-	setNuhTempIdPlus1(nuhTempIdPlus1);
-	fragmentIndicator()->setUnitType(H265NalUnitFragment::nal_type_fu);
-	setFragmentType(type);
-	setUnitType(unitType);
-	copy(data.begin(), data.end(), begin() + H265_NAL_HEADER_SIZE + H265_FU_HEADER_SIZE);
+std::vector<binary> H265NalUnit::GenerateFragments(const std::vector<H265NalUnit> &nalus,
+                                                   size_t maxFragmentSize) {
+	std::vector<binary> result;
+	for (auto nalu : nalus) {
+		if (nalu.size() > maxFragmentSize) {
+			auto fragments = nalu.generateFragments(maxFragmentSize);
+			result.insert(result.end(), fragments.begin(), fragments.end());
+		} else {
+			// TODO: check
+			result.push_back(nalu);
+		}
+	}
+	return result;
 }
 
-std::vector<shared_ptr<H265NalUnitFragment>>
-H265NalUnitFragment::fragmentsFrom(shared_ptr<H265NalUnit> nalu, uint16_t maxFragmentSize) {
-	assert(nalu->size() > maxFragmentSize);
-	auto fragments_count = ceil(double(nalu->size()) / maxFragmentSize);
-	maxFragmentSize = uint16_t(int(ceil(nalu->size() / fragments_count)));
+std::vector<H265NalUnitFragment> H265NalUnit::generateFragments(size_t maxFragmentSize) const {
+	// TODO: check
+	assert(size() > maxFragmentSize);
+	auto fragments_count = ceil(double(size()) / maxFragmentSize);
+	maxFragmentSize = uint16_t(int(ceil(size() / fragments_count)));
 
-	// 3 bytes for NALU header and FU header
+	// 3 bytes for FU indicator and FU header
 	maxFragmentSize -= (H265_NAL_HEADER_SIZE + H265_FU_HEADER_SIZE);
-	auto f = nalu->forbiddenBit();
-	uint8_t nuhLayerId = nalu->nuhLayerId() & 0x3F;        // 6 bits
-	uint8_t nuhTempIdPlus1 = nalu->nuhTempIdPlus1() & 0x7; // 3 bits
-	uint8_t naluType = nalu->unitType() & 0x3F;            // 6 bits
-	auto payload = nalu->payload();
-	vector<shared_ptr<H265NalUnitFragment>> result{};
+	bool forbiddenBit = this->forbiddenBit();
+	uint8_t nuhLayerId = this->nuhLayerId() & 0x3F;        // 6 bits
+	uint8_t nuhTempIdPlus1 = this->nuhTempIdPlus1() & 0x7; // 3 bits
+	uint8_t naluType = this->unitType() & 0x3F;            // 6 bits
+	auto payload = this->payload();
+	vector<H265NalUnitFragment> result;
 	uint64_t offset = 0;
 	while (offset < payload.size()) {
 		vector<byte> fragmentData;
+		using FragmentType = H265NalUnitFragment::FragmentType;
 		FragmentType fragmentType;
 		if (offset == 0) {
 			fragmentType = FragmentType::Start;
@@ -57,14 +61,36 @@ H265NalUnitFragment::fragmentsFrom(shared_ptr<H265NalUnit> nalu, uint16_t maxFra
 			fragmentType = FragmentType::End;
 		}
 		fragmentData = {payload.begin() + offset, payload.begin() + offset + maxFragmentSize};
-		auto fragment = std::make_shared<H265NalUnitFragment>(
-		    fragmentType, f, nuhLayerId, nuhTempIdPlus1, naluType, fragmentData);
-		result.push_back(fragment);
+		result.emplace_back(fragmentType, forbiddenBit, nuhLayerId, nuhTempIdPlus1, naluType,
+		                    fragmentData);
 		offset += maxFragmentSize;
 	}
 	return result;
 }
 
+H265NalUnitFragment::H265NalUnitFragment(FragmentType type, bool forbiddenBit, uint8_t nuhLayerId,
+                                         uint8_t nuhTempIdPlus1, uint8_t unitType, binary data)
+    : H265NalUnit(data.size() + H265_NAL_HEADER_SIZE + H265_FU_HEADER_SIZE) {
+	setForbiddenBit(forbiddenBit);
+	setNuhLayerId(nuhLayerId);
+	setNuhTempIdPlus1(nuhTempIdPlus1);
+	fragmentIndicator()->setUnitType(H265NalUnitFragment::nal_type_fu);
+	setFragmentType(type);
+	setUnitType(unitType);
+	copy(data.begin(), data.end(), begin() + H265_NAL_HEADER_SIZE + H265_FU_HEADER_SIZE);
+}
+
+std::vector<shared_ptr<H265NalUnitFragment>>
+H265NalUnitFragment::fragmentsFrom(shared_ptr<H265NalUnit> nalu, uint16_t maxFragmentSize) {
+	auto fragments = nalu->generateFragments(maxFragmentSize);
+	std::vector<shared_ptr<H265NalUnitFragment>> result;
+	result.reserve(fragments.size());
+	for (auto fragment : fragments)
+		result.push_back(std::make_shared<H265NalUnitFragment>(std::move(fragment)));
+
+	return result;
+}
+
 void H265NalUnitFragment::setFragmentType(FragmentType type) {
 	switch (type) {
 	case FragmentType::Start:
@@ -82,16 +108,16 @@ void H265NalUnitFragment::setFragmentType(FragmentType type) {
 }
 
 std::vector<shared_ptr<binary>> H265NalUnits::generateFragments(uint16_t maxFragmentSize) {
-	vector<shared_ptr<binary>> result{};
-	for (auto nalu : *this) {
-		if (nalu->size() > maxFragmentSize) {
-			std::vector<shared_ptr<H265NalUnitFragment>> fragments =
-			    H265NalUnitFragment::fragmentsFrom(nalu, maxFragmentSize);
-			result.insert(result.end(), fragments.begin(), fragments.end());
-		} else {
-			result.push_back(nalu);
-		}
-	}
+	std::vector<H265NalUnit> nalus;
+	for (auto nalu : *this)
+		nalus.push_back(*nalu);
+
+	auto fragments = H265NalUnit::GenerateFragments(nalus, maxFragmentSize);
+	std::vector<shared_ptr<binary>> result;
+	result.reserve(fragments.size());
+	for (auto fragment : fragments)
+		result.push_back(std::make_shared<binary>(std::move(fragment)));
+
 	return result;
 }
 

+ 6 - 5
src/h265rtpdepacketizer.cpp

@@ -59,9 +59,8 @@ void H265RtpDepacketizer::addSeparator(binary& accessUnit)
 message_vector H265RtpDepacketizer::buildFrames(message_vector::iterator begin,
                                                 message_vector::iterator end,
                                                 uint8_t payloadType, uint32_t timestamp) {
-	message_vector out = {};
-	auto accessUnit = binary{};
-	auto frameInfo = std::make_shared<FrameInfo>(payloadType, timestamp);
+	message_vector out;
+	binary accessUnit;
 
 	for (auto it = begin; it != end; ++it) {
 		auto pkt = it->get();
@@ -132,8 +131,10 @@ message_vector H265RtpDepacketizer::buildFrames(message_vector::iterator begin,
 	}
 
 	if (!accessUnit.empty()) {
-		out.emplace_back(make_message(accessUnit.begin(), accessUnit.end(), Message::Binary, 0,
-		                              nullptr, frameInfo));
+		auto frameInfo = std::make_shared<FrameInfo>(timestamp);
+		frameInfo->timestampSeconds = std::chrono::duration<double>(double(timestamp) / double(ClockRate));
+		frameInfo->payloadType = payloadType;
+		out.emplace_back(make_message(std::move(accessUnit), frameInfo));
 	}
 
 	return out;

+ 36 - 50
src/h265rtppacketizer.cpp

@@ -22,37 +22,51 @@
 
 namespace rtc {
 
-shared_ptr<H265NalUnits> H265RtpPacketizer::splitMessage(binary_ptr message) {
-	auto nalus = std::make_shared<H265NalUnits>();
-	if (separator == NalUnit::Separator::Length) {
+H265RtpPacketizer::H265RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig,
+                                     size_t maxFragmentSize)
+    : RtpPacketizer(std::move(rtpConfig)), mSeparator(NalUnit::Separator::Length),
+      mMaxFragmentSize(maxFragmentSize) {}
+
+H265RtpPacketizer::H265RtpPacketizer(NalUnit::Separator separator,
+                                     shared_ptr<RtpPacketizationConfig> rtpConfig,
+                                     size_t maxFragmentSize)
+    : RtpPacketizer(std::move(rtpConfig)), mSeparator(separator), mMaxFragmentSize(maxFragmentSize) {}
+
+std::vector<binary> H265RtpPacketizer::fragment(binary data) {
+	return H265NalUnit::GenerateFragments(splitFrame(data), mMaxFragmentSize);
+}
+
+std::vector<H265NalUnit> H265RtpPacketizer::splitFrame(const binary &frame) {
+	std::vector<H265NalUnit> nalus;
+	if (mSeparator == NalUnit::Separator::Length) {
 		size_t index = 0;
-		while (index < message->size()) {
-			assert(index + 4 < message->size());
-			if (index + 4 >= message->size()) {
+		while (index < frame.size()) {
+			assert(index + 4 < frame.size());
+			if (index + 4 >= frame.size()) {
 				LOG_WARNING << "Invalid NAL Unit data (incomplete length), ignoring!";
 				break;
 			}
 			uint32_t length;
-			std::memcpy(&length, message->data() + index, sizeof(uint32_t));
+			std::memcpy(&length, frame.data() + index, sizeof(uint32_t));
 			length = ntohl(length);
 			auto naluStartIndex = index + 4;
 			auto naluEndIndex = naluStartIndex + length;
 
-			assert(naluEndIndex <= message->size());
-			if (naluEndIndex > message->size()) {
+			assert(naluEndIndex <= frame.size());
+			if (naluEndIndex > frame.size()) {
 				LOG_WARNING << "Invalid NAL Unit data (incomplete unit), ignoring!";
 				break;
 			}
-			auto begin = message->begin() + naluStartIndex;
-			auto end = message->begin() + naluEndIndex;
-			nalus->push_back(std::make_shared<H265NalUnit>(begin, end));
+			auto begin = frame.begin() + naluStartIndex;
+			auto end = frame.begin() + naluEndIndex;
+			nalus.emplace_back(begin, end);
 			index = naluEndIndex;
 		}
 	} else {
 		NalUnitStartSequenceMatch match = NUSM_noMatch;
 		size_t index = 0;
-		while (index < message->size()) {
-			match = NalUnit::StartSequenceMatchSucc(match, (*message)[index++], separator);
+		while (index < frame.size()) {
+			match = NalUnit::StartSequenceMatchSucc(match, frame[index++], mSeparator);
 			if (match == NUSM_longMatch || match == NUSM_shortMatch) {
 				match = NUSM_noMatch;
 				break;
@@ -61,54 +75,26 @@ shared_ptr<H265NalUnits> H265RtpPacketizer::splitMessage(binary_ptr message) {
 
 		size_t naluStartIndex = index;
 
-		while (index < message->size()) {
-			match = NalUnit::StartSequenceMatchSucc(match, (*message)[index], separator);
+		while (index < frame.size()) {
+			match = NalUnit::StartSequenceMatchSucc(match, frame[index], mSeparator);
 			if (match == NUSM_longMatch || match == NUSM_shortMatch) {
 				auto sequenceLength = match == NUSM_longMatch ? 4 : 3;
 				size_t naluEndIndex = index - sequenceLength;
 				match = NUSM_noMatch;
-				auto begin = message->begin() + naluStartIndex;
-				auto end = message->begin() + naluEndIndex + 1;
-				nalus->push_back(std::make_shared<H265NalUnit>(begin, end));
+				auto begin = frame.begin() + naluStartIndex;
+				auto end = frame.begin() + naluEndIndex + 1;
+				nalus.emplace_back(begin, end);
 				naluStartIndex = index + 1;
 			}
 			index++;
 		}
-		auto begin = message->begin() + naluStartIndex;
-		auto end = message->end();
-		nalus->push_back(std::make_shared<H265NalUnit>(begin, end));
+		auto begin = frame.begin() + naluStartIndex;
+		auto end = frame.end();
+		nalus.emplace_back(begin, end);
 	}
 	return nalus;
 }
 
-H265RtpPacketizer::H265RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig,
-                                     uint16_t maxFragmentSize)
-    : RtpPacketizer(std::move(rtpConfig)), maxFragmentSize(maxFragmentSize),
-      separator(NalUnit::Separator::Length) {}
-
-H265RtpPacketizer::H265RtpPacketizer(NalUnit::Separator separator,
-                                     shared_ptr<RtpPacketizationConfig> rtpConfig,
-                                     uint16_t maxFragmentSize)
-    : RtpPacketizer(std::move(rtpConfig)), maxFragmentSize(maxFragmentSize),
-      separator(separator) {}
-
-void H265RtpPacketizer::outgoing(message_vector &messages, [[maybe_unused]] const message_callback &send) {
-	message_vector result;
-	for (const auto &message : messages) {
-		auto nalus = splitMessage(message);
-		auto fragments = nalus->generateFragments(maxFragmentSize);
-		if (fragments.size() == 0)
-			continue;
-
-		for (size_t i = 0; i < fragments.size() - 1; i++)
-			result.push_back(packetize(fragments[i], false));
-
-		result.push_back(packetize(fragments[fragments.size() - 1], true));
-	}
-
-	messages.swap(result);
-}
-
 } // namespace rtc
 
 #endif /* RTC_ENABLE_MEDIA */

+ 2 - 7
src/impl/track.cpp

@@ -240,11 +240,6 @@ shared_ptr<MediaHandler> Track::getMediaHandler() {
 	return mMediaHandler;
 }
 
-void Track::onFrame(std::function<void(binary data, FrameInfo frame)> callback) {
-	frameCallback = callback;
-	flushPendingMessages();
-}
-
 void Track::flushPendingMessages() {
 	if (!mOpenTriggered)
 		return;
@@ -256,9 +251,9 @@ void Track::flushPendingMessages() {
 
 		auto message = next.value();
 		try {
-			if (message->frameInfo != nullptr && frameCallback) {
+			if (message->frameInfo && frameCallback) {
 				frameCallback(std::move(*message), std::move(*message->frameInfo));
-			} else if (message->frameInfo == nullptr && messageCallback) {
+			} else if (!message->frameInfo && messageCallback) {
 				messageCallback(trackMessageToVariant(message));
 			}
 		} catch (const std::exception &e) {

+ 3 - 2
src/impl/track.hpp

@@ -41,7 +41,7 @@ public:
 	void flushPendingMessages() override;
 	message_variant trackMessageToVariant(message_ptr message);
 
-	void onFrame(std::function<void(binary data, FrameInfo frame)> callback);
+	void sendFrame(binary data, const FrameInfo &frame);
 
 	bool isOpen() const;
 	bool isClosed() const;
@@ -61,6 +61,8 @@ public:
 
 	bool transportSend(message_ptr message);
 
+	synchronized_callback<binary, FrameInfo> frameCallback;
+
 private:
 	const weak_ptr<PeerConnection> mPeerConnection;
 #if RTC_ENABLE_MEDIA
@@ -76,7 +78,6 @@ private:
 
 	Queue<message_ptr> mRecvQueue;
 
-	synchronized_callback<binary, FrameInfo> frameCallback;
 };
 
 } // namespace rtc::impl

+ 5 - 2
src/message.cpp

@@ -18,11 +18,14 @@ message_ptr make_message(size_t size, Message::Type type, unsigned int stream,
 	return message;
 }
 
-message_ptr make_message(binary &&data, Message::Type type, unsigned int stream,
-                         shared_ptr<Reliability> reliability, shared_ptr<FrameInfo> frameInfo) {
+message_ptr make_message(binary &&data, Message::Type type, unsigned int stream, shared_ptr<Reliability> reliability) {
 	auto message = std::make_shared<Message>(std::move(data), type);
 	message->stream = stream;
 	message->reliability = reliability;
+	return message;
+}
+message_ptr make_message(binary &&data, shared_ptr<FrameInfo> frameInfo) {
+	auto message = std::make_shared<Message>(std::move(data));
 	message->frameInfo = frameInfo;
 	return message;
 }

+ 103 - 33
src/nalunit.cpp

@@ -16,33 +16,38 @@
 
 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);
+std::vector<binary> NalUnit::GenerateFragments(const std::vector<NalUnit> &nalus,
+                                               size_t maxFragmentSize) {
+	std::vector<binary> result;
+	for (const auto &nalu : nalus) {
+		if (nalu.size() > maxFragmentSize) {
+			auto fragments = nalu.generateFragments(maxFragmentSize);
+			result.insert(result.end(), fragments.begin(), fragments.end());
+		} else {
+			// TODO: check this
+			result.push_back(nalu);
+		}
+	}
+	return result;
 }
 
-std::vector<shared_ptr<NalUnitFragmentA>>
-NalUnitFragmentA::fragmentsFrom(shared_ptr<NalUnit> nalu, uint16_t maxFragmentSize) {
-	assert(nalu->size() > maxFragmentSize);
-	auto fragments_count = ceil(double(nalu->size()) / maxFragmentSize);
-	maxFragmentSize = uint16_t(int(ceil(nalu->size() / fragments_count)));
+std::vector<NalUnitFragmentA> NalUnit::generateFragments(size_t maxFragmentSize) const {
+	assert(size() > maxFragmentSize);
+	// TODO: check this
+	auto fragments_count = ceil(double(size()) / maxFragmentSize);
+	maxFragmentSize = uint16_t(int(ceil(size() / fragments_count)));
 
 	// 2 bytes for FU indicator and FU header
 	maxFragmentSize -= 2;
-	auto f = nalu->forbiddenBit();
-	uint8_t nri = nalu->nri() & 0x03;
-	uint8_t naluType = nalu->unitType() & 0x1F;
-	auto payload = nalu->payload();
-	vector<shared_ptr<NalUnitFragmentA>> result{};
-	uint64_t offset = 0;
+	auto f = forbiddenBit();
+	uint8_t nri = this->nri() & 0x03;
+	uint8_t unitType = this->unitType() & 0x1F;
+	auto payload = this->payload();
+	size_t offset = 0;
+	std::vector<NalUnitFragmentA> result;
 	while (offset < payload.size()) {
 		vector<byte> fragmentData;
+		using FragmentType = NalUnitFragmentA::FragmentType;
 		FragmentType fragmentType;
 		if (offset == 0) {
 			fragmentType = FragmentType::Start;
@@ -55,14 +60,78 @@ NalUnitFragmentA::fragmentsFrom(shared_ptr<NalUnit> nalu, uint16_t maxFragmentSi
 			fragmentType = FragmentType::End;
 		}
 		fragmentData = {payload.begin() + offset, payload.begin() + offset + maxFragmentSize};
-		auto fragment =
-		    std::make_shared<NalUnitFragmentA>(fragmentType, f, nri, naluType, fragmentData);
-		result.push_back(fragment);
+		result.emplace_back(fragmentType, f, nri, unitType, fragmentData);
 		offset += maxFragmentSize;
 	}
 	return result;
 }
 
+NalUnitStartSequenceMatch NalUnit::StartSequenceMatchSucc(NalUnitStartSequenceMatch match,
+                                                          std::byte _byte, Separator separator) {
+	assert(separator != Separator::Length);
+	auto byte = (uint8_t)_byte;
+	auto detectShort =
+	    separator == Separator::ShortStartSequence || separator == Separator::StartSequence;
+	auto detectLong =
+	    separator == Separator::LongStartSequence || separator == 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 == 0x00 && detectShort) {
+			return NUSM_secondZero;
+		} else if (byte == 0x01 && detectShort) {
+			return NUSM_shortMatch;
+		}
+		break;
+	case NUSM_thirdZero:
+		if (byte == 0x00 && detectLong) {
+			return NUSM_thirdZero;
+		} else if (byte == 0x01 && detectLong) {
+			return NUSM_longMatch;
+		}
+		break;
+	case NUSM_shortMatch:
+		return NUSM_shortMatch;
+	case NUSM_longMatch:
+		return NUSM_longMatch;
+	}
+	return NUSM_noMatch;
+}
+
+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);
+}
+
+// For backward compatibility, do not use
+std::vector<shared_ptr<NalUnitFragmentA>>
+NalUnitFragmentA::fragmentsFrom(shared_ptr<NalUnit> nalu, uint16_t maxFragmentSize) {
+	auto fragments = nalu->generateFragments(maxFragmentSize);
+	std::vector<shared_ptr<NalUnitFragmentA>> result;
+	result.reserve(fragments.size());
+	for (auto fragment : fragments)
+		result.push_back(std::make_shared<NalUnitFragmentA>(std::move(fragment)));
+
+	return result;
+}
+
 void NalUnitFragmentA::setFragmentType(FragmentType type) {
 	fragmentHeader()->setReservedBit6(false);
 	switch (type) {
@@ -80,17 +149,18 @@ void NalUnitFragmentA::setFragmentType(FragmentType type) {
 	}
 }
 
+// For backward compatibility, do not use
 std::vector<shared_ptr<binary>> NalUnits::generateFragments(uint16_t maxFragmentSize) {
-	vector<shared_ptr<binary>> result{};
-	for (auto nalu : *this) {
-		if (nalu->size() > maxFragmentSize) {
-			std::vector<shared_ptr<NalUnitFragmentA>> fragments =
-			    NalUnitFragmentA::fragmentsFrom(nalu, maxFragmentSize);
-			result.insert(result.end(), fragments.begin(), fragments.end());
-		} else {
-			result.push_back(nalu);
-		}
-	}
+	std::vector<NalUnit> nalus;
+	for (auto nalu : *this)
+		nalus.push_back(*nalu);
+
+	auto fragments = NalUnit::GenerateFragments(nalus, maxFragmentSize);
+	std::vector<shared_ptr<binary>> result;
+	result.reserve(fragments.size());
+	for (auto fragment : fragments)
+		result.push_back(std::make_shared<binary>(std::move(fragment)));
+
 	return result;
 }
 

+ 8 - 9
src/rtcpnackresponder.cpp

@@ -46,10 +46,9 @@ void RtcpNackResponder::incoming(message_vector &messages, const message_callbac
 				                              newMissingSeqenceNumbers.end());
 			}
 
-			for (auto sequenceNumber : missingSequenceNumbers) {
-				if (auto optPacket = mStorage->get(sequenceNumber))
-					send(make_message(*optPacket.value()));
-			}
+			for (auto sequenceNumber : missingSequenceNumbers)
+				if (auto packet = mStorage->get(sequenceNumber))
+					send(packet);
 		}
 	}
 }
@@ -61,7 +60,7 @@ void RtcpNackResponder::outgoing(message_vector &messages,
 			mStorage->store(message);
 }
 
-RtcpNackResponder::Storage::Element::Element(binary_ptr packet, uint16_t sequenceNumber,
+RtcpNackResponder::Storage::Element::Element(message_ptr packet, uint16_t sequenceNumber,
                                              shared_ptr<Element> next)
     : packet(packet), sequenceNumber(sequenceNumber), next(next) {}
 
@@ -72,14 +71,14 @@ RtcpNackResponder::Storage::Storage(size_t _maxSize) : maxSize(_maxSize) {
 	storage.reserve(maxSize);
 }
 
-optional<binary_ptr> RtcpNackResponder::Storage::get(uint16_t sequenceNumber) {
+message_ptr RtcpNackResponder::Storage::get(uint16_t sequenceNumber) {
 	std::lock_guard lock(mutex);
 	auto position = storage.find(sequenceNumber);
-	return position != storage.end() ? std::make_optional(storage.at(sequenceNumber)->packet)
-	                                 : nullopt;
+	return position != storage.end() ? storage.at(sequenceNumber)->packet
+	                                 : nullptr;
 }
 
-void RtcpNackResponder::Storage::store(binary_ptr packet) {
+void RtcpNackResponder::Storage::store(message_ptr packet) {
 	if (!packet || packet->size() < sizeof(RtpHeader))
 		return;
 

+ 27 - 15
src/rtcpsrreporter.cpp

@@ -14,6 +14,8 @@
 #include <chrono>
 #include <cmath>
 
+using namespace std::chrono_literals;
+
 namespace {
 
 // TODO: move to utils
@@ -28,16 +30,21 @@ uint64_t ntp_time() {
 
 namespace rtc {
 
-RtcpSrReporter::RtcpSrReporter(shared_ptr<RtpPacketizationConfig> rtpConfig)
-    : rtpConfig(rtpConfig) {
-	mLastReportedTimestamp = rtpConfig->timestamp;
-}
+RtcpSrReporter::RtcpSrReporter(shared_ptr<RtpPacketizationConfig> rtpConfig) : rtpConfig(rtpConfig) {}
 
-void RtcpSrReporter::setNeedsToReport() { mNeedsToReport = true; }
+RtcpSrReporter::~RtcpSrReporter() {}
+
+void RtcpSrReporter::setNeedsToReport() {
+	// Dummy
+}
 
 uint32_t RtcpSrReporter::lastReportedTimestamp() const { return mLastReportedTimestamp; }
 
 void RtcpSrReporter::outgoing(message_vector &messages, const message_callback &send) {
+	if (messages.empty())
+		return;
+
+	uint32_t timestamp = 0;
 	for (const auto &message : messages) {
 		if (message->type == Message::Control)
 			continue;
@@ -45,21 +52,27 @@ void RtcpSrReporter::outgoing(message_vector &messages, const message_callback &
 		if (message->size() < sizeof(RtpHeader))
 			continue;
 
-		auto rtp = reinterpret_cast<RtpHeader *>(message->data());
-		addToReport(rtp, uint32_t(message->size()));
+		auto header = reinterpret_cast<RtpHeader *>(message->data());
+		if(header->ssrc() != rtpConfig->ssrc)
+			continue;
+
+		timestamp = header->timestamp();
+
+		addToReport(header, message->size());
 	}
 
-	if (std::exchange(mNeedsToReport, false)) {
-		auto timestamp = rtpConfig->timestamp;
-		auto sr = getSenderReport(timestamp);
-		send(sr);
+	auto now = std::chrono::steady_clock::now();
+	if (now >= mLastReportTime + 1s) {
+		send(getSenderReport(timestamp));
+		mLastReportedTimestamp = timestamp;
+		mLastReportTime = now;
 	}
 }
 
-void RtcpSrReporter::addToReport(RtpHeader *rtp, uint32_t rtpSize) {
+void RtcpSrReporter::addToReport(RtpHeader *header, size_t size) {
 	mPacketCount += 1;
-	assert(!rtp->padding());
-	mPayloadOctets += rtpSize - uint32_t(rtp->getSize());
+	assert(!header->padding());
+	mPayloadOctets += uint32_t(size - header->getSize());
 }
 
 message_ptr RtcpSrReporter::getSenderReport(uint32_t timestamp) {
@@ -81,7 +94,6 @@ message_ptr RtcpSrReporter::getSenderReport(uint32_t timestamp) {
 	item->setText(rtpConfig->cname);
 	sdes->preparePacket(1);
 
-	mLastReportedTimestamp = timestamp;
 	return msg;
 }
 

+ 13 - 3
src/rtpdepacketizer.cpp

@@ -18,6 +18,12 @@
 
 namespace rtc {
 
+RtpDepacketizer::RtpDepacketizer() : mClockRate(0) {}
+
+RtpDepacketizer::RtpDepacketizer(uint32_t clockRate) : mClockRate(clockRate) {}
+
+RtpDepacketizer::~RtpDepacketizer() {}
+
 void RtpDepacketizer::incoming([[maybe_unused]] message_vector &messages,
                                [[maybe_unused]] const message_callback &send) {
 	message_vector result;
@@ -34,9 +40,13 @@ void RtpDepacketizer::incoming([[maybe_unused]] message_vector &messages,
 
 		auto pkt = reinterpret_cast<const rtc::RtpHeader *>(message->data());
 		auto headerSize = sizeof(rtc::RtpHeader) + pkt->csrcCount() + pkt->getExtensionHeaderSize();
-		result.push_back(make_message(message->begin() + headerSize, message->end(),
-		                              Message::Binary, 0, nullptr,
-		                              std::make_shared<FrameInfo>(pkt->payloadType(), pkt->timestamp())));
+
+		auto frameInfo = std::make_shared<FrameInfo>(pkt->timestamp());
+		if (mClockRate > 0)
+			frameInfo->timestampSeconds =
+			    std::chrono::duration<double>(double(pkt->timestamp()) / double(mClockRate));
+		frameInfo->payloadType = pkt->payloadType();
+		result.push_back(make_message(message->begin() + headerSize, message->end(), frameInfo));
 	}
 
 	messages.swap(result);

+ 40 - 13
src/rtppacketizer.cpp

@@ -19,7 +19,12 @@ RtpPacketizer::RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig) : rtp
 
 RtpPacketizer::~RtpPacketizer() {}
 
-message_ptr RtpPacketizer::packetize(shared_ptr<binary> payload, bool mark) {
+std::vector<binary> RtpPacketizer::fragment(binary data) {
+	// Default implementation
+	return {std::move(data)};
+}
+
+message_ptr RtpPacketizer::packetize(const binary &payload, bool mark) {
 	size_t rtpExtHeaderSize = 0;
 
 	const bool setVideoRotation = (rtpConfig->videoOrientationId != 0) &&
@@ -47,7 +52,7 @@ message_ptr RtpPacketizer::packetize(shared_ptr<binary> payload, bool mark) {
 
 	rtpExtHeaderSize = (rtpExtHeaderSize + 3) & ~3;
 
-	auto message = make_message(RtpHeaderSize + rtpExtHeaderSize + payload->size());
+	auto message = make_message(RtpHeaderSize + rtpExtHeaderSize + payload.size());
 	auto *rtp = (RtpHeader *)message->data();
 	rtp->setPayloadType(rtpConfig->payloadType);
 	rtp->setSeqNumber(rtpConfig->sequenceNumber++); // increase sequence number
@@ -96,11 +101,8 @@ message_ptr RtpPacketizer::packetize(shared_ptr<binary> payload, bool mark) {
 			uint16_t max = rtpConfig->playoutDelayMax & 0xFFF;
 
 			// 12 bits for min + 12 bits for max
-			byte data[] = {
-				byte((min >> 4) & 0xFF), 
-				byte(((min & 0xF) << 4) | ((max >> 8) & 0xF)),
-				byte(max & 0xFF)
-			};
+			byte data[] = {byte((min >> 4) & 0xFF), byte(((min & 0xF) << 4) | ((max >> 8) & 0xF)),
+			               byte(max & 0xFF)};
 
 			extHeader->writeOneByteHeader(offset, rtpConfig->playoutDelayId, data, 3);
 			offset += 4;
@@ -109,19 +111,44 @@ message_ptr RtpPacketizer::packetize(shared_ptr<binary> payload, bool mark) {
 
 	rtp->preparePacket();
 
-	std::memcpy(message->data() + RtpHeaderSize + rtpExtHeaderSize, payload->data(),
-	            payload->size());
+	std::memcpy(message->data() + RtpHeaderSize + rtpExtHeaderSize, payload.data(), payload.size());
 
 	return message;
 }
 
+message_ptr RtpPacketizer::packetize(shared_ptr<binary> payload, bool mark) {
+	return packetize(*payload, mark);
+}
+
 void RtpPacketizer::media([[maybe_unused]] const Description::Media &desc) {}
 
-void RtpPacketizer::outgoing([[maybe_unused]] message_vector &messages,
+void RtpPacketizer::outgoing(message_vector &messages,
                              [[maybe_unused]] const message_callback &send) {
-	// Default implementation
-	for (auto &message : messages)
-		message = packetize(message, false);
+	message_vector result;
+	for (const auto &message : messages) {
+		if (const auto &frameInfo = message->frameInfo) {
+			if (frameInfo->payloadType && frameInfo->payloadType != rtpConfig->payloadType)
+				continue;
+
+			if (frameInfo->timestampSeconds)
+				rtpConfig->timestamp =
+				    rtpConfig->startTimestamp +
+				    rtpConfig->secondsToTimestamp(
+				        std::chrono::duration<double>(*frameInfo->timestampSeconds).count());
+			else
+				rtpConfig->timestamp = frameInfo->timestamp;
+		}
+
+		auto payloads = fragment(std::move(*message));
+		if (payloads.size() > 0) {
+			for (size_t i = 0; i < payloads.size() - 1; i++)
+				result.push_back(packetize(payloads[i], false));
+
+			result.push_back(packetize(payloads[payloads.size() - 1], true));
+		}
+	}
+
+	messages.swap(result);
 }
 
 } // namespace rtc

+ 9 - 4
src/track.cpp

@@ -40,6 +40,15 @@ bool Track::isClosed(void) const { return impl()->isClosed(); }
 
 size_t Track::maxMessageSize() const { return impl()->maxMessageSize(); }
 
+void Track::sendFrame(binary data, FrameInfo info) {
+	impl()->outgoing(make_message(std::move(data), std::make_shared<FrameInfo>(std::move(info))));
+}
+
+void Track::onFrame(std::function<void(binary data, FrameInfo frame)> callback) {
+	impl()->frameCallback = callback;
+	impl()->flushPendingMessages();
+}
+
 void Track::setMediaHandler(shared_ptr<MediaHandler> handler) {
 	impl()->setMediaHandler(std::move(handler));
 }
@@ -70,8 +79,4 @@ bool Track::requestBitrate(unsigned int bitrate) {
 
 shared_ptr<MediaHandler> Track::getMediaHandler() { return impl()->getMediaHandler(); }
 
-void Track::onFrame(std::function<void(binary data, FrameInfo frame)> callback) {
-	impl()->onFrame(callback);
-}
-
 } // namespace rtc