#if RTC_ENABLE_MEDIA
#include "av1rtppacketizer.hpp"
#include "impl/internals.hpp"
#include <algorithm>
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;
AV1RtpPacketizer::AV1RtpPacketizer(Packetization packetization,
shared_ptr<RtpPacketizationConfig> rtpConfig,
size_t maxFragmentSize)
: RtpPacketizer(rtpConfig), mPacketization(packetization), mMaxFragmentSize(maxFragmentSize) {}
std::vector<binary> AV1RtpPacketizer::extractTemporalUnitObus(const binary &data) {
std::vector<binary> obus;
if (data.size() <= 2 || (data.at(0) != obuTemporalUnitDelimiter.at(0)) ||
(data.at(1) != obuTemporalUnitDelimiter.at(1))) {
return {};
}
size_t index = 2;
while (index < data.size()) {
if ((data.at(index) & obuHasSizeMask) == byte(0)) {
return obus;
}
if ((data.at(index) & obuHasExtensionMask) != byte(0)) {
index++;
}
uint32_t obuLength = 0;
uint8_t leb128Size = 0;
while (leb128Size < 8) {
auto leb128Index = index + leb128Size + obuHeaderSize;
if (data.size() < leb128Index) {
break;
}
auto leb128_byte = uint8_t(data.at(leb128Index));
obuLength |= ((leb128_byte & sevenLsbBitmask) << (leb128Size * 7));
leb128Size++;
if (!(leb128_byte & msbBitmask)) {
break;
}
}
obus.emplace_back(data.begin() + index,
data.begin() + index + obuHeaderSize + leb128Size + obuLength);
index += obuHeaderSize + leb128Size + obuLength;
}
return obus;
}
std::vector<binary> AV1RtpPacketizer::fragment(binary data) {
if (mPacketization == AV1RtpPacketizer::Packetization::TemporalUnit) {
std::vector<binary> result;
auto obus = extractTemporalUnitObus(data);
for (auto obu : obus) {
auto fragments = fragmentObu(obu);
result.reserve(result.size() + fragments.size());
for(auto &fragment : fragments)
result.push_back(std::move(fragment));
}
return result;
} else {
return fragmentObu(data);
}
}
std::vector<binary> AV1RtpPacketizer::fragmentObu(const binary &data) {
std::vector<binary> payloads;
if (data.size() < 1)
return {};
auto frameType = (data.at(0) & obuFrameTypeMask) >> obuFrameTypeBitshift;
if (frameType == obuFrameTypeSequenceHeader) {
mSequenceHeader = std::make_unique<binary>(data.begin(), data.end());
return {};
}
size_t index = 0;
size_t remaining = data.size();
while (remaining > 0) {
size_t obuCount = 1;
size_t metadataSize = payloadHeaderSize;
if (mSequenceHeader) {
obuCount++;
metadataSize += 1 + int(mSequenceHeader->size()); }
binary payload(std::min(size_t(mMaxFragmentSize), remaining + metadataSize));
size_t payloadOffset = payloadHeaderSize;
payload.at(0) = byte(obuCount) << wBitshift;
if (obuCount == 2) {
payload.at(0) ^= nMask;
payload.at(1) = byte(mSequenceHeader->size() & sevenLsbBitmask);
payloadOffset += oneByteLeb128Size;
std::memcpy(payload.data() + payloadOffset, mSequenceHeader->data(),
mSequenceHeader->size());
payloadOffset += int(mSequenceHeader->size());
mSequenceHeader = nullptr;
}
size_t payloadRemaining = payload.size() - payloadOffset;
std::memcpy(payload.data() + payloadOffset, data.data() + index,
payloadRemaining);
remaining -= payloadRemaining;
index += payloadRemaining;
if (payloads.size() > 0) {
payload.at(0) ^= zMask;
}
if (index < data.size()) {
payload.at(0) ^= yMask;
}
payloads.push_back(std::move(payload));
}
return payloads;
}
}
#endif