Add RtpDepacketizerAv1::AssembleFrame function

Bug: webrtc:11042
Change-Id: I677fc6a9affacf3b7c80adc2c3493c16806db1f6
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/160003
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Philip Eliasson <philipel@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29862}
This commit is contained in:
Danil Chapovalov 2019-11-21 14:08:28 +01:00 committed by Commit Bot
parent ad020f5a5c
commit 038fd99780
4 changed files with 574 additions and 0 deletions

View File

@ -240,6 +240,7 @@ rtc_library("rtp_rtcp") {
"../../api/units:data_rate",
"../../api/units:time_delta",
"../../api/units:timestamp",
"../../api/video:encoded_image",
"../../api/video:video_bitrate_allocation",
"../../api/video:video_bitrate_allocator",
"../../api/video:video_codec_constants",

View File

@ -17,6 +17,7 @@
#include "rtc_base/byte_buffer.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc {
namespace {
@ -58,7 +59,111 @@ namespace {
// +-+-+-+-+-+-+-+-+
// | OBU payload |
// | ... |
class ArrayOfArrayViews {
public:
class const_iterator;
ArrayOfArrayViews() = default;
ArrayOfArrayViews(const ArrayOfArrayViews&) = default;
ArrayOfArrayViews& operator=(const ArrayOfArrayViews&) = default;
~ArrayOfArrayViews() = default;
const_iterator begin() const;
const_iterator end() const;
bool empty() const { return data_.empty(); }
size_t size() const { return size_; }
void CopyTo(uint8_t* destination, const_iterator first) const;
void Append(const uint8_t* data, size_t size) {
data_.emplace_back(data, size);
size_ += size;
}
private:
using Storage = absl::InlinedVector<rtc::ArrayView<const uint8_t>, 2>;
size_t size_ = 0;
Storage data_;
};
class ArrayOfArrayViews::const_iterator {
public:
const_iterator() = default;
const_iterator(const const_iterator&) = default;
const_iterator& operator=(const const_iterator&) = default;
const_iterator& operator++() {
if (++inner_ == outer_->size()) {
++outer_;
inner_ = 0;
}
return *this;
}
uint8_t operator*() const { return (*outer_)[inner_]; }
friend bool operator==(const const_iterator& lhs, const const_iterator& rhs) {
return lhs.outer_ == rhs.outer_ && lhs.inner_ == rhs.inner_;
}
private:
friend ArrayOfArrayViews;
const_iterator(ArrayOfArrayViews::Storage::const_iterator outer, size_t inner)
: outer_(outer), inner_(inner) {}
Storage::const_iterator outer_;
size_t inner_;
};
ArrayOfArrayViews::const_iterator ArrayOfArrayViews::begin() const {
return const_iterator(data_.begin(), 0);
}
ArrayOfArrayViews::const_iterator ArrayOfArrayViews::end() const {
return const_iterator(data_.end(), 0);
}
void ArrayOfArrayViews::CopyTo(uint8_t* destination,
const_iterator first) const {
if (first == end()) {
// Empty OBU payload. E.g. Temporal Delimiters are always empty.
return;
}
size_t first_chunk_size = first.outer_->size() - first.inner_;
memcpy(destination, first.outer_->data() + first.inner_, first_chunk_size);
destination += first_chunk_size;
for (auto it = std::next(first.outer_); it != data_.end(); ++it) {
memcpy(destination, it->data(), it->size());
destination += it->size();
}
}
struct ObuInfo {
// Size of the obu_header and obu_size fields in the ouput frame.
size_t prefix_size = 0;
// obu_header() and obu_size (leb128 encoded payload_size).
// obu_header can be up to 2 bytes, obu_size - up to 5.
std::array<uint8_t, 7> prefix;
// Size of the obu payload in the output frame, i.e. excluding header
size_t payload_size = 0;
// iterator pointing to the beginning of the obu payload.
ArrayOfArrayViews::const_iterator payload_offset;
// OBU payloads as written in the rtp packet payloads.
ArrayOfArrayViews data;
};
// Expect that majority of the frame won't use more than 4 obus.
// In a simple stream delta frame consist of single Frame OBU, while key frame
// also has Sequence Header OBU.
using VectorObuInfo = absl::InlinedVector<ObuInfo, 4>;
constexpr int kObuTypeSequenceHeader = 1;
constexpr uint8_t kObuSizePresentBit = 0b0'0000'010;
bool ObuHasExtension(uint8_t obu_header) {
return obu_header & 0b0'0000'100u;
}
bool ObuHasSize(uint8_t obu_header) {
return obu_header & kObuSizePresentBit;
}
int ObuType(uint8_t obu_header) {
return (obu_header & 0b0'1111'000u) >> 3;
@ -74,8 +179,191 @@ int RtpNumObus(uint8_t aggregation_header) { // 0 for any number of obus.
return (aggregation_header & 0b0011'0000u) >> 4;
}
// Reorgonizes array of rtp payloads into array of obus:
// fills ObuInfo::data field.
// Returns empty vector on error.
VectorObuInfo ParseObus(
rtc::ArrayView<const rtc::ArrayView<const uint8_t>> rtp_payloads) {
VectorObuInfo obu_infos;
bool expect_continues_obu = false;
for (rtc::ArrayView<const uint8_t> rtp_payload : rtp_payloads) {
rtc::ByteBufferReader payload(
reinterpret_cast<const char*>(rtp_payload.data()), rtp_payload.size());
uint8_t aggregation_header;
if (!payload.ReadUInt8(&aggregation_header)) {
RTC_DLOG(WARNING) << "Failed to find aggregation header in the packet.";
return {};
}
// Z-bit: 1 if the first OBU contained in the packet is a continuation of a
// previous OBU.
bool continues_obu = RtpStartsWithFragment(aggregation_header);
if (continues_obu != expect_continues_obu) {
RTC_DLOG(WARNING) << "Unexpected Z-bit " << continues_obu;
return {};
}
int num_expected_obus = RtpNumObus(aggregation_header);
if (payload.Length() == 0) {
// rtp packet has just the aggregation header. That may be valid only when
// there is exactly one fragment in the packet of size 0.
if (num_expected_obus != 1) {
RTC_DLOG(WARNING) << "Invalid packet with just an aggregation header.";
return {};
}
if (!continues_obu) {
// Empty packet just to notify there is a new OBU.
obu_infos.emplace_back();
}
expect_continues_obu = RtpEndsWithFragment(aggregation_header);
continue;
}
for (int obu_index = 1; payload.Length() > 0; ++obu_index) {
ObuInfo& obu_info = (obu_index == 1 && continues_obu)
? obu_infos.back()
: obu_infos.emplace_back();
uint64_t fragment_size;
// When num_expected_obus > 0, last OBU (fragment) is not preceeded by
// the size field. See W field in
// https://aomediacodec.github.io/av1-rtp-spec/#43-av1-aggregation-header
bool has_fragment_size = (obu_index != num_expected_obus);
if (has_fragment_size) {
if (!payload.ReadUVarint(&fragment_size)) {
RTC_DLOG(WARNING) << "Failed to read fragment size for obu #"
<< obu_index << "/" << num_expected_obus;
return {};
}
if (fragment_size > payload.Length()) {
// Malformed input: written size is larger than remaining buffer.
RTC_DLOG(WARNING) << "Malformed fragment size " << fragment_size
<< " is larger than remaining size "
<< payload.Length() << " while reading obu #"
<< obu_index << "/" << num_expected_obus;
return {};
}
} else {
fragment_size = payload.Length();
}
// While it is in-practical to pass empty fragments, it is still possible.
if (fragment_size > 0) {
obu_info.data.Append(reinterpret_cast<const uint8_t*>(payload.Data()),
fragment_size);
payload.Consume(fragment_size);
}
}
// Z flag should be same as Y flag of the next packet.
expect_continues_obu = RtpEndsWithFragment(aggregation_header);
}
if (expect_continues_obu) {
RTC_DLOG(WARNING) << "Last packet shouldn't have last obu fragmented.";
return {};
}
return obu_infos;
}
// Returns number of bytes consumed.
int WriteLeb128(uint32_t value, uint8_t* buffer) {
int size = 0;
while (value >= 0x80) {
buffer[size] = 0x80 | (value & 0x7F);
++size;
value >>= 7;
}
buffer[size] = value;
++size;
return size;
}
// Calculates sizes for the Obu, i.e. base on ObuInfo::data field calculates
// all other fields in the ObuInfo structure.
// Returns false if obu found to be misformed.
bool CalculateObuSizes(ObuInfo* obu_info) {
if (obu_info->data.empty()) {
RTC_DLOG(WARNING) << "Invalid bitstream: empty obu provided.";
return false;
}
auto it = obu_info->data.begin();
uint8_t obu_header = *it;
obu_info->prefix[0] = obu_header | kObuSizePresentBit;
obu_info->prefix_size = 1;
++it;
if (ObuHasExtension(obu_header)) {
if (it == obu_info->data.end()) {
return false;
}
obu_info->prefix[1] = *it; // obu_extension_header
obu_info->prefix_size = 2;
++it;
}
// Read, validate, and skip size, if present.
if (!ObuHasSize(obu_header)) {
obu_info->payload_size = obu_info->data.size() - obu_info->prefix_size;
} else {
// Read leb128 encoded field obu_size.
uint64_t obu_size_bytes = 0;
// Number of bytes obu_size field occupy in the bitstream.
int size_of_obu_size_bytes = 0;
uint8_t leb128_byte;
do {
if (it == obu_info->data.end() || size_of_obu_size_bytes >= 8) {
RTC_DLOG(WARNING)
<< "Failed to read obu_size. obu_size field is too long: "
<< size_of_obu_size_bytes << " bytes processed.";
return false;
}
leb128_byte = *it;
obu_size_bytes |= (leb128_byte & 0x7F) << (size_of_obu_size_bytes * 7);
++size_of_obu_size_bytes;
++it;
} while ((leb128_byte & 0x80) != 0);
obu_info->payload_size =
obu_info->data.size() - obu_info->prefix_size - size_of_obu_size_bytes;
if (obu_size_bytes != obu_info->payload_size) {
// obu_size was present in the bitstream and mismatches calculated size.
RTC_DLOG(WARNING) << "Mismatch in obu_size. signaled: " << obu_size_bytes
<< ", actual: " << obu_info->payload_size;
return false;
}
}
obu_info->payload_offset = it;
obu_info->prefix_size +=
WriteLeb128(rtc::dchecked_cast<uint32_t>(obu_info->payload_size),
obu_info->prefix.data() + obu_info->prefix_size);
return true;
}
} // namespace
rtc::scoped_refptr<EncodedImageBuffer> RtpDepacketizerAv1::AssembleFrame(
rtc::ArrayView<const rtc::ArrayView<const uint8_t>> rtp_payloads) {
VectorObuInfo obu_infos = ParseObus(rtp_payloads);
if (obu_infos.empty()) {
return nullptr;
}
size_t frame_size = 0;
for (ObuInfo& obu_info : obu_infos) {
if (!CalculateObuSizes(&obu_info)) {
return nullptr;
}
frame_size += (obu_info.prefix_size + obu_info.payload_size);
}
rtc::scoped_refptr<EncodedImageBuffer> bitstream =
EncodedImageBuffer::Create(frame_size);
uint8_t* write_at = bitstream->data();
for (const ObuInfo& obu_info : obu_infos) {
// Copy the obu_header and obu_size fields.
memcpy(write_at, obu_info.prefix.data(), obu_info.prefix_size);
write_at += obu_info.prefix_size;
// Copy the obu payload.
obu_info.data.CopyTo(write_at, obu_info.payload_offset);
write_at += obu_info.payload_size;
}
RTC_CHECK_EQ(write_at - bitstream->data(), bitstream->size());
return bitstream;
}
bool RtpDepacketizerAv1::Parse(ParsedPayload* parsed_payload,
const uint8_t* payload_data,
size_t payload_data_length) {

View File

@ -14,6 +14,9 @@
#include <stddef.h>
#include <stdint.h>
#include "api/array_view.h"
#include "api/scoped_refptr.h"
#include "api/video/encoded_image.h"
#include "modules/rtp_rtcp/source/rtp_format.h"
namespace webrtc {
@ -25,6 +28,9 @@ class RtpDepacketizerAv1 : public RtpDepacketizer {
RtpDepacketizerAv1& operator=(const RtpDepacketizerAv1&) = delete;
~RtpDepacketizerAv1() override = default;
static rtc::scoped_refptr<EncodedImageBuffer> AssembleFrame(
rtc::ArrayView<const rtc::ArrayView<const uint8_t>> rtp_payloads);
bool Parse(ParsedPayload* parsed_payload,
const uint8_t* payload_data,
size_t payload_data_length) override;

View File

@ -10,10 +10,14 @@
#include "modules/rtp_rtcp/source/rtp_depacketizer_av1.h"
#include "test/gmock.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
using ::testing::ElementsAre;
// Signals number of the OBU (fragments) in the packet.
constexpr uint8_t kObuCountAny = 0b0000'0000;
constexpr uint8_t kObuCountOne = 0b0001'0000;
@ -23,6 +27,8 @@ constexpr uint8_t kObuHeaderSequenceHeader = 0b0'0001'000;
constexpr uint8_t kObuHeaderTemporalDelimiter = 0b0'0010'000;
constexpr uint8_t kObuHeaderFrame = 0b0'0110'000;
constexpr uint8_t kObuHeaderHasSize = 0b0'0000'010;
TEST(RtpDepacketizerAv1Test, ParsePassFullRtpPayloadAsCodecPayload) {
const uint8_t packet[] = {(uint8_t{1} << 7) | kObuCountOne, 1, 2, 3, 4};
RtpDepacketizerAv1 depacketizer;
@ -192,5 +198,278 @@ TEST(RtpDepacketizerAv1Test, ParseSkipsEmptyFragments) {
EXPECT_TRUE(parsed.video.frame_type == VideoFrameType::kVideoFrameDelta);
}
TEST(RtpDepacketizerAv1Test, AssembleFrameSetsOBUPayloadSizeWhenAbsent) {
const uint8_t payload1[] = {0b00'01'0000, // aggregation header
0b0'0110'000, // / Frame
20, 30, 40}; // \ OBU
rtc::ArrayView<const uint8_t> payloads[] = {payload1};
auto frame = RtpDepacketizerAv1::AssembleFrame(payloads);
ASSERT_TRUE(frame);
rtc::ArrayView<const uint8_t> frame_view(*frame);
EXPECT_TRUE(frame_view[0] & kObuHeaderHasSize);
EXPECT_EQ(frame_view[1], 3);
}
TEST(RtpDepacketizerAv1Test, AssembleFrameSetsOBUPayloadSizeWhenPresent) {
const uint8_t payload1[] = {0b00'01'0000, // aggregation header
0b0'0110'010, // / Frame OBU header
3, // obu_size
20,
30,
40}; // \ obu_payload
rtc::ArrayView<const uint8_t> payloads[] = {payload1};
auto frame = RtpDepacketizerAv1::AssembleFrame(payloads);
ASSERT_TRUE(frame);
rtc::ArrayView<const uint8_t> frame_view(*frame);
EXPECT_TRUE(frame_view[0] & kObuHeaderHasSize);
EXPECT_EQ(frame_view[1], 3);
}
TEST(RtpDepacketizerAv1Test,
AssembleFrameSetsOBUPayloadSizeAfterExtensionWhenAbsent) {
const uint8_t payload1[] = {0b00'01'0000, // aggregation header
0b0'0110'100, // / Frame
0b010'01'000, // | extension_header
20, 30, 40}; // \ OBU
rtc::ArrayView<const uint8_t> payloads[] = {payload1};
auto frame = RtpDepacketizerAv1::AssembleFrame(payloads);
ASSERT_TRUE(frame);
rtc::ArrayView<const uint8_t> frame_view(*frame);
EXPECT_TRUE(frame_view[0] & kObuHeaderHasSize);
EXPECT_EQ(frame_view[2], 3);
}
TEST(RtpDepacketizerAv1Test,
AssembleFrameSetsOBUPayloadSizeAfterExtensionWhenPresent) {
const uint8_t payload1[] = {0b00'01'0000, // aggregation header
0b0'0110'110, // / Frame OBU header
0b010'01'000, // | extension_header
3, // | obu_size
20,
30,
40}; // \ obu_payload
rtc::ArrayView<const uint8_t> payloads[] = {payload1};
auto frame = RtpDepacketizerAv1::AssembleFrame(payloads);
ASSERT_TRUE(frame);
rtc::ArrayView<const uint8_t> frame_view(*frame);
EXPECT_TRUE(frame_view[0] & kObuHeaderHasSize);
EXPECT_EQ(frame_view[2], 3);
}
TEST(RtpDepacketizerAv1Test, AssembleFrameFromOnePacketWithOneObu) {
const uint8_t payload1[] = {0b00'01'0000, // aggregation header
0b0'0110'000, // / Frame
20}; // \ OBU
rtc::ArrayView<const uint8_t> payloads[] = {payload1};
auto frame = RtpDepacketizerAv1::AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame),
ElementsAre(0b0'0110'010, 1, 20));
}
TEST(RtpDepacketizerAv1Test, AssembleFrameFromOnePacketWithTwoObus) {
const uint8_t payload1[] = {0b00'10'0000, // aggregation header
2, // / Sequence
0b0'0001'000, // | Header
10, // \ OBU
0b0'0110'000, // / Frame
20}; // \ OBU
rtc::ArrayView<const uint8_t> payloads[] = {payload1};
auto frame = RtpDepacketizerAv1::AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame),
ElementsAre(0b0'0001'010, 1, 10, // Sequence Header OBU
0b0'0110'010, 1, 20)); // Frame OBU
}
TEST(RtpDepacketizerAv1Test, AssembleFrameFromTwoPacketsWithOneObu) {
const uint8_t payload1[] = {0b01'01'0000, // aggregation header
0b0'0110'000, 20, 30};
const uint8_t payload2[] = {0b10'01'0000, // aggregation header
40};
rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2};
auto frame = RtpDepacketizerAv1::AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame),
ElementsAre(0b0'0110'010, 3, 20, 30, 40));
}
TEST(RtpDepacketizerAv1Test, AssembleFrameFromTwoPacketsWithTwoObu) {
const uint8_t payload1[] = {0b01'10'0000, // aggregation header
2, // / Sequence
0b0'0001'000, // | Header
10, // \ OBU
0b0'0110'000, //
20,
30}; //
const uint8_t payload2[] = {0b10'01'0000, // aggregation header
40}; //
rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2};
auto frame = RtpDepacketizerAv1::AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame),
ElementsAre(0b0'0001'010, 1, 10, // SH
0b0'0110'010, 3, 20, 30, 40)); // Frame
}
TEST(RtpDepacketizerAv1Test,
AssembleFrameFromTwoPacketsWithManyObusSomeWithExtensions) {
const uint8_t payload1[] = {0b01'00'0000, // aggregation header
2, // /
0b0'0001'000, // | Sequence Header
10, // \ OBU
2, // /
0b0'0101'000, // | Metadata OBU
20, // \ without extension
4, // /
0b0'0101'100, // | Metadata OBU
0b001'10'000, // | with extension
20, // |
30, // \ metadata payload
5, // /
0b0'0110'100, // | Frame OBU
0b001'10'000, // | with extension
40, // |
50, // |
60}; // |
const uint8_t payload2[] = {0b10'01'0000, // aggregation header
70, 80, 90}; // \ tail of the frame OBU
rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2};
auto frame = RtpDepacketizerAv1::AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame),
ElementsAre( // Sequence header OBU
0b0'0001'010, 1, 10,
// Metadata OBU without extension
0b0'0101'010, 1, 20,
// Metadata OBU with extenion
0b0'0101'110, 0b001'10'000, 2, 20, 30,
// Frame OBU with extension
0b0'0110'110, 0b001'10'000, 6, 40, 50, 60, 70, 80, 90));
}
TEST(RtpDepacketizerAv1Test, AssembleFrameWithOneObuFromManyPackets) {
const uint8_t payload1[] = {0b01'01'0000, // aggregation header
0b0'0110'000, 11, 12};
const uint8_t payload2[] = {0b11'01'0000, // aggregation header
13, 14};
const uint8_t payload3[] = {0b11'01'0000, // aggregation header
15, 16, 17};
const uint8_t payload4[] = {0b10'01'0000, // aggregation header
18};
rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2, payload3,
payload4};
auto frame = RtpDepacketizerAv1::AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame),
ElementsAre(0b0'0110'010, 8, 11, 12, 13, 14, 15, 16, 17, 18));
}
TEST(RtpDepacketizerAv1Test,
AssembleFrameFromManyPacketsWithSomeObuBorderAligned) {
const uint8_t payload1[] = {0b01'10'0000, // aggregation header
3, // size of the 1st fragment
0b0'0011'000, // Frame header OBU
11,
12,
0b0'0100'000, // Tile group OBU
21,
22,
23};
const uint8_t payload2[] = {0b10'01'0000, // aggregation header
24, 25, 26, 27};
// payload2 ends an OBU, payload3 starts a new one.
const uint8_t payload3[] = {0b01'10'0000, // aggregation header
3, // size of the 1st fragment
0b0'0111'000, // Redundant frame header OBU
11,
12,
0b0'0100'000, // Tile group OBU
31,
32};
const uint8_t payload4[] = {0b10'01'0000, // aggregation header
33, 34, 35, 36};
rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2, payload3,
payload4};
auto frame = RtpDepacketizerAv1::AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame),
ElementsAre(0b0'0011'010, 2, 11, 12, // Frame header
0b0'0100'010, 7, 21, 22, 23, 24, 25, 26, 27, //
0b0'0111'010, 2, 11, 12, //
0b0'0100'010, 6, 31, 32, 33, 34, 35, 36));
}
TEST(RtpDepacketizerAv1Test,
AssembleFrameFromOnePacketsOneObuPayloadSize127Bytes) {
uint8_t payload1[4 + 127];
memset(payload1, 0, sizeof(payload1));
payload1[0] = 0b00'00'0000; // aggregation header
payload1[1] = 0x80; // leb128 encoded size of 128 bytes
payload1[2] = 0x01; // in two bytes
payload1[3] = 0b0'0110'000; // obu_header with size and extension bits unset.
payload1[4 + 42] = 0x42;
rtc::ArrayView<const uint8_t> payloads[] = {payload1};
auto frame = RtpDepacketizerAv1::AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_EQ(frame->size(), 2 + 127u);
rtc::ArrayView<const uint8_t> frame_view(*frame);
EXPECT_EQ(frame_view[0], 0b0'0110'010); // obu_header with size bit set.
EXPECT_EQ(frame_view[1], 127); // obu payload size, 1 byte enough to encode.
// Check 'random' byte from the payload is at the same 'random' offset.
EXPECT_EQ(frame_view[2 + 42], 0x42);
}
TEST(RtpDepacketizerAv1Test,
AssembleFrameFromTwoPacketsOneObuPayloadSize128Bytes) {
uint8_t payload1[3 + 32];
memset(payload1, 0, sizeof(payload1));
payload1[0] = 0b01'00'0000; // aggregation header
payload1[1] = 33; // leb128 encoded size of 33 bytes in one byte
payload1[2] = 0b0'0110'000; // obu_header with size and extension bits unset.
payload1[3 + 10] = 0x10;
uint8_t payload2[2 + 96];
memset(payload2, 0, sizeof(payload2));
payload2[0] = 0b10'00'0000; // aggregation header
payload2[1] = 96; // leb128 encoded size of 96 bytes in one byte
payload2[2 + 20] = 0x20;
rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2};
auto frame = RtpDepacketizerAv1::AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_EQ(frame->size(), 3 + 128u);
rtc::ArrayView<const uint8_t> frame_view(*frame);
EXPECT_EQ(frame_view[0], 0b0'0110'010); // obu_header with size bit set.
EXPECT_EQ(frame_view[1], 0x80); // obu payload size of 128 bytes.
EXPECT_EQ(frame_view[2], 0x01); // encoded in two byes
// Check two 'random' byte from the payload is at the same 'random' offset.
EXPECT_EQ(frame_view[3 + 10], 0x10);
EXPECT_EQ(frame_view[3 + 32 + 20], 0x20);
}
TEST(RtpDepacketizerAv1Test, AssembleFrameFromAlmostEmptyPacketStartingAnOBU) {
const uint8_t payload1[] = {0b01'01'0000};
const uint8_t payload2[] = {0b10'01'0000, 0b0'0110'000, 10, 20, 30};
rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2};
auto frame = RtpDepacketizerAv1::AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame),
ElementsAre(0b0'0110'010, 3, 10, 20, 30));
}
TEST(RtpDepacketizerAv1Test, AssembleFrameFromAlmostEmptyPacketFinishingAnOBU) {
const uint8_t payload1[] = {0b01'01'0000, 0b0'0110'000, 10, 20, 30};
const uint8_t payload2[] = {0b10'01'0000};
rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2};
auto frame = RtpDepacketizerAv1::AssembleFrame(payloads);
ASSERT_TRUE(frame);
EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame),
ElementsAre(0b0'0110'010, 3, 10, 20, 30));
}
} // namespace
} // namespace webrtc