Skip to content

Commit

Permalink
Add H264RtpDepacketizer
Browse files Browse the repository at this point in the history
Inverse of H264RtpPacketizer. Takes incoming H264 packets and emits H264
NALUs.

Co-authored-by: Paul-Louis Ageneau <[email protected]>
  • Loading branch information
Sean-Der and paullouisageneau committed Feb 16, 2024
1 parent e87cbee commit ac302f1
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ set(LIBDATACHANNEL_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/rtcpsrreporter.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/rtppacketizer.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/h265rtppacketizer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/h265nalunit.cpp
Expand Down Expand Up @@ -110,6 +111,7 @@ set(LIBDATACHANNEL_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtcpsrreporter.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/rtppacketizer.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/h265rtppacketizer.hpp
${CMAKE_CURRENT_SOURCE_DIR}/include/rtc/h265nalunit.hpp
Expand Down
42 changes: 42 additions & 0 deletions include/rtc/h264rtpdepacketizer.hpp
Original file line number Diff line number Diff line change
@@ -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 buildFrame(message_vector::iterator firstPkt, message_vector::iterator lastPkt);
};

} // namespace rtc

#endif // RTC_ENABLE_MEDIA

#endif /* RTC_H264_RTP_DEPACKETIZER_H */
1 change: 1 addition & 0 deletions include/rtc/rtc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
// Media
#include "av1rtppacketizer.hpp"
#include "h264rtppacketizer.hpp"
#include "h264rtpdepacketizer.hpp"
#include "h265rtppacketizer.hpp"
#include "mediahandler.hpp"
#include "plihandler.hpp"
Expand Down
138 changes: 138 additions & 0 deletions src/h264rtpdepacketizer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/**
* 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 "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 naluTypeBitmask = 0x1F;
const uint8_t naluTypeSTAPA = 24;
const uint8_t naluTypeFUA = 28;
const uint8_t fuaEndBitmask = 0x40;
const uint8_t naluRefIdcBitmask = 0x60;

message_vector H264RtpDepacketizer::buildFrame(message_vector::iterator first,
message_vector::iterator last) {
message_vector out = {};
auto fua_buffer = std::vector<std::byte>{};

for (auto it = first; (it - 1) != last; it++) {
auto pkt = it->get();
auto pktParsed = reinterpret_cast<const rtc::RtpHeader *>(pkt->data());
auto headerSize =
sizeof(rtc::RtpHeader) + pktParsed->csrcCount() + pktParsed->getExtensionHeaderSize();
auto firstByte = std::to_integer<uint8_t>(pkt->at(headerSize));
auto secondByte = std::to_integer<uint8_t>(pkt->at(headerSize + 1));
auto naluType = firstByte & naluTypeBitmask;

if (fua_buffer.size() != 0 || naluType == naluTypeFUA) {
if (fua_buffer.size() == 0) {
fua_buffer.push_back(std::byte(0));
}

std::copy(pkt->begin() + headerSize + fuaHeaderSize, pkt->end(),
std::back_inserter(fua_buffer));

if ((secondByte & fuaEndBitmask) != 0) {
auto naluRefIdc = firstByte & naluRefIdcBitmask;
auto fragmentedNaluType = secondByte & naluTypeBitmask;
fua_buffer.at(0) = std::byte(naluRefIdc | fragmentedNaluType);

out.push_back(make_message(std::move(fua_buffer)));
fua_buffer.clear();
}
} else if (naluType > 0 && naluType < 24) {
out.push_back(make_message(pkt->begin() + headerSize, pkt->end()));
} else if (naluType == 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(make_message(message->begin(), message->end()));
}

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 first = mRtpBuffer.begin();
auto last = mRtpBuffer.begin() + (packets_in_timestamp - 1);

messages = buildFrame(first, last);
mRtpBuffer.erase(mRtpBuffer.begin(), mRtpBuffer.begin() + packets_in_timestamp);
}
}

} // namespace rtc

#endif // RTC_ENABLE_MEDIA

0 comments on commit ac302f1

Please sign in to comment.