瀏覽代碼

Refactor media handlers

Paul-Louis Ageneau 2 年之前
父節點
當前提交
8643f2473a
共有 50 個文件被更改,包括 697 次插入1550 次删除
  1. 3 18
      CMakeLists.txt
  2. 0 60
      include/rtc/aacrtppacketizer.hpp
  3. 0 32
      include/rtc/av1packetizationhandler.hpp
  4. 15 15
      include/rtc/av1rtppacketizer.hpp
  5. 4 0
      include/rtc/description.hpp
  6. 0 32
      include/rtc/h264packetizationhandler.hpp
  7. 18 14
      include/rtc/h264rtppacketizer.hpp
  8. 0 32
      include/rtc/h265packetizationhandler.hpp
  9. 19 17
      include/rtc/h265rtppacketizer.hpp
  10. 0 48
      include/rtc/mediachainablehandler.hpp
  11. 35 17
      include/rtc/mediahandler.hpp
  12. 0 112
      include/rtc/mediahandlerelement.hpp
  13. 0 36
      include/rtc/mediahandlerrootelement.hpp
  14. 1 0
      include/rtc/message.hpp
  15. 0 32
      include/rtc/opuspacketizationhandler.hpp
  16. 0 50
      include/rtc/opusrtppacketizer.hpp
  17. 5 5
      include/rtc/plihandler.hpp
  18. 7 11
      include/rtc/rtc.hpp
  19. 15 24
      include/rtc/rtcpnackresponder.hpp
  20. 15 10
      include/rtc/rtcpreceivingsession.hpp
  21. 14 15
      include/rtc/rtcpsrreporter.hpp
  22. 17 17
      include/rtc/rtp.hpp
  23. 4 4
      include/rtc/rtppacketizationconfig.hpp
  24. 54 11
      include/rtc/rtppacketizer.hpp
  25. 2 0
      include/rtc/track.hpp
  26. 0 38
      src/aacrtppacketizer.cpp
  27. 0 20
      src/av1packetizationhandler.cpp
  28. 16 21
      src/av1rtppacketizer.cpp
  29. 46 80
      src/capi.cpp
  30. 16 0
      src/description.cpp
  31. 0 20
      src/h264packetizationhandler.cpp
  32. 15 18
      src/h264rtppacketizer.cpp
  33. 0 20
      src/h265packetizationhandler.cpp
  34. 14 16
      src/h265rtppacketizer.cpp
  35. 29 8
      src/impl/peerconnection.cpp
  36. 1 0
      src/impl/peerconnection.hpp
  37. 35 31
      src/impl/track.cpp
  38. 3 3
      src/impl/track.hpp
  39. 0 163
      src/mediachainablehandler.cpp
  40. 80 0
      src/mediahandler.cpp
  41. 0 211
      src/mediahandlerelement.cpp
  42. 0 34
      src/mediahandlerrootelement.cpp
  43. 0 20
      src/opuspacketizationhandler.cpp
  44. 0 38
      src/opusrtppacketizer.cpp
  45. 22 21
      src/plihandler.cpp
  46. 52 60
      src/rtcpnackresponder.cpp
  47. 72 73
      src/rtcpreceivingsession.cpp
  48. 23 21
      src/rtcpsrreporter.cpp
  49. 26 17
      src/rtppacketizer.cpp
  50. 19 5
      src/track.cpp

+ 3 - 18
CMakeLists.txt

@@ -65,6 +65,7 @@ set(LIBDATACHANNEL_SOURCES
 	${CMAKE_CURRENT_SOURCE_DIR}/src/configuration.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/datachannel.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/description.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/mediahandler.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/global.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/message.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/peerconnection.cpp
@@ -75,20 +76,12 @@ set(LIBDATACHANNEL_SOURCES
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizationconfig.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpsrreporter.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/aacrtppacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/h264rtppacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/nalunit.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/h264packetizationhandler.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/h265rtppacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/h265nalunit.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/h265packetizationhandler.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/av1rtppacketizer.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/av1packetizationhandler.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/mediachainablehandler.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/mediahandlerelement.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/src/mediahandlerrootelement.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/nalunit.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpnackresponder.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtp.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/capi.cpp
@@ -117,20 +110,12 @@ set(LIBDATACHANNEL_HEADERS
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtppacketizationconfig.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpsrreporter.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/aacrtppacketizer.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
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h265rtppacketizer.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h265nalunit.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h265packetizationhandler.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/av1rtppacketizer.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/av1packetizationhandler.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/mediachainablehandler.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/mediahandlerelement.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/mediahandlerrootelement.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/nalunit.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpnackresponder.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/utils.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/plihandler.hpp

+ 0 - 60
include/rtc/aacrtppacketizer.hpp

@@ -1,60 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#ifndef RTC_AAC_RTP_PACKETIZER_H
-#define RTC_AAC_RTP_PACKETIZER_H
-
-#if RTC_ENABLE_MEDIA
-
-#include "mediachainablehandler.hpp"
-#include "mediahandlerrootelement.hpp"
-#include "rtppacketizer.hpp"
-
-namespace rtc {
-
-/// RTP packetizer for aac
-class RTC_CPP_EXPORT AACRtpPacketizer final : public RtpPacketizer, public MediaHandlerRootElement {
-public:
-	/// default clock rate used in aac RTP communication
-	inline static const uint32_t defaultClockRate = 48 * 1000;
-
-	/// Constructs aac 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
-	AACRtpPacketizer(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 This needs to be `false` for all RTP packets with aac payload
-	binary_ptr packetize(binary_ptr payload, bool setMark) override;
-
-	/// Creates RTP packet for given samples (all samples share same RTP timesamp)
-	/// @param messages aac samples
-	/// @param control RTCP
-	/// @returns RTP packets and unchanged `control`
-	ChainedOutgoingProduct processOutgoingBinaryMessage(ChainedMessagesProduct messages,
-	                                                    message_ptr control) override;
-};
-
-/// Handler for aac packetization
-class RTC_CPP_EXPORT AACPacketizationHandler final : public MediaChainableHandler {
-
-public:
-	/// Construct handler for aac packetization.
-	/// @param packetizer RTP packetizer for aac
-	AACPacketizationHandler(shared_ptr<AACRtpPacketizer> packetizer)
-	    : MediaChainableHandler(packetizer) {}
-};
-
-} // namespace rtc
-
-#endif /* RTC_ENABLE_MEDIA */
-
-#endif /* RTC_AAC_RTP_PACKETIZER_H */

+ 0 - 32
include/rtc/av1packetizationhandler.hpp

@@ -1,32 +0,0 @@
-/**
- * Copyright (c) 2023 Paul-Louis Ageneau
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#ifndef RTC_AV1_PACKETIZATION_HANDLER_H
-#define RTC_AV1_PACKETIZATION_HANDLER_H
-
-#if RTC_ENABLE_MEDIA
-
-#include "av1rtppacketizer.hpp"
-#include "mediachainablehandler.hpp"
-#include "nalunit.hpp"
-
-namespace rtc {
-
-/// Handler for AV1 packetization
-class RTC_CPP_EXPORT AV1PacketizationHandler final : public MediaChainableHandler {
-public:
-	/// Construct handler for AV1 packetization.
-	/// @param packetizer RTP packetizer for AV1
-	AV1PacketizationHandler(shared_ptr<AV1RtpPacketizer> packetizer);
-};
-
-} // namespace rtc
-
-#endif /* RTC_ENABLE_MEDIA */
-
-#endif /* RTC_AV1_PACKETIZATION_HANDLER_H */

+ 15 - 15
include/rtc/av1rtppacketizer.hpp

@@ -11,19 +11,16 @@
 
 #if RTC_ENABLE_MEDIA
 
-#include "mediahandlerrootelement.hpp"
+#include "mediahandler.hpp"
 #include "nalunit.hpp"
 #include "rtppacketizer.hpp"
 
 namespace rtc {
 
-/// RTP packetization of AV1 payload
-class RTC_CPP_EXPORT AV1RtpPacketizer final : public RtpPacketizer, public MediaHandlerRootElement {
-	shared_ptr<NalUnits> splitMessage(binary_ptr message);
-	const uint16_t maximumFragmentSize;
-
+// RTP packetization of AV1 payload
+class RTC_CPP_EXPORT AV1RtpPacketizer final : public RtpPacketizer {
 public:
-	/// Default clock rate for AV1 in RTP
+	// Default clock rate for AV1 in RTP
 	inline static const uint32_t defaultClockRate = 90 * 1000;
 
 	// Define how OBUs are seperated in a AV1 Sample
@@ -32,23 +29,26 @@ public:
 		TemporalUnit = RTC_OBU_PACKETIZED_TEMPORAL_UNIT,
 	};
 
-	/// Constructs AV1 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
+	// Constructs AV1 payload packetizer with given RTP configuration.
+	// @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 maximumFragmentSize = NalUnits::defaultMaximumFragmentSize);
 
-	ChainedOutgoingProduct processOutgoingBinaryMessage(ChainedMessagesProduct messages,
-	                                                    message_ptr control) override;
+	void outgoing(message_vector &messages, const message_callback &send) override;
 
 private:
+	shared_ptr<NalUnits> splitMessage(binary_ptr message);
+	std::vector<shared_ptr<binary>> packetizeObu(binary_ptr message, uint16_t maximumFragmentSize);
+
+	const uint16_t maximumFragmentSize;
 	const Packetization packetization;
 	std::shared_ptr<binary> sequenceHeader;
-
-	std::vector<shared_ptr<binary>> packetizeObu(binary_ptr message, uint16_t maximumFragmentSize);
 };
 
+// For backward compatibility, do not use
+using AV1PacketizationHandler [[deprecated("Add AV1RtpPacketizer directly")]] = PacketizationHandler;
+
 } // namespace rtc
 
 #endif /* RTC_ENABLE_MEDIA */

+ 4 - 0
include/rtc/description.hpp

@@ -109,6 +109,7 @@ public:
 
 		std::vector<int> extIds();
 		ExtMap *extMap(int id);
+		const ExtMap *extMap(int id) const;
 		void addExtMap(ExtMap map);
 		void removeExtMap(int id);
 
@@ -208,6 +209,7 @@ public:
 		bool hasPayloadType(int payloadType) const;
 		std::vector<int> payloadTypes() const;
 		RtpMap *rtpMap(int payloadType);
+		const RtpMap *rtpMap(int payloadType) const;
 		void addRtpMap(RtpMap map);
 		void removeRtpMap(int payloadType);
 		void removeFormat(const string &format);
@@ -233,7 +235,9 @@ public:
 		void addAudioCodec(int payloadType, string codec, optional<string> profile = std::nullopt);
 
 		void addOpusCodec(int payloadType, optional<string> profile = DEFAULT_OPUS_AUDIO_PROFILE);
+
 		void addPCMACodec(int payloadType, optional<string> profile = std::nullopt);
+
 		void addPCMUCodec(int payloadType, optional<string> profile = std::nullopt);
 		void addAacCodec(int payloadType, optional<string> profile = std::nullopt);
 	};

+ 0 - 32
include/rtc/h264packetizationhandler.hpp

@@ -1,32 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#ifndef RTC_H264_PACKETIZATION_HANDLER_H
-#define RTC_H264_PACKETIZATION_HANDLER_H
-
-#if RTC_ENABLE_MEDIA
-
-#include "h264rtppacketizer.hpp"
-#include "mediachainablehandler.hpp"
-#include "nalunit.hpp"
-
-namespace rtc {
-
-/// Handler for H264 packetization
-class RTC_CPP_EXPORT H264PacketizationHandler final : public MediaChainableHandler {
-public:
-	/// Construct handler for H264 packetization.
-	/// @param packetizer RTP packetizer for h264
-	H264PacketizationHandler(shared_ptr<H264RtpPacketizer> packetizer);
-};
-
-} // namespace rtc
-
-#endif /* RTC_ENABLE_MEDIA */
-
-#endif /* RTC_H264_PACKETIZATION_HANDLER_H */

+ 18 - 14
include/rtc/h264rtppacketizer.hpp

@@ -1,5 +1,6 @@
 /**
  * Copyright (c) 2020 Filip Klembara (in2core)
+ * Copyright (c) 2023 Paul-Louis Ageneau
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -11,42 +12,45 @@
 
 #if RTC_ENABLE_MEDIA
 
-#include "mediahandlerrootelement.hpp"
 #include "nalunit.hpp"
 #include "rtppacketizer.hpp"
 
 namespace rtc {
 
-/// RTP packetization of h264 payload
-class RTC_CPP_EXPORT H264RtpPacketizer final : public RtpPacketizer,
-                                               public MediaHandlerRootElement {
-	shared_ptr<NalUnits> splitMessage(binary_ptr message);
-	const uint16_t maximumFragmentSize;
-
+/// RTP packetization for H264
+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;
 
-	H264RtpPacketizer(Separator separator, shared_ptr<RtpPacketizationConfig> rtpConfig,
-	                  uint16_t maximumFragmentSize = NalUnits::defaultMaximumFragmentSize);
-
 	/// 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
+	/// @param separator NAL unit separator
+	/// @param rtpConfig RTP configuration
 	/// @param maximumFragmentSize maximum size of one NALU fragment
-	H264RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig,
+	H264RtpPacketizer(Separator separator, shared_ptr<RtpPacketizationConfig> rtpConfig,
 	                  uint16_t maximumFragmentSize = NalUnits::defaultMaximumFragmentSize);
 
-	ChainedOutgoingProduct processOutgoingBinaryMessage(ChainedMessagesProduct messages,
-	                                                    message_ptr control) override;
+	// For backward compatibility, do not use
+	[[deprecated]] H264RtpPacketizer(
+	    shared_ptr<RtpPacketizationConfig> rtpConfig,
+	    uint16_t maximumFragmentSize = NalUnits::defaultMaximumFragmentSize);
+
+	void outgoing(message_vector &messages, const message_callback &send) override;
 
 private:
+	shared_ptr<NalUnits> splitMessage(binary_ptr message);
+
+	const uint16_t maximumFragmentSize;
 	const Separator separator;
 };
 
+// For backward compatibility, do not use
+using H264PacketizationHandler [[deprecated("Add H264RtpPacketizer directly")]] = PacketizationHandler;
+
 } // namespace rtc
 
 #endif /* RTC_ENABLE_MEDIA */

+ 0 - 32
include/rtc/h265packetizationhandler.hpp

@@ -1,32 +0,0 @@
-/**
- * Copyright (c) 2023 Zita Liao (Dolby)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#ifndef RTC_H265_PACKETIZATION_HANDLER_H
-#define RTC_H265_PACKETIZATION_HANDLER_H
-
-#if RTC_ENABLE_MEDIA
-
-#include "h265nalunit.hpp"
-#include "h265rtppacketizer.hpp"
-#include "mediachainablehandler.hpp"
-
-namespace rtc {
-
-/// Handler for H265 packetization
-class RTC_CPP_EXPORT H265PacketizationHandler final : public MediaChainableHandler {
-public:
-	/// Construct handler for H265 packetization.
-	/// @param packetizer RTP packetizer for h265
-	H265PacketizationHandler(shared_ptr<H265RtpPacketizer> packetizer);
-};
-
-} // namespace rtc
-
-#endif /* RTC_ENABLE_MEDIA */
-
-#endif /* RTC_H265_PACKETIZATION_HANDLER_H */

+ 19 - 17
include/rtc/h265rtppacketizer.hpp

@@ -12,41 +12,43 @@
 #if RTC_ENABLE_MEDIA
 
 #include "h265nalunit.hpp"
-#include "mediahandlerrootelement.hpp"
 #include "rtppacketizer.hpp"
 
 namespace rtc {
 
-/// RTP packetization of h265 payload
-class RTC_CPP_EXPORT H265RtpPacketizer final : public RtpPacketizer,
-                                               public MediaHandlerRootElement {
-	shared_ptr<H265NalUnits> splitMessage(binary_ptr message);
-	const uint16_t maximumFragmentSize;
-
+// RTP packetization for H265
+class RTC_CPP_EXPORT H265RtpPacketizer final : public RtpPacketizer {
 public:
 	using Separator = NalUnit::Separator;
 
-	/// Default clock rate for H265 in RTP
+	// Default clock rate for H265 in RTP
 	inline static const uint32_t defaultClockRate = 90 * 1000;
 
-	H265RtpPacketizer(NalUnit::Separator separator, shared_ptr<RtpPacketizationConfig> rtpConfig,
+	// Constructs h265 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 separator NAL unit separator
+	// @param rtpConfig  RTP configuration
+	// @param maximumFragmentSize maximum size of one NALU fragment
+	H265RtpPacketizer(Separator separator, shared_ptr<RtpPacketizationConfig> rtpConfig,
 	                  uint16_t maximumFragmentSize = H265NalUnits::defaultMaximumFragmentSize);
 
-	/// Constructs h265 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
-	/// @param maximumFragmentSize maximum size of one NALU fragment
-	H265RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig,
+	// for backward compatibility
+	[[deprecated]] H265RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig,
 	                  uint16_t maximumFragmentSize = H265NalUnits::defaultMaximumFragmentSize);
 
-	ChainedOutgoingProduct processOutgoingBinaryMessage(ChainedMessagesProduct messages,
-	                                                    message_ptr control) override;
+	void outgoing(message_vector &messages, const message_callback &send) override;
 
 private:
+	shared_ptr<H265NalUnits> splitMessage(binary_ptr message);
+
+	const uint16_t maximumFragmentSize;
 	const NalUnit::Separator separator;
 };
 
+// For backward compatibility, do not use
+using H265PacketizationHandler [[deprecated("Add H265RtpPacketizer directly")]] = PacketizationHandler;
+
 } // namespace rtc
 
 #endif /* RTC_ENABLE_MEDIA */

+ 0 - 48
include/rtc/mediachainablehandler.hpp

@@ -1,48 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#ifndef RTC_MEDIA_CHAINABLE_HANDLER_H
-#define RTC_MEDIA_CHAINABLE_HANDLER_H
-
-#if RTC_ENABLE_MEDIA
-
-#include "mediahandler.hpp"
-#include "mediahandlerrootelement.hpp"
-
-namespace rtc {
-
-class RTC_CPP_EXPORT MediaChainableHandler : public MediaHandler {
-	const shared_ptr<MediaHandlerRootElement> root;
-	shared_ptr<MediaHandlerElement> leaf;
-	mutable std::mutex mutex;
-
-	message_ptr handleIncomingBinary(message_ptr);
-	message_ptr handleIncomingControl(message_ptr);
-	message_ptr handleOutgoingBinary(message_ptr);
-	message_ptr handleOutgoingControl(message_ptr);
-	bool sendProduct(ChainedOutgoingProduct product);
-	shared_ptr<MediaHandlerElement> getLeaf() const;
-
-public:
-	MediaChainableHandler(shared_ptr<MediaHandlerRootElement> root);
-	~MediaChainableHandler();
-	message_ptr incoming(message_ptr ptr) override;
-	message_ptr outgoing(message_ptr ptr) override;
-
-	bool send(message_ptr msg);
-
-	/// Adds element to chain
-	/// @param chainable Chainable element
-	void addToChain(shared_ptr<MediaHandlerElement> chainable);
-};
-
-} // namespace rtc
-
-#endif // RTC_ENABLE_MEDIA
-
-#endif // RTC_MEDIA_CHAINABLE_HANDLER_H

+ 35 - 17
include/rtc/mediahandler.hpp

@@ -11,28 +11,46 @@
 #define RTC_MEDIA_HANDLER_H
 
 #include "common.hpp"
+#include "description.hpp"
 #include "message.hpp"
 
 namespace rtc {
 
-class RTC_CPP_EXPORT MediaHandler {
-protected:
-	// Use this callback when trying to send custom data (such as RTCP) to the client.
-	synchronized_callback<message_ptr> outgoingCallback;
-
+class RTC_CPP_EXPORT MediaHandler : public std::enable_shared_from_this<MediaHandler> {
 public:
-	// Called when there is traffic coming from the peer
-	virtual message_ptr incoming(message_ptr ptr) = 0;
-
-	// Called when there is traffic that needs to be sent to the peer
-	virtual message_ptr outgoing(message_ptr ptr) = 0;
-
-	// This callback is used to send traffic back to the peer.
-	void onOutgoing(const std::function<void(message_ptr)> &cb) {
-		this->outgoingCallback = synchronized_callback<message_ptr>(cb);
-	}
-
-	virtual bool requestKeyframe() { return false; }
+	MediaHandler();
+	virtual ~MediaHandler();
+
+	/// Called when a media is added or updated
+	/// @param desc Description of the media
+	virtual void media([[maybe_unused]] const Description::Media &desc) {}
+
+	/// Called when there is traffic coming from the peer
+	/// @param messages Incoming messages from the peer, can be modified by the handler
+	/// @param send Send callback to send messages back to the peer
+	virtual void incoming([[maybe_unused]] message_vector &messages, [[maybe_unused]] const message_callback &send) {}
+
+	/// Called when there is traffic that needs to be sent to the peer
+	/// @param messages Outgoing messages to the peer, can be modified by the handler
+	/// @param send Send callback to send messages back to the peer
+	virtual void outgoing([[maybe_unused]] message_vector &messages, [[maybe_unused]] const message_callback &send) {}
+
+	virtual bool requestKeyframe(const message_callback &send);
+	virtual bool requestBitrate(unsigned int bitrate, const message_callback &send);
+
+	void addToChain(shared_ptr<MediaHandler> handler);
+	void setNext(shared_ptr<MediaHandler> handler);
+	shared_ptr<MediaHandler> next();
+	shared_ptr<const MediaHandler> next() const;
+	shared_ptr<MediaHandler> last();             // never null
+	shared_ptr<const MediaHandler> last() const; // never null
+
+	void mediaChain(const Description::Media &desc);
+	void incomingChain(message_vector &messages, const message_callback &send);
+	void outgoingChain(message_vector &messages, const message_callback &send);
+
+private:
+	shared_ptr<MediaHandler> mNext;
 };
 
 } // namespace rtc

+ 0 - 112
include/rtc/mediahandlerelement.hpp

@@ -1,112 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#ifndef RTC_MEDIA_HANDLER_ELEMENT_H
-#define RTC_MEDIA_HANDLER_ELEMENT_H
-
-#if RTC_ENABLE_MEDIA
-
-#include "common.hpp"
-#include "message.hpp"
-#include "rtp.hpp"
-
-namespace rtc {
-
-using ChainedMessagesProduct = shared_ptr<std::vector<binary_ptr>>;
-
-RTC_CPP_EXPORT ChainedMessagesProduct make_chained_messages_product();
-RTC_CPP_EXPORT ChainedMessagesProduct make_chained_messages_product(message_ptr msg);
-
-/// Ougoing messages
-struct RTC_CPP_EXPORT ChainedOutgoingProduct {
-	ChainedOutgoingProduct(ChainedMessagesProduct messages = nullptr,
-	                       message_ptr control = nullptr);
-	const ChainedMessagesProduct messages;
-	const message_ptr control;
-};
-
-/// Incoming messages with response
-struct RTC_CPP_EXPORT ChainedIncomingProduct {
-	ChainedIncomingProduct(ChainedMessagesProduct incoming = nullptr,
-	                       ChainedMessagesProduct outgoing = nullptr);
-	const ChainedMessagesProduct incoming;
-	const ChainedOutgoingProduct outgoing;
-};
-
-/// Incoming control messages with response
-struct RTC_CPP_EXPORT ChainedIncomingControlProduct {
-	ChainedIncomingControlProduct(message_ptr incoming,
-	                              optional<ChainedOutgoingProduct> outgoing = nullopt);
-	const message_ptr incoming;
-	const optional<ChainedOutgoingProduct> outgoing;
-};
-
-/// Chainable handler
-class RTC_CPP_EXPORT MediaHandlerElement
-    : public std::enable_shared_from_this<MediaHandlerElement> {
-	shared_ptr<MediaHandlerElement> upstream = nullptr;
-	shared_ptr<MediaHandlerElement> downstream = nullptr;
-
-	void prepareAndSendResponse(optional<ChainedOutgoingProduct> outgoing,
-	                            std::function<bool(ChainedOutgoingProduct)> send);
-
-	void removeFromChain();
-
-public:
-	MediaHandlerElement();
-
-	/// Creates response to incoming message
-	/// @param messages Current repsonse
-	/// @returns New response
-	optional<ChainedOutgoingProduct> processOutgoingResponse(ChainedOutgoingProduct messages);
-
-	// Process incoming and ougoing messages
-	message_ptr formIncomingControlMessage(message_ptr message,
-	                                       std::function<bool(ChainedOutgoingProduct)> send);
-	ChainedMessagesProduct
-	formIncomingBinaryMessage(ChainedMessagesProduct messages,
-	                          std::function<bool(ChainedOutgoingProduct)> send);
-	message_ptr formOutgoingControlMessage(message_ptr message);
-	optional<ChainedOutgoingProduct> formOutgoingBinaryMessage(ChainedOutgoingProduct product);
-
-	/// Process current control message
-	/// @param messages current message
-	/// @returns Modified message and response
-	virtual ChainedIncomingControlProduct processIncomingControlMessage(message_ptr messages);
-
-	/// Process current control message
-	/// @param messages current message
-	/// @returns Modified message
-	virtual message_ptr processOutgoingControlMessage(message_ptr messages);
-
-	/// Process current binary message
-	/// @param messages current message
-	/// @returns Modified message and response
-	virtual ChainedIncomingProduct processIncomingBinaryMessage(ChainedMessagesProduct messages);
-
-	/// Process current binary message
-	/// @param messages current message
-	/// @param control current control message
-	/// @returns Modified binary message and control message
-	virtual ChainedOutgoingProduct processOutgoingBinaryMessage(ChainedMessagesProduct messages,
-	                                                            message_ptr control);
-
-	/// Set given element as upstream to this
-	/// @param upstream Upstream element
-	/// @returns Upstream element
-	shared_ptr<MediaHandlerElement> chainWith(shared_ptr<MediaHandlerElement> upstream);
-
-	/// Remove all downstream elements from chain
-	void recursiveRemoveChain();
-};
-
-} // namespace rtc
-
-#endif // RTC_ENABLE_MEDIA
-
-#endif // RTC_MEDIA_HANDLER_ELEMENT_H

+ 0 - 36
include/rtc/mediahandlerrootelement.hpp

@@ -1,36 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#ifndef RTC_MEDIA_HANDLER_ROOT_ELEMENT_H
-#define RTC_MEDIA_HANDLER_ROOT_ELEMENT_H
-
-#if RTC_ENABLE_MEDIA
-
-#include "mediahandlerelement.hpp"
-
-namespace rtc {
-
-/// Chainable message handler
-class RTC_CPP_EXPORT MediaHandlerRootElement : public MediaHandlerElement {
-public:
-	MediaHandlerRootElement() {}
-
-	/// Reduce multiple messages into one message
-	/// @param messages Messages to reduce
-	virtual message_ptr reduce(ChainedMessagesProduct messages);
-
-	/// Splits message into multiple messages
-	/// @param message Message to split
-	virtual ChainedMessagesProduct split(message_ptr message);
-};
-
-} // namespace rtc
-
-#endif // RTC_ENABLE_MEDIA
-
-#endif // RTC_MEDIA_HANDLER_ROOT_ELEMENT_H

+ 1 - 0
include/rtc/message.hpp

@@ -36,6 +36,7 @@ struct RTC_CPP_EXPORT Message : binary {
 
 using message_ptr = shared_ptr<Message>;
 using message_callback = std::function<void(message_ptr message)>;
+using message_vector = std::vector<message_ptr>;
 
 inline size_t message_size_func(const message_ptr &m) {
 	return m->type == Message::Binary || m->type == Message::String ? m->size() : 0;

+ 0 - 32
include/rtc/opuspacketizationhandler.hpp

@@ -1,32 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#ifndef RTC_OPUS_PACKETIZATION_HANDLER_H
-#define RTC_OPUS_PACKETIZATION_HANDLER_H
-
-#if RTC_ENABLE_MEDIA
-
-#include "mediachainablehandler.hpp"
-#include "opusrtppacketizer.hpp"
-
-namespace rtc {
-
-/// Handler for opus packetization
-class RTC_CPP_EXPORT OpusPacketizationHandler final : public MediaChainableHandler {
-
-public:
-	/// Construct handler for opus packetization.
-	/// @param packetizer RTP packetizer for opus
-	OpusPacketizationHandler(shared_ptr<OpusRtpPacketizer> packetizer);
-};
-
-} // namespace rtc
-
-#endif /* RTC_ENABLE_MEDIA */
-
-#endif /* RTC_OPUS_PACKETIZATION_HANDLER_H */

+ 0 - 50
include/rtc/opusrtppacketizer.hpp

@@ -1,50 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#ifndef RTC_OPUS_RTP_PACKETIZER_H
-#define RTC_OPUS_RTP_PACKETIZER_H
-
-#if RTC_ENABLE_MEDIA
-
-#include "mediahandlerrootelement.hpp"
-#include "rtppacketizer.hpp"
-
-namespace rtc {
-
-/// RTP packetizer for opus
-class RTC_CPP_EXPORT OpusRtpPacketizer final : public RtpPacketizer,
-                                               public MediaHandlerRootElement {
-public:
-	/// default clock rate used in opus RTP communication
-	inline 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(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 This needs to be `false` for all RTP packets with opus payload
-	binary_ptr packetize(binary_ptr payload, bool setMark) override;
-
-	/// Creates RTP packet for given samples (all samples share same RTP timesamp)
-	/// @param messages opus samples
-	/// @param control RTCP
-	/// @returns RTP packets and unchanged `control`
-	ChainedOutgoingProduct processOutgoingBinaryMessage(ChainedMessagesProduct messages,
-	                                                    message_ptr control) override;
-};
-
-} // namespace rtc
-
-#endif /* RTC_ENABLE_MEDIA */
-
-#endif /* RTC_OPUS_RTP_PACKETIZER_H */

+ 5 - 5
include/rtc/plihandler.hpp

@@ -11,22 +11,22 @@
 
 #if RTC_ENABLE_MEDIA
 
-#include "mediahandlerelement.hpp"
+#include "mediahandler.hpp"
 #include "utils.hpp"
-#include <functional>
 
 namespace rtc {
 
 /// Responds to PLI and FIR messages sent by the receiver. The sender should respond to these
-/// messages by sending an intra. 
-class RTC_CPP_EXPORT PliHandler final : public MediaHandlerElement {
+/// messages by sending an intra.
+class RTC_CPP_EXPORT PliHandler final : public MediaHandler {
     rtc::synchronized_callback<> mOnPli;
 
 public:
 	/// Constructs the PLIResponder object to notify whenever a new intra frame is requested
 	/// @param onPli The callback that gets called whenever an intra frame is requested by the receiver
     PliHandler(std::function<void(void)> onPli);
-    ChainedIncomingControlProduct processIncomingControlMessage(message_ptr) override;
+
+	void incoming(message_vector &messages, const message_callback &send) override;
 };
 
 }

+ 7 - 11
include/rtc/rtc.hpp

@@ -27,19 +27,15 @@
 
 #if RTC_ENABLE_MEDIA
 
-// Media handling
-#include "mediachainablehandler.hpp"
+// Media
+#include "av1rtppacketizer.hpp"
+#include "h264rtppacketizer.hpp"
+#include "h265rtppacketizer.hpp"
+#include "mediahandler.hpp"
+#include "plihandler.hpp"
 #include "rtcpnackresponder.hpp"
 #include "rtcpreceivingsession.hpp"
 #include "rtcpsrreporter.hpp"
-
-#include "plihandler.hpp"
-
-// Opus/AAC/h264/h265/AV1 streaming
-#include "aacrtppacketizer.hpp"
-#include "av1packetizationhandler.hpp"
-#include "h264packetizationhandler.hpp"
-#include "h265packetizationhandler.hpp"
-#include "opuspacketizationhandler.hpp"
+#include "rtppacketizer.hpp"
 
 #endif // RTC_ENABLE_MEDIA

+ 15 - 24
include/rtc/rtcpnackresponder.hpp

@@ -11,16 +11,24 @@
 
 #if RTC_ENABLE_MEDIA
 
-#include "mediahandlerelement.hpp"
+#include "mediahandler.hpp"
 
 #include <queue>
 #include <unordered_map>
 
 namespace rtc {
 
-class RTC_CPP_EXPORT RtcpNackResponder final : public MediaHandlerElement {
+class RTC_CPP_EXPORT RtcpNackResponder final : public MediaHandler {
+public:
+	static const size_t DefaultMaxSize = 512;
+
+	RtcpNackResponder(size_t maxSize = DefaultMaxSize);
+
+	void incoming(message_vector &messages, const message_callback &send) override;
+	void outgoing(message_vector &messages, const message_callback &send) override;
 
-	/// Packet storage
+private:
+	// Packet storage
 	class RTC_CPP_EXPORT Storage {
 
 		/// Packet storage element
@@ -42,15 +50,13 @@ class RTC_CPP_EXPORT RtcpNackResponder final : public MediaHandlerElement {
 		std::mutex mutex;
 
 		/// Maximum storage size
-		const unsigned maximumSize;
+		const size_t maxSize;
 
 		/// Returns current size
-		unsigned size();
+		size_t size();
 
 	public:
-		static const unsigned defaultMaximumSize = 512;
-
-		Storage(unsigned _maximumSize);
+		Storage(size_t _maxSize);
 
 		/// Returns packet with given sequence number
 		optional<binary_ptr> get(uint16_t sequenceNumber);
@@ -60,22 +66,7 @@ class RTC_CPP_EXPORT RtcpNackResponder final : public MediaHandlerElement {
 		void store(binary_ptr packet);
 	};
 
-	const shared_ptr<Storage> storage;
-
-public:
-	RtcpNackResponder(unsigned maxStoredPacketCount = Storage::defaultMaximumSize);
-
-	/// Checks for RTCP NACK and handles it,
-	/// @param message RTCP message
-	/// @returns unchanged RTCP message and requested RTP packets
-	ChainedIncomingControlProduct processIncomingControlMessage(message_ptr message) override;
-
-	/// Stores RTP packets in internal storage
-	/// @param messages RTP packets
-	/// @param control RTCP
-	/// @returns Unchanged RTP and RTCP
-	ChainedOutgoingProduct processOutgoingBinaryMessage(ChainedMessagesProduct messages,
-	                                                    message_ptr control) override;
+	const shared_ptr<Storage> mStorage;
 };
 
 } // namespace rtc

+ 15 - 10
include/rtc/rtcpreceivingsession.hpp

@@ -17,29 +17,34 @@
 #include "message.hpp"
 #include "rtp.hpp"
 
+#include <atomic>
+
 namespace rtc {
 
 // An RtcpSession can be plugged into a Track to handle the whole RTCP session
 class RTC_CPP_EXPORT RtcpReceivingSession : public MediaHandler {
 public:
-	message_ptr incoming(message_ptr ptr) override;
-	message_ptr outgoing(message_ptr ptr) override;
-	bool send(message_ptr ptr);
+	RtcpReceivingSession() = default;
+	virtual ~RtcpReceivingSession() = default;
 
-	void requestBitrate(unsigned int newBitrate);
+	void incoming(message_vector &messages, const message_callback &send) override;
+	bool requestKeyframe(const message_callback &send) override;
+	bool requestBitrate(unsigned int bitrate, const message_callback &send) override;
 
-	bool requestKeyframe() override;
+	// For backward compatibility
+	[[deprecated("Use Track::requestKeyframe()")]] inline bool requestKeyframe() { return false; };
+	[[deprecated("Use Track::requestBitrate()")]] inline void requestBitrate(unsigned int) {};
 
 protected:
-	void pushREMB(unsigned int bitrate);
-	void pushRR(unsigned int lastSR_delay);
-
-	void pushPLI();
+	void pushREMB(const message_callback &send, unsigned int bitrate);
+	void pushRR(const message_callback &send,unsigned int lastSrDelay);
+	void pushPLI(const message_callback &send);
 
-	unsigned int mRequestedBitrate = 0;
 	SSRC mSsrc = 0;
 	uint32_t mGreatestSeqNo = 0;
 	uint64_t mSyncRTPTS, mSyncNTPTS;
+
+	std::atomic<unsigned int> mRequestedBitrate = 0;
 };
 
 } // namespace rtc

+ 14 - 15
include/rtc/rtcpsrreporter.hpp

@@ -6,34 +6,33 @@
  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
  */
 
-#ifndef RTC_RTCP_SENDER_REPORTABLE_H
-#define RTC_RTCP_SENDER_REPORTABLE_H
+#ifndef RTC_RTCP_SR_REPORTER_H
+#define RTC_RTCP_SR_REPORTER_H
 
 #if RTC_ENABLE_MEDIA
 
-#include "mediahandlerelement.hpp"
-#include "message.hpp"
+#include "mediahandler.hpp"
 #include "rtppacketizationconfig.hpp"
+#include "rtp.hpp"
 
 namespace rtc {
 
-class RTC_CPP_EXPORT RtcpSrReporter final : public MediaHandlerElement {
-	void addToReport(RtpHeader *rtp, uint32_t rtpSize);
-	message_ptr getSenderReport(uint32_t timestamp);
-
+class RTC_CPP_EXPORT RtcpSrReporter final : public MediaHandler {
 public:
-	/// RTP configuration
-	const shared_ptr<RtpPacketizationConfig> rtpConfig;
-
 	RtcpSrReporter(shared_ptr<RtpPacketizationConfig> rtpConfig);
 
-	ChainedOutgoingProduct processOutgoingBinaryMessage(ChainedMessagesProduct messages,
-	                                                    message_ptr control) override;
-
 	uint32_t lastReportedTimestamp() const;
 	void setNeedsToReport();
 
+	void outgoing(message_vector &messages, const message_callback &send) override;
+
+	// TODO: remove this
+	const shared_ptr<RtpPacketizationConfig> rtpConfig;
+
 private:
+	void addToReport(RtpHeader *rtp, uint32_t rtpSize);
+	message_ptr getSenderReport(uint32_t timestamp);
+
 	uint32_t mPacketCount = 0;
 	uint32_t mPayloadOctets = 0;
 	uint32_t mLastReportedTimestamp = 0;
@@ -44,4 +43,4 @@ private:
 
 #endif /* RTC_ENABLE_MEDIA */
 
-#endif /* RTC_RTCP_SENDER_REPORTABLE_H */
+#endif /* RTC_RTCP_SR_REPORTER_H */

+ 17 - 17
include/rtc/rtp.hpp

@@ -355,23 +355,23 @@ struct RTC_CPP_EXPORT RtpRtx {
 };
 
 // For backward compatibility, do not use
-using RTP_ExtensionHeader = RtpExtensionHeader;
-using RTP = RtpHeader;
-using RTCP_ReportBlock = RtcpReportBlock;
-using RTCP_HEADER = RtcpHeader;
-using RTCP_FB_HEADER = RtcpFbHeader;
-using RTCP_SR = RtcpSr;
-using RTCP_SDES_ITEM = RtcpSdesItem;
-using RTCP_SDES_CHUNK = RtcpSdesChunk;
-using RTCP_SDES = RtcpSdes;
-using RTCP_RR = RtcpRr;
-using RTCP_REMB = RtcpRemb;
-using RTCP_PLI = RtcpPli;
-using RTCP_FIR_PART = RtcpFirPart;
-using RTCP_FIR = RtcpFir;
-using RTCP_NACK_PART = RtcpNackPart;
-using RTCP_NACK = RtcpNack;
-using RTP_RTX = RtpRtx;
+using RTP_ExtensionHeader [[deprecated]] = RtpExtensionHeader;
+using RTP [[deprecated]] = RtpHeader;
+using RTCP_ReportBlock [[deprecated]] = RtcpReportBlock;
+using RTCP_HEADER [[deprecated]] = RtcpHeader;
+using RTCP_FB_HEADER [[deprecated]] = RtcpFbHeader;
+using RTCP_SR [[deprecated]] = RtcpSr;
+using RTCP_SDES_ITEM [[deprecated]] = RtcpSdesItem;
+using RTCP_SDES_CHUNK [[deprecated]] = RtcpSdesChunk;
+using RTCP_SDES [[deprecated]] = RtcpSdes;
+using RTCP_RR [[deprecated]] = RtcpRr;
+using RTCP_REMB [[deprecated]] = RtcpRemb;
+using RTCP_PLI [[deprecated]] = RtcpPli;
+using RTCP_FIR_PART [[deprecated]] = RtcpFirPart;
+using RTCP_FIR [[deprecated]] = RtcpFir;
+using RTCP_NACK_PART [[deprecated]] = RtcpNackPart;
+using RTCP_NACK [[deprecated]] = RtcpNack;
+using RTP_RTX [[deprecated]] = RtpRtx;
 
 #pragma pack(pop)
 

+ 4 - 4
include/rtc/rtppacketizationconfig.hpp

@@ -19,10 +19,10 @@ namespace rtc {
 class RTC_CPP_EXPORT RtpPacketizationConfig {
 public:
 	SSRC ssrc;
-	const std::string cname;
-	const uint8_t payloadType;
-	const uint32_t clockRate;
-	const uint8_t videoOrientationId;
+	std::string cname;
+	uint8_t payloadType;
+	uint32_t clockRate;
+	uint8_t videoOrientationId;
 
 	// current sequence number
 	uint16_t sequenceNumber;

+ 54 - 11
include/rtc/rtppacketizer.hpp

@@ -11,33 +11,76 @@
 
 #if RTC_ENABLE_MEDIA
 
+#include "mediahandler.hpp"
 #include "message.hpp"
 #include "rtppacketizationconfig.hpp"
 
 namespace rtc {
 
-/// Class responsible for RTP packetization
-class RTC_CPP_EXPORT RtpPacketizer {
-	static const auto rtpHeaderSize = 12;
-	static const auto rtpExtHeaderCvoSize = 8;
-
+/// RTP packetizer
+class RTC_CPP_EXPORT RtpPacketizer : public MediaHandler {
 public:
-	// RTP configuration
-	const shared_ptr<RtpPacketizationConfig> rtpConfig;
-
-	/// Constructs packetizer with given RTP configuration.
+	/// 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(shared_ptr<RtpPacketizationConfig> rtpConfig);
+	virtual ~RtpPacketizer();
 
-	/// Creates RTP packet for given payload based on `rtpConfig`.
+	virtual void media(const Description::Media &desc) override;
+	virtual void outgoing(message_vector &messages, const message_callback &send) override;
+
+	/// RTP packetization config
+	const shared_ptr<RtpPacketizationConfig> rtpConfig;
+
+protected:
+	/// Creates RTP packet for given payload
 	/// @note This function increase sequence number after packetization.
 	/// @param payload RTP payload
 	/// @param setMark Set marker flag in RTP packet if true
-	virtual shared_ptr<binary> packetize(shared_ptr<binary> payload, bool setMark);
+	virtual message_ptr packetize(shared_ptr<binary> payload, bool mark);
+
+private:
+	static const auto RtpHeaderSize = 12;
+	static const auto RtpExtHeaderCvoSize = 8;
+};
+
+// Generic audio RTP packetizer
+template <uint32_t DEFAULT_CLOCK_RATE>
+class RTC_CPP_EXPORT AudioRtpPacketizer final : public RtpPacketizer {
+public:
+	inline static const uint32_t DefaultClockRate = DEFAULT_CLOCK_RATE;
+	inline static const uint32_t defaultClockRate [[deprecated("Use DefaultClockRate")]] =
+	    DEFAULT_CLOCK_RATE; // for backward compatibility
+
+	AudioRtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig)
+	    : RtpPacketizer(std::move(rtpConfig)) {}
 };
 
+// Audio RTP packetizers
+using OpusRtpPacketizer = AudioRtpPacketizer<48000>;
+using AACRtpPacketizer = AudioRtpPacketizer<48000>;
+
+// Dummy wrapper for backward compatibility, do not use
+class RTC_CPP_EXPORT PacketizationHandler final : public MediaHandler {
+public:
+	PacketizationHandler(shared_ptr<RtpPacketizer> packetizer)
+	    : mPacketizer(std::move(packetizer)) {}
+
+	inline void outgoing(message_vector &messages, const message_callback &send) {
+		return mPacketizer->outgoing(messages, send);
+	}
+
+private:
+	shared_ptr<RtpPacketizer> mPacketizer;
+};
+
+// Audio packetization handlers for backward compatibility, do not use
+using OpusPacketizationHandler [[deprecated("Add OpusRtpPacketizer directly")]] =
+    PacketizationHandler;
+using AACPacketizationHandler [[deprecated("Add AACRtpPacketizer directly")]] =
+    PacketizationHandler;
+
 } // namespace rtc
 
 #endif /* RTC_ENABLE_MEDIA */

+ 2 - 0
include/rtc/track.hpp

@@ -42,8 +42,10 @@ public:
 	size_t maxMessageSize() const override;
 
 	bool requestKeyframe();
+	bool requestBitrate(unsigned int bitrate);
 
 	void setMediaHandler(shared_ptr<MediaHandler> handler);
+	void chainMediaHandler(shared_ptr<MediaHandler> handler);
 	shared_ptr<MediaHandler> getMediaHandler();
 
 	// Deprecated, use setMediaHandler() and getMediaHandler()

+ 0 - 38
src/aacrtppacketizer.cpp

@@ -1,38 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#if RTC_ENABLE_MEDIA
-
-#include "aacrtppacketizer.hpp"
-
-#include <cassert>
-
-namespace rtc {
-
-AACRtpPacketizer::AACRtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig)
-    : RtpPacketizer(rtpConfig), MediaHandlerRootElement() {}
-
-binary_ptr AACRtpPacketizer::packetize(binary_ptr payload, [[maybe_unused]] bool setMark) {
-	assert(!setMark);
-	return RtpPacketizer::packetize(payload, false);
-}
-
-ChainedOutgoingProduct
-AACRtpPacketizer::processOutgoingBinaryMessage(ChainedMessagesProduct messages,
-                                               message_ptr control) {
-	ChainedMessagesProduct packets = make_chained_messages_product();
-	packets->reserve(messages->size());
-	for (auto message : *messages) {
-		packets->push_back(packetize(message, false));
-	}
-	return {packets, control};
-}
-
-} // namespace rtc
-
-#endif /* RTC_ENABLE_MEDIA */

+ 0 - 20
src/av1packetizationhandler.cpp

@@ -1,20 +0,0 @@
-/**
- * Copyright (c) 2023 Paul-Louis Ageneau
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#if RTC_ENABLE_MEDIA
-
-#include "av1packetizationhandler.hpp"
-
-namespace rtc {
-
-AV1PacketizationHandler::AV1PacketizationHandler(shared_ptr<AV1RtpPacketizer> packetizer)
-    : MediaChainableHandler(packetizer) {}
-
-} // namespace rtc
-
-#endif /* RTC_ENABLE_MEDIA */

+ 16 - 21
src/av1rtppacketizer.cpp

@@ -188,41 +188,36 @@ std::vector<binary_ptr> AV1RtpPacketizer::packetizeObu(binary_ptr message,
 AV1RtpPacketizer::AV1RtpPacketizer(AV1RtpPacketizer::Packetization packetization,
                                    shared_ptr<RtpPacketizationConfig> rtpConfig,
                                    uint16_t maximumFragmentSize)
-    : RtpPacketizer(rtpConfig), MediaHandlerRootElement(), maximumFragmentSize(maximumFragmentSize),
+    : RtpPacketizer(rtpConfig), maximumFragmentSize(maximumFragmentSize),
       packetization(packetization) {}
 
-ChainedOutgoingProduct
-AV1RtpPacketizer::processOutgoingBinaryMessage(ChainedMessagesProduct messages,
-                                               message_ptr control) {
-	ChainedMessagesProduct packets = std::make_shared<std::vector<binary_ptr>>();
-	for (auto message : *messages) {
+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 payloads = packetizeObu(obu, maximumFragmentSize);
-			if (payloads.size() == 0) {
-				continue;
-			}
-
-			unsigned i = 0;
-			for (; i < payloads.size() - 1; i++) {
-				packets->push_back(packetize(payloads[i], false));
-			}
-			packets->push_back(packetize(payloads[i], true));
+			auto p = packetizeObu(obu, maximumFragmentSize);
+			fragments.insert(fragments.end(), p.begin(), p.end());
 		}
-	}
 
-	if (packets->size() == 0) {
-		return ChainedOutgoingProduct();
+		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));
 	}
 
-	return {packets, control};
+	messages.swap(result);
 }
 
 } // namespace rtc

+ 46 - 80
src/capi.cpp

@@ -29,7 +29,6 @@ std::unordered_map<int, shared_ptr<PeerConnection>> peerConnectionMap;
 std::unordered_map<int, shared_ptr<DataChannel>> dataChannelMap;
 std::unordered_map<int, shared_ptr<Track>> trackMap;
 #if RTC_ENABLE_MEDIA
-std::unordered_map<int, shared_ptr<MediaChainableHandler>> rtcpChainableHandlerMap;
 std::unordered_map<int, shared_ptr<RtcpSrReporter>> rtcpSrReporterMap;
 std::unordered_map<int, shared_ptr<RtpPacketizationConfig>> rtpConfigMap;
 #endif
@@ -120,7 +119,6 @@ void eraseTrack(int tr) {
 		throw std::invalid_argument("Track ID does not exist");
 #if RTC_ENABLE_MEDIA
 	rtcpSrReporterMap.erase(tr);
-	rtcpChainableHandlerMap.erase(tr);
 	rtpConfigMap.erase(tr);
 #endif
 	userPointerMap.erase(tr);
@@ -133,8 +131,7 @@ size_t eraseAll() {
 	trackMap.clear();
 	peerConnectionMap.clear();
 #if RTC_ENABLE_MEDIA
-	count += rtcpChainableHandlerMap.size() + rtcpSrReporterMap.size() + rtpConfigMap.size();
-	rtcpChainableHandlerMap.clear();
+	count += rtcpSrReporterMap.size() + rtpConfigMap.size();
 	rtcpSrReporterMap.clear();
 	rtpConfigMap.clear();
 #endif
@@ -170,7 +167,6 @@ void eraseChannel(int id) {
 		userPointerMap.erase(id);
 #if RTC_ENABLE_MEDIA
 		rtcpSrReporterMap.erase(id);
-		rtcpChainableHandlerMap.erase(id);
 		rtpConfigMap.erase(id);
 #endif
 		return;
@@ -253,20 +249,6 @@ void emplaceRtcpSrReporter(shared_ptr<RtcpSrReporter> ptr, int tr) {
 	rtcpSrReporterMap.emplace(std::make_pair(tr, ptr));
 }
 
-shared_ptr<MediaChainableHandler> getMediaChainableHandler(int id) {
-	std::lock_guard lock(mutex);
-	if (auto it = rtcpChainableHandlerMap.find(id); it != rtcpChainableHandlerMap.end()) {
-		return it->second;
-	} else {
-		throw std::invalid_argument("RTCP chainable handler ID does not exist");
-	}
-}
-
-void emplaceMediaChainableHandler(shared_ptr<MediaChainableHandler> ptr, int tr) {
-	std::lock_guard lock(mutex);
-	rtcpChainableHandlerMap.emplace(std::make_pair(tr, ptr));
-}
-
 shared_ptr<RtpPacketizationConfig> getRtpConfig(int id) {
 	std::lock_guard lock(mutex);
 	if (auto it = rtpConfigMap.find(id); it != rtpConfigMap.end()) {
@@ -303,31 +285,30 @@ public:
 	MediaInterceptor(MessageCallback cb) : incomingCallback(cb) {}
 
 	// Called when there is traffic coming from the peer
-	message_ptr incoming(message_ptr msg) override {
+	void incoming(message_vector &messages,
+	              [[maybe_unused]] const message_callback &send) override {
 		// If no callback is provided, just forward the message on
-		if (!incomingCallback) {
-			return msg;
-		}
+		if (!incomingCallback)
+			return;
 
-		auto res = incomingCallback(reinterpret_cast<void *>(msg->data()), int(msg->size()));
+		message_vector result;
+		for (auto &msg : messages) {
+			auto res = incomingCallback(reinterpret_cast<void *>(msg->data()), int(msg->size()));
 
-		// If a null pointer was returned, drop the incoming message
-		if (res == nullptr) {
-			return nullptr;
-		}
+			// If a null pointer was returned, drop the incoming message
+			if (!res)
+				continue;
 
-		// If the original data pointer was returned, forward the incoming message
-		if (res == msg->data()) {
-			return msg;
+			if (res == msg->data()) {
+				// If the original data pointer was returned, forward the incoming message
+				result.push_back(std::move(msg));
+			} else {
+				// else construct a true message_ptr from the returned opaque pointer
+				result.push_back(
+				    make_message_from_opaque_ptr(std::move(reinterpret_cast<rtcMessage *>(res))));
+			}
 		}
-
-		// Construct a true message_ptr from the returned opaque pointer
-		return make_message_from_opaque_ptr(std::move(reinterpret_cast<rtcMessage *>(res)));
-	};
-
-	// Called when there is traffic that needs to be sent to the peer
-	// This is a no-op for media interceptors
-	message_ptr outgoing(message_ptr ptr) override { return ptr; };
+	}
 
 private:
 	MessageCallback incomingCallback;
@@ -1224,18 +1205,14 @@ int rtcSetH264PacketizationHandler(int tr, const rtcPacketizationHandlerInit *in
 		auto track = getTrack(tr);
 		// create RTP configuration
 		auto rtpConfig = createRtpPacketizationConfig(init);
+		emplaceRtpConfig(rtpConfig, tr);
 		// create packetizer
 		auto nalSeparator = init ? init->nalSeparator : RTC_NAL_SEPARATOR_LENGTH;
 		auto maxFragmentSize = init && init->maxFragmentSize ? init->maxFragmentSize
 		                                                     : RTC_DEFAULT_MAXIMUM_FRAGMENT_SIZE;
 		auto packetizer = std::make_shared<H264RtpPacketizer>(
 		    static_cast<rtc::NalUnit::Separator>(nalSeparator), rtpConfig, maxFragmentSize);
-		// create H264 handler
-		auto h264Handler = std::make_shared<H264PacketizationHandler>(packetizer);
-		emplaceMediaChainableHandler(h264Handler, tr);
-		emplaceRtpConfig(rtpConfig, tr);
-		// set handler
-		track->setMediaHandler(h264Handler);
+		track->setMediaHandler(packetizer);
 		return RTC_ERR_SUCCESS;
 	});
 }
@@ -1251,12 +1228,7 @@ int rtcSetH265PacketizationHandler(int tr, const rtcPacketizationHandlerInit *in
 		                                                     : RTC_DEFAULT_MAXIMUM_FRAGMENT_SIZE;
 		auto packetizer = std::make_shared<H265RtpPacketizer>(
 		    static_cast<rtc::NalUnit::Separator>(nalSeparator), rtpConfig, maxFragmentSize);
-		// create H265 handler
-		auto h265Handler = std::make_shared<H265PacketizationHandler>(packetizer);
-		emplaceMediaChainableHandler(h265Handler, tr);
-		emplaceRtpConfig(rtpConfig, tr);
-		// set handler
-		track->setMediaHandler(h265Handler);
+		track->setMediaHandler(packetizer);
 		return RTC_ERR_SUCCESS;
 	});
 }
@@ -1274,12 +1246,7 @@ int rtcSetAV1PacketizationHandler(int tr, const rtcPacketizationHandlerInit *ini
 		                         : AV1RtpPacketizer::Packetization::Obu;
 		auto packetizer =
 		    std::make_shared<AV1RtpPacketizer>(packetization, rtpConfig, maxFragmentSize);
-		// create AV1 handler
-		auto av1Handler = std::make_shared<AV1PacketizationHandler>(packetizer);
-		emplaceMediaChainableHandler(av1Handler, tr);
-		emplaceRtpConfig(rtpConfig, tr);
-		// set handler
-		track->setMediaHandler(av1Handler);
+		track->setMediaHandler(packetizer);
 		return RTC_ERR_SUCCESS;
 	});
 }
@@ -1289,14 +1256,10 @@ int rtcSetOpusPacketizationHandler(int tr, const rtcPacketizationHandlerInit *in
 		auto track = getTrack(tr);
 		// create RTP configuration
 		auto rtpConfig = createRtpPacketizationConfig(init);
+		emplaceRtpConfig(rtpConfig, tr);
 		// create packetizer
 		auto packetizer = std::make_shared<OpusRtpPacketizer>(rtpConfig);
-		// create Opus handler
-		auto opusHandler = std::make_shared<OpusPacketizationHandler>(packetizer);
-		emplaceMediaChainableHandler(opusHandler, tr);
-		emplaceRtpConfig(rtpConfig, tr);
-		// set handler
-		track->setMediaHandler(opusHandler);
+		track->setMediaHandler(packetizer);
 		return RTC_ERR_SUCCESS;
 	});
 }
@@ -1308,44 +1271,39 @@ int rtcSetAACPacketizationHandler(int tr, const rtcPacketizationHandlerInit *ini
 		auto rtpConfig = createRtpPacketizationConfig(init);
 		// create packetizer
 		auto packetizer = std::make_shared<AACRtpPacketizer>(rtpConfig);
-		// create AAC handler
-		auto aacHandler = std::make_shared<AACPacketizationHandler>(packetizer);
-		emplaceMediaChainableHandler(aacHandler, tr);
-		emplaceRtpConfig(rtpConfig, tr);
-		// set handler
-		track->setMediaHandler(aacHandler);
+		track->setMediaHandler(packetizer);
 		return RTC_ERR_SUCCESS;
 	});
 }
 
 int rtcChainRtcpSrReporter(int tr) {
 	return wrap([&] {
+		auto track = getTrack(tr);
 		auto config = getRtpConfig(tr);
 		auto reporter = std::make_shared<RtcpSrReporter>(config);
+		track->chainMediaHandler(reporter);
 		emplaceRtcpSrReporter(reporter, tr);
-		auto chainableHandler = getMediaChainableHandler(tr);
-		chainableHandler->addToChain(reporter);
 		return RTC_ERR_SUCCESS;
 	});
 }
 
 int rtcChainRtcpNackResponder(int tr, unsigned int maxStoredPacketsCount) {
 	return wrap([&] {
+		auto track = getTrack(tr);
 		auto responder = std::make_shared<RtcpNackResponder>(maxStoredPacketsCount);
-		auto chainableHandler = getMediaChainableHandler(tr);
-		chainableHandler->addToChain(responder);
+		track->chainMediaHandler(responder);
 		return RTC_ERR_SUCCESS;
 	});
 }
 
 int rtcChainPliHandler(int tr, rtcPliHandlerCallbackFunc cb) {
 	return wrap([&] {
-		auto pliHandler = std::make_shared<PliHandler>([tr, cb] {
+		auto track = getTrack(tr);
+		auto handler = std::make_shared<PliHandler>([tr, cb] {
 			if (auto ptr = getUserPointer(tr))
 				cb(tr, *ptr);
 		});
-		auto chainableHandler = getMediaChainableHandler(tr);
-		chainableHandler->addToChain(pliHandler);
+		track->chainMediaHandler(handler);
 		return RTC_ERR_SUCCESS;
 	});
 }
@@ -1353,7 +1311,9 @@ int rtcChainPliHandler(int tr, rtcPliHandlerCallbackFunc cb) {
 int rtcTransformSecondsToTimestamp(int id, double seconds, uint32_t *timestamp) {
 	return wrap([&] {
 		auto config = getRtpConfig(id);
-		*timestamp = config->secondsToTimestamp(seconds);
+		if (timestamp)
+			*timestamp = config->secondsToTimestamp(seconds);
+
 		return RTC_ERR_SUCCESS;
 	});
 }
@@ -1361,7 +1321,9 @@ int rtcTransformSecondsToTimestamp(int id, double seconds, uint32_t *timestamp)
 int rtcTransformTimestampToSeconds(int id, uint32_t timestamp, double *seconds) {
 	return wrap([&] {
 		auto config = getRtpConfig(id);
-		*seconds = config->timestampToSeconds(timestamp);
+		if (seconds)
+			*seconds = config->timestampToSeconds(timestamp);
+
 		return RTC_ERR_SUCCESS;
 	});
 }
@@ -1369,7 +1331,9 @@ int rtcTransformTimestampToSeconds(int id, uint32_t timestamp, double *seconds)
 int rtcGetCurrentTrackTimestamp(int id, uint32_t *timestamp) {
 	return wrap([&] {
 		auto config = getRtpConfig(id);
-		*timestamp = config->timestamp;
+		if (timestamp)
+			*timestamp = config->timestamp;
+
 		return RTC_ERR_SUCCESS;
 	});
 }
@@ -1385,7 +1349,9 @@ int rtcSetTrackRtpTimestamp(int id, uint32_t timestamp) {
 int rtcGetLastTrackSenderReportTimestamp(int id, uint32_t *timestamp) {
 	return wrap([&] {
 		auto sender = getRtcpSrReporter(id);
-		*timestamp = sender->lastReportedTimestamp();
+		if (timestamp)
+			*timestamp = sender->lastReportedTimestamp();
+
 		return RTC_ERR_SUCCESS;
 	});
 }

+ 16 - 0
src/description.cpp

@@ -583,6 +583,14 @@ Description::Entry::ExtMap *Description::Entry::extMap(int id) {
 	return &it->second;
 }
 
+const Description::Entry::ExtMap *Description::Entry::extMap(int id) const {
+	auto it = mExtMaps.find(id);
+	if (it == mExtMaps.end())
+		throw std::invalid_argument("extmap not found");
+
+	return &it->second;
+}
+
 void Description::Entry::addExtMap(ExtMap map) {
 	auto id = map.id;
 	mExtMaps.emplace(id, std::move(map));
@@ -953,6 +961,14 @@ Description::Media::RtpMap *Description::Media::rtpMap(int payloadType) {
 	return &it->second;
 }
 
+const Description::Media::RtpMap *Description::Media::rtpMap(int payloadType) const {
+	auto it = mRtpMaps.find(payloadType);
+	if (it == mRtpMaps.end())
+		throw std::invalid_argument("rtpmap not found");
+
+	return &it->second;
+}
+
 void Description::Media::addRtpMap(RtpMap map) {
 	auto payloadType = map.payloadType;
 	mRtpMaps.emplace(payloadType, std::move(map));

+ 0 - 20
src/h264packetizationhandler.cpp

@@ -1,20 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#if RTC_ENABLE_MEDIA
-
-#include "h264packetizationhandler.hpp"
-
-namespace rtc {
-
-H264PacketizationHandler::H264PacketizationHandler(shared_ptr<H264RtpPacketizer> packetizer)
-    : MediaChainableHandler(packetizer) {}
-
-} // namespace rtc
-
-#endif /* RTC_ENABLE_MEDIA */

+ 15 - 18
src/h264rtppacketizer.cpp

@@ -82,32 +82,29 @@ shared_ptr<NalUnits> H264RtpPacketizer::splitMessage(binary_ptr message) {
 
 H264RtpPacketizer::H264RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig,
                                      uint16_t maximumFragmentSize)
-    : RtpPacketizer(rtpConfig), MediaHandlerRootElement(), maximumFragmentSize(maximumFragmentSize),
+    : RtpPacketizer(std::move(rtpConfig)), maximumFragmentSize(maximumFragmentSize),
       separator(Separator::Length) {}
 
 H264RtpPacketizer::H264RtpPacketizer(Separator separator,
                                      shared_ptr<RtpPacketizationConfig> rtpConfig,
                                      uint16_t maximumFragmentSize)
-    : RtpPacketizer(rtpConfig), MediaHandlerRootElement(), maximumFragmentSize(maximumFragmentSize),
-      separator(separator) {}
-
-ChainedOutgoingProduct
-H264RtpPacketizer::processOutgoingBinaryMessage(ChainedMessagesProduct messages,
-                                                message_ptr control) {
-	ChainedMessagesProduct packets = std::make_shared<std::vector<binary_ptr>>();
-	for (auto message : *messages) {
+    : RtpPacketizer(rtpConfig), maximumFragmentSize(maximumFragmentSize), 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(maximumFragmentSize);
-		if (fragments.size() == 0) {
-			return ChainedOutgoingProduct();
-		}
-		unsigned i = 0;
-		for (; i < fragments.size() - 1; i++) {
-			packets->push_back(packetize(fragments[i], false));
-		}
-		packets->push_back(packetize(fragments[i], true));
+		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));
 	}
-	return {packets, control};
+
+	messages.swap(result);
 }
 
 } // namespace rtc

+ 0 - 20
src/h265packetizationhandler.cpp

@@ -1,20 +0,0 @@
-/**
- * Copyright (c) 2023 Zita Liao (Dolby)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#if RTC_ENABLE_MEDIA
-
-#include "h265packetizationhandler.hpp"
-
-namespace rtc {
-
-H265PacketizationHandler::H265PacketizationHandler(shared_ptr<H265RtpPacketizer> packetizer)
-    : MediaChainableHandler(packetizer) {}
-
-} // namespace rtc
-
-#endif /* RTC_ENABLE_MEDIA */

+ 14 - 16
src/h265rtppacketizer.cpp

@@ -82,32 +82,30 @@ shared_ptr<H265NalUnits> H265RtpPacketizer::splitMessage(binary_ptr message) {
 
 H265RtpPacketizer::H265RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig,
                                      uint16_t maximumFragmentSize)
-    : RtpPacketizer(rtpConfig), MediaHandlerRootElement(), maximumFragmentSize(maximumFragmentSize),
+    : RtpPacketizer(std::move(rtpConfig)), maximumFragmentSize(maximumFragmentSize),
       separator(NalUnit::Separator::Length) {}
 
 H265RtpPacketizer::H265RtpPacketizer(NalUnit::Separator separator,
                                      shared_ptr<RtpPacketizationConfig> rtpConfig,
                                      uint16_t maximumFragmentSize)
-    : RtpPacketizer(rtpConfig), MediaHandlerRootElement(), maximumFragmentSize(maximumFragmentSize),
+    : RtpPacketizer(std::move(rtpConfig)), maximumFragmentSize(maximumFragmentSize),
       separator(separator) {}
 
-ChainedOutgoingProduct
-H265RtpPacketizer::processOutgoingBinaryMessage(ChainedMessagesProduct messages,
-                                                message_ptr control) {
-	ChainedMessagesProduct packets = std::make_shared<std::vector<binary_ptr>>();
-	for (auto message : *messages) {
+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(maximumFragmentSize);
-		if (fragments.size() == 0) {
-			return ChainedOutgoingProduct();
-		}
-		unsigned i = 0;
-		for (; i < fragments.size() - 1; i++) {
-			packets->push_back(packetize(fragments[i], false));
-		}
-		packets->push_back(packetize(fragments[i], true));
+		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));
 	}
-	return {packets, control};
+
+	messages.swap(result);
 }
 
 } // namespace rtc

+ 29 - 8
src/impl/peerconnection.cpp

@@ -493,18 +493,32 @@ void PeerConnection::forwardMessage(message_ptr message) {
 	}
 }
 
-void PeerConnection::forwardMedia(message_ptr message) {
+void PeerConnection::forwardMedia([[maybe_unused]] message_ptr message) {
+#if RTC_ENABLE_MEDIA
 	if (!message)
 		return;
 
-	auto handler = getMediaHandler();
+	// TODO: outgoing
+	if (auto handler = getMediaHandler()) {
+		message_vector messages{std::move(message)};
 
-	if (handler) {
-		message = handler->incoming(message);
-		if (!message)
-			return;
+		handler->incoming(messages, [this](message_ptr message) {
+			auto transport = std::atomic_load(&mDtlsTransport);
+			if (auto srtpTransport = std::dynamic_pointer_cast<DtlsSrtpTransport>(transport))
+				srtpTransport->send(std::move(message));
+		});
+
+		for (auto &m : messages)
+			dispatchMedia(std::move(m));
+
+	} else {
+		dispatchMedia(std::move(message));
 	}
+#endif
+}
 
+void PeerConnection::dispatchMedia([[maybe_unused]] message_ptr message) {
+#if RTC_ENABLE_MEDIA
 	// Browsers like to compound their packets with a random SSRC.
 	// we have to do this monstrosity to distribute the report blocks
 	if (message->type == Message::Control) {
@@ -581,6 +595,7 @@ void PeerConnection::forwardMedia(message_ptr message) {
 		// PLOG_WARNING << "Track not found for SSRC " << ssrc << ", dropping";
 		return;
 	}
+#endif
 }
 
 void PeerConnection::forwardBufferedAmount(uint16_t stream, size_t amount) {
@@ -741,6 +756,10 @@ shared_ptr<Track> PeerConnection::emplaceTrack(Description::Media description) {
 		mTrackLines.emplace_back(track);
 	}
 
+	auto handler = getMediaHandler();
+	if (handler)
+		handler->media(track->description());
+
 	if (track->description().isRemoved())
 		track->close();
 
@@ -910,6 +929,10 @@ void PeerConnection::processLocalDescription(Description description) {
 				        mTrackLines.emplace_back(track);
 				        triggerTrack(track); // The user may modify the track description
 
+				        auto handler = getMediaHandler();
+				        if (handler)
+					        handler->media(track->description());
+
 				        if (track->description().isRemoved())
 					        track->close();
 
@@ -1091,8 +1114,6 @@ string PeerConnection::localBundleMid() const {
 
 void PeerConnection::setMediaHandler(shared_ptr<MediaHandler> handler) {
 	std::unique_lock lock(mMediaHandlerMutex);
-	if (mMediaHandler)
-		mMediaHandler->onOutgoing(nullptr);
 	mMediaHandler = handler;
 }
 

+ 1 - 0
src/impl/peerconnection.hpp

@@ -127,6 +127,7 @@ struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
 	synchronized_callback<shared_ptr<rtc::Track>> trackCallback;
 
 private:
+	void dispatchMedia(message_ptr message);
 	void updateTrackSsrcCache(const Description &description);
 
 	const init_token mInitToken = Init::Instance().token();

+ 35 - 31
src/impl/track.cpp

@@ -19,8 +19,8 @@ static LogCounter COUNTER_MEDIA_BAD_DIRECTION(plog::warning,
 static LogCounter COUNTER_QUEUE_FULL(plog::warning,
                                      "Number of media packets dropped due to a full queue");
 
-Track::Track(weak_ptr<PeerConnection> pc, Description::Media description)
-    : mPeerConnection(pc), mMediaDescription(std::move(description)),
+Track::Track(weak_ptr<PeerConnection> pc, Description::Media desc)
+    : mPeerConnection(pc), mMediaDescription(std::move(desc)),
       mRecvQueue(RECV_QUEUE_LIMIT, [](const message_ptr &m) { return m->size(); }) {
 
 	// Discard messages by default if track is send only
@@ -52,12 +52,17 @@ Description::Media Track::description() const {
 	return mMediaDescription;
 }
 
-void Track::setDescription(Description::Media description) {
-	std::unique_lock lock(mMutex);
-	if (description.mid() != mMediaDescription.mid())
-		throw std::logic_error("Media description mid does not match track mid");
+void Track::setDescription(Description::Media desc) {
+	{
+		std::unique_lock lock(mMutex);
+		if (desc.mid() != mMediaDescription.mid())
+			throw std::logic_error("Media description mid does not match track mid");
+
+		mMediaDescription = std::move(desc);
+	}
 
-	mMediaDescription = std::move(description);
+	if (auto handler = getMediaHandler())
+		handler->media(description());
 }
 
 void Track::close() {
@@ -129,8 +134,6 @@ void Track::incoming(message_ptr message) {
 	if (!message)
 		return;
 
-	auto handler = getMediaHandler();
-
 	auto dir = direction();
 	if ((dir == Description::Direction::SendOnly || dir == Description::Direction::Inactive) &&
 	    message->type != Message::Control) {
@@ -138,20 +141,20 @@ void Track::incoming(message_ptr message) {
 		return;
 	}
 
-	if (handler) {
-		message = handler->incoming(message);
-		if (!message)
+	message_vector messages{std::move(message)};
+	if (auto handler = getMediaHandler())
+		handler->incomingChain(messages, [this](message_ptr m) { transportSend(m); });
+
+	for (auto &m : messages) {
+		// Tail drop if queue is full
+		if (mRecvQueue.full()) {
+			COUNTER_QUEUE_FULL++;
 			return;
-	}
+		}
 
-	// Tail drop if queue is full
-	if (mRecvQueue.full()) {
-		COUNTER_QUEUE_FULL++;
-		return;
+		mRecvQueue.push(m);
+		triggerAvailable(mRecvQueue.size());
 	}
-
-	mRecvQueue.push(message);
-	triggerAvailable(mRecvQueue.size());
 }
 
 bool Track::outgoing(message_ptr message) {
@@ -172,12 +175,17 @@ bool Track::outgoing(message_ptr message) {
 	}
 
 	if (handler) {
-		message = handler->outgoing(message);
-		if (!message)
-			return false;
-	}
+		message_vector messages{std::move(message)};
+		handler->outgoingChain(messages, [this](message_ptr m) { transportSend(m); });
+		bool ret = false;
+		for (auto &m : messages)
+			ret = transportSend(std::move(m));
 
-	return transportSend(message);
+		return ret;
+
+	} else {
+		return transportSend(std::move(message));
+	}
 }
 
 bool Track::transportSend([[maybe_unused]] message_ptr message) {
@@ -204,17 +212,13 @@ bool Track::transportSend([[maybe_unused]] message_ptr message) {
 }
 
 void Track::setMediaHandler(shared_ptr<MediaHandler> handler) {
-	auto currentHandler = getMediaHandler();
-	if (currentHandler)
-		currentHandler->onOutgoing(nullptr);
-
 	{
 		std::unique_lock lock(mMutex);
 		mMediaHandler = handler;
 	}
 
-	if (handler)
-		handler->onOutgoing(std::bind(&Track::transportSend, this, std::placeholders::_1));
+	if(handler)
+		handler->media(description());
 }
 
 shared_ptr<MediaHandler> Track::getMediaHandler() {

+ 3 - 3
src/impl/track.hpp

@@ -28,7 +28,7 @@ struct PeerConnection;
 
 class Track final : public std::enable_shared_from_this<Track>, public Channel {
 public:
-	Track(weak_ptr<PeerConnection> pc, Description::Media description);
+	Track(weak_ptr<PeerConnection> pc, Description::Media desc);
 	~Track();
 
 	void close();
@@ -46,7 +46,7 @@ public:
 	string mid() const;
 	Description::Direction direction() const;
 	Description::Media description() const;
-	void setDescription(Description::Media description);
+	void setDescription(Description::Media desc);
 
 	shared_ptr<MediaHandler> getMediaHandler();
 	void setMediaHandler(shared_ptr<MediaHandler> handler);
@@ -55,9 +55,9 @@ public:
 	void open(shared_ptr<DtlsSrtpTransport> transport);
 #endif
 
-private:
 	bool transportSend(message_ptr message);
 
+private:
 	const weak_ptr<PeerConnection> mPeerConnection;
 #if RTC_ENABLE_MEDIA
 	weak_ptr<DtlsSrtpTransport> mDtlsSrtpTransport;

+ 0 - 163
src/mediachainablehandler.cpp

@@ -1,163 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#if RTC_ENABLE_MEDIA
-
-#include "mediachainablehandler.hpp"
-
-#include "impl/internals.hpp"
-
-#include <cassert>
-
-namespace rtc {
-
-MediaChainableHandler::MediaChainableHandler(shared_ptr<MediaHandlerRootElement> root)
-    : MediaHandler(), root(root), leaf(root) {}
-
-MediaChainableHandler::~MediaChainableHandler() { leaf->recursiveRemoveChain(); }
-
-bool MediaChainableHandler::sendProduct(ChainedOutgoingProduct product) {
-	bool result = true;
-	if (product.control) {
-		assert(product.control->type == Message::Control);
-		auto sendResult = send(product.control);
-		if (!sendResult) {
-			LOG_DEBUG << "Failed to send control message";
-		}
-		result = result && sendResult;
-	}
-	if (product.messages) {
-		auto messages = product.messages;
-		for (unsigned i = 0; i < messages->size(); i++) {
-			auto message = messages->at(i);
-			if (!message) {
-				LOG_DEBUG << "Invalid message to send " << i + 1 << "/" << messages->size();
-			}
-			auto sendResult = send(make_message(*message));
-			if (!sendResult) {
-				LOG_DEBUG << "Failed to send message " << i + 1 << "/" << messages->size();
-			}
-			result = result && sendResult;
-		}
-	}
-	return result;
-}
-
-message_ptr MediaChainableHandler::handleIncomingBinary(message_ptr msg) {
-	assert(msg->type == Message::Binary);
-	auto messages = root->split(msg);
-	auto incoming = getLeaf()->formIncomingBinaryMessage(
-	    messages, [this](ChainedOutgoingProduct outgoing) { return sendProduct(outgoing); });
-	if (incoming) {
-		return root->reduce(incoming);
-	} else {
-		return nullptr;
-	}
-}
-
-message_ptr MediaChainableHandler::handleIncomingControl(message_ptr msg) {
-	assert(msg->type == Message::Control);
-	auto incoming = getLeaf()->formIncomingControlMessage(
-	    msg, [this](ChainedOutgoingProduct outgoing) { return sendProduct(outgoing); });
-	assert(!incoming || incoming->type == Message::Control);
-	return incoming;
-}
-
-message_ptr MediaChainableHandler::handleOutgoingBinary(message_ptr msg) {
-	assert(msg->type == Message::Binary);
-	auto messages = make_chained_messages_product(msg);
-	auto optOutgoing = root->formOutgoingBinaryMessage(ChainedOutgoingProduct(messages));
-	if (!optOutgoing.has_value()) {
-		LOG_ERROR << "Generating outgoing message failed";
-		return nullptr;
-	}
-	auto outgoing = optOutgoing.value();
-	if (outgoing.control) {
-		if (!send(outgoing.control)) {
-			LOG_DEBUG << "Failed to send control message";
-		}
-	}
-	auto lastMessage = outgoing.messages->back();
-	if (!lastMessage) {
-		LOG_DEBUG << "Invalid message to send";
-		return nullptr;
-	}
-	for (unsigned i = 0; i < outgoing.messages->size() - 1; i++) {
-		auto message = outgoing.messages->at(i);
-		if (!message) {
-			LOG_DEBUG << "Invalid message to send " << i + 1 << "/" << outgoing.messages->size();
-		}
-		if (!send(make_message(*message))) {
-			LOG_DEBUG << "Failed to send message " << i + 1 << "/" << outgoing.messages->size();
-		}
-	}
-	return make_message(*lastMessage);
-}
-
-message_ptr MediaChainableHandler::handleOutgoingControl(message_ptr msg) {
-	assert(msg->type == Message::Control);
-	auto outgoing = root->formOutgoingControlMessage(msg);
-	assert(!outgoing || outgoing->type == Message::Control);
-	if (!outgoing) {
-		LOG_ERROR << "Generating outgoing control message failed";
-		return nullptr;
-	}
-	return outgoing;
-}
-
-message_ptr MediaChainableHandler::outgoing(message_ptr ptr) {
-	assert(ptr);
-	if (!ptr) {
-		LOG_ERROR << "Outgoing message is nullptr, ignoring";
-		return nullptr;
-	}
-	if (ptr->type == Message::Binary) {
-		return handleOutgoingBinary(ptr);
-	} else if (ptr->type == Message::Control) {
-		return handleOutgoingControl(ptr);
-	}
-	return ptr;
-}
-
-message_ptr MediaChainableHandler::incoming(message_ptr ptr) {
-	if (!ptr) {
-		LOG_ERROR << "Incoming message is nullptr, ignoring";
-		return nullptr;
-	}
-	if (ptr->type == Message::Binary) {
-		return handleIncomingBinary(ptr);
-	} else if (ptr->type == Message::Control) {
-		return handleIncomingControl(ptr);
-	}
-	return ptr;
-}
-
-bool MediaChainableHandler::send(message_ptr msg) {
-	try {
-		outgoingCallback(std::move(msg));
-		return true;
-	} catch (const std::exception &e) {
-		LOG_DEBUG << "Send in RTCP chain handler failed: " << e.what();
-	}
-	return false;
-}
-
-shared_ptr<MediaHandlerElement> MediaChainableHandler::getLeaf() const {
-	std::lock_guard lock(mutex);
-	return leaf;
-}
-
-void MediaChainableHandler::addToChain(shared_ptr<MediaHandlerElement> chainable) {
-	std::lock_guard lock(mutex);
-	assert(leaf);
-	leaf = leaf->chainWith(chainable);
-}
-
-} // namespace rtc
-
-#endif /* RTC_ENABLE_MEDIA */

+ 80 - 0
src/mediahandler.cpp

@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2023 Paul-Louis Ageneau
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include "mediahandler.hpp"
+
+#include "impl/internals.hpp"
+
+namespace rtc {
+
+MediaHandler::MediaHandler() {}
+
+MediaHandler::~MediaHandler() {}
+
+void MediaHandler::addToChain(shared_ptr<MediaHandler> handler) { last()->setNext(handler); }
+
+void MediaHandler::setNext(shared_ptr<MediaHandler> handler) {
+	return std::atomic_store(&mNext, handler);
+}
+
+shared_ptr<MediaHandler> MediaHandler::next() { return std::atomic_load(&mNext); }
+
+shared_ptr<const MediaHandler> MediaHandler::next() const { return std::atomic_load(&mNext); }
+
+shared_ptr<MediaHandler> MediaHandler::last() {
+	if (auto handler = next())
+		return handler->last();
+	else
+		return shared_from_this();
+}
+
+shared_ptr<const MediaHandler> MediaHandler::last() const {
+	if (auto handler = next())
+		return handler->last();
+	else
+		return shared_from_this();
+}
+
+bool MediaHandler::requestKeyframe(const message_callback &send) {
+	// Default implementation is to call next handler
+	if (auto handler = next())
+		return handler->requestKeyframe(send);
+	else
+		return false;
+}
+
+bool MediaHandler::requestBitrate(unsigned int bitrate, const message_callback &send) {
+	// Default implementation is to call next handler
+	if (auto handler = next())
+		return handler->requestBitrate(bitrate, send);
+	else
+		return false;
+}
+
+void MediaHandler::mediaChain(const Description::Media &desc) {
+	media(desc);
+
+	if (auto handler = next())
+		handler->mediaChain(desc);
+}
+
+void MediaHandler::incomingChain(message_vector &messages, const message_callback &send) {
+	if (auto handler = next())
+		handler->incomingChain(messages, send);
+
+	incoming(messages, send);
+}
+
+void MediaHandler::outgoingChain(message_vector &messages, const message_callback &send) {
+	outgoing(messages, send);
+
+	if (auto handler = next())
+		return handler->outgoingChain(messages, send);
+}
+
+} // namespace rtc

+ 0 - 211
src/mediahandlerelement.cpp

@@ -1,211 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#if RTC_ENABLE_MEDIA
-
-#include "mediahandlerelement.hpp"
-
-#include "impl/internals.hpp"
-
-#include <cassert>
-
-namespace rtc {
-
-ChainedMessagesProduct make_chained_messages_product() {
-	return std::make_shared<std::vector<binary_ptr>>();
-}
-
-ChainedMessagesProduct make_chained_messages_product(message_ptr msg) {
-	std::vector<binary_ptr> msgs = {msg};
-	return std::make_shared<std::vector<binary_ptr>>(msgs);
-}
-
-ChainedOutgoingProduct::ChainedOutgoingProduct(ChainedMessagesProduct messages, message_ptr control)
-    : messages(messages), control(control) {}
-
-ChainedIncomingProduct::ChainedIncomingProduct(ChainedMessagesProduct incoming,
-                                               ChainedMessagesProduct outgoing)
-    : incoming(incoming), outgoing(outgoing) {}
-
-ChainedIncomingControlProduct::ChainedIncomingControlProduct(
-    message_ptr incoming, optional<ChainedOutgoingProduct> outgoing)
-    : incoming(incoming), outgoing(outgoing) {}
-
-MediaHandlerElement::MediaHandlerElement() {}
-
-void MediaHandlerElement::removeFromChain() {
-	if (upstream) {
-		upstream->downstream = downstream;
-	}
-	if (downstream) {
-		downstream->upstream = upstream;
-	}
-	upstream = nullptr;
-	downstream = nullptr;
-}
-
-void MediaHandlerElement::recursiveRemoveChain() {
-	if (downstream) {
-		// `recursiveRemoveChain` removes last strong reference to downstream element
-		// we need to keep strong reference to prevent deallocation of downstream element
-		// during `recursiveRemoveChain`
-		auto strongDownstreamPtr = downstream;
-		downstream->recursiveRemoveChain();
-	}
-	removeFromChain();
-}
-
-optional<ChainedOutgoingProduct>
-MediaHandlerElement::processOutgoingResponse(ChainedOutgoingProduct messages) {
-	if (messages.messages) {
-		if (upstream) {
-			auto msgs = upstream->formOutgoingBinaryMessage(
-			    ChainedOutgoingProduct(messages.messages, messages.control));
-			if (msgs.has_value()) {
-				return msgs.value();
-			} else {
-				LOG_ERROR << "Generating outgoing message failed";
-				return nullopt;
-			}
-		} else {
-			return messages;
-		}
-	} else if (messages.control) {
-		if (upstream) {
-			auto control = upstream->formOutgoingControlMessage(messages.control);
-			if (control) {
-				return ChainedOutgoingProduct(nullptr, control);
-			} else {
-				LOG_ERROR << "Generating outgoing control message failed";
-				return nullopt;
-			}
-		} else {
-			return messages;
-		}
-	} else {
-		return ChainedOutgoingProduct();
-	}
-}
-
-void MediaHandlerElement::prepareAndSendResponse(optional<ChainedOutgoingProduct> outgoing,
-                                                 std::function<bool(ChainedOutgoingProduct)> send) {
-	if (outgoing.has_value()) {
-		auto message = outgoing.value();
-		auto response = processOutgoingResponse(message);
-		if (response.has_value()) {
-			if (!send(response.value())) {
-				LOG_DEBUG << "Send failed";
-			}
-		} else {
-			LOG_DEBUG << "No response to send";
-		}
-	}
-}
-
-message_ptr
-MediaHandlerElement::formIncomingControlMessage(message_ptr message,
-                                                std::function<bool(ChainedOutgoingProduct)> send) {
-	assert(message);
-	auto product = processIncomingControlMessage(message);
-	prepareAndSendResponse(product.outgoing, send);
-	if (product.incoming) {
-		if (downstream) {
-			return downstream->formIncomingControlMessage(product.incoming, send);
-		} else {
-			return product.incoming;
-		}
-	} else {
-		return nullptr;
-	}
-}
-
-ChainedMessagesProduct
-MediaHandlerElement::formIncomingBinaryMessage(ChainedMessagesProduct messages,
-                                               std::function<bool(ChainedOutgoingProduct)> send) {
-	assert(messages);
-	auto product = processIncomingBinaryMessage(messages);
-	prepareAndSendResponse(product.outgoing, send);
-	if (product.incoming) {
-		if (downstream) {
-			return downstream->formIncomingBinaryMessage(product.incoming, send);
-		} else {
-			return product.incoming;
-		}
-	} else {
-		return nullptr;
-	}
-}
-
-message_ptr MediaHandlerElement::formOutgoingControlMessage(message_ptr message) {
-	assert(message);
-	auto newMessage = processOutgoingControlMessage(message);
-	assert(newMessage);
-	if (!newMessage) {
-		LOG_ERROR << "Failed to generate outgoing message";
-		return nullptr;
-	}
-	if (upstream) {
-		return upstream->formOutgoingControlMessage(newMessage);
-	} else {
-		return newMessage;
-	}
-}
-
-optional<ChainedOutgoingProduct>
-MediaHandlerElement::formOutgoingBinaryMessage(ChainedOutgoingProduct product) {
-	assert(product.messages && !product.messages->empty());
-	auto newProduct = processOutgoingBinaryMessage(product.messages, product.control);
-	assert(!product.control || newProduct.control);
-	assert(newProduct.messages && !newProduct.messages->empty());
-	if (product.control && !newProduct.control) {
-		LOG_ERROR << "Outgoing message must not remove control message";
-		return nullopt;
-	}
-	if (!newProduct.messages || newProduct.messages->empty()) {
-		LOG_ERROR << "Failed to generate message";
-		return nullopt;
-	}
-	if (upstream) {
-		return upstream->formOutgoingBinaryMessage(newProduct);
-	} else {
-		return newProduct;
-	}
-}
-
-ChainedIncomingControlProduct
-MediaHandlerElement::processIncomingControlMessage(message_ptr messages) {
-	return {messages};
-}
-
-message_ptr MediaHandlerElement::processOutgoingControlMessage(message_ptr messages) {
-	return messages;
-}
-
-ChainedIncomingProduct
-MediaHandlerElement::processIncomingBinaryMessage(ChainedMessagesProduct messages) {
-	return {messages};
-}
-
-ChainedOutgoingProduct
-MediaHandlerElement::processOutgoingBinaryMessage(ChainedMessagesProduct messages,
-                                                  message_ptr control) {
-	return {messages, control};
-}
-
-shared_ptr<MediaHandlerElement>
-MediaHandlerElement::chainWith(shared_ptr<MediaHandlerElement> upstream) {
-	assert(this->upstream == nullptr);
-	assert(upstream->downstream == nullptr);
-	this->upstream = upstream;
-	upstream->downstream = shared_from_this();
-	return upstream;
-}
-
-} // namespace rtc
-
-#endif /* RTC_ENABLE_MEDIA */

+ 0 - 34
src/mediahandlerrootelement.cpp

@@ -1,34 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#if RTC_ENABLE_MEDIA
-
-#include "mediahandlerrootelement.hpp"
-
-namespace rtc {
-
-message_ptr MediaHandlerRootElement::reduce(ChainedMessagesProduct messages) {
-	if (messages && !messages->empty()) {
-		auto msg_ptr = messages->front();
-		if (msg_ptr) {
-			return make_message(*msg_ptr);
-		} else {
-			return nullptr;
-		}
-	} else {
-		return nullptr;
-	}
-}
-
-ChainedMessagesProduct MediaHandlerRootElement::split(message_ptr message) {
-	return make_chained_messages_product(message);
-}
-
-} // namespace rtc
-
-#endif /* RTC_ENABLE_MEDIA */

+ 0 - 20
src/opuspacketizationhandler.cpp

@@ -1,20 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#if RTC_ENABLE_MEDIA
-
-#include "opuspacketizationhandler.hpp"
-
-namespace rtc {
-
-OpusPacketizationHandler::OpusPacketizationHandler(shared_ptr<OpusRtpPacketizer> packetizer)
-    : MediaChainableHandler(packetizer) {}
-
-} // namespace rtc
-
-#endif /* RTC_ENABLE_MEDIA */

+ 0 - 38
src/opusrtppacketizer.cpp

@@ -1,38 +0,0 @@
-/**
- * Copyright (c) 2020 Filip Klembara (in2core)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
-
-#if RTC_ENABLE_MEDIA
-
-#include "opusrtppacketizer.hpp"
-
-#include <cassert>
-
-namespace rtc {
-
-OpusRtpPacketizer::OpusRtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig)
-    : RtpPacketizer(rtpConfig), MediaHandlerRootElement() {}
-
-binary_ptr OpusRtpPacketizer::packetize(binary_ptr payload, [[maybe_unused]] bool setMark) {
-	assert(!setMark);
-	return RtpPacketizer::packetize(payload, false);
-}
-
-ChainedOutgoingProduct
-OpusRtpPacketizer::processOutgoingBinaryMessage(ChainedMessagesProduct messages,
-                                                message_ptr control) {
-	ChainedMessagesProduct packets = make_chained_messages_product();
-	packets->reserve(messages->size());
-	for (auto message : *messages) {
-		packets->push_back(packetize(message, false));
-	}
-	return {packets, control};
-}
-
-} // namespace rtc
-
-#endif /* RTC_ENABLE_MEDIA */

+ 22 - 21
src/plihandler.cpp

@@ -7,38 +7,39 @@
  */
 
 #include "plihandler.hpp"
+#include "rtp.hpp"
 
 #if RTC_ENABLE_MEDIA
 
 namespace rtc {
 
-ChainedIncomingControlProduct PliHandler::processIncomingControlMessage(message_ptr message) {
-	size_t offset = 0;
-
-	while ((sizeof(RtcpHeader) + offset) <= message->size()) {
-		auto header = reinterpret_cast<rtc::RtcpHeader*>(message->data() + offset);
-		uint8_t payload_type = header->payloadType();
-
-		if (payload_type == 196) { 
-			// FIR message, call pli handler anyway
-			mOnPli();
-			break;
-		} else if (payload_type == 206) {
-			// On a payload specific fb message, there is a "feedback message type" (FMT) in the
-			// header instead of a report count. PT = 206, FMT = 1 means a PLI message
-			uint8_t feedback_message_type = header->reportCount();
-			if (feedback_message_type == 1) {
+PliHandler::PliHandler(std::function<void(void)> onPli) : mOnPli(onPli) {}
+
+void PliHandler::incoming(message_vector &messages, [[maybe_unused]] const message_callback &send) {
+	for (const auto &message : messages) {
+		size_t offset = 0;
+		while ((sizeof(RtcpHeader) + offset) <= message->size()) {
+			auto header = reinterpret_cast<RtcpHeader *>(message->data() + offset);
+			uint8_t payload_type = header->payloadType();
+
+			if (payload_type == 196) {
+				// FIR message, call pli handler anyway
 				mOnPli();
 				break;
+			} else if (payload_type == 206) {
+				// On a payload specific fb message, there is a "feedback message type" (FMT) in the
+				// header instead of a report count. PT = 206, FMT = 1 means a PLI message
+				uint8_t feedback_message_type = header->reportCount();
+				if (feedback_message_type == 1) {
+					mOnPli();
+					break;
+				}
 			}
+			offset += header->lengthInBytes();
 		}
-		offset += header->lengthInBytes();
 	}
-	return { message, std::nullopt };
 }
 
-PliHandler::PliHandler(std::function<void(void)> onPli) : mOnPli(onPli) { }
-
-}
+} // namespace rtc
 
 #endif // RTC_ENABLE_MEDIA

+ 52 - 60
src/rtcpnackresponder.cpp

@@ -9,6 +9,7 @@
 #if RTC_ENABLE_MEDIA
 
 #include "rtcpnackresponder.hpp"
+#include "rtp.hpp"
 
 #include "impl/internals.hpp"
 
@@ -16,15 +17,59 @@
 
 namespace rtc {
 
+RtcpNackResponder::RtcpNackResponder(size_t maxSize)
+    : mStorage(std::make_shared<Storage>(maxSize)) {}
+
+void RtcpNackResponder::incoming(message_vector &messages, const message_callback &send) {
+	for (const auto &message : messages) {
+		if (message->type != Message::Control)
+			continue;
+
+		size_t p = 0;
+		while (p + sizeof(RtcpNack) <= message->size()) {
+			auto nack = reinterpret_cast<RtcpNack *>(message->data() + p);
+			p += nack->header.header.lengthInBytes();
+			if (p > message->size())
+				break;
+
+			// check if RTCP is NACK
+			if (nack->header.header.payloadType() != 205 || nack->header.header.reportCount() != 1)
+				continue;
+
+			unsigned int fieldsCount = nack->getSeqNoCount();
+			std::vector<uint16_t> missingSequenceNumbers;
+			for (unsigned int i = 0; i < fieldsCount; i++) {
+				auto field = nack->parts[i];
+				auto newMissingSeqenceNumbers = field.getSequenceNumbers();
+				missingSequenceNumbers.insert(missingSequenceNumbers.end(),
+				                              newMissingSeqenceNumbers.begin(),
+				                              newMissingSeqenceNumbers.end());
+			}
+
+			for (auto sequenceNumber : missingSequenceNumbers) {
+				if (auto optPacket = mStorage->get(sequenceNumber))
+					send(make_message(*optPacket.value()));
+			}
+		}
+	}
+}
+
+void RtcpNackResponder::outgoing(message_vector &messages,
+                                 [[maybe_unused]] const message_callback &send) {
+	for (const auto &message : messages)
+		if (message->type != Message::Control)
+			mStorage->store(message);
+}
+
 RtcpNackResponder::Storage::Element::Element(binary_ptr packet, uint16_t sequenceNumber,
                                              shared_ptr<Element> next)
     : packet(packet), sequenceNumber(sequenceNumber), next(next) {}
 
-unsigned RtcpNackResponder::Storage::size() { return unsigned(storage.size()); }
+size_t RtcpNackResponder::Storage::size() { return storage.size(); }
 
-RtcpNackResponder::Storage::Storage(unsigned _maximumSize) : maximumSize(_maximumSize) {
-	assert(maximumSize > 0);
-	storage.reserve(maximumSize);
+RtcpNackResponder::Storage::Storage(size_t _maxSize) : maxSize(_maxSize) {
+	assert(maxSize > 0);
+	storage.reserve(maxSize);
 }
 
 optional<binary_ptr> RtcpNackResponder::Storage::get(uint16_t sequenceNumber) {
@@ -35,9 +80,9 @@ optional<binary_ptr> RtcpNackResponder::Storage::get(uint16_t sequenceNumber) {
 }
 
 void RtcpNackResponder::Storage::store(binary_ptr packet) {
-	if (!packet || packet->size() < 12) {
+	if (!packet || packet->size() < sizeof(RtpHeader))
 		return;
-	}
+
 	auto rtp = reinterpret_cast<RtpHeader *>(packet->data());
 	auto sequenceNumber = rtp->seqNumber();
 
@@ -55,7 +100,7 @@ void RtcpNackResponder::Storage::store(binary_ptr packet) {
 
 	storage.emplace(sequenceNumber, newest);
 
-	if (size() > maximumSize) {
+	if (size() > maxSize) {
 		assert(oldest);
 		if (oldest) {
 			storage.erase(oldest->sequenceNumber);
@@ -64,59 +109,6 @@ void RtcpNackResponder::Storage::store(binary_ptr packet) {
 	}
 }
 
-RtcpNackResponder::RtcpNackResponder(unsigned maxStoredPacketCount)
-    : MediaHandlerElement(), storage(std::make_shared<Storage>(maxStoredPacketCount)) {}
-
-ChainedIncomingControlProduct
-RtcpNackResponder::processIncomingControlMessage(message_ptr message) {
-	optional<ChainedOutgoingProduct> optPackets = ChainedOutgoingProduct(nullptr);
-	auto packets = make_chained_messages_product();
-
-	size_t p = 0;
-	while (p < message->size()) {
-		auto nack = reinterpret_cast<RtcpNack *>(message->data() + p);
-		p += nack->header.header.lengthInBytes();
-		// check if rtcp is nack
-		if (nack->header.header.payloadType() != 205 || nack->header.header.reportCount() != 1) {
-			continue;
-		}
-
-		auto fieldsCount = nack->getSeqNoCount();
-
-		std::vector<uint16_t> missingSequenceNumbers{};
-		for (unsigned int i = 0; i < fieldsCount; i++) {
-			auto field = nack->parts[i];
-			auto newMissingSeqenceNumbers = field.getSequenceNumbers();
-			missingSequenceNumbers.insert(missingSequenceNumbers.end(),
-			                              newMissingSeqenceNumbers.begin(),
-			                              newMissingSeqenceNumbers.end());
-		}
-		packets->reserve(packets->size() + missingSequenceNumbers.size());
-		for (auto sequenceNumber : missingSequenceNumbers) {
-			auto optPacket = storage->get(sequenceNumber);
-			if (optPacket.has_value()) {
-				auto packet = optPacket.value();
-				packets->push_back(packet);
-			}
-		}
-	}
-
-	if (!packets->empty()) {
-		return {message, ChainedOutgoingProduct(packets)};
-	} else {
-		return {message, nullopt};
-	}
-}
-
-ChainedOutgoingProduct
-RtcpNackResponder::processOutgoingBinaryMessage(ChainedMessagesProduct messages,
-                                                message_ptr control) {
-	for (auto message : *messages) {
-		storage->store(message);
-	}
-	return {messages, control};
-}
-
 } // namespace rtc
 
 #endif /* RTC_ENABLE_MEDIA */

+ 72 - 73
src/rtcpreceivingsession.cpp

@@ -32,102 +32,101 @@ static impl::LogCounter COUNTER_BAD_NOTIF_LEN(plog::warning,
 static impl::LogCounter COUNTER_BAD_SCTP_STATUS(plog::warning,
                                                 "Number of unknown SCTP_STATUS errors");
 
-message_ptr RtcpReceivingSession::outgoing(message_ptr ptr) { return ptr; }
-
-message_ptr RtcpReceivingSession::incoming(message_ptr ptr) {
-	if (ptr->type == Message::Binary) {
-		auto rtp = reinterpret_cast<const RtpHeader *>(ptr->data());
-
-		// https://www.rfc-editor.org/rfc/rfc3550.html#appendix-A.1
-		if (rtp->version() != 2) {
-			COUNTER_BAD_RTP_HEADER++;
-			PLOG_VERBOSE << "RTP packet is not version 2";
-
-			return nullptr;
+void RtcpReceivingSession::incoming(message_vector &messages, const message_callback &send) {
+	message_vector result;
+	result.resize(messages.size());
+	for (auto message : messages) {
+		switch (message->type) {
+		case Message::Binary: {
+			if (message->size() < sizeof(RtpHeader)) {
+				COUNTER_BAD_RTP_HEADER++;
+				PLOG_VERBOSE << "RTP packet is too small, size=" << message->size();
+				continue;
+			}
+
+			auto rtp = reinterpret_cast<const RtpHeader *>(message->data());
+
+			// https://www.rfc-editor.org/rfc/rfc3550.html#appendix-A.1
+			if (rtp->version() != 2) {
+				COUNTER_BAD_RTP_HEADER++;
+				PLOG_VERBOSE << "RTP packet is not version 2";
+				continue;
+			}
+
+			if (rtp->payloadType() == 201 || rtp->payloadType() == 200) {
+				COUNTER_BAD_RTP_HEADER++;
+				PLOG_VERBOSE << "RTP packet has a payload type indicating RR/SR";
+				continue;
+			}
+
+			mSsrc = rtp->ssrc();
+			result.push_back(std::move(message));
+			break;
 		}
-		if (rtp->payloadType() == 201 || rtp->payloadType() == 200) {
-			COUNTER_BAD_RTP_HEADER++;
-			PLOG_VERBOSE << "RTP packet has a payload type indicating RR/SR";
 
-			return nullptr;
+		case Message::Control: {
+			auto rr = reinterpret_cast<const RtcpRr *>(message->data());
+			if (rr->header.payloadType() == 201) { // RR
+				mSsrc = rr->senderSSRC();
+				rr->log();
+			} else if (rr->header.payloadType() == 200) { // SR
+				mSsrc = rr->senderSSRC();
+				auto sr = reinterpret_cast<const RtcpSr *>(message->data());
+				mSyncRTPTS = sr->rtpTimestamp();
+				mSyncNTPTS = sr->ntpTimestamp();
+				sr->log();
+
+				// TODO For the time being, we will send RR's/REMB's when we get an SR
+				pushRR(send, 0);
+				if (unsigned int bitrate = mRequestedBitrate.load(); bitrate > 0)
+					pushREMB(send, bitrate);
+			}
+			break;
 		}
 
-		// Padding-processing is a user-level thing
-
-		mSsrc = rtp->ssrc();
-
-		return ptr;
+		default:
+			break;
+		}
 	}
 
-	assert(ptr->type == Message::Control);
-	auto rr = reinterpret_cast<const RtcpRr *>(ptr->data());
-	if (rr->header.payloadType() == 201) {
-		// RR
-		mSsrc = rr->senderSSRC();
-		rr->log();
-	} else if (rr->header.payloadType() == 200) {
-		// SR
-		mSsrc = rr->senderSSRC();
-		auto sr = reinterpret_cast<const RtcpSr *>(ptr->data());
-		mSyncRTPTS = sr->rtpTimestamp();
-		mSyncNTPTS = sr->ntpTimestamp();
-		sr->log();
-
-		// TODO For the time being, we will send RR's/REMB's when we get an SR
-		pushRR(0);
-		if (mRequestedBitrate > 0)
-			pushREMB(mRequestedBitrate);
-	}
-	return nullptr;
+	messages.swap(result);
 }
 
-void RtcpReceivingSession::requestBitrate(unsigned int newBitrate) {
-	mRequestedBitrate = newBitrate;
-
-	PLOG_DEBUG << "[GOOG-REMB] Requesting bitrate: " << newBitrate << std::endl;
-	pushREMB(newBitrate);
+bool RtcpReceivingSession::requestBitrate(unsigned int bitrate, const message_callback &send) {
+	PLOG_DEBUG << "Requesting bitrate: " << bitrate << std::endl;
+	mRequestedBitrate.store(bitrate);
+	pushREMB(send, bitrate);
+	return true;
 }
 
-void RtcpReceivingSession::pushREMB(unsigned int bitrate) {
-	message_ptr msg = make_message(RtcpRemb::SizeWithSSRCs(1), Message::Control);
-	auto remb = reinterpret_cast<RtcpRemb *>(msg->data());
+void RtcpReceivingSession::pushREMB(const message_callback &send, unsigned int bitrate) {
+	auto message = make_message(RtcpRemb::SizeWithSSRCs(1), Message::Control);
+	auto remb = reinterpret_cast<RtcpRemb *>(message->data());
 	remb->preparePacket(mSsrc, 1, bitrate);
 	remb->setSsrc(0, mSsrc);
-
-	send(msg);
+	send(message);
 }
 
-void RtcpReceivingSession::pushRR(unsigned int lastSR_delay) {
-	auto msg = make_message(RtcpRr::SizeWithReportBlocks(1), Message::Control);
-	auto rr = reinterpret_cast<RtcpRr *>(msg->data());
+void RtcpReceivingSession::pushRR(const message_callback &send, unsigned int lastSrDelay) {
+	auto message = make_message(RtcpRr::SizeWithReportBlocks(1), Message::Control);
+	auto rr = reinterpret_cast<RtcpRr *>(message->data());
 	rr->preparePacket(mSsrc, 1);
 	rr->getReportBlock(0)->preparePacket(mSsrc, 0, 0, uint16_t(mGreatestSeqNo), 0, 0, mSyncNTPTS,
-	                                     lastSR_delay);
+	                                     lastSrDelay);
 	rr->log();
-
-	send(msg);
-}
-
-bool RtcpReceivingSession::send(message_ptr msg) {
-	try {
-		outgoingCallback(std::move(msg));
-		return true;
-	} catch (const std::exception &e) {
-		LOG_DEBUG << "RTCP tx failed: " << e.what();
-	}
-	return false;
+	send(message);
 }
 
-bool RtcpReceivingSession::requestKeyframe() {
-	pushPLI();
+bool RtcpReceivingSession::requestKeyframe(const message_callback &send) {
+	pushPLI(send);
 	return true;
 }
 
-void RtcpReceivingSession::pushPLI() {
-	auto msg = make_message(RtcpPli::Size(), Message::Control);
-	auto *pli = reinterpret_cast<RtcpPli *>(msg->data());
+void RtcpReceivingSession::pushPLI(const message_callback &send) {
+	auto message = make_message(RtcpPli::Size(), Message::Control);
+	auto *pli = reinterpret_cast<RtcpPli *>(message->data());
 	pli->preparePacket(mSsrc);
-	send(msg);
+	send(message);
 }
 
 } // namespace rtc

+ 23 - 21
src/rtcpsrreporter.cpp

@@ -16,6 +16,7 @@
 
 namespace {
 
+// TODO: move to utils
 uint64_t ntp_time() {
 	const auto now = std::chrono::system_clock::now();
 	const double secs = std::chrono::duration<double>(now.time_since_epoch()).count();
@@ -27,22 +28,32 @@ uint64_t ntp_time() {
 
 namespace rtc {
 
-ChainedOutgoingProduct RtcpSrReporter::processOutgoingBinaryMessage(ChainedMessagesProduct messages,
-                                                                    message_ptr control) {
+RtcpSrReporter::RtcpSrReporter(shared_ptr<RtpPacketizationConfig> rtpConfig)
+    : rtpConfig(rtpConfig) {
+	mLastReportedTimestamp = rtpConfig->timestamp;
+}
+
+void RtcpSrReporter::setNeedsToReport() { mNeedsToReport = true; }
+
+uint32_t RtcpSrReporter::lastReportedTimestamp() const { return mLastReportedTimestamp; }
+
+void RtcpSrReporter::outgoing(message_vector &messages, const message_callback &send) {
+	for (const auto &message : messages) {
+		if (message->type == Message::Control)
+			continue;
+
+		if (message->size() < sizeof(RtpHeader))
+			continue;
+
+		auto rtp = reinterpret_cast<RtpHeader *>(message->data());
+		addToReport(rtp, uint32_t(message->size()));
+	}
+
 	if (std::exchange(mNeedsToReport, false)) {
 		auto timestamp = rtpConfig->timestamp;
 		auto sr = getSenderReport(timestamp);
-		if (control) {
-			control->insert(control->end(), sr->begin(), sr->end());
-		} else {
-			control = sr;
-		}
-	}
-	for (auto message : *messages) {
-		auto rtp = reinterpret_cast<RtpHeader *>(message->data());
-		addToReport(rtp, uint32_t(message->size()));
+		send(sr);
 	}
-	return {messages, control};
 }
 
 void RtcpSrReporter::addToReport(RtpHeader *rtp, uint32_t rtpSize) {
@@ -51,11 +62,6 @@ void RtcpSrReporter::addToReport(RtpHeader *rtp, uint32_t rtpSize) {
 	mPayloadOctets += rtpSize - uint32_t(rtp->getSize());
 }
 
-RtcpSrReporter::RtcpSrReporter(shared_ptr<RtpPacketizationConfig> rtpConfig)
-    : MediaHandlerElement(), rtpConfig(rtpConfig) {
-	mLastReportedTimestamp = rtpConfig->timestamp;
-}
-
 message_ptr RtcpSrReporter::getSenderReport(uint32_t timestamp) {
 	auto srSize = RtcpSr::Size(0);
 	auto msg = make_message(srSize + RtcpSdes::Size({{uint8_t(rtpConfig->cname.size())}}),
@@ -79,10 +85,6 @@ message_ptr RtcpSrReporter::getSenderReport(uint32_t timestamp) {
 	return msg;
 }
 
-void RtcpSrReporter::setNeedsToReport() { mNeedsToReport = true; }
-
-uint32_t RtcpSrReporter::lastReportedTimestamp() const { return mLastReportedTimestamp; }
-
 } // namespace rtc
 
 #endif /* RTC_ENABLE_MEDIA */

+ 26 - 17
src/rtppacketizer.cpp

@@ -17,40 +17,37 @@ namespace rtc {
 
 RtpPacketizer::RtpPacketizer(shared_ptr<RtpPacketizationConfig> rtpConfig) : rtpConfig(rtpConfig) {}
 
-binary_ptr RtpPacketizer::packetize(shared_ptr<binary> payload, bool setMark) {
+RtpPacketizer::~RtpPacketizer() {}
+
+message_ptr RtpPacketizer::packetize(shared_ptr<binary> payload, bool mark) {
 	size_t rtpExtHeaderSize = 0;
 
 	const bool setVideoRotation = (rtpConfig->videoOrientationId != 0) &&
 	                              (rtpConfig->videoOrientationId <
 	                               15) && // needs fixing if longer extension headers are supported
-	                              setMark &&
+	                              mark &&
 	                              (rtpConfig->videoOrientation != 0);
 
-	if (setVideoRotation) {
+	if (setVideoRotation)
 		rtpExtHeaderSize += 2;
-	}
 
-	if (rtpConfig->mid.has_value()) {
+	if (rtpConfig->mid.has_value())
 		rtpExtHeaderSize += (1 + rtpConfig->mid->length());
-	}
 
-	if (rtpConfig->rid.has_value()) {
+	if (rtpConfig->rid.has_value())
 		rtpExtHeaderSize += (1 + rtpConfig->rid->length());
-	}
 
-	if (rtpExtHeaderSize != 0) {
+	if (rtpExtHeaderSize != 0)
 		rtpExtHeaderSize += 4;
-	}
 
-	auto msg = std::make_shared<binary>(rtpHeaderSize + rtpExtHeaderSize + payload->size());
-	auto *rtp = (RtpHeader *)msg->data();
+	auto message = make_message(RtpHeaderSize + rtpExtHeaderSize + payload->size());
+	auto *rtp = (RtpHeader *)message->data();
 	rtp->setPayloadType(rtpConfig->payloadType);
-	// increase sequence number
-	rtp->setSeqNumber(rtpConfig->sequenceNumber++);
+	rtp->setSeqNumber(rtpConfig->sequenceNumber++); // increase sequence number
 	rtp->setTimestamp(rtpConfig->timestamp);
 	rtp->setSsrc(rtpConfig->ssrc);
 
-	if (setMark) {
+	if (mark) {
 		rtp->setMarker(true);
 	}
 
@@ -90,8 +87,20 @@ binary_ptr RtpPacketizer::packetize(shared_ptr<binary> payload, bool setMark) {
 	}
 
 	rtp->preparePacket();
-	std::memcpy(msg->data() + rtpHeaderSize + rtpExtHeaderSize, payload->data(), payload->size());
-	return msg;
+
+	std::memcpy(message->data() + RtpHeaderSize + rtpExtHeaderSize, payload->data(),
+	            payload->size());
+
+	return message;
+}
+
+void RtpPacketizer::media([[maybe_unused]] const Description::Media &desc) {}
+
+void RtpPacketizer::outgoing([[maybe_unused]] message_vector &messages,
+                             [[maybe_unused]] const message_callback &send) {
+	// Default implementation
+	for (auto &message : messages)
+		message = packetize(message, false);
 }
 
 } // namespace rtc

+ 19 - 5
src/track.cpp

@@ -44,13 +44,27 @@ void Track::setMediaHandler(shared_ptr<MediaHandler> handler) {
 	impl()->setMediaHandler(std::move(handler));
 }
 
+void Track::chainMediaHandler(shared_ptr<MediaHandler> handler) {
+	if (auto first = impl()->getMediaHandler())
+		first->addToChain(std::move(handler));
+	else
+		impl()->setMediaHandler(std::move(handler));
+}
+
 bool Track::requestKeyframe() {
 	// only push PLI for video
-	if (description().type() == "video") {
-		if (auto handler = impl()->getMediaHandler()) {
-			return handler->requestKeyframe();
-		}
-	}
+	if (description().type() == "video")
+		if (auto handler = impl()->getMediaHandler())
+			return handler->requestKeyframe([this](message_ptr m) { impl()->transportSend(m); });
+
+	return false;
+}
+
+bool Track::requestBitrate(unsigned int bitrate) {
+	if (auto handler = impl()->getMediaHandler())
+		return handler->requestBitrate(bitrate,
+		                               [this](message_ptr m) { impl()->transportSend(m); });
+
 	return false;
 }