blob: 84f5020ec39b26d8f8e36477c2fba348bcb08e56 [file] [log] [blame]
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
#include "common_video/test/utilities.h"
#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
#include "rtc_base/random.h"
#include "test/gmock.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
using ::testing::Each;
using ::testing::ElementsAre;
using ::testing::ElementsAreArray;
using ::testing::IsEmpty;
using ::testing::make_tuple;
constexpr int8_t kPayloadType = 100;
constexpr uint32_t kSsrc = 0x12345678;
constexpr uint16_t kSeqNum = 0x1234;
constexpr uint8_t kSeqNumFirstByte = kSeqNum >> 8;
constexpr uint8_t kSeqNumSecondByte = kSeqNum & 0xff;
constexpr uint32_t kTimestamp = 0x65431278;
constexpr uint8_t kTransmissionOffsetExtensionId = 1;
constexpr uint8_t kAudioLevelExtensionId = 9;
constexpr uint8_t kRtpStreamIdExtensionId = 0xa;
constexpr uint8_t kRtpMidExtensionId = 0xb;
constexpr uint8_t kVideoTimingExtensionId = 0xc;
constexpr uint8_t kTwoByteExtensionId = 0xf0;
constexpr int32_t kTimeOffset = 0x56ce;
constexpr bool kVoiceActive = true;
constexpr uint8_t kAudioLevel = 0x5a;
constexpr char kStreamId[] = "streamid";
constexpr char kMid[] = "mid";
constexpr char kLongMid[] = "extra-long string to test two-byte header";
constexpr size_t kMaxPaddingSize = 224u;
// clang-format off
constexpr uint8_t kMinimumPacket[] = {
0x80, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
0x65, 0x43, 0x12, 0x78,
0x12, 0x34, 0x56, 0x78};
constexpr uint8_t kPacketWithTO[] = {
0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
0x65, 0x43, 0x12, 0x78,
0x12, 0x34, 0x56, 0x78,
0xbe, 0xde, 0x00, 0x01,
0x12, 0x00, 0x56, 0xce};
constexpr uint8_t kPacketWithTOAndAL[] = {
0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
0x65, 0x43, 0x12, 0x78,
0x12, 0x34, 0x56, 0x78,
0xbe, 0xde, 0x00, 0x02,
0x12, 0x00, 0x56, 0xce,
0x90, 0x80|kAudioLevel, 0x00, 0x00};
constexpr uint8_t kPacketWithTwoByteExtensionIdLast[] = {
0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
0x65, 0x43, 0x12, 0x78,
0x12, 0x34, 0x56, 0x78,
0x10, 0x00, 0x00, 0x04,
0x01, 0x03, 0x00, 0x56,
0xce, 0x09, 0x01, 0x80|kAudioLevel,
kTwoByteExtensionId, 0x03, 0x00, 0x30, // => 0x00 0x30 0x22
0x22, 0x00, 0x00, 0x00}; // => Playout delay.min_ms = 3*10
// => Playout delay.max_ms = 34*10
constexpr uint8_t kPacketWithTwoByteExtensionIdFirst[] = {
0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
0x65, 0x43, 0x12, 0x78,
0x12, 0x34, 0x56, 0x78,
0x10, 0x00, 0x00, 0x04,
kTwoByteExtensionId, 0x03, 0x00, 0x30, // => 0x00 0x30 0x22
0x22, 0x01, 0x03, 0x00, // => Playout delay.min_ms = 3*10
0x56, 0xce, 0x09, 0x01, // => Playout delay.max_ms = 34*10
0x80|kAudioLevel, 0x00, 0x00, 0x00};
constexpr uint8_t kPacketWithTOAndALInvalidPadding[] = {
0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
0x65, 0x43, 0x12, 0x78,
0x12, 0x34, 0x56, 0x78,
0xbe, 0xde, 0x00, 0x03,
0x12, 0x00, 0x56, 0xce,
0x00, 0x02, 0x00, 0x00, // 0x02 is invalid padding, parsing should stop.
0x90, 0x80|kAudioLevel, 0x00, 0x00};
constexpr uint8_t kPacketWithTOAndALReservedExtensionId[] = {
0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
0x65, 0x43, 0x12, 0x78,
0x12, 0x34, 0x56, 0x78,
0xbe, 0xde, 0x00, 0x03,
0x12, 0x00, 0x56, 0xce,
0x00, 0xF0, 0x00, 0x00, // F is a reserved id, parsing should stop.
0x90, 0x80|kAudioLevel, 0x00, 0x00};
constexpr uint8_t kPacketWithRsid[] = {
0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
0x65, 0x43, 0x12, 0x78,
0x12, 0x34, 0x56, 0x78,
0xbe, 0xde, 0x00, 0x03,
0xa7, 's', 't', 'r',
'e', 'a', 'm', 'i',
'd' , 0x00, 0x00, 0x00};
constexpr uint8_t kPacketWithMid[] = {
0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
0x65, 0x43, 0x12, 0x78,
0x12, 0x34, 0x56, 0x78,
0xbe, 0xde, 0x00, 0x01,
0xb2, 'm', 'i', 'd'};
constexpr uint32_t kCsrcs[] = {0x34567890, 0x32435465};
constexpr uint8_t kPayload[] = {'p', 'a', 'y', 'l', 'o', 'a', 'd'};
constexpr uint8_t kPacketPaddingSize = 8;
constexpr uint8_t kPacket[] = {
0xb2, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
0x65, 0x43, 0x12, 0x78,
0x12, 0x34, 0x56, 0x78,
0x34, 0x56, 0x78, 0x90,
0x32, 0x43, 0x54, 0x65,
0xbe, 0xde, 0x00, 0x01,
0x12, 0x00, 0x56, 0xce,
'p', 'a', 'y', 'l', 'o', 'a', 'd',
'p', 'a', 'd', 'd', 'i', 'n', 'g', kPacketPaddingSize};
constexpr uint8_t kPacketWithTwoByteHeaderExtension[] = {
0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
0x65, 0x43, 0x12, 0x78,
0x12, 0x34, 0x56, 0x78,
0x10, 0x00, 0x00, 0x02, // Two-byte header extension profile id + length.
kTwoByteExtensionId, 0x03, 0x00, 0x56,
0xce, 0x00, 0x00, 0x00};
constexpr uint8_t kPacketWithLongTwoByteHeaderExtension[] = {
0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
0x65, 0x43, 0x12, 0x78,
0x12, 0x34, 0x56, 0x78,
0x10, 0x00, 0x00, 0x0B, // Two-byte header extension profile id + length.
kTwoByteExtensionId, 0x29, 'e', 'x',
't', 'r', 'a', '-', 'l', 'o', 'n', 'g',
' ', 's', 't', 'r', 'i', 'n', 'g', ' ',
't', 'o', ' ', 't', 'e', 's', 't', ' ',
't', 'w', 'o', '-', 'b', 'y', 't', 'e',
' ', 'h', 'e', 'a', 'd', 'e', 'r', 0x00};
constexpr uint8_t kPacketWithTwoByteHeaderExtensionWithPadding[] = {
0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
0x65, 0x43, 0x12, 0x78,
0x12, 0x34, 0x56, 0x78,
0x10, 0x00, 0x00, 0x03, // Two-byte header extension profile id + length.
kTwoByteExtensionId, 0x03, 0x00, 0x56,
0xce, 0x00, 0x00, 0x00, // Three padding bytes.
kAudioLevelExtensionId, 0x01, 0x80|kAudioLevel, 0x00};
constexpr uint8_t kPacketWithInvalidExtension[] = {
0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
0x65, 0x43, 0x12, 0x78, // kTimestamp.
0x12, 0x34, 0x56, 0x78, // kSSrc.
0xbe, 0xde, 0x00, 0x02, // Extension block of size 2 x 32bit words.
(kTransmissionOffsetExtensionId << 4) | 6, // (6+1)-byte extension, but
'e', 'x', 't', // Transmission Offset
'd', 'a', 't', 'a', // expected to be 3-bytes.
'p', 'a', 'y', 'l', 'o', 'a', 'd'};
constexpr uint8_t kPacketWithLegacyTimingExtension[] = {
0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
0x65, 0x43, 0x12, 0x78, // kTimestamp.
0x12, 0x34, 0x56, 0x78, // kSSrc.
0xbe, 0xde, 0x00, 0x04, // Extension block of size 4 x 32bit words.
(kVideoTimingExtensionId << 4)
| VideoTimingExtension::kValueSizeBytes - 2, // Old format without flags.
0x00, 0x01, 0x00,
0x02, 0x00, 0x03, 0x00,
0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00};
// clang-format on
void TestCreateAndParseColorSpaceExtension(bool with_hdr_metadata) {
// Create packet with extension.
RtpPacket::ExtensionManager extensions(/*extmap-allow-mixed=*/true);
extensions.Register<ColorSpaceExtension>(1);
RtpPacket packet(&extensions);
const ColorSpace kColorSpace = CreateTestColorSpace(with_hdr_metadata);
EXPECT_TRUE(packet.SetExtension<ColorSpaceExtension>(kColorSpace));
packet.SetPayloadSize(42);
// Read packet with the extension.
RtpPacketReceived parsed(&extensions);
EXPECT_TRUE(parsed.Parse(packet.Buffer()));
ColorSpace parsed_color_space;
EXPECT_TRUE(parsed.GetExtension<ColorSpaceExtension>(&parsed_color_space));
EXPECT_EQ(kColorSpace, parsed_color_space);
}
} // namespace
TEST(RtpPacketTest, CreateMinimum) {
RtpPacketToSend packet(nullptr);
packet.SetPayloadType(kPayloadType);
packet.SetSequenceNumber(kSeqNum);
packet.SetTimestamp(kTimestamp);
packet.SetSsrc(kSsrc);
EXPECT_THAT(kMinimumPacket, ElementsAreArray(packet.data(), packet.size()));
}
TEST(RtpPacketTest, CreateWithExtension) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
RtpPacketToSend packet(&extensions);
packet.SetPayloadType(kPayloadType);
packet.SetSequenceNumber(kSeqNum);
packet.SetTimestamp(kTimestamp);
packet.SetSsrc(kSsrc);
packet.SetExtension<TransmissionOffset>(kTimeOffset);
EXPECT_THAT(kPacketWithTO, ElementsAreArray(packet.data(), packet.size()));
}
TEST(RtpPacketTest, CreateWith2Extensions) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
extensions.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
RtpPacketToSend packet(&extensions);
packet.SetPayloadType(kPayloadType);
packet.SetSequenceNumber(kSeqNum);
packet.SetTimestamp(kTimestamp);
packet.SetSsrc(kSsrc);
packet.SetExtension<TransmissionOffset>(kTimeOffset);
packet.SetExtension<AudioLevel>(kVoiceActive, kAudioLevel);
EXPECT_THAT(kPacketWithTOAndAL,
ElementsAreArray(packet.data(), packet.size()));
}
TEST(RtpPacketTest, CreateWithTwoByteHeaderExtensionFirst) {
RtpPacketToSend::ExtensionManager extensions(true);
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
extensions.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
extensions.Register(kRtpExtensionPlayoutDelay, kTwoByteExtensionId);
RtpPacketToSend packet(&extensions);
packet.SetPayloadType(kPayloadType);
packet.SetSequenceNumber(kSeqNum);
packet.SetTimestamp(kTimestamp);
packet.SetSsrc(kSsrc);
// Set extension that requires two-byte header.
PlayoutDelay playoutDelay = {30, 340};
ASSERT_TRUE(packet.SetExtension<PlayoutDelayLimits>(playoutDelay));
packet.SetExtension<TransmissionOffset>(kTimeOffset);
packet.SetExtension<AudioLevel>(kVoiceActive, kAudioLevel);
EXPECT_THAT(kPacketWithTwoByteExtensionIdFirst,
ElementsAreArray(packet.data(), packet.size()));
}
TEST(RtpPacketTest, CreateWithTwoByteHeaderExtensionLast) {
// This test will trigger RtpPacket::PromoteToTwoByteHeaderExtension().
RtpPacketToSend::ExtensionManager extensions(true);
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
extensions.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
extensions.Register(kRtpExtensionPlayoutDelay, kTwoByteExtensionId);
RtpPacketToSend packet(&extensions);
packet.SetPayloadType(kPayloadType);
packet.SetSequenceNumber(kSeqNum);
packet.SetTimestamp(kTimestamp);
packet.SetSsrc(kSsrc);
packet.SetExtension<TransmissionOffset>(kTimeOffset);
packet.SetExtension<AudioLevel>(kVoiceActive, kAudioLevel);
EXPECT_THAT(kPacketWithTOAndAL,
ElementsAreArray(packet.data(), packet.size()));
// Set extension that requires two-byte header.
PlayoutDelay playoutDelay = {30, 340};
ASSERT_TRUE(packet.SetExtension<PlayoutDelayLimits>(playoutDelay));
EXPECT_THAT(kPacketWithTwoByteExtensionIdLast,
ElementsAreArray(packet.data(), packet.size()));
}
TEST(RtpPacketTest, CreateWithDynamicSizedExtensions) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register<RtpStreamId>(kRtpStreamIdExtensionId);
RtpPacketToSend packet(&extensions);
packet.SetPayloadType(kPayloadType);
packet.SetSequenceNumber(kSeqNum);
packet.SetTimestamp(kTimestamp);
packet.SetSsrc(kSsrc);
packet.SetExtension<RtpStreamId>(kStreamId);
EXPECT_THAT(kPacketWithRsid, ElementsAreArray(packet.data(), packet.size()));
}
TEST(RtpPacketTest, TryToCreateWithEmptyRsid) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register<RtpStreamId>(kRtpStreamIdExtensionId);
RtpPacketToSend packet(&extensions);
EXPECT_FALSE(packet.SetExtension<RtpStreamId>(""));
}
TEST(RtpPacketTest, TryToCreateWithLongRsid) {
RtpPacketToSend::ExtensionManager extensions;
constexpr char kLongStreamId[] = "LoooooooooongRsid";
ASSERT_EQ(strlen(kLongStreamId), 17u);
extensions.Register<RtpStreamId>(kRtpStreamIdExtensionId);
RtpPacketToSend packet(&extensions);
EXPECT_FALSE(packet.SetExtension<RtpStreamId>(kLongStreamId));
}
TEST(RtpPacketTest, TryToCreateWithEmptyMid) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register<RtpMid>(kRtpMidExtensionId);
RtpPacketToSend packet(&extensions);
EXPECT_FALSE(packet.SetExtension<RtpMid>(""));
}
TEST(RtpPacketTest, TryToCreateWithLongMid) {
RtpPacketToSend::ExtensionManager extensions;
constexpr char kLongMid[] = "LoooooooooonogMid";
ASSERT_EQ(strlen(kLongMid), 17u);
extensions.Register<RtpMid>(kRtpMidExtensionId);
RtpPacketToSend packet(&extensions);
EXPECT_FALSE(packet.SetExtension<RtpMid>(kLongMid));
}
TEST(RtpPacketTest, TryToCreateTwoByteHeaderNotSupported) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionAudioLevel, kTwoByteExtensionId);
RtpPacketToSend packet(&extensions);
// Set extension that requires two-byte header.
EXPECT_FALSE(packet.SetExtension<AudioLevel>(kVoiceActive, kAudioLevel));
}
TEST(RtpPacketTest, CreateWithMaxSizeHeaderExtension) {
const std::string kValue = "123456789abcdef";
RtpPacket::ExtensionManager extensions;
extensions.Register<RtpMid>(1);
extensions.Register<RtpStreamId>(2);
RtpPacket packet(&extensions);
EXPECT_TRUE(packet.SetExtension<RtpMid>(kValue));
packet.SetPayloadSize(42);
// Rewriting allocated extension is allowed.
EXPECT_TRUE(packet.SetExtension<RtpMid>(kValue));
// Adding another extension after payload is set is not allowed.
EXPECT_FALSE(packet.SetExtension<RtpStreamId>(kValue));
// Read packet with the extension.
RtpPacketReceived parsed(&extensions);
EXPECT_TRUE(parsed.Parse(packet.Buffer()));
std::string read;
EXPECT_TRUE(parsed.GetExtension<RtpMid>(&read));
EXPECT_EQ(read, kValue);
}
TEST(RtpPacketTest, SetReservedExtensionsAfterPayload) {
const size_t kPayloadSize = 4;
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
extensions.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
RtpPacketToSend packet(&extensions);
EXPECT_TRUE(packet.ReserveExtension<TransmissionOffset>());
packet.SetPayloadSize(kPayloadSize);
// Can't set extension after payload.
EXPECT_FALSE(packet.SetExtension<AudioLevel>(kVoiceActive, kAudioLevel));
// Unless reserved.
EXPECT_TRUE(packet.SetExtension<TransmissionOffset>(kTimeOffset));
}
TEST(RtpPacketTest, CreatePurePadding) {
const size_t kPaddingSize = kMaxPaddingSize - 1;
RtpPacketToSend packet(nullptr, 12 + kPaddingSize);
packet.SetPayloadType(kPayloadType);
packet.SetSequenceNumber(kSeqNum);
packet.SetTimestamp(kTimestamp);
packet.SetSsrc(kSsrc);
EXPECT_LT(packet.size(), packet.capacity());
EXPECT_FALSE(packet.SetPadding(kPaddingSize + 1));
EXPECT_TRUE(packet.SetPadding(kPaddingSize));
EXPECT_EQ(packet.size(), packet.capacity());
}
TEST(RtpPacketTest, CreateUnalignedPadding) {
const size_t kPayloadSize = 3; // Make padding start at unaligned address.
RtpPacketToSend packet(nullptr, 12 + kPayloadSize + kMaxPaddingSize);
packet.SetPayloadType(kPayloadType);
packet.SetSequenceNumber(kSeqNum);
packet.SetTimestamp(kTimestamp);
packet.SetSsrc(kSsrc);
packet.SetPayloadSize(kPayloadSize);
EXPECT_LT(packet.size(), packet.capacity());
EXPECT_TRUE(packet.SetPadding(kMaxPaddingSize));
EXPECT_EQ(packet.size(), packet.capacity());
}
TEST(RtpPacketTest, WritesPaddingSizeToLastByte) {
const size_t kPaddingSize = 5;
RtpPacket packet;
EXPECT_TRUE(packet.SetPadding(kPaddingSize));
EXPECT_EQ(packet.data()[packet.size() - 1], kPaddingSize);
}
TEST(RtpPacketTest, UsesZerosForPadding) {
const size_t kPaddingSize = 5;
RtpPacket packet;
EXPECT_TRUE(packet.SetPadding(kPaddingSize));
EXPECT_THAT(rtc::MakeArrayView(packet.data() + 12, kPaddingSize - 1),
Each(0));
}
TEST(RtpPacketTest, CreateOneBytePadding) {
size_t kPayloadSize = 123;
RtpPacket packet(nullptr, 12 + kPayloadSize + 1);
packet.SetPayloadSize(kPayloadSize);
EXPECT_TRUE(packet.SetPadding(1));
EXPECT_EQ(packet.size(), 12 + kPayloadSize + 1);
EXPECT_EQ(packet.padding_size(), 1u);
}
TEST(RtpPacketTest, FailsToAddPaddingWithoutCapacity) {
size_t kPayloadSize = 123;
RtpPacket packet(nullptr, 12 + kPayloadSize);
packet.SetPayloadSize(kPayloadSize);
EXPECT_FALSE(packet.SetPadding(1));
}
TEST(RtpPacketTest, ParseMinimum) {
RtpPacketReceived packet;
EXPECT_TRUE(packet.Parse(kMinimumPacket, sizeof(kMinimumPacket)));
EXPECT_EQ(kPayloadType, packet.PayloadType());
EXPECT_EQ(kSeqNum, packet.SequenceNumber());
EXPECT_EQ(kTimestamp, packet.Timestamp());
EXPECT_EQ(kSsrc, packet.Ssrc());
EXPECT_EQ(0u, packet.padding_size());
EXPECT_EQ(0u, packet.payload_size());
}
TEST(RtpPacketTest, ParseBuffer) {
rtc::CopyOnWriteBuffer unparsed(kMinimumPacket);
const uint8_t* raw = unparsed.data();
RtpPacketReceived packet;
EXPECT_TRUE(packet.Parse(std::move(unparsed)));
EXPECT_EQ(raw, packet.data()); // Expect packet take the buffer without copy.
EXPECT_EQ(kSeqNum, packet.SequenceNumber());
EXPECT_EQ(kTimestamp, packet.Timestamp());
EXPECT_EQ(kSsrc, packet.Ssrc());
EXPECT_EQ(0u, packet.padding_size());
EXPECT_EQ(0u, packet.payload_size());
}
TEST(RtpPacketTest, ParseWithExtension) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
RtpPacketReceived packet(&extensions);
EXPECT_TRUE(packet.Parse(kPacketWithTO, sizeof(kPacketWithTO)));
EXPECT_EQ(kPayloadType, packet.PayloadType());
EXPECT_EQ(kSeqNum, packet.SequenceNumber());
EXPECT_EQ(kTimestamp, packet.Timestamp());
EXPECT_EQ(kSsrc, packet.Ssrc());
int32_t time_offset;
EXPECT_TRUE(packet.GetExtension<TransmissionOffset>(&time_offset));
EXPECT_EQ(kTimeOffset, time_offset);
EXPECT_EQ(0u, packet.payload_size());
EXPECT_EQ(0u, packet.padding_size());
}
TEST(RtpPacketTest, GetExtensionWithoutParametersReturnsOptionalValue) {
RtpPacket::ExtensionManager extensions;
extensions.Register<TransmissionOffset>(kTransmissionOffsetExtensionId);
extensions.Register<RtpStreamId>(kRtpStreamIdExtensionId);
RtpPacketReceived packet(&extensions);
EXPECT_TRUE(packet.Parse(kPacketWithTO, sizeof(kPacketWithTO)));
auto time_offset = packet.GetExtension<TransmissionOffset>();
static_assert(
std::is_same<decltype(time_offset),
absl::optional<TransmissionOffset::value_type>>::value,
"");
EXPECT_EQ(time_offset, kTimeOffset);
EXPECT_FALSE(packet.GetExtension<RtpStreamId>().has_value());
}
TEST(RtpPacketTest, GetRawExtensionWhenPresent) {
constexpr uint8_t kRawPacket[] = {
// comment for clang-format to align kRawPacket nicer.
0x90, 100, 0x5e, 0x04, //
0x65, 0x43, 0x12, 0x78, // Timestamp.
0x12, 0x34, 0x56, 0x78, // Ssrc
0xbe, 0xde, 0x00, 0x01, // Extension header
0x12, 'm', 'i', 'd', // 3-byte extension with id=1.
'p', 'a', 'y', 'l', 'o', 'a', 'd'};
RtpPacketToSend::ExtensionManager extensions;
extensions.Register<RtpMid>(1);
RtpPacket packet(&extensions);
ASSERT_TRUE(packet.Parse(kRawPacket, sizeof(kRawPacket)));
EXPECT_THAT(packet.GetRawExtension<RtpMid>(), ElementsAre('m', 'i', 'd'));
}
TEST(RtpPacketTest, GetRawExtensionWhenAbsent) {
constexpr uint8_t kRawPacket[] = {
// comment for clang-format to align kRawPacket nicer.
0x90, 100, 0x5e, 0x04, //
0x65, 0x43, 0x12, 0x78, // Timestamp.
0x12, 0x34, 0x56, 0x78, // Ssrc
0xbe, 0xde, 0x00, 0x01, // Extension header
0x12, 'm', 'i', 'd', // 3-byte extension with id=1.
'p', 'a', 'y', 'l', 'o', 'a', 'd'};
RtpPacketToSend::ExtensionManager extensions;
extensions.Register<RtpMid>(2);
RtpPacket packet(&extensions);
ASSERT_TRUE(packet.Parse(kRawPacket, sizeof(kRawPacket)));
EXPECT_THAT(packet.GetRawExtension<RtpMid>(), IsEmpty());
}
TEST(RtpPacketTest, ParseWithInvalidSizedExtension) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
RtpPacketReceived packet(&extensions);
EXPECT_TRUE(packet.Parse(kPacketWithInvalidExtension,
sizeof(kPacketWithInvalidExtension)));
// Extension should be ignored.
int32_t time_offset;
EXPECT_FALSE(packet.GetExtension<TransmissionOffset>(&time_offset));
// But shouldn't prevent reading payload.
EXPECT_THAT(packet.payload(), ElementsAreArray(kPayload));
}
TEST(RtpPacketTest, ParseWithOverSizedExtension) {
// clang-format off
const uint8_t bad_packet[] = {
0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
0x65, 0x43, 0x12, 0x78, // kTimestamp.
0x12, 0x34, 0x56, 0x78, // kSsrc.
0xbe, 0xde, 0x00, 0x01, // Extension of size 1x32bit word.
0x00, // Add a byte of padding.
0x12, // Extension id 1 size (2+1).
0xda, 0x1a // Only 2 bytes of extension payload.
};
// clang-format on
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(TransmissionOffset::kId, 1);
RtpPacketReceived packet(&extensions);
// Parse should ignore bad extension and proceed.
EXPECT_TRUE(packet.Parse(bad_packet, sizeof(bad_packet)));
int32_t time_offset;
// But extracting extension should fail.
EXPECT_FALSE(packet.GetExtension<TransmissionOffset>(&time_offset));
}
TEST(RtpPacketTest, ParseWith2Extensions) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
extensions.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
RtpPacketReceived packet(&extensions);
EXPECT_TRUE(packet.Parse(kPacketWithTOAndAL, sizeof(kPacketWithTOAndAL)));
int32_t time_offset;
EXPECT_TRUE(packet.GetExtension<TransmissionOffset>(&time_offset));
EXPECT_EQ(kTimeOffset, time_offset);
bool voice_active;
uint8_t audio_level;
EXPECT_TRUE(packet.GetExtension<AudioLevel>(&voice_active, &audio_level));
EXPECT_EQ(kVoiceActive, voice_active);
EXPECT_EQ(kAudioLevel, audio_level);
}
TEST(RtpPacketTest, ParseSecondPacketWithFewerExtensions) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
extensions.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
RtpPacketReceived packet(&extensions);
EXPECT_TRUE(packet.Parse(kPacketWithTOAndAL, sizeof(kPacketWithTOAndAL)));
EXPECT_TRUE(packet.HasExtension<TransmissionOffset>());
EXPECT_TRUE(packet.HasExtension<AudioLevel>());
// Second packet without audio level.
EXPECT_TRUE(packet.Parse(kPacketWithTO, sizeof(kPacketWithTO)));
EXPECT_TRUE(packet.HasExtension<TransmissionOffset>());
EXPECT_FALSE(packet.HasExtension<AudioLevel>());
}
TEST(RtpPacketTest, ParseWith2ExtensionsInvalidPadding) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
extensions.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
RtpPacketReceived packet(&extensions);
EXPECT_TRUE(packet.Parse(kPacketWithTOAndALInvalidPadding,
sizeof(kPacketWithTOAndALInvalidPadding)));
int32_t time_offset;
EXPECT_TRUE(packet.GetExtension<TransmissionOffset>(&time_offset));
EXPECT_EQ(kTimeOffset, time_offset);
bool voice_active;
uint8_t audio_level;
EXPECT_FALSE(packet.GetExtension<AudioLevel>(&voice_active, &audio_level));
}
TEST(RtpPacketTest, ParseWith2ExtensionsReservedExtensionId) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
extensions.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
RtpPacketReceived packet(&extensions);
EXPECT_TRUE(packet.Parse(kPacketWithTOAndALReservedExtensionId,
sizeof(kPacketWithTOAndALReservedExtensionId)));
int32_t time_offset;
EXPECT_TRUE(packet.GetExtension<TransmissionOffset>(&time_offset));
EXPECT_EQ(kTimeOffset, time_offset);
bool voice_active;
uint8_t audio_level;
EXPECT_FALSE(packet.GetExtension<AudioLevel>(&voice_active, &audio_level));
}
TEST(RtpPacketTest, ParseWithAllFeatures) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
RtpPacketReceived packet(&extensions);
EXPECT_TRUE(packet.Parse(kPacket, sizeof(kPacket)));
EXPECT_EQ(kPayloadType, packet.PayloadType());
EXPECT_EQ(kSeqNum, packet.SequenceNumber());
EXPECT_EQ(kTimestamp, packet.Timestamp());
EXPECT_EQ(kSsrc, packet.Ssrc());
EXPECT_THAT(packet.Csrcs(), ElementsAreArray(kCsrcs));
EXPECT_THAT(packet.payload(), ElementsAreArray(kPayload));
EXPECT_EQ(kPacketPaddingSize, packet.padding_size());
int32_t time_offset;
EXPECT_TRUE(packet.GetExtension<TransmissionOffset>(&time_offset));
}
TEST(RtpPacketTest, ParseTwoByteHeaderExtension) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset, kTwoByteExtensionId);
RtpPacketReceived packet(&extensions);
EXPECT_TRUE(packet.Parse(kPacketWithTwoByteHeaderExtension,
sizeof(kPacketWithTwoByteHeaderExtension)));
int32_t time_offset;
EXPECT_TRUE(packet.GetExtension<TransmissionOffset>(&time_offset));
EXPECT_EQ(kTimeOffset, time_offset);
}
TEST(RtpPacketTest, ParseLongTwoByteHeaderExtension) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionMid, kTwoByteExtensionId);
RtpPacketReceived packet(&extensions);
EXPECT_TRUE(packet.Parse(kPacketWithLongTwoByteHeaderExtension,
sizeof(kPacketWithLongTwoByteHeaderExtension)));
std::string long_rtp_mid;
EXPECT_TRUE(packet.GetExtension<RtpMid>(&long_rtp_mid));
EXPECT_EQ(kLongMid, long_rtp_mid);
}
TEST(RtpPacketTest, ParseTwoByteHeaderExtensionWithPadding) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset, kTwoByteExtensionId);
extensions.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
RtpPacketReceived packet(&extensions);
EXPECT_TRUE(
packet.Parse(kPacketWithTwoByteHeaderExtensionWithPadding,
sizeof(kPacketWithTwoByteHeaderExtensionWithPadding)));
int32_t time_offset;
EXPECT_TRUE(packet.GetExtension<TransmissionOffset>(&time_offset));
EXPECT_EQ(kTimeOffset, time_offset);
bool voice_active;
uint8_t audio_level;
EXPECT_TRUE(packet.GetExtension<AudioLevel>(&voice_active, &audio_level));
EXPECT_EQ(kVoiceActive, voice_active);
EXPECT_EQ(kAudioLevel, audio_level);
}
TEST(RtpPacketTest, ParseWithExtensionDelayed) {
RtpPacketReceived packet;
EXPECT_TRUE(packet.Parse(kPacketWithTO, sizeof(kPacketWithTO)));
EXPECT_EQ(kPayloadType, packet.PayloadType());
EXPECT_EQ(kSeqNum, packet.SequenceNumber());
EXPECT_EQ(kTimestamp, packet.Timestamp());
EXPECT_EQ(kSsrc, packet.Ssrc());
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
int32_t time_offset;
EXPECT_FALSE(packet.GetExtension<TransmissionOffset>(&time_offset));
packet.IdentifyExtensions(extensions);
EXPECT_TRUE(packet.GetExtension<TransmissionOffset>(&time_offset));
EXPECT_EQ(kTimeOffset, time_offset);
EXPECT_EQ(0u, packet.payload_size());
EXPECT_EQ(0u, packet.padding_size());
}
TEST(RtpPacketTest, ParseDynamicSizeExtension) {
// clang-format off
const uint8_t kPacket1[] = {
0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
0x65, 0x43, 0x12, 0x78, // Timestamp.
0x12, 0x34, 0x56, 0x78, // Ssrc.
0xbe, 0xde, 0x00, 0x02, // Extensions block of size 2x32bit words.
0x21, 'H', 'D', // Extension with id = 2, size = (1+1).
0x12, 'r', 't', 'x', // Extension with id = 1, size = (2+1).
0x00}; // Extension padding.
const uint8_t kPacket2[] = {
0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
0x65, 0x43, 0x12, 0x78, // Timestamp.
0x12, 0x34, 0x56, 0x79, // Ssrc.
0xbe, 0xde, 0x00, 0x01, // Extensions block of size 1x32bit words.
0x11, 'H', 'D', // Extension with id = 1, size = (1+1).
0x00}; // Extension padding.
// clang-format on
RtpPacketReceived::ExtensionManager extensions;
extensions.Register<RtpStreamId>(1);
extensions.Register<RepairedRtpStreamId>(2);
RtpPacketReceived packet(&extensions);
ASSERT_TRUE(packet.Parse(kPacket1, sizeof(kPacket1)));
std::string rsid;
EXPECT_TRUE(packet.GetExtension<RtpStreamId>(&rsid));
EXPECT_EQ(rsid, "rtx");
std::string repaired_rsid;
EXPECT_TRUE(packet.GetExtension<RepairedRtpStreamId>(&repaired_rsid));
EXPECT_EQ(repaired_rsid, "HD");
// Parse another packet with RtpStreamId extension of different size.
ASSERT_TRUE(packet.Parse(kPacket2, sizeof(kPacket2)));
EXPECT_TRUE(packet.GetExtension<RtpStreamId>(&rsid));
EXPECT_EQ(rsid, "HD");
EXPECT_FALSE(packet.GetExtension<RepairedRtpStreamId>(&repaired_rsid));
}
TEST(RtpPacketTest, ParseWithMid) {
RtpPacketReceived::ExtensionManager extensions;
extensions.Register<RtpMid>(kRtpMidExtensionId);
RtpPacketReceived packet(&extensions);
ASSERT_TRUE(packet.Parse(kPacketWithMid, sizeof(kPacketWithMid)));
std::string mid;
EXPECT_TRUE(packet.GetExtension<RtpMid>(&mid));
EXPECT_EQ(mid, kMid);
}
TEST(RtpPacketTest, CreateAndParseTimingFrameExtension) {
// Create a packet with video frame timing extension populated.
RtpPacketToSend::ExtensionManager send_extensions;
send_extensions.Register(kRtpExtensionVideoTiming, kVideoTimingExtensionId);
RtpPacketToSend send_packet(&send_extensions);
send_packet.SetPayloadType(kPayloadType);
send_packet.SetSequenceNumber(kSeqNum);
send_packet.SetTimestamp(kTimestamp);
send_packet.SetSsrc(kSsrc);
VideoSendTiming timing;
timing.encode_start_delta_ms = 1;
timing.encode_finish_delta_ms = 2;
timing.packetization_finish_delta_ms = 3;
timing.pacer_exit_delta_ms = 4;
timing.flags =
VideoSendTiming::kTriggeredByTimer | VideoSendTiming::kTriggeredBySize;
send_packet.SetExtension<VideoTimingExtension>(timing);
// Serialize the packet and then parse it again.
RtpPacketReceived::ExtensionManager extensions;
extensions.Register<VideoTimingExtension>(kVideoTimingExtensionId);
RtpPacketReceived receive_packet(&extensions);
EXPECT_TRUE(receive_packet.Parse(send_packet.Buffer()));
VideoSendTiming receivied_timing;
EXPECT_TRUE(
receive_packet.GetExtension<VideoTimingExtension>(&receivied_timing));
// Only check first and last timestamp (covered by other tests) plus flags.
EXPECT_EQ(receivied_timing.encode_start_delta_ms,
timing.encode_start_delta_ms);
EXPECT_EQ(receivied_timing.pacer_exit_delta_ms, timing.pacer_exit_delta_ms);
EXPECT_EQ(receivied_timing.flags, timing.flags);
}
TEST(RtpPacketTest, ParseLegacyTimingFrameExtension) {
// Parse the modified packet.
RtpPacketReceived::ExtensionManager extensions;
extensions.Register<VideoTimingExtension>(kVideoTimingExtensionId);
RtpPacketReceived packet(&extensions);
EXPECT_TRUE(packet.Parse(kPacketWithLegacyTimingExtension,
sizeof(kPacketWithLegacyTimingExtension)));
VideoSendTiming receivied_timing;
EXPECT_TRUE(packet.GetExtension<VideoTimingExtension>(&receivied_timing));
// Check first and last timestamp are still OK. Flags should now be 0.
EXPECT_EQ(receivied_timing.encode_start_delta_ms, 1);
EXPECT_EQ(receivied_timing.pacer_exit_delta_ms, 4);
EXPECT_EQ(receivied_timing.flags, 0);
}
TEST(RtpPacketTest, CreateAndParseColorSpaceExtension) {
TestCreateAndParseColorSpaceExtension(/*with_hdr_metadata=*/true);
}
TEST(RtpPacketTest, CreateAndParseColorSpaceExtensionWithoutHdrMetadata) {
TestCreateAndParseColorSpaceExtension(/*with_hdr_metadata=*/false);
}
TEST(RtpPacketTest, CreateAndParseTransportSequenceNumber) {
// Create a packet with transport sequence number extension populated.
RtpPacketToSend::ExtensionManager extensions;
constexpr int kExtensionId = 1;
extensions.Register<TransportSequenceNumber>(kExtensionId);
RtpPacketToSend send_packet(&extensions);
send_packet.SetPayloadType(kPayloadType);
send_packet.SetSequenceNumber(kSeqNum);
send_packet.SetTimestamp(kTimestamp);
send_packet.SetSsrc(kSsrc);
constexpr int kTransportSequenceNumber = 12345;
send_packet.SetExtension<TransportSequenceNumber>(kTransportSequenceNumber);
// Serialize the packet and then parse it again.
RtpPacketReceived receive_packet(&extensions);
EXPECT_TRUE(receive_packet.Parse(send_packet.Buffer()));
uint16_t received_transport_sequeunce_number;
EXPECT_TRUE(receive_packet.GetExtension<TransportSequenceNumber>(
&received_transport_sequeunce_number));
EXPECT_EQ(received_transport_sequeunce_number, kTransportSequenceNumber);
}
TEST(RtpPacketTest, CreateAndParseTransportSequenceNumberV2) {
// Create a packet with transport sequence number V2 extension populated.
// No feedback request means that the extension will be two bytes unless it's
// pre-allocated.
RtpPacketToSend::ExtensionManager extensions;
constexpr int kExtensionId = 1;
extensions.Register<TransportSequenceNumberV2>(kExtensionId);
RtpPacketToSend send_packet(&extensions);
send_packet.SetPayloadType(kPayloadType);
send_packet.SetSequenceNumber(kSeqNum);
send_packet.SetTimestamp(kTimestamp);
send_packet.SetSsrc(kSsrc);
constexpr int kTransportSequenceNumber = 12345;
send_packet.SetExtension<TransportSequenceNumberV2>(kTransportSequenceNumber,
absl::nullopt);
EXPECT_EQ(send_packet.GetRawExtension<TransportSequenceNumberV2>().size(),
2u);
// Serialize the packet and then parse it again.
RtpPacketReceived receive_packet(&extensions);
EXPECT_TRUE(receive_packet.Parse(send_packet.Buffer()));
uint16_t received_transport_sequeunce_number;
absl::optional<FeedbackRequest> received_feedback_request;
EXPECT_TRUE(receive_packet.GetExtension<TransportSequenceNumberV2>(
&received_transport_sequeunce_number, &received_feedback_request));
EXPECT_EQ(received_transport_sequeunce_number, kTransportSequenceNumber);
EXPECT_FALSE(received_feedback_request);
}
TEST(RtpPacketTest, CreateAndParseTransportSequenceNumberV2Preallocated) {
// Create a packet with transport sequence number V2 extension populated.
// No feedback request means that the extension could be two bytes, but since
// it's pre-allocated we don't know if it is with or without feedback request
// therefore the size is four bytes.
RtpPacketToSend::ExtensionManager extensions;
constexpr int kExtensionId = 1;
extensions.Register<TransportSequenceNumberV2>(kExtensionId);
RtpPacketToSend send_packet(&extensions);
send_packet.SetPayloadType(kPayloadType);
send_packet.SetSequenceNumber(kSeqNum);
send_packet.SetTimestamp(kTimestamp);
send_packet.SetSsrc(kSsrc);
constexpr int kTransportSequenceNumber = 12345;
constexpr absl::optional<FeedbackRequest> kNoFeedbackRequest =
FeedbackRequest{/*include_timestamps=*/false, /*sequence_count=*/0};
send_packet.ReserveExtension<TransportSequenceNumberV2>();
send_packet.SetExtension<TransportSequenceNumberV2>(kTransportSequenceNumber,
kNoFeedbackRequest);
EXPECT_EQ(send_packet.GetRawExtension<TransportSequenceNumberV2>().size(),
4u);
// Serialize the packet and then parse it again.
RtpPacketReceived receive_packet(&extensions);
EXPECT_TRUE(receive_packet.Parse(send_packet.Buffer()));
uint16_t received_transport_sequeunce_number;
absl::optional<FeedbackRequest> received_feedback_request;
EXPECT_TRUE(receive_packet.GetExtension<TransportSequenceNumberV2>(
&received_transport_sequeunce_number, &received_feedback_request));
EXPECT_EQ(received_transport_sequeunce_number, kTransportSequenceNumber);
EXPECT_FALSE(received_feedback_request);
}
TEST(RtpPacketTest,
CreateAndParseTransportSequenceNumberV2WithFeedbackRequest) {
// Create a packet with TransportSequenceNumberV2 extension populated.
RtpPacketToSend::ExtensionManager extensions;
constexpr int kExtensionId = 1;
extensions.Register<TransportSequenceNumberV2>(kExtensionId);
RtpPacketToSend send_packet(&extensions);
send_packet.SetPayloadType(kPayloadType);
send_packet.SetSequenceNumber(kSeqNum);
send_packet.SetTimestamp(kTimestamp);
send_packet.SetSsrc(kSsrc);
constexpr int kTransportSequenceNumber = 12345;
constexpr absl::optional<FeedbackRequest> kFeedbackRequest =
FeedbackRequest{/*include_timestamps=*/true, /*sequence_count=*/3};
send_packet.SetExtension<TransportSequenceNumberV2>(kTransportSequenceNumber,
kFeedbackRequest);
// Serialize the packet and then parse it again.
RtpPacketReceived receive_packet(&extensions);
EXPECT_TRUE(receive_packet.Parse(send_packet.Buffer()));
// Parse transport sequence number and feedback request.
uint16_t received_transport_sequeunce_number;
absl::optional<FeedbackRequest> received_feedback_request;
EXPECT_TRUE(receive_packet.GetExtension<TransportSequenceNumberV2>(
&received_transport_sequeunce_number, &received_feedback_request));
EXPECT_EQ(received_transport_sequeunce_number, kTransportSequenceNumber);
ASSERT_TRUE(received_feedback_request);
EXPECT_EQ(received_feedback_request->include_timestamps,
kFeedbackRequest->include_timestamps);
EXPECT_EQ(received_feedback_request->sequence_count,
kFeedbackRequest->sequence_count);
}
} // namespace webrtc