Skip to content

Commit f5acd35

Browse files
edmondsSean-Der
authored andcommitted
H264RtpDepacketizer: De-packetize access units rather than individual NALUs
This commit updates the `H264RtpDepacketizer` to accumulate the NALUs for a particular RTP timestamp into a single output message, rather than returning each NALU as an individual message. This helps decoders which may want to see the non-VCL SPS/PPS/etc. NALUs in the same access unit as a VCL NALU rather than as standalone messages. Each NALU in the access unit buffer is prepended with an configurable H.264 start code
1 parent 1f4d08a commit f5acd35

File tree

2 files changed

+67
-52
lines changed

2 files changed

+67
-52
lines changed

include/rtc/h264rtpdepacketizer.hpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "common.hpp"
1616
#include "mediahandler.hpp"
1717
#include "message.hpp"
18+
#include "nalunit.hpp"
1819
#include "rtp.hpp"
1920

2021
#include <iterator>
@@ -24,14 +25,18 @@ namespace rtc {
2425
/// RTP depacketization for H264
2526
class RTC_CPP_EXPORT H264RtpDepacketizer : public MediaHandler {
2627
public:
27-
H264RtpDepacketizer() = default;
28+
using Separator = NalUnit::Separator;
29+
30+
H264RtpDepacketizer(Separator separator = Separator::LongStartSequence);
2831
virtual ~H264RtpDepacketizer() = default;
2932

3033
void incoming(message_vector &messages, const message_callback &send) override;
3134

3235
private:
3336
std::vector<message_ptr> mRtpBuffer;
37+
const NalUnit::Separator mSeparator;
3438

39+
void addSeparator(binary &accessUnit);
3540
message_vector buildFrames(message_vector::iterator firstPkt, message_vector::iterator lastPkt,
3641
uint8_t payloadType, uint32_t timestamp);
3742
};

src/h264rtpdepacketizer.cpp

+61-51
Original file line numberDiff line numberDiff line change
@@ -10,96 +10,105 @@
1010

1111
#include "h264rtpdepacketizer.hpp"
1212
#include "nalunit.hpp"
13-
#include "track.hpp"
1413

15-
#include "impl/logcounter.hpp"
16-
17-
#include <cmath>
18-
#include <utility>
19-
20-
#ifdef _WIN32
21-
#include <winsock2.h>
22-
#else
23-
#include <arpa/inet.h>
24-
#endif
14+
#include "impl/internals.hpp"
2515

2616
namespace rtc {
2717

28-
const unsigned long stapaHeaderSize = 1;
29-
const auto fuaHeaderSize = 2;
18+
const binary naluLongStartCode = {byte{0}, byte{0}, byte{0}, byte{1}};
19+
const binary naluShortStartCode = {byte{0}, byte{0}, byte{1}};
3020

3121
const uint8_t naluTypeSTAPA = 24;
3222
const uint8_t naluTypeFUA = 28;
3323

24+
H264RtpDepacketizer::H264RtpDepacketizer(Separator separator) : mSeparator(separator) {
25+
if (separator != Separator::StartSequence && separator != Separator::LongStartSequence &&
26+
separator != Separator::ShortStartSequence) {
27+
throw std::invalid_argument("Invalid separator");
28+
}
29+
}
30+
31+
void H264RtpDepacketizer::addSeparator(binary &accessUnit) {
32+
if (mSeparator == Separator::StartSequence || mSeparator == Separator::LongStartSequence) {
33+
accessUnit.insert(accessUnit.end(), naluLongStartCode.begin(), naluLongStartCode.end());
34+
} else if (mSeparator == Separator::ShortStartSequence) {
35+
accessUnit.insert(accessUnit.end(), naluShortStartCode.begin(), naluShortStartCode.end());
36+
} else {
37+
throw std::invalid_argument("Invalid separator");
38+
}
39+
}
40+
3441
message_vector H264RtpDepacketizer::buildFrames(message_vector::iterator begin,
35-
message_vector::iterator end, uint8_t payloadType, uint32_t timestamp) {
42+
message_vector::iterator end, uint8_t payloadType,
43+
uint32_t timestamp) {
3644
message_vector out = {};
37-
auto fua_buffer = std::vector<std::byte>{};
45+
auto accessUnit = binary{};
3846
auto frameInfo = std::make_shared<FrameInfo>(payloadType, timestamp);
47+
auto nFrags = 0;
3948

40-
for (auto it = begin; it != end; it++) {
49+
for (auto it = begin; it != end; ++it) {
4150
auto pkt = it->get();
4251
auto pktParsed = reinterpret_cast<const rtc::RtpHeader *>(pkt->data());
43-
auto headerSize =
44-
sizeof(rtc::RtpHeader) + pktParsed->csrcCount() + pktParsed->getExtensionHeaderSize();
45-
auto paddingSize = 0;
52+
auto rtpHeaderSize = pktParsed->getSize() + pktParsed->getExtensionHeaderSize();
53+
auto rtpPaddingSize = 0;
4654

4755
if (pktParsed->padding()) {
48-
paddingSize = std::to_integer<uint8_t>(pkt->at(pkt->size() - 1));
56+
rtpPaddingSize = std::to_integer<uint8_t>(pkt->at(pkt->size() - 1));
4957
}
5058

51-
if (pkt->size() == headerSize + paddingSize) {
59+
if (pkt->size() == rtpHeaderSize + rtpPaddingSize) {
5260
PLOG_VERBOSE << "H.264 RTP packet has empty payload";
5361
continue;
5462
}
5563

56-
auto nalUnitHeader = NalUnitHeader{std::to_integer<uint8_t>(pkt->at(headerSize))};
64+
auto nalUnitHeader = NalUnitHeader{std::to_integer<uint8_t>(pkt->at(rtpHeaderSize))};
5765

58-
if (fua_buffer.size() != 0 || nalUnitHeader.unitType() == naluTypeFUA) {
59-
if (fua_buffer.size() == 0) {
60-
fua_buffer.push_back(std::byte(0));
61-
}
62-
63-
auto nalUnitFragmentHeader =
64-
NalUnitFragmentHeader{std::to_integer<uint8_t>(pkt->at(headerSize + 1))};
65-
66-
std::copy(pkt->begin() + headerSize + fuaHeaderSize, pkt->end(),
67-
std::back_inserter(fua_buffer));
68-
69-
if (nalUnitFragmentHeader.isEnd()) {
70-
fua_buffer.at(0) =
71-
std::byte(nalUnitHeader.idc() | nalUnitFragmentHeader.unitType());
66+
if (nalUnitHeader.unitType() == naluTypeFUA) {
67+
auto nalUnitFragmentHeader = NalUnitFragmentHeader{
68+
std::to_integer<uint8_t>(pkt->at(rtpHeaderSize + sizeof(NalUnitHeader)))};
7269

73-
out.push_back(
74-
make_message(std::move(fua_buffer), Message::Binary, 0, nullptr, frameInfo));
75-
fua_buffer.clear();
70+
if (nFrags++ == 0) {
71+
addSeparator(accessUnit);
72+
accessUnit.emplace_back(
73+
byte(nalUnitHeader.idc() | nalUnitFragmentHeader.unitType()));
7674
}
75+
76+
accessUnit.insert(accessUnit.end(),
77+
pkt->begin() + rtpHeaderSize + sizeof(NalUnitHeader) +
78+
sizeof(NalUnitFragmentHeader),
79+
pkt->end());
7780
} else if (nalUnitHeader.unitType() > 0 && nalUnitHeader.unitType() < 24) {
78-
out.push_back(make_message(pkt->begin() + headerSize, pkt->end(), Message::Binary, 0,
79-
nullptr, frameInfo));
81+
addSeparator(accessUnit);
82+
accessUnit.insert(accessUnit.end(), pkt->begin() + rtpHeaderSize, pkt->end());
8083
} else if (nalUnitHeader.unitType() == naluTypeSTAPA) {
81-
auto currOffset = stapaHeaderSize + headerSize;
84+
auto currOffset = rtpHeaderSize + sizeof(NalUnitHeader);
8285

83-
while (currOffset < pkt->size()) {
84-
auto naluSize =
85-
uint16_t(pkt->at(currOffset)) << 8 | uint8_t(pkt->at(currOffset + 1));
86+
while (currOffset + sizeof(uint16_t) < pkt->size()) {
87+
auto naluSize = std::to_integer<uint16_t>(pkt->at(currOffset)) << 8 |
88+
std::to_integer<uint16_t>(pkt->at(currOffset + 1));
8689

87-
currOffset += 2;
90+
currOffset += sizeof(uint16_t);
8891

8992
if (pkt->size() < currOffset + naluSize) {
90-
throw std::runtime_error("STAP-A declared size is larger then buffer");
93+
throw std::runtime_error("H264 STAP-A declared size is larger than buffer");
9194
}
9295

93-
out.push_back(make_message(pkt->begin() + currOffset,
94-
pkt->begin() + currOffset + naluSize, Message::Binary, 0,
95-
nullptr, frameInfo));
96+
addSeparator(accessUnit);
97+
accessUnit.insert(accessUnit.end(), pkt->begin() + currOffset,
98+
pkt->begin() + currOffset + naluSize);
99+
96100
currOffset += naluSize;
97101
}
98102
} else {
99103
throw std::runtime_error("Unknown H264 RTP Packetization");
100104
}
101105
}
102106

107+
if (!accessUnit.empty()) {
108+
out.emplace_back(make_message(accessUnit.begin(), accessUnit.end(), Message::Binary, 0,
109+
nullptr, frameInfo));
110+
}
111+
103112
return out;
104113
}
105114

@@ -131,7 +140,8 @@ void H264RtpDepacketizer::incoming(message_vector &messages, const message_callb
131140

132141
if (current_timestamp == 0) {
133142
current_timestamp = p->timestamp();
134-
payload_type = p->payloadType(); // should all be the same for data of the same codec
143+
payload_type =
144+
p->payloadType(); // should all be the same for data of the same codec
135145
} else if (current_timestamp != p->timestamp()) {
136146
break;
137147
}

0 commit comments

Comments
 (0)