Skip to content

Commit

Permalink
Merge pull request #1082 from Sean-Der/h264-rtp-depacketizer
Browse files Browse the repository at this point in the history
Add H264RtpDepacketizer
  • Loading branch information
paullouisageneau authored Feb 22, 2024
2 parents d498c84 + 70a1fc3 commit b7f1f03
Show file tree
Hide file tree
Showing 5 changed files with 185 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 buildFrames(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/nalunit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ struct RTC_CPP_EXPORT NalUnitHeader {

bool forbiddenBit() const { return _first >> 7; }
uint8_t nri() const { return _first >> 5 & 0x03; }
uint8_t idc() const { return _first & 0x60; }
uint8_t unitType() const { return _first & 0x1F; }

void setForbiddenBit(bool isSet) { _first = (_first & 0x7F) | (isSet << 7); }
Expand Down
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
139 changes: 139 additions & 0 deletions src/h264rtpdepacketizer.cpp
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit b7f1f03

Please sign in to comment.