Skip to content

Commit ac302f1

Browse files
Add H264RtpDepacketizer
Inverse of H264RtpPacketizer. Takes incoming H264 packets and emits H264 NALUs. Co-authored-by: Paul-Louis Ageneau <paul-louis@ageneau.org>
1 parent e87cbee commit ac302f1

File tree

4 files changed

+183
-0
lines changed

4 files changed

+183
-0
lines changed

CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ set(LIBDATACHANNEL_SOURCES
7676
${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpsrreporter.cpp
7777
${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizer.cpp
7878
${CMAKE_CURRENT_SOURCE_DIR}/src/h264rtppacketizer.cpp
79+
${CMAKE_CURRENT_SOURCE_DIR}/src/h264rtpdepacketizer.cpp
7980
${CMAKE_CURRENT_SOURCE_DIR}/src/nalunit.cpp
8081
${CMAKE_CURRENT_SOURCE_DIR}/src/h265rtppacketizer.cpp
8182
${CMAKE_CURRENT_SOURCE_DIR}/src/h265nalunit.cpp
@@ -110,6 +111,7 @@ set(LIBDATACHANNEL_HEADERS
110111
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpsrreporter.hpp
111112
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtppacketizer.hpp
112113
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h264rtppacketizer.hpp
114+
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h264rtpdepacketizer.hpp
113115
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/nalunit.hpp
114116
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h265rtppacketizer.hpp
115117
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h265nalunit.hpp

include/rtc/h264rtpdepacketizer.hpp

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Copyright (c) 2020 Staz Modrzynski
3+
* Copyright (c) 2020 Paul-Louis Ageneau
4+
*
5+
* This Source Code Form is subject to the terms of the Mozilla Public
6+
* License, v. 2.0. If a copy of the MPL was not distributed with this
7+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
8+
*/
9+
10+
#ifndef RTC_H264_RTP_DEPACKETIZER_H
11+
#define RTC_H264_RTP_DEPACKETIZER_H
12+
13+
#if RTC_ENABLE_MEDIA
14+
15+
#include "common.hpp"
16+
#include "mediahandler.hpp"
17+
#include "message.hpp"
18+
#include "rtp.hpp"
19+
20+
#include <iterator>
21+
22+
namespace rtc {
23+
24+
/// RTP depacketization for H264
25+
class RTC_CPP_EXPORT H264RtpDepacketizer : public MediaHandler {
26+
public:
27+
H264RtpDepacketizer() = default;
28+
virtual ~H264RtpDepacketizer() = default;
29+
30+
void incoming(message_vector &messages, const message_callback &send) override;
31+
32+
private:
33+
std::vector<message_ptr> mRtpBuffer;
34+
35+
message_vector buildFrame(message_vector::iterator firstPkt, message_vector::iterator lastPkt);
36+
};
37+
38+
} // namespace rtc
39+
40+
#endif // RTC_ENABLE_MEDIA
41+
42+
#endif /* RTC_H264_RTP_DEPACKETIZER_H */

include/rtc/rtc.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
// Media
3131
#include "av1rtppacketizer.hpp"
3232
#include "h264rtppacketizer.hpp"
33+
#include "h264rtpdepacketizer.hpp"
3334
#include "h265rtppacketizer.hpp"
3435
#include "mediahandler.hpp"
3536
#include "plihandler.hpp"

src/h264rtpdepacketizer.cpp

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/**
2+
* Copyright (c) 2023 Paul-Louis Ageneau
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
7+
*/
8+
9+
#if RTC_ENABLE_MEDIA
10+
11+
#include "h264rtpdepacketizer.hpp"
12+
#include "track.hpp"
13+
14+
#include "impl/logcounter.hpp"
15+
16+
#include <cmath>
17+
#include <utility>
18+
19+
#ifdef _WIN32
20+
#include <winsock2.h>
21+
#else
22+
#include <arpa/inet.h>
23+
#endif
24+
25+
namespace rtc {
26+
27+
const unsigned long stapaHeaderSize = 1;
28+
const auto fuaHeaderSize = 2;
29+
30+
const uint8_t naluTypeBitmask = 0x1F;
31+
const uint8_t naluTypeSTAPA = 24;
32+
const uint8_t naluTypeFUA = 28;
33+
const uint8_t fuaEndBitmask = 0x40;
34+
const uint8_t naluRefIdcBitmask = 0x60;
35+
36+
message_vector H264RtpDepacketizer::buildFrame(message_vector::iterator first,
37+
message_vector::iterator last) {
38+
message_vector out = {};
39+
auto fua_buffer = std::vector<std::byte>{};
40+
41+
for (auto it = first; (it - 1) != last; it++) {
42+
auto pkt = it->get();
43+
auto pktParsed = reinterpret_cast<const rtc::RtpHeader *>(pkt->data());
44+
auto headerSize =
45+
sizeof(rtc::RtpHeader) + pktParsed->csrcCount() + pktParsed->getExtensionHeaderSize();
46+
auto firstByte = std::to_integer<uint8_t>(pkt->at(headerSize));
47+
auto secondByte = std::to_integer<uint8_t>(pkt->at(headerSize + 1));
48+
auto naluType = firstByte & naluTypeBitmask;
49+
50+
if (fua_buffer.size() != 0 || naluType == naluTypeFUA) {
51+
if (fua_buffer.size() == 0) {
52+
fua_buffer.push_back(std::byte(0));
53+
}
54+
55+
std::copy(pkt->begin() + headerSize + fuaHeaderSize, pkt->end(),
56+
std::back_inserter(fua_buffer));
57+
58+
if ((secondByte & fuaEndBitmask) != 0) {
59+
auto naluRefIdc = firstByte & naluRefIdcBitmask;
60+
auto fragmentedNaluType = secondByte & naluTypeBitmask;
61+
fua_buffer.at(0) = std::byte(naluRefIdc | fragmentedNaluType);
62+
63+
out.push_back(make_message(std::move(fua_buffer)));
64+
fua_buffer.clear();
65+
}
66+
} else if (naluType > 0 && naluType < 24) {
67+
out.push_back(make_message(pkt->begin() + headerSize, pkt->end()));
68+
} else if (naluType == naluTypeSTAPA) {
69+
auto currOffset = stapaHeaderSize + headerSize;
70+
71+
while (currOffset < pkt->size()) {
72+
auto naluSize =
73+
uint16_t(pkt->at(currOffset)) << 8 | uint8_t(pkt->at(currOffset + 1));
74+
75+
currOffset += 2;
76+
77+
if (pkt->size() < currOffset + naluSize) {
78+
throw std::runtime_error("STAP-A declared size is larger then buffer");
79+
}
80+
81+
out.push_back(
82+
make_message(pkt->begin() + currOffset, pkt->begin() + currOffset + naluSize));
83+
currOffset += naluSize;
84+
}
85+
86+
} else {
87+
throw std::runtime_error("Unknown H264 RTP Packetization");
88+
}
89+
}
90+
91+
return out;
92+
}
93+
94+
void H264RtpDepacketizer::incoming(message_vector &messages, const message_callback &) {
95+
for (auto message : messages) {
96+
if (message->type == Message::Control) {
97+
continue; // RTCP
98+
}
99+
100+
if (message->size() < sizeof(RtpHeader)) {
101+
PLOG_VERBOSE << "RTP packet is too small, size=" << message->size();
102+
continue;
103+
}
104+
105+
mRtpBuffer.push_back(make_message(message->begin(), message->end()));
106+
}
107+
108+
while (mRtpBuffer.size() != 0) {
109+
uint32_t current_timestamp = 0;
110+
size_t packets_in_timestamp = 0;
111+
112+
for (const auto &pkt : mRtpBuffer) {
113+
auto p = reinterpret_cast<const rtc::RtpHeader *>(pkt->data());
114+
115+
if (current_timestamp == 0) {
116+
current_timestamp = p->timestamp();
117+
} else if (current_timestamp != p->timestamp()) {
118+
break;
119+
}
120+
121+
packets_in_timestamp++;
122+
}
123+
124+
if (packets_in_timestamp == mRtpBuffer.size()) {
125+
break;
126+
}
127+
128+
auto first = mRtpBuffer.begin();
129+
auto last = mRtpBuffer.begin() + (packets_in_timestamp - 1);
130+
131+
messages = buildFrame(first, last);
132+
mRtpBuffer.erase(mRtpBuffer.begin(), mRtpBuffer.begin() + packets_in_timestamp);
133+
}
134+
}
135+
136+
} // namespace rtc
137+
138+
#endif // RTC_ENABLE_MEDIA

0 commit comments

Comments
 (0)