#if RTC_ENABLE_MEDIA
#include "av1rtppacketizer.hpp"
#include "impl/internals.hpp"
namespace rtc {
const auto payloadHeaderSize = 1;
const auto zMask = byte(0b10000000);
const auto yMask = byte(0b01000000);
const auto nMask = byte(0b00001000);
const auto wBitshift = 4;
const auto obuFrameTypeMask = byte(0b01111000);
const auto obuFrameTypeBitshift = 3;
const auto obuHeaderSize = 1;
const auto obuHasExtensionMask = byte(0b00000100);
const auto obuHasSizeMask = byte(0b00000010);
const auto obuFrameTypeSequenceHeader = byte(1);
const auto obuTemporalUnitDelimiter = std::vector<byte>{byte(0x12), byte(0x00)};
const auto oneByteLeb128Size = 1;
const uint8_t sevenLsbBitmask = 0b01111111;
const uint8_t msbBitmask = 0b10000000;
std::vector<binary_ptr> extractTemporalUnitObus(binary_ptr message) {
std::vector<shared_ptr<binary>> obus{};
if (message->size() <= 2 || (message->at(0) != obuTemporalUnitDelimiter.at(0)) ||
(message->at(1) != obuTemporalUnitDelimiter.at(1))) {
return obus;
}
size_t messageIndex = 2;
while (messageIndex < message->size()) {
if ((message->at(messageIndex) & obuHasSizeMask) == byte(0)) {
return obus;
}
if ((message->at(messageIndex) & obuHasExtensionMask) != byte(0)) {
messageIndex++;
}
uint32_t obuLength = 0;
uint8_t leb128Size = 0;
while (leb128Size < 8) {
auto leb128Index = messageIndex + leb128Size + obuHeaderSize;
if (message->size() < leb128Index) {
break;
}
auto leb128_byte = uint8_t(message->at(leb128Index));
obuLength |= ((leb128_byte & sevenLsbBitmask) << (leb128Size * 7));
leb128Size++;
if (!(leb128_byte & msbBitmask)) {
break;
}
}
obus.push_back(std::make_shared<binary>(message->begin() + messageIndex,
message->begin() + messageIndex + obuHeaderSize +
leb128Size + obuLength));
messageIndex += obuHeaderSize + leb128Size + obuLength;
}
return obus;
}
std::vector<binary_ptr> AV1RtpPacketizer::packetizeObu(binary_ptr message,
uint16_t maxFragmentSize) {
std::vector<shared_ptr<binary>> payloads{};
size_t messageIndex = 0;
if (message->size() < 1) {
return payloads;
}
auto frameType = (message->at(0) & obuFrameTypeMask) >> obuFrameTypeBitshift;
if (frameType == obuFrameTypeSequenceHeader) {
sequenceHeader = std::make_shared<binary>(message->begin(), message->end());
return payloads;
}
size_t messageRemaining = message->size();
while (messageRemaining > 0) {
auto obuCount = 1;
auto metadataSize = payloadHeaderSize;
if (sequenceHeader != nullptr) {
obuCount++;
metadataSize += 1 + int(sequenceHeader->size());
}
auto payload = std::make_shared<binary>(
std::min(size_t(maxFragmentSize), messageRemaining + metadataSize));
auto payloadOffset = payloadHeaderSize;
payload->at(0) = byte(obuCount) << wBitshift;
if (obuCount == 2) {
payload->at(0) ^= nMask;
payload->at(1) = byte(sequenceHeader->size() & sevenLsbBitmask);
payloadOffset += oneByteLeb128Size;
std::memcpy(payload->data() + payloadOffset, sequenceHeader->data(),
sequenceHeader->size());
payloadOffset += int(sequenceHeader->size());
sequenceHeader = nullptr;
}
auto payloadRemaining = payload->size() - payloadOffset;
std::memcpy(payload->data() + payloadOffset, message->data() + messageIndex,
payloadRemaining);
messageRemaining -= payloadRemaining;
messageIndex += payloadRemaining;
if (payloads.size() > 0) {
payload->at(0) ^= zMask;
}
if (messageIndex < message->size()) {
payload->at(0) ^= yMask;
}
payloads.push_back(payload);
}
return payloads;
}
AV1RtpPacketizer::AV1RtpPacketizer(AV1RtpPacketizer::Packetization packetization,
shared_ptr<RtpPacketizationConfig> rtpConfig,
uint16_t maxFragmentSize)
: RtpPacketizer(rtpConfig), maxFragmentSize(maxFragmentSize),
packetization(packetization) {}
void AV1RtpPacketizer::outgoing(message_vector &messages,
[[maybe_unused]] const message_callback &send) {
message_vector result;
for (const auto &message : messages) {
std::vector<binary_ptr> obus;
if (packetization == AV1RtpPacketizer::Packetization::TemporalUnit) {
obus = extractTemporalUnitObus(message);
} else {
obus.push_back(message);
}
std::vector<binary_ptr> fragments;
for (auto obu : obus) {
auto p = packetizeObu(obu, maxFragmentSize);
fragments.insert(fragments.end(), p.begin(), p.end());
}
if (fragments.size() == 0)
continue;
for (size_t i = 0; i < fragments.size() - 1; i++)
result.push_back(packetize(fragments[i], false));
result.push_back(packetize(fragments[fragments.size() - 1], true));
}
messages.swap(result);
}
}
#endif