Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add H264RtpDepacketizer #1082

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading