-
-
Notifications
You must be signed in to change notification settings - Fork 376
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 AV1 Support #936
Merged
Merged
Add AV1 Support #936
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/** | ||
* 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/. | ||
*/ | ||
|
||
#ifndef RTC_AV1_PACKETIZATION_HANDLER_H | ||
#define RTC_AV1_PACKETIZATION_HANDLER_H | ||
|
||
#if RTC_ENABLE_MEDIA | ||
|
||
#include "av1rtppacketizer.hpp" | ||
#include "mediachainablehandler.hpp" | ||
#include "nalunit.hpp" | ||
|
||
namespace rtc { | ||
|
||
/// Handler for AV1 packetization | ||
class RTC_CPP_EXPORT AV1PacketizationHandler final : public MediaChainableHandler { | ||
public: | ||
/// Construct handler for AV1 packetization. | ||
/// @param packetizer RTP packetizer for AV1 | ||
AV1PacketizationHandler(shared_ptr<AV1RtpPacketizer> packetizer); | ||
}; | ||
|
||
} // namespace rtc | ||
|
||
#endif /* RTC_ENABLE_MEDIA */ | ||
|
||
#endif /* RTC_AV1_PACKETIZATION_HANDLER_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/** | ||
* 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/. | ||
*/ | ||
|
||
#ifndef RTC_AV1_RTP_PACKETIZER_H | ||
#define RTC_AV1_RTP_PACKETIZER_H | ||
|
||
#if RTC_ENABLE_MEDIA | ||
|
||
#include "mediahandlerrootelement.hpp" | ||
#include "nalunit.hpp" | ||
#include "rtppacketizer.hpp" | ||
|
||
namespace rtc { | ||
|
||
/// RTP packetization of AV1 payload | ||
class RTC_CPP_EXPORT AV1RtpPacketizer final : public RtpPacketizer, public MediaHandlerRootElement { | ||
shared_ptr<NalUnits> splitMessage(binary_ptr message); | ||
const uint16_t maximumFragmentSize; | ||
|
||
public: | ||
/// Default clock rate for AV1 in RTP | ||
inline static const uint32_t defaultClockRate = 90 * 1000; | ||
|
||
// Define how OBUs are seperated in a AV1 Sample | ||
enum class Packetization { | ||
Obu = RTC_OBU_PACKETIZED_OBU, | ||
TemporalUnit = RTC_OBU_PACKETIZED_TEMPORAL_UNIT, | ||
}; | ||
|
||
/// Constructs AV1 payload packetizer with given RTP configuration. | ||
/// @note RTP configuration is used in packetization process which may change some configuration | ||
/// properties such as sequence number. | ||
/// @param rtpConfig RTP configuration | ||
AV1RtpPacketizer(Packetization packetization, shared_ptr<RtpPacketizationConfig> rtpConfig, | ||
uint16_t maximumFragmentSize = NalUnits::defaultMaximumFragmentSize); | ||
|
||
ChainedOutgoingProduct processOutgoingBinaryMessage(ChainedMessagesProduct messages, | ||
message_ptr control) override; | ||
|
||
private: | ||
const Packetization packetization; | ||
std::shared_ptr<binary> sequenceHeader; | ||
|
||
std::vector<shared_ptr<binary>> packetizeObu(binary_ptr message, uint16_t maximumFragmentSize); | ||
}; | ||
|
||
} // namespace rtc | ||
|
||
#endif /* RTC_ENABLE_MEDIA */ | ||
|
||
#endif /* RTC_AV1_RTP_PACKETIZER_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/** | ||
* 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 "av1packetizationhandler.hpp" | ||
|
||
namespace rtc { | ||
|
||
AV1PacketizationHandler::AV1PacketizationHandler(shared_ptr<AV1RtpPacketizer> packetizer) | ||
: MediaChainableHandler(packetizer) {} | ||
|
||
} // namespace rtc | ||
|
||
#endif /* RTC_ENABLE_MEDIA */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
/** | ||
* 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 "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++; | ||
} | ||
|
||
// https://aomediacodec.github.io/av1-spec/#leb128 | ||
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; | ||
} | ||
|
||
/* | ||
* 0 1 2 3 4 5 6 7 | ||
* +-+-+-+-+-+-+-+-+ | ||
* |Z|Y| W |N|-|-|-| | ||
* +-+-+-+-+-+-+-+-+ | ||
* | ||
* Z: MUST be set to 1 if the first OBU element is an | ||
* OBU fragment that is a continuation of an OBU fragment | ||
* from the previous packet, and MUST be set to 0 otherwise. | ||
* | ||
* Y: MUST be set to 1 if the last OBU element is an OBU fragment | ||
* that will continue in the next packet, and MUST be set to 0 otherwise. | ||
* | ||
* W: two bit field that describes the number of OBU elements in the packet. | ||
* This field MUST be set equal to 0 or equal to the number of OBU elements | ||
* contained in the packet. If set to 0, each OBU element MUST be preceded by | ||
* a length field. If not set to 0 (i.e., W = 1, 2 or 3) the last OBU element | ||
* MUST NOT be preceded by a length field. Instead, the length of the last OBU | ||
* element contained in the packet can be calculated as follows: | ||
* Length of the last OBU element = | ||
* length of the RTP payload | ||
* - length of aggregation header | ||
* - length of previous OBU elements including length fields | ||
* | ||
* N: MUST be set to 1 if the packet is the first packet of a coded video sequence, and MUST be set | ||
* to 0 otherwise. | ||
* | ||
* https://aomediacodec.github.io/av1-rtp-spec/#44-av1-aggregation-header | ||
* | ||
**/ | ||
|
||
std::vector<binary_ptr> AV1RtpPacketizer::packetizeObu(binary_ptr message, | ||
uint16_t maximumFragmentSize) { | ||
|
||
std::vector<shared_ptr<binary>> payloads{}; | ||
size_t messageIndex = 0; | ||
|
||
if (message->size() < 1) { | ||
return payloads; | ||
} | ||
|
||
// Cache sequence header and packetize with next OBU | ||
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 byte leb128 */ 1 + int(sequenceHeader->size()); | ||
} | ||
|
||
auto payload = std::make_shared<binary>( | ||
std::min(size_t(maximumFragmentSize), messageRemaining + metadataSize)); | ||
auto payloadOffset = payloadHeaderSize; | ||
|
||
payload->at(0) = byte(obuCount) << wBitshift; | ||
|
||
// Packetize cached SequenceHeader | ||
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; | ||
} | ||
|
||
// Copy as much of OBU as possible into Payload | ||
auto payloadRemaining = payload->size() - payloadOffset; | ||
std::memcpy(payload->data() + payloadOffset, message->data() + messageIndex, | ||
payloadRemaining); | ||
messageRemaining -= payloadRemaining; | ||
messageIndex += payloadRemaining; | ||
|
||
// Does this Fragment contain an OBU that started in a previous payload | ||
if (payloads.size() > 0) { | ||
payload->at(0) ^= zMask; | ||
} | ||
|
||
// This OBU will be continued in next Payload | ||
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 maximumFragmentSize) | ||
: RtpPacketizer(rtpConfig), MediaHandlerRootElement(), maximumFragmentSize(maximumFragmentSize), | ||
packetization(packetization) {} | ||
|
||
ChainedOutgoingProduct | ||
AV1RtpPacketizer::processOutgoingBinaryMessage(ChainedMessagesProduct messages, | ||
message_ptr control) { | ||
ChainedMessagesProduct packets = std::make_shared<std::vector<binary_ptr>>(); | ||
for (auto message : *messages) { | ||
std::vector<binary_ptr> obus; | ||
|
||
if (packetization == AV1RtpPacketizer::Packetization::TemporalUnit) { | ||
obus = extractTemporalUnitObus(message); | ||
} else { | ||
obus.push_back(message); | ||
} | ||
|
||
for (auto obu : obus) { | ||
auto payloads = packetizeObu(obu, maximumFragmentSize); | ||
if (payloads.size() == 0) { | ||
continue; | ||
} | ||
|
||
unsigned i = 0; | ||
for (; i < payloads.size() - 1; i++) { | ||
packets->push_back(packetize(payloads[i], false)); | ||
} | ||
packets->push_back(packetize(payloads[i], true)); | ||
} | ||
} | ||
|
||
if (packets->size() == 0) { | ||
return ChainedOutgoingProduct(); | ||
} | ||
|
||
return {packets, control}; | ||
} | ||
|
||
} // namespace rtc | ||
|
||
#endif /* RTC_ENABLE_MEDIA */ |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm currently refactoring media handlers to simplify the design in #929. One of the main changes will be that packetization handlers are not necessary anymore. Don't worry about it, I'll merge this and add it to the refactoring.