Browse Source

Merge pull request #1082 from Sean-Der/h264-rtp-depacketizer

Add H264RtpDepacketizer
Paul-Louis Ageneau 1 year ago
parent
commit
b7f1f03a52
5 changed files with 185 additions and 0 deletions
  1. 2 0
      CMakeLists.txt
  2. 42 0
      include/rtc/h264rtpdepacketizer.hpp
  3. 1 0
      include/rtc/nalunit.hpp
  4. 1 0
      include/rtc/rtc.hpp
  5. 139 0
      src/h264rtpdepacketizer.cpp

+ 2 - 0
CMakeLists.txt

@@ -76,6 +76,7 @@ set(LIBDATACHANNEL_SOURCES
 	${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/h264rtppacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/h264rtppacketizer.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/h264rtpdepacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/nalunit.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/nalunit.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/h265rtppacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/h265rtppacketizer.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/h265nalunit.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/h265nalunit.cpp
@@ -110,6 +111,7 @@ set(LIBDATACHANNEL_HEADERS
 	${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/h264rtppacketizer.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h264rtppacketizer.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
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h265rtppacketizer.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h265rtppacketizer.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h265nalunit.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h265nalunit.hpp

+ 42 - 0
include/rtc/h264rtpdepacketizer.hpp

@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2020 Staz Modrzynski
+ * Copyright (c) 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_H264_RTP_DEPACKETIZER_H
+#define RTC_H264_RTP_DEPACKETIZER_H
+
+#if RTC_ENABLE_MEDIA
+
+#include "common.hpp"
+#include "mediahandler.hpp"
+#include "message.hpp"
+#include "rtp.hpp"
+
+#include <iterator>
+
+namespace rtc {
+
+/// RTP depacketization for H264
+class RTC_CPP_EXPORT H264RtpDepacketizer : public MediaHandler {
+public:
+	H264RtpDepacketizer() = default;
+	virtual ~H264RtpDepacketizer() = default;
+
+	void incoming(message_vector &messages, const message_callback &send) override;
+
+private:
+	std::vector<message_ptr> mRtpBuffer;
+
+	message_vector buildFrames(message_vector::iterator firstPkt, message_vector::iterator lastPkt);
+};
+
+} // namespace rtc
+
+#endif // RTC_ENABLE_MEDIA
+
+#endif /* RTC_H264_RTP_DEPACKETIZER_H */

+ 1 - 0
include/rtc/nalunit.hpp

@@ -25,6 +25,7 @@ struct RTC_CPP_EXPORT NalUnitHeader {
 
 
 	bool forbiddenBit() const { return _first >> 7; }
 	bool forbiddenBit() const { return _first >> 7; }
 	uint8_t nri() const { return _first >> 5 & 0x03; }
 	uint8_t nri() const { return _first >> 5 & 0x03; }
+	uint8_t idc() const { return _first & 0x60; }
 	uint8_t unitType() const { return _first & 0x1F; }
 	uint8_t unitType() const { return _first & 0x1F; }
 
 
 	void setForbiddenBit(bool isSet) { _first = (_first & 0x7F) | (isSet << 7); }
 	void setForbiddenBit(bool isSet) { _first = (_first & 0x7F) | (isSet << 7); }

+ 1 - 0
include/rtc/rtc.hpp

@@ -30,6 +30,7 @@
 // Media
 // Media
 #include "av1rtppacketizer.hpp"
 #include "av1rtppacketizer.hpp"
 #include "h264rtppacketizer.hpp"
 #include "h264rtppacketizer.hpp"
+#include "h264rtpdepacketizer.hpp"
 #include "h265rtppacketizer.hpp"
 #include "h265rtppacketizer.hpp"
 #include "mediahandler.hpp"
 #include "mediahandler.hpp"
 #include "plihandler.hpp"
 #include "plihandler.hpp"

+ 139 - 0
src/h264rtpdepacketizer.cpp

@@ -0,0 +1,139 @@
+/**
+ * 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 "h264rtpdepacketizer.hpp"
+#include "nalunit.hpp"
+#include "track.hpp"
+
+#include "impl/logcounter.hpp"
+
+#include <cmath>
+#include <utility>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+namespace rtc {
+
+const unsigned long stapaHeaderSize = 1;
+const auto fuaHeaderSize = 2;
+
+const uint8_t naluTypeSTAPA = 24;
+const uint8_t naluTypeFUA = 28;
+
+message_vector H264RtpDepacketizer::buildFrames(message_vector::iterator begin,
+                                                message_vector::iterator end) {
+	message_vector out = {};
+	auto fua_buffer = std::vector<std::byte>{};
+
+	for (auto it = begin; it != end; it++) {
+		auto pkt = it->get();
+		auto pktParsed = reinterpret_cast<const rtc::RtpHeader *>(pkt->data());
+		auto headerSize =
+		    sizeof(rtc::RtpHeader) + pktParsed->csrcCount() + pktParsed->getExtensionHeaderSize();
+		auto nalUnitHeader = NalUnitHeader{std::to_integer<uint8_t>(pkt->at(headerSize))};
+
+		if (fua_buffer.size() != 0 || nalUnitHeader.unitType() == naluTypeFUA) {
+			if (fua_buffer.size() == 0) {
+				fua_buffer.push_back(std::byte(0));
+			}
+
+			auto nalUnitFragmentHeader =
+			    NalUnitFragmentHeader{std::to_integer<uint8_t>(pkt->at(headerSize + 1))};
+
+			std::copy(pkt->begin() + headerSize + fuaHeaderSize, pkt->end(),
+			          std::back_inserter(fua_buffer));
+
+			if (nalUnitFragmentHeader.isEnd()) {
+				fua_buffer.at(0) =
+				    std::byte(nalUnitHeader.idc() | nalUnitFragmentHeader.unitType());
+
+				out.push_back(make_message(std::move(fua_buffer)));
+				fua_buffer.clear();
+			}
+		} else if (nalUnitHeader.unitType() > 0 && nalUnitHeader.unitType() < 24) {
+			out.push_back(make_message(pkt->begin() + headerSize, pkt->end()));
+		} else if (nalUnitHeader.unitType() == naluTypeSTAPA) {
+			auto currOffset = stapaHeaderSize + headerSize;
+
+			while (currOffset < pkt->size()) {
+				auto naluSize =
+				    uint16_t(pkt->at(currOffset)) << 8 | uint8_t(pkt->at(currOffset + 1));
+
+				currOffset += 2;
+
+				if (pkt->size() < currOffset + naluSize) {
+					throw std::runtime_error("STAP-A declared size is larger then buffer");
+				}
+
+				out.push_back(
+				    make_message(pkt->begin() + currOffset, pkt->begin() + currOffset + naluSize));
+				currOffset += naluSize;
+			}
+
+		} else {
+			throw std::runtime_error("Unknown H264 RTP Packetization");
+		}
+	}
+
+	return out;
+}
+
+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();
+
+	while (mRtpBuffer.size() != 0) {
+		uint32_t current_timestamp = 0;
+		size_t packets_in_timestamp = 0;
+
+		for (const auto &pkt : mRtpBuffer) {
+			auto p = reinterpret_cast<const rtc::RtpHeader *>(pkt->data());
+
+			if (current_timestamp == 0) {
+				current_timestamp = p->timestamp();
+			} else if (current_timestamp != p->timestamp()) {
+				break;
+			}
+
+			packets_in_timestamp++;
+		}
+
+		if (packets_in_timestamp == mRtpBuffer.size()) {
+			break;
+		}
+
+		auto begin = mRtpBuffer.begin();
+		auto end = mRtpBuffer.begin() + (packets_in_timestamp - 1);
+
+		auto frames = buildFrames(begin, end + 1);
+		messages.insert(messages.end(), frames.begin(), frames.end());
+		mRtpBuffer.erase(mRtpBuffer.begin(), mRtpBuffer.begin() + packets_in_timestamp);
+	}
+}
+
+} // namespace rtc
+
+#endif // RTC_ENABLE_MEDIA