Browse Source

Merge branch 'master' into dependency-descriptor

melpon 1 year ago
parent
commit
518c0ec9db

+ 4 - 1
CMakeLists.txt

@@ -1,6 +1,6 @@
 cmake_minimum_required(VERSION 3.7)
 cmake_minimum_required(VERSION 3.7)
 project(libdatachannel
 project(libdatachannel
-	VERSION 0.20.1
+	VERSION 0.20.2
 	LANGUAGES CXX)
 	LANGUAGES CXX)
 set(PROJECT_DESCRIPTION "C/C++ WebRTC network library featuring Data Channels, Media Transport, and WebSockets")
 set(PROJECT_DESCRIPTION "C/C++ WebRTC network library featuring Data Channels, Media Transport, and WebSockets")
 
 
@@ -76,6 +76,7 @@ set(LIBDATACHANNEL_SOURCES
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizationconfig.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizationconfig.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpsrreporter.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpsrreporter.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizer.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/rtpdepacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/h264rtppacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/h264rtppacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/h264rtpdepacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/h264rtpdepacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/nalunit.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/nalunit.cpp
@@ -101,6 +102,7 @@ set(LIBDATACHANNEL_HEADERS
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/common.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/common.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/global.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/global.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/message.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/message.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/frameinfo.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/peerconnection.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/peerconnection.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/reliability.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/reliability.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtc.h
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtc.h
@@ -112,6 +114,7 @@ set(LIBDATACHANNEL_HEADERS
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtppacketizationconfig.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtppacketizationconfig.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpsrreporter.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpsrreporter.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtppacketizer.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtppacketizer.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtpdepacketizer.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h264rtppacketizer.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h264rtppacketizer.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h264rtpdepacketizer.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h264rtpdepacketizer.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/nalunit.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/nalunit.hpp

+ 11 - 0
examples/media-receiver/README.md

@@ -18,3 +18,14 @@ Use the following gstreamer demo pipeline to display the traffic (You might need
 $ gst-launch-1.0 udpsrc address=127.0.0.1 port=5000 caps="application/x-rtp" ! queue ! rtph264depay ! video/x-h264,stream-format=byte-stream ! queue ! avdec_h264 ! queue ! autovideosink
 $ gst-launch-1.0 udpsrc address=127.0.0.1 port=5000 caps="application/x-rtp" ! queue ! rtph264depay ! video/x-h264,stream-format=byte-stream ! queue ! avdec_h264 ! queue ! autovideosink
 ```
 ```
 
 
+For saving the stream to a file, use the following pipeline (mp4):
+
+```
+gst-launch-1.0 -e udpsrc address=127.0.0.1 port=5000 caps="application/x-rtp" ! queue ! rtph264depay ! h264parse ! mp4mux ! filesink location=out.mp4
+```
+
+## Requirements
+
+-   GStreamer 1.0
+-   gstreamer1.0-libav (sudo apt-get install gstreamer1.0-libav)
+-   h264enc (sudo apt-get install h264enc)

+ 79 - 18
examples/signaling-server-nodejs/package-lock.json

@@ -39,13 +39,18 @@
       }
       }
     },
     },
     "node_modules/es5-ext": {
     "node_modules/es5-ext": {
-      "version": "0.10.53",
-      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
-      "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
+      "version": "0.10.63",
+      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.63.tgz",
+      "integrity": "sha512-hUCZd2Byj/mNKjfP9jXrdVZ62B8KuA/VoK7X8nUh5qT+AxDmcbvZz041oDVZdbIN1qW6XY9VDNwzkvKnZvK2TQ==",
+      "hasInstallScript": true,
       "dependencies": {
       "dependencies": {
-        "es6-iterator": "~2.0.3",
-        "es6-symbol": "~3.1.3",
-        "next-tick": "~1.0.0"
+        "es6-iterator": "^2.0.3",
+        "es6-symbol": "^3.1.3",
+        "esniff": "^2.0.1",
+        "next-tick": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=0.10"
       }
       }
     },
     },
     "node_modules/es6-iterator": {
     "node_modules/es6-iterator": {
@@ -67,6 +72,34 @@
         "ext": "^1.1.2"
         "ext": "^1.1.2"
       }
       }
     },
     },
+    "node_modules/esniff": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz",
+      "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==",
+      "dependencies": {
+        "d": "^1.0.1",
+        "es5-ext": "^0.10.62",
+        "event-emitter": "^0.3.5",
+        "type": "^2.7.2"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/esniff/node_modules/type": {
+      "version": "2.7.2",
+      "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
+      "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw=="
+    },
+    "node_modules/event-emitter": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
+      "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==",
+      "dependencies": {
+        "d": "1",
+        "es5-ext": "~0.10.14"
+      }
+    },
     "node_modules/ext": {
     "node_modules/ext": {
       "version": "1.4.0",
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
       "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
@@ -91,9 +124,9 @@
       "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
       "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
     },
     },
     "node_modules/next-tick": {
     "node_modules/next-tick": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
-      "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
+      "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
     },
     },
     "node_modules/node-gyp-build": {
     "node_modules/node-gyp-build": {
       "version": "4.2.3",
       "version": "4.2.3",
@@ -179,13 +212,14 @@
       }
       }
     },
     },
     "es5-ext": {
     "es5-ext": {
-      "version": "0.10.53",
-      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
-      "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
+      "version": "0.10.63",
+      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.63.tgz",
+      "integrity": "sha512-hUCZd2Byj/mNKjfP9jXrdVZ62B8KuA/VoK7X8nUh5qT+AxDmcbvZz041oDVZdbIN1qW6XY9VDNwzkvKnZvK2TQ==",
       "requires": {
       "requires": {
-        "es6-iterator": "~2.0.3",
-        "es6-symbol": "~3.1.3",
-        "next-tick": "~1.0.0"
+        "es6-iterator": "^2.0.3",
+        "es6-symbol": "^3.1.3",
+        "esniff": "^2.0.1",
+        "next-tick": "^1.1.0"
       }
       }
     },
     },
     "es6-iterator": {
     "es6-iterator": {
@@ -207,6 +241,33 @@
         "ext": "^1.1.2"
         "ext": "^1.1.2"
       }
       }
     },
     },
+    "esniff": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz",
+      "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==",
+      "requires": {
+        "d": "^1.0.1",
+        "es5-ext": "^0.10.62",
+        "event-emitter": "^0.3.5",
+        "type": "^2.7.2"
+      },
+      "dependencies": {
+        "type": {
+          "version": "2.7.2",
+          "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
+          "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw=="
+        }
+      }
+    },
+    "event-emitter": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
+      "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==",
+      "requires": {
+        "d": "1",
+        "es5-ext": "~0.10.14"
+      }
+    },
     "ext": {
     "ext": {
       "version": "1.4.0",
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
       "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
@@ -233,9 +294,9 @@
       "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
       "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
     },
     },
     "next-tick": {
     "next-tick": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
-      "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
+      "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
     },
     },
     "node-gyp-build": {
     "node-gyp-build": {
       "version": "4.2.3",
       "version": "4.2.3",

+ 2 - 2
examples/signaling-server-rust/Cargo.lock

@@ -306,9 +306,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "mio"
 name = "mio"
-version = "0.8.8"
+version = "0.8.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
+checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
 dependencies = [
 dependencies = [
  "libc",
  "libc",
  "wasi",
  "wasi",

+ 23 - 0
include/rtc/frameinfo.hpp

@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2019-2020 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_FRAMEINFO_H
+#define RTC_FRAMEINFO_H
+
+#include "common.hpp"
+
+namespace rtc {
+
+struct RTC_CPP_EXPORT FrameInfo {
+	FrameInfo(uint32_t timestamp) : timestamp(timestamp){};
+	uint32_t timestamp = 0; // RTP Timestamp
+};
+
+} // namespace rtc
+
+#endif // RTC_FRAMEINFO_H

+ 2 - 1
include/rtc/h264rtpdepacketizer.hpp

@@ -32,7 +32,8 @@ public:
 private:
 private:
 	std::vector<message_ptr> mRtpBuffer;
 	std::vector<message_ptr> mRtpBuffer;
 
 
-	message_vector buildFrames(message_vector::iterator firstPkt, message_vector::iterator lastPkt);
+	message_vector buildFrames(message_vector::iterator firstPkt, message_vector::iterator lastPkt,
+	                           uint32_t timestamp);
 };
 };
 
 
 } // namespace rtc
 } // namespace rtc

+ 7 - 2
include/rtc/message.hpp

@@ -10,6 +10,7 @@
 #define RTC_MESSAGE_H
 #define RTC_MESSAGE_H
 
 
 #include "common.hpp"
 #include "common.hpp"
+#include "frameinfo.hpp"
 #include "reliability.hpp"
 #include "reliability.hpp"
 
 
 #include <functional>
 #include <functional>
@@ -32,6 +33,7 @@ struct RTC_CPP_EXPORT Message : binary {
 	unsigned int stream = 0; // Stream id (SCTP stream or SSRC)
 	unsigned int stream = 0; // Stream id (SCTP stream or SSRC)
 	unsigned int dscp = 0;   // Differentiated Services Code Point
 	unsigned int dscp = 0;   // Differentiated Services Code Point
 	shared_ptr<Reliability> reliability;
 	shared_ptr<Reliability> reliability;
+	shared_ptr<FrameInfo> frameInfo;
 };
 };
 
 
 using message_ptr = shared_ptr<Message>;
 using message_ptr = shared_ptr<Message>;
@@ -44,10 +46,12 @@ inline size_t message_size_func(const message_ptr &m) {
 
 
 template <typename Iterator>
 template <typename Iterator>
 message_ptr make_message(Iterator begin, Iterator end, Message::Type type = Message::Binary,
 message_ptr make_message(Iterator begin, Iterator end, Message::Type type = Message::Binary,
-                         unsigned int stream = 0, shared_ptr<Reliability> reliability = nullptr) {
+                         unsigned int stream = 0, shared_ptr<Reliability> reliability = nullptr,
+                         shared_ptr<FrameInfo> frameInfo = nullptr) {
 	auto message = std::make_shared<Message>(begin, end, type);
 	auto message = std::make_shared<Message>(begin, end, type);
 	message->stream = stream;
 	message->stream = stream;
 	message->reliability = reliability;
 	message->reliability = reliability;
+	message->frameInfo = frameInfo;
 	return message;
 	return message;
 }
 }
 
 
@@ -57,7 +61,8 @@ RTC_CPP_EXPORT message_ptr make_message(size_t size, Message::Type type = Messag
 
 
 RTC_CPP_EXPORT message_ptr make_message(binary &&data, Message::Type type = Message::Binary,
 RTC_CPP_EXPORT message_ptr make_message(binary &&data, Message::Type type = Message::Binary,
                                         unsigned int stream = 0,
                                         unsigned int stream = 0,
-                                        shared_ptr<Reliability> reliability = nullptr);
+                                        shared_ptr<Reliability> reliability = nullptr,
+                                        shared_ptr<FrameInfo> frameInfo = nullptr);
 
 
 RTC_CPP_EXPORT message_ptr make_message(size_t size, message_ptr orig);
 RTC_CPP_EXPORT message_ptr make_message(size_t size, message_ptr orig);
 
 

+ 1 - 0
include/rtc/rtc.hpp

@@ -39,5 +39,6 @@
 #include "rtcpreceivingsession.hpp"
 #include "rtcpreceivingsession.hpp"
 #include "rtcpsrreporter.hpp"
 #include "rtcpsrreporter.hpp"
 #include "rtppacketizer.hpp"
 #include "rtppacketizer.hpp"
+#include "rtpdepacketizer.hpp"
 
 
 #endif // RTC_ENABLE_MEDIA
 #endif // RTC_ENABLE_MEDIA

+ 34 - 0
include/rtc/rtpdepacketizer.hpp

@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2024 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_RTP_DEPACKETIZER_H
+#define RTC_RTP_DEPACKETIZER_H
+
+#if RTC_ENABLE_MEDIA
+
+#include "mediahandler.hpp"
+#include "message.hpp"
+
+namespace rtc {
+
+class RTC_CPP_EXPORT RtpDepacketizer : public MediaHandler {
+public:
+	RtpDepacketizer() = default;
+	virtual ~RtpDepacketizer() = default;
+
+	virtual void incoming(message_vector &messages, const message_callback &send) override;
+};
+
+using OpusRtpDepacketizer = RtpDepacketizer;
+using AACRtpDepacketizer = RtpDepacketizer;
+
+} // namespace rtc
+
+#endif /* RTC_ENABLE_MEDIA */
+
+#endif /* RTC_RTP_DEPACKETIZER_H */

+ 2 - 0
include/rtc/track.hpp

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

+ 2 - 2
include/rtc/version.h

@@ -3,7 +3,7 @@
 
 
 #define RTC_VERSION_MAJOR 0
 #define RTC_VERSION_MAJOR 0
 #define RTC_VERSION_MINOR 20
 #define RTC_VERSION_MINOR 20
-#define RTC_VERSION_PATCH 1
-#define RTC_VERSION "0.20.1"
+#define RTC_VERSION_PATCH 2
+#define RTC_VERSION "0.20.2"
 
 
 #endif
 #endif

+ 1 - 1
src/candidate.cpp

@@ -89,7 +89,7 @@ void Candidate::parse(string candidate) {
 
 
 	PLOG_VERBOSE << "Parsing candidate: " << candidate;
 	PLOG_VERBOSE << "Parsing candidate: " << candidate;
 
 
-	// See RFC 8445 for format
+	// See RFC 8839 for format
 	std::istringstream iss(candidate);
 	std::istringstream iss(candidate);
 	string typ_;
 	string typ_;
 	if (!(iss >> mFoundation >> mComponent >> mTransportString >> mPriority &&
 	if (!(iss >> mFoundation >> mComponent >> mTransportString >> mPriority &&

+ 1 - 1
src/capi.cpp

@@ -512,7 +512,7 @@ int rtcSetSignalingStateChangeCallback(int pc, rtcSignalingStateCallbackFunc cb)
 					cb(pc, static_cast<rtcSignalingState>(state), *ptr);
 					cb(pc, static_cast<rtcSignalingState>(state), *ptr);
 			});
 			});
 		else
 		else
-			peerConnection->onGatheringStateChange(nullptr);
+			peerConnection->onSignalingStateChange(nullptr);
 		return RTC_ERR_SUCCESS;
 		return RTC_ERR_SUCCESS;
 	});
 	});
 }
 }

+ 26 - 21
src/h264rtpdepacketizer.cpp

@@ -32,9 +32,10 @@ const uint8_t naluTypeSTAPA = 24;
 const uint8_t naluTypeFUA = 28;
 const uint8_t naluTypeFUA = 28;
 
 
 message_vector H264RtpDepacketizer::buildFrames(message_vector::iterator begin,
 message_vector H264RtpDepacketizer::buildFrames(message_vector::iterator begin,
-                                                message_vector::iterator end) {
+                                                message_vector::iterator end, uint32_t timestamp) {
 	message_vector out = {};
 	message_vector out = {};
 	auto fua_buffer = std::vector<std::byte>{};
 	auto fua_buffer = std::vector<std::byte>{};
+	auto frameInfo = std::make_shared<FrameInfo>(timestamp);
 
 
 	for (auto it = begin; it != end; it++) {
 	for (auto it = begin; it != end; it++) {
 		auto pkt = it->get();
 		auto pkt = it->get();
@@ -58,11 +59,13 @@ message_vector H264RtpDepacketizer::buildFrames(message_vector::iterator begin,
 				fua_buffer.at(0) =
 				fua_buffer.at(0) =
 				    std::byte(nalUnitHeader.idc() | nalUnitFragmentHeader.unitType());
 				    std::byte(nalUnitHeader.idc() | nalUnitFragmentHeader.unitType());
 
 
-				out.push_back(make_message(std::move(fua_buffer)));
+				out.push_back(
+				    make_message(std::move(fua_buffer), Message::Binary, 0, nullptr, frameInfo));
 				fua_buffer.clear();
 				fua_buffer.clear();
 			}
 			}
 		} else if (nalUnitHeader.unitType() > 0 && nalUnitHeader.unitType() < 24) {
 		} else if (nalUnitHeader.unitType() > 0 && nalUnitHeader.unitType() < 24) {
-			out.push_back(make_message(pkt->begin() + headerSize, pkt->end()));
+			out.push_back(make_message(pkt->begin() + headerSize, pkt->end(), Message::Binary, 0,
+			                           nullptr, frameInfo));
 		} else if (nalUnitHeader.unitType() == naluTypeSTAPA) {
 		} else if (nalUnitHeader.unitType() == naluTypeSTAPA) {
 			auto currOffset = stapaHeaderSize + headerSize;
 			auto currOffset = stapaHeaderSize + headerSize;
 
 
@@ -76,11 +79,11 @@ message_vector H264RtpDepacketizer::buildFrames(message_vector::iterator begin,
 					throw std::runtime_error("STAP-A declared size is larger then buffer");
 					throw std::runtime_error("STAP-A declared size is larger then buffer");
 				}
 				}
 
 
-				out.push_back(
-				    make_message(pkt->begin() + currOffset, pkt->begin() + currOffset + naluSize));
+				out.push_back(make_message(pkt->begin() + currOffset,
+				                           pkt->begin() + currOffset + naluSize, Message::Binary, 0,
+				                           nullptr, frameInfo));
 				currOffset += naluSize;
 				currOffset += naluSize;
 			}
 			}
-
 		} else {
 		} else {
 			throw std::runtime_error("Unknown H264 RTP Packetization");
 			throw std::runtime_error("Unknown H264 RTP Packetization");
 		}
 		}
@@ -90,20 +93,22 @@ message_vector H264RtpDepacketizer::buildFrames(message_vector::iterator begin,
 }
 }
 
 
 void H264RtpDepacketizer::incoming(message_vector &messages, const message_callback &) {
 void H264RtpDepacketizer::incoming(message_vector &messages, const message_callback &) {
-	for (auto message : messages) {
-		if (message->type == Message::Control) {
-			continue; // RTCP
-		}
-
-		if (message->size() < sizeof(RtpHeader)) {
-			PLOG_VERBOSE << "RTP packet is too small, size=" << message->size();
-			continue;
-		}
-
-		mRtpBuffer.push_back(message);
-	}
-
-	messages.clear();
+	messages.erase(std::remove_if(messages.begin(), messages.end(),
+	                              [&](message_ptr message) {
+		                              if (message->type == Message::Control) {
+			                              return false;
+		                              }
+
+		                              if (message->size() < sizeof(RtpHeader)) {
+			                              PLOG_VERBOSE << "RTP packet is too small, size="
+			                                           << message->size();
+			                              return true;
+		                              }
+
+		                              mRtpBuffer.push_back(std::move(message));
+		                              return true;
+	                              }),
+	               messages.end());
 
 
 	while (mRtpBuffer.size() != 0) {
 	while (mRtpBuffer.size() != 0) {
 		uint32_t current_timestamp = 0;
 		uint32_t current_timestamp = 0;
@@ -128,7 +133,7 @@ void H264RtpDepacketizer::incoming(message_vector &messages, const message_callb
 		auto begin = mRtpBuffer.begin();
 		auto begin = mRtpBuffer.begin();
 		auto end = mRtpBuffer.begin() + (packets_in_timestamp - 1);
 		auto end = mRtpBuffer.begin() + (packets_in_timestamp - 1);
 
 
-		auto frames = buildFrames(begin, end + 1);
+		auto frames = buildFrames(begin, end + 1, current_timestamp);
 		messages.insert(messages.end(), frames.begin(), frames.end());
 		messages.insert(messages.end(), frames.begin(), frames.end());
 		mRtpBuffer.erase(mRtpBuffer.begin(), mRtpBuffer.begin() + packets_in_timestamp);
 		mRtpBuffer.erase(mRtpBuffer.begin(), mRtpBuffer.begin() + packets_in_timestamp);
 	}
 	}

+ 2 - 2
src/impl/channel.hpp

@@ -28,7 +28,7 @@ struct Channel {
 	virtual void triggerAvailable(size_t count);
 	virtual void triggerAvailable(size_t count);
 	virtual void triggerBufferedAmount(size_t amount);
 	virtual void triggerBufferedAmount(size_t amount);
 
 
-	void flushPendingMessages();
+	virtual void flushPendingMessages();
 	void resetOpenCallback();
 	void resetOpenCallback();
 	void resetCallbacks();
 	void resetCallbacks();
 
 
@@ -43,7 +43,7 @@ struct Channel {
 	std::atomic<size_t> bufferedAmount = 0;
 	std::atomic<size_t> bufferedAmount = 0;
 	std::atomic<size_t> bufferedAmountLowThreshold = 0;
 	std::atomic<size_t> bufferedAmountLowThreshold = 0;
 
 
-private:
+protected:
 	std::atomic<bool> mOpenTriggered = false;
 	std::atomic<bool> mOpenTriggered = false;
 };
 };
 
 

+ 5 - 8
src/impl/dtlstransport.cpp

@@ -394,27 +394,24 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr cer
 	mbedtls_ctr_drbg_set_prediction_resistance(&mDrbg, MBEDTLS_CTR_DRBG_PR_ON);
 	mbedtls_ctr_drbg_set_prediction_resistance(&mDrbg, MBEDTLS_CTR_DRBG_PR_ON);
 
 
 	try {
 	try {
-		mbedtls::check(mbedtls_ctr_drbg_seed(&mDrbg, mbedtls_entropy_func, &mEntropy, NULL, 0),
-		               "Failed creating Mbed TLS Context");
+		mbedtls::check(mbedtls_ctr_drbg_seed(&mDrbg, mbedtls_entropy_func, &mEntropy, NULL, 0));
 
 
 		mbedtls::check(mbedtls_ssl_config_defaults(
 		mbedtls::check(mbedtls_ssl_config_defaults(
 		                   &mConf, mIsClient ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
 		                   &mConf, mIsClient ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
-		                   MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_PRESET_DEFAULT),
-		               "Failed creating Mbed TLS Context");
+		                   MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_PRESET_DEFAULT));
 
 
+		mbedtls_ssl_conf_max_version(&mConf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); // TLS 1.2
 		mbedtls_ssl_conf_authmode(&mConf, MBEDTLS_SSL_VERIFY_OPTIONAL);
 		mbedtls_ssl_conf_authmode(&mConf, MBEDTLS_SSL_VERIFY_OPTIONAL);
 		mbedtls_ssl_conf_verify(&mConf, DtlsTransport::CertificateCallback, this);
 		mbedtls_ssl_conf_verify(&mConf, DtlsTransport::CertificateCallback, this);
-
 		mbedtls_ssl_conf_rng(&mConf, mbedtls_ctr_drbg_random, &mDrbg);
 		mbedtls_ssl_conf_rng(&mConf, mbedtls_ctr_drbg_random, &mDrbg);
 
 
 		auto [crt, pk] = mCertificate->credentials();
 		auto [crt, pk] = mCertificate->credentials();
-		mbedtls::check(mbedtls_ssl_conf_own_cert(&mConf, crt.get(), pk.get()),
-		               "Failed creating Mbed TLS Context");
+		mbedtls::check(mbedtls_ssl_conf_own_cert(&mConf, crt.get(), pk.get()));
 
 
 		mbedtls_ssl_conf_dtls_cookies(&mConf, NULL, NULL, NULL);
 		mbedtls_ssl_conf_dtls_cookies(&mConf, NULL, NULL, NULL);
 		mbedtls_ssl_conf_dtls_srtp_protection_profiles(&mConf, srtpSupportedProtectionProfiles);
 		mbedtls_ssl_conf_dtls_srtp_protection_profiles(&mConf, srtpSupportedProtectionProfiles);
 
 
-		mbedtls::check(mbedtls_ssl_setup(&mSsl, &mConf), "Failed creating Mbed TLS Context");
+		mbedtls::check(mbedtls_ssl_setup(&mSsl, &mConf));
 
 
 		mbedtls_ssl_set_export_keys_cb(&mSsl, DtlsTransport::ExportKeysCallback, this);
 		mbedtls_ssl_set_export_keys_cb(&mSsl, DtlsTransport::ExportKeysCallback, this);
 		mbedtls_ssl_set_bio(&mSsl, this, WriteCallback, ReadCallback, NULL);
 		mbedtls_ssl_set_bio(&mSsl, this, WriteCallback, ReadCallback, NULL);

+ 3 - 5
src/impl/icetransport.cpp

@@ -604,16 +604,14 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi
 }
 }
 
 
 IceTransport::~IceTransport() {
 IceTransport::~IceTransport() {
-	if (mTimeoutId) {
-		g_source_remove(mTimeoutId);
-		mTimeoutId = 0;
-	}
-
 	PLOG_DEBUG << "Destroying ICE transport";
 	PLOG_DEBUG << "Destroying ICE transport";
 	nice_agent_attach_recv(mNiceAgent.get(), mStreamId, 1, g_main_loop_get_context(MainLoop.get()),
 	nice_agent_attach_recv(mNiceAgent.get(), mStreamId, 1, g_main_loop_get_context(MainLoop.get()),
 	                       NULL, NULL);
 	                       NULL, NULL);
 	nice_agent_remove_stream(mNiceAgent.get(), mStreamId);
 	nice_agent_remove_stream(mNiceAgent.get(), mStreamId);
 	mNiceAgent.reset();
 	mNiceAgent.reset();
+
+	if (mTimeoutId)
+		g_source_remove(mTimeoutId);
 }
 }
 
 
 Description::Role IceTransport::role() const { return mRole; }
 Description::Role IceTransport::role() const { return mRole; }

+ 2 - 1
src/impl/tlstransport.cpp

@@ -336,6 +336,7 @@ TlsTransport::TlsTransport(variant<shared_ptr<TcpTransport>, shared_ptr<HttpProx
 		    &mConf, mIsClient ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
 		    &mConf, mIsClient ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
 		    MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT));
 		    MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT));
 
 
+		mbedtls_ssl_conf_max_version(&mConf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); // TLS 1.2
 		mbedtls_ssl_conf_authmode(&mConf, MBEDTLS_SSL_VERIFY_OPTIONAL);
 		mbedtls_ssl_conf_authmode(&mConf, MBEDTLS_SSL_VERIFY_OPTIONAL);
 		mbedtls_ssl_conf_rng(&mConf, mbedtls_ctr_drbg_random, &mDrbg);
 		mbedtls_ssl_conf_rng(&mConf, mbedtls_ctr_drbg_random, &mDrbg);
 
 
@@ -577,7 +578,7 @@ TlsTransport::TlsTransport(variant<shared_ptr<TcpTransport>, shared_ptr<HttpProx
 	PLOG_DEBUG << "Initializing TLS transport (OpenSSL)";
 	PLOG_DEBUG << "Initializing TLS transport (OpenSSL)";
 
 
 	try {
 	try {
-		if (!(mCtx = SSL_CTX_new(SSLv23_method()))) // version-flexible
+		if (!(mCtx = SSL_CTX_new(TLS_method()))) // version-flexible
 			throw std::runtime_error("Failed to create SSL context");
 			throw std::runtime_error("Failed to create SSL context");
 
 
 		openssl::check(SSL_CTX_set_cipher_list(mCtx, "ALL:!LOW:!EXP:!RC4:!MD5:@STRENGTH"),
 		openssl::check(SSL_CTX_set_cipher_list(mCtx, "ALL:!LOW:!EXP:!RC4:!MD5:@STRENGTH"),

+ 37 - 11
src/impl/track.cpp

@@ -75,24 +75,23 @@ void Track::close() {
 	resetCallbacks();
 	resetCallbacks();
 }
 }
 
 
+message_variant Track::trackMessageToVariant(message_ptr message) {
+	if (message->type == Message::Control)
+		return to_variant(*message); // The same message may be frowarded into multiple Tracks
+	else
+		return to_variant(std::move(*message));
+}
+
 optional<message_variant> Track::receive() {
 optional<message_variant> Track::receive() {
 	if (auto next = mRecvQueue.pop()) {
 	if (auto next = mRecvQueue.pop()) {
-		message_ptr message = *next;
-		if (message->type == Message::Control)
-			return to_variant(**next); // The same message may be frowarded into multiple Tracks
-		else
-			return to_variant(std::move(*message));
+		return trackMessageToVariant(*next);
 	}
 	}
 	return nullopt;
 	return nullopt;
 }
 }
 
 
 optional<message_variant> Track::peek() {
 optional<message_variant> Track::peek() {
 	if (auto next = mRecvQueue.peek()) {
 	if (auto next = mRecvQueue.peek()) {
-		message_ptr message = *next;
-		if (message->type == Message::Control)
-			return to_variant(**next); // The same message may be forwarded into multiple Tracks
-		else
-			return to_variant(std::move(*message));
+		return trackMessageToVariant(*next);
 	}
 	}
 	return nullopt;
 	return nullopt;
 }
 }
@@ -217,7 +216,7 @@ void Track::setMediaHandler(shared_ptr<MediaHandler> handler) {
 		mMediaHandler = handler;
 		mMediaHandler = handler;
 	}
 	}
 
 
-	if(handler)
+	if (handler)
 		handler->media(description());
 		handler->media(description());
 }
 }
 
 
@@ -226,4 +225,31 @@ shared_ptr<MediaHandler> Track::getMediaHandler() {
 	return mMediaHandler;
 	return mMediaHandler;
 }
 }
 
 
+void Track::onFrame(std::function<void(binary data, FrameInfo frame)> callback) {
+	frameCallback = callback;
+	flushPendingMessages();
+}
+
+void Track::flushPendingMessages() {
+	if (!mOpenTriggered)
+		return;
+
+	while (messageCallback || frameCallback) {
+		auto next = mRecvQueue.pop();
+		if (!next)
+			break;
+
+		auto message = next.value();
+		try {
+			if (message->frameInfo != nullptr && frameCallback) {
+				frameCallback(std::move(*message), std::move(*message->frameInfo));
+			} else if (message->frameInfo == nullptr && messageCallback) {
+				messageCallback(trackMessageToVariant(message));
+			}
+		} catch (const std::exception &e) {
+			PLOG_WARNING << "Uncaught exception in callback: " << e.what();
+		}
+	}
+}
+
 } // namespace rtc::impl
 } // namespace rtc::impl

+ 6 - 0
src/impl/track.hpp

@@ -38,6 +38,10 @@ public:
 	optional<message_variant> receive() override;
 	optional<message_variant> receive() override;
 	optional<message_variant> peek() override;
 	optional<message_variant> peek() override;
 	size_t availableAmount() const override;
 	size_t availableAmount() const override;
+	void flushPendingMessages() override;
+	message_variant trackMessageToVariant(message_ptr message);
+
+	void onFrame(std::function<void(binary data, FrameInfo frame)> callback);
 
 
 	bool isOpen() const;
 	bool isOpen() const;
 	bool isClosed() const;
 	bool isClosed() const;
@@ -71,6 +75,8 @@ private:
 	std::atomic<bool> mIsClosed = false;
 	std::atomic<bool> mIsClosed = false;
 
 
 	Queue<message_ptr> mRecvQueue;
 	Queue<message_ptr> mRecvQueue;
+
+	synchronized_callback<binary, FrameInfo> frameCallback;
 };
 };
 
 
 } // namespace rtc::impl
 } // namespace rtc::impl

+ 5 - 3
src/message.cpp

@@ -19,21 +19,23 @@ message_ptr make_message(size_t size, Message::Type type, unsigned int stream,
 }
 }
 
 
 message_ptr make_message(binary &&data, Message::Type type, unsigned int stream,
 message_ptr make_message(binary &&data, Message::Type type, unsigned int stream,
-                         shared_ptr<Reliability> reliability) {
+                         shared_ptr<Reliability> reliability, shared_ptr<FrameInfo> frameInfo) {
 	auto message = std::make_shared<Message>(std::move(data), type);
 	auto message = std::make_shared<Message>(std::move(data), type);
 	message->stream = stream;
 	message->stream = stream;
 	message->reliability = reliability;
 	message->reliability = reliability;
+	message->frameInfo = frameInfo;
 	return message;
 	return message;
 }
 }
 
 
 message_ptr make_message(size_t size, message_ptr orig) {
 message_ptr make_message(size_t size, message_ptr orig) {
-	if(!orig)
+	if (!orig)
 		return nullptr;
 		return nullptr;
 
 
 	auto message = std::make_shared<Message>(size, orig->type);
 	auto message = std::make_shared<Message>(size, orig->type);
-	std::copy(orig->begin(), std::min(orig->end(), orig->begin() + size), message->begin());
+	std::copy(orig->begin(), orig->begin() + std::min(size, orig->size()), message->begin());
 	message->stream = orig->stream;
 	message->stream = orig->stream;
 	message->reliability = orig->reliability;
 	message->reliability = orig->reliability;
+	message->frameInfo = orig->frameInfo;
 	return message;
 	return message;
 }
 }
 
 

+ 47 - 0
src/rtpdepacketizer.cpp

@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2024 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 "rtpdepacketizer.hpp"
+#include "rtp.hpp"
+
+#include "impl/logcounter.hpp"
+
+#include <cmath>
+#include <cstring>
+
+namespace rtc {
+
+void RtpDepacketizer::incoming([[maybe_unused]] message_vector &messages,
+                               [[maybe_unused]] const message_callback &send) {
+	message_vector result;
+	for (auto &message : messages) {
+		if (message->type == Message::Control) {
+			result.push_back(std::move(message));
+			continue;
+		}
+
+		if (message->size() < sizeof(RtpHeader)) {
+			PLOG_VERBOSE << "RTP packet is too small, size=" << message->size();
+			continue;
+		}
+
+		auto pkt = reinterpret_cast<const rtc::RtpHeader *>(message->data());
+		auto headerSize = sizeof(rtc::RtpHeader) + pkt->csrcCount() + pkt->getExtensionHeaderSize();
+		result.push_back(make_message(message->begin() + headerSize, message->end(),
+		                              Message::Binary, 0, nullptr,
+		                              std::make_shared<FrameInfo>(pkt->timestamp())));
+	}
+
+	messages.swap(result);
+}
+
+} // namespace rtc
+
+#endif /* RTC_ENABLE_MEDIA */

+ 4 - 0
src/track.cpp

@@ -70,4 +70,8 @@ bool Track::requestBitrate(unsigned int bitrate) {
 
 
 shared_ptr<MediaHandler> Track::getMediaHandler() { return impl()->getMediaHandler(); }
 shared_ptr<MediaHandler> Track::getMediaHandler() { return impl()->getMediaHandler(); }
 
 
+void Track::onFrame(std::function<void(binary data, FrameInfo frame)> callback) {
+	impl()->onFrame(callback);
+}
+
 } // namespace rtc
 } // namespace rtc