blob: 2589ad2715c71c6514bc15acc915c2a32aa3825c [file] [log] [blame]
/*
* Copyright (c) 2019 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 <string>
#include <vector>
#include "api/video/video_codec_constants.h"
#include "api/video/video_timing.h"
#include "modules/rtp_rtcp/include/rtp_cvo.h"
#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/rtp_format_video_generic.h"
#include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h"
#include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h"
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "modules/rtp_rtcp/source/rtp_sender.h"
#include "modules/rtp_rtcp/source/rtp_sender_video.h"
#include "rtc_base/arraysize.h"
#include "rtc_base/rate_limiter.h"
#include "test/gmock.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
using ::testing::ElementsAre;
enum : int { // The first valid value is 1.
kAbsoluteSendTimeExtensionId = 1,
kFrameMarkingExtensionId,
kGenericDescriptorId00,
kGenericDescriptorId01,
kTransmissionTimeOffsetExtensionId,
kTransportSequenceNumberExtensionId,
kVideoRotationExtensionId,
kVideoTimingExtensionId,
};
constexpr int kPayload = 100;
constexpr uint32_t kTimestamp = 10;
constexpr uint16_t kSeqNum = 33;
constexpr uint32_t kSsrc = 725242;
constexpr int kMaxPacketLength = 1500;
constexpr uint64_t kStartTime = 123456789;
constexpr int64_t kDefaultExpectedRetransmissionTimeMs = 125;
class LoopbackTransportTest : public webrtc::Transport {
public:
LoopbackTransportTest() {
receivers_extensions_.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionTimeOffsetExtensionId);
receivers_extensions_.Register(kRtpExtensionAbsoluteSendTime,
kAbsoluteSendTimeExtensionId);
receivers_extensions_.Register(kRtpExtensionTransportSequenceNumber,
kTransportSequenceNumberExtensionId);
receivers_extensions_.Register(kRtpExtensionVideoRotation,
kVideoRotationExtensionId);
receivers_extensions_.Register(kRtpExtensionVideoTiming,
kVideoTimingExtensionId);
receivers_extensions_.Register(kRtpExtensionGenericFrameDescriptor00,
kGenericDescriptorId00);
receivers_extensions_.Register(kRtpExtensionGenericFrameDescriptor01,
kGenericDescriptorId01);
receivers_extensions_.Register(kRtpExtensionFrameMarking,
kFrameMarkingExtensionId);
}
bool SendRtp(const uint8_t* data,
size_t len,
const PacketOptions& options) override {
sent_packets_.push_back(RtpPacketReceived(&receivers_extensions_));
EXPECT_TRUE(sent_packets_.back().Parse(data, len));
return true;
}
bool SendRtcp(const uint8_t* data, size_t len) override { return false; }
const RtpPacketReceived& last_sent_packet() { return sent_packets_.back(); }
int packets_sent() { return sent_packets_.size(); }
private:
RtpHeaderExtensionMap receivers_extensions_;
std::vector<RtpPacketReceived> sent_packets_;
};
} // namespace
class TestRtpSenderVideo : public RTPSenderVideo {
public:
TestRtpSenderVideo(Clock* clock,
RTPSender* rtp_sender,
FlexfecSender* flexfec_sender,
const WebRtcKeyValueConfig& field_trials)
: RTPSenderVideo(clock,
rtp_sender,
flexfec_sender,
&playout_delay_oracle_,
nullptr,
false,
false,
field_trials) {}
~TestRtpSenderVideo() override {}
StorageType GetStorageType(const RTPVideoHeader& header,
int32_t retransmission_settings,
int64_t expected_retransmission_time_ms) {
return RTPSenderVideo::GetStorageType(GetTemporalId(header),
retransmission_settings,
expected_retransmission_time_ms);
}
PlayoutDelayOracle playout_delay_oracle_;
};
class FieldTrials : public WebRtcKeyValueConfig {
public:
explicit FieldTrials(bool use_send_side_bwe_with_overhead)
: use_send_side_bwe_with_overhead_(use_send_side_bwe_with_overhead) {}
std::string Lookup(absl::string_view key) const override {
return key == "WebRTC-SendSideBwe-WithOverhead" &&
use_send_side_bwe_with_overhead_
? "Enabled"
: "";
}
private:
bool use_send_side_bwe_with_overhead_;
};
class RtpSenderVideoTest : public ::testing::TestWithParam<bool> {
public:
RtpSenderVideoTest()
: field_trials_(GetParam()),
fake_clock_(kStartTime),
retransmission_rate_limiter_(&fake_clock_, 1000),
rtp_sender_([&] {
RtpRtcp::Configuration config;
config.clock = &fake_clock_;
config.outgoing_transport = &transport_;
config.retransmission_rate_limiter = &retransmission_rate_limiter_;
config.field_trials = &field_trials_;
config.media_send_ssrc = kSsrc;
return config;
}()),
rtp_sender_video_(&fake_clock_, &rtp_sender_, nullptr, field_trials_) {
rtp_sender_.SetSequenceNumber(kSeqNum);
rtp_sender_.SetTimestampOffset(0);
rtp_sender_video_.RegisterPayloadType(kPayload, "generic",
/*raw_payload=*/false);
}
void PopulateGenericFrameDescriptor(int version);
void UsesMinimalVp8DescriptorWhenGenericFrameDescriptorExtensionIsUsed(
int version);
protected:
FieldTrials field_trials_;
SimulatedClock fake_clock_;
LoopbackTransportTest transport_;
RateLimiter retransmission_rate_limiter_;
RTPSender rtp_sender_;
TestRtpSenderVideo rtp_sender_video_;
};
TEST_P(RtpSenderVideoTest, KeyFrameHasCVO) {
uint8_t kFrame[kMaxPacketLength];
EXPECT_EQ(0, rtp_sender_.RegisterRtpHeaderExtension(
kRtpExtensionVideoRotation, kVideoRotationExtensionId));
RTPVideoHeader hdr;
hdr.rotation = kVideoRotation_0;
rtp_sender_video_.SendVideo(VideoFrameType::kVideoFrameKey, kPayload,
kTimestamp, 0, kFrame, sizeof(kFrame), nullptr,
&hdr, kDefaultExpectedRetransmissionTimeMs);
VideoRotation rotation;
EXPECT_TRUE(
transport_.last_sent_packet().GetExtension<VideoOrientation>(&rotation));
EXPECT_EQ(kVideoRotation_0, rotation);
}
TEST_P(RtpSenderVideoTest, TimingFrameHasPacketizationTimstampSet) {
uint8_t kFrame[kMaxPacketLength];
const int64_t kPacketizationTimeMs = 100;
const int64_t kEncodeStartDeltaMs = 10;
const int64_t kEncodeFinishDeltaMs = 50;
EXPECT_EQ(0, rtp_sender_.RegisterRtpHeaderExtension(kRtpExtensionVideoTiming,
kVideoTimingExtensionId));
const int64_t kCaptureTimestamp = fake_clock_.TimeInMilliseconds();
RTPVideoHeader hdr;
hdr.video_timing.flags = VideoSendTiming::kTriggeredByTimer;
hdr.video_timing.encode_start_delta_ms = kEncodeStartDeltaMs;
hdr.video_timing.encode_finish_delta_ms = kEncodeFinishDeltaMs;
fake_clock_.AdvanceTimeMilliseconds(kPacketizationTimeMs);
rtp_sender_video_.SendVideo(VideoFrameType::kVideoFrameKey, kPayload,
kTimestamp, kCaptureTimestamp, kFrame,
sizeof(kFrame), nullptr, &hdr,
kDefaultExpectedRetransmissionTimeMs);
VideoSendTiming timing;
EXPECT_TRUE(transport_.last_sent_packet().GetExtension<VideoTimingExtension>(
&timing));
EXPECT_EQ(kPacketizationTimeMs, timing.packetization_finish_delta_ms);
EXPECT_EQ(kEncodeStartDeltaMs, timing.encode_start_delta_ms);
EXPECT_EQ(kEncodeFinishDeltaMs, timing.encode_finish_delta_ms);
}
TEST_P(RtpSenderVideoTest, DeltaFrameHasCVOWhenChanged) {
uint8_t kFrame[kMaxPacketLength];
EXPECT_EQ(0, rtp_sender_.RegisterRtpHeaderExtension(
kRtpExtensionVideoRotation, kVideoRotationExtensionId));
RTPVideoHeader hdr;
hdr.rotation = kVideoRotation_90;
EXPECT_TRUE(rtp_sender_video_.SendVideo(
VideoFrameType::kVideoFrameKey, kPayload, kTimestamp, 0, kFrame,
sizeof(kFrame), nullptr, &hdr, kDefaultExpectedRetransmissionTimeMs));
hdr.rotation = kVideoRotation_0;
EXPECT_TRUE(rtp_sender_video_.SendVideo(
VideoFrameType::kVideoFrameDelta, kPayload, kTimestamp + 1, 0, kFrame,
sizeof(kFrame), nullptr, &hdr, kDefaultExpectedRetransmissionTimeMs));
VideoRotation rotation;
EXPECT_TRUE(
transport_.last_sent_packet().GetExtension<VideoOrientation>(&rotation));
EXPECT_EQ(kVideoRotation_0, rotation);
}
TEST_P(RtpSenderVideoTest, DeltaFrameHasCVOWhenNonZero) {
uint8_t kFrame[kMaxPacketLength];
EXPECT_EQ(0, rtp_sender_.RegisterRtpHeaderExtension(
kRtpExtensionVideoRotation, kVideoRotationExtensionId));
RTPVideoHeader hdr;
hdr.rotation = kVideoRotation_90;
EXPECT_TRUE(rtp_sender_video_.SendVideo(
VideoFrameType::kVideoFrameKey, kPayload, kTimestamp, 0, kFrame,
sizeof(kFrame), nullptr, &hdr, kDefaultExpectedRetransmissionTimeMs));
EXPECT_TRUE(rtp_sender_video_.SendVideo(
VideoFrameType::kVideoFrameDelta, kPayload, kTimestamp + 1, 0, kFrame,
sizeof(kFrame), nullptr, &hdr, kDefaultExpectedRetransmissionTimeMs));
VideoRotation rotation;
EXPECT_TRUE(
transport_.last_sent_packet().GetExtension<VideoOrientation>(&rotation));
EXPECT_EQ(kVideoRotation_90, rotation);
}
TEST_P(RtpSenderVideoTest, CheckH264FrameMarking) {
uint8_t kFrame[kMaxPacketLength];
EXPECT_EQ(0, rtp_sender_.RegisterRtpHeaderExtension(
kRtpExtensionFrameMarking, kFrameMarkingExtensionId));
RTPFragmentationHeader frag;
frag.VerifyAndAllocateFragmentationHeader(1);
frag.fragmentationOffset[0] = 0;
frag.fragmentationLength[0] = sizeof(kFrame);
RTPVideoHeader hdr;
hdr.video_type_header.emplace<RTPVideoHeaderH264>().packetization_mode =
H264PacketizationMode::NonInterleaved;
hdr.codec = kVideoCodecH264;
hdr.frame_marking.temporal_id = kNoTemporalIdx;
hdr.frame_marking.tl0_pic_idx = 99;
hdr.frame_marking.base_layer_sync = true;
rtp_sender_video_.SendVideo(VideoFrameType::kVideoFrameDelta, kPayload,
kTimestamp, 0, kFrame, sizeof(kFrame), &frag,
&hdr, kDefaultExpectedRetransmissionTimeMs);
FrameMarking fm;
EXPECT_FALSE(
transport_.last_sent_packet().GetExtension<FrameMarkingExtension>(&fm));
hdr.frame_marking.temporal_id = 0;
rtp_sender_video_.SendVideo(VideoFrameType::kVideoFrameDelta, kPayload,
kTimestamp + 1, 0, kFrame, sizeof(kFrame), &frag,
&hdr, kDefaultExpectedRetransmissionTimeMs);
EXPECT_TRUE(
transport_.last_sent_packet().GetExtension<FrameMarkingExtension>(&fm));
EXPECT_EQ(hdr.frame_marking.temporal_id, fm.temporal_id);
EXPECT_EQ(hdr.frame_marking.tl0_pic_idx, fm.tl0_pic_idx);
EXPECT_EQ(hdr.frame_marking.base_layer_sync, fm.base_layer_sync);
}
// Make sure rotation is parsed correctly when the Camera (C) and Flip (F) bits
// are set in the CVO byte.
TEST_P(RtpSenderVideoTest, SendVideoWithCameraAndFlipCVO) {
// Test extracting rotation when Camera (C) and Flip (F) bits are zero.
EXPECT_EQ(kVideoRotation_0, ConvertCVOByteToVideoRotation(0));
EXPECT_EQ(kVideoRotation_90, ConvertCVOByteToVideoRotation(1));
EXPECT_EQ(kVideoRotation_180, ConvertCVOByteToVideoRotation(2));
EXPECT_EQ(kVideoRotation_270, ConvertCVOByteToVideoRotation(3));
// Test extracting rotation when Camera (C) and Flip (F) bits are set.
const int flip_bit = 1 << 2;
const int camera_bit = 1 << 3;
EXPECT_EQ(kVideoRotation_0,
ConvertCVOByteToVideoRotation(flip_bit | camera_bit | 0));
EXPECT_EQ(kVideoRotation_90,
ConvertCVOByteToVideoRotation(flip_bit | camera_bit | 1));
EXPECT_EQ(kVideoRotation_180,
ConvertCVOByteToVideoRotation(flip_bit | camera_bit | 2));
EXPECT_EQ(kVideoRotation_270,
ConvertCVOByteToVideoRotation(flip_bit | camera_bit | 3));
}
TEST_P(RtpSenderVideoTest, RetransmissionTypesGeneric) {
RTPVideoHeader header;
header.codec = kVideoCodecGeneric;
EXPECT_EQ(kDontRetransmit,
rtp_sender_video_.GetStorageType(
header, kRetransmitOff, kDefaultExpectedRetransmissionTimeMs));
EXPECT_EQ(kAllowRetransmission, rtp_sender_video_.GetStorageType(
header, kRetransmitBaseLayer,
kDefaultExpectedRetransmissionTimeMs));
EXPECT_EQ(kAllowRetransmission, rtp_sender_video_.GetStorageType(
header, kRetransmitHigherLayers,
kDefaultExpectedRetransmissionTimeMs));
EXPECT_EQ(kAllowRetransmission,
rtp_sender_video_.GetStorageType(
header, kConditionallyRetransmitHigherLayers,
kDefaultExpectedRetransmissionTimeMs));
}
TEST_P(RtpSenderVideoTest, RetransmissionTypesH264) {
RTPVideoHeader header;
header.video_type_header.emplace<RTPVideoHeaderH264>().packetization_mode =
H264PacketizationMode::NonInterleaved;
header.codec = kVideoCodecH264;
header.frame_marking.temporal_id = kNoTemporalIdx;
EXPECT_EQ(kDontRetransmit,
rtp_sender_video_.GetStorageType(
header, kRetransmitOff, kDefaultExpectedRetransmissionTimeMs));
EXPECT_EQ(kAllowRetransmission, rtp_sender_video_.GetStorageType(
header, kRetransmitBaseLayer,
kDefaultExpectedRetransmissionTimeMs));
EXPECT_EQ(kAllowRetransmission, rtp_sender_video_.GetStorageType(
header, kRetransmitHigherLayers,
kDefaultExpectedRetransmissionTimeMs));
EXPECT_EQ(kAllowRetransmission,
rtp_sender_video_.GetStorageType(
header, kConditionallyRetransmitHigherLayers,
kDefaultExpectedRetransmissionTimeMs));
// Test higher level retransmit.
for (int tid = 0; tid <= kMaxTemporalStreams; ++tid) {
header.frame_marking.temporal_id = tid;
EXPECT_EQ(kAllowRetransmission,
rtp_sender_video_.GetStorageType(
header, kRetransmitHigherLayers | kRetransmitBaseLayer,
kDefaultExpectedRetransmissionTimeMs));
}
}
TEST_P(RtpSenderVideoTest, RetransmissionTypesVP8BaseLayer) {
RTPVideoHeader header;
header.codec = kVideoCodecVP8;
auto& vp8_header = header.video_type_header.emplace<RTPVideoHeaderVP8>();
vp8_header.temporalIdx = 0;
EXPECT_EQ(kDontRetransmit,
rtp_sender_video_.GetStorageType(
header, kRetransmitOff, kDefaultExpectedRetransmissionTimeMs));
EXPECT_EQ(kAllowRetransmission, rtp_sender_video_.GetStorageType(
header, kRetransmitBaseLayer,
kDefaultExpectedRetransmissionTimeMs));
EXPECT_EQ(kDontRetransmit, rtp_sender_video_.GetStorageType(
header, kRetransmitHigherLayers,
kDefaultExpectedRetransmissionTimeMs));
EXPECT_EQ(kAllowRetransmission,
rtp_sender_video_.GetStorageType(
header, kRetransmitHigherLayers | kRetransmitBaseLayer,
kDefaultExpectedRetransmissionTimeMs));
EXPECT_EQ(kDontRetransmit, rtp_sender_video_.GetStorageType(
header, kConditionallyRetransmitHigherLayers,
kDefaultExpectedRetransmissionTimeMs));
EXPECT_EQ(
kAllowRetransmission,
rtp_sender_video_.GetStorageType(
header, kRetransmitBaseLayer | kConditionallyRetransmitHigherLayers,
kDefaultExpectedRetransmissionTimeMs));
}
TEST_P(RtpSenderVideoTest, RetransmissionTypesVP8HigherLayers) {
RTPVideoHeader header;
header.codec = kVideoCodecVP8;
auto& vp8_header = header.video_type_header.emplace<RTPVideoHeaderVP8>();
for (int tid = 1; tid <= kMaxTemporalStreams; ++tid) {
vp8_header.temporalIdx = tid;
EXPECT_EQ(kDontRetransmit, rtp_sender_video_.GetStorageType(
header, kRetransmitOff,
kDefaultExpectedRetransmissionTimeMs));
EXPECT_EQ(kDontRetransmit, rtp_sender_video_.GetStorageType(
header, kRetransmitBaseLayer,
kDefaultExpectedRetransmissionTimeMs));
EXPECT_EQ(kAllowRetransmission, rtp_sender_video_.GetStorageType(
header, kRetransmitHigherLayers,
kDefaultExpectedRetransmissionTimeMs));
EXPECT_EQ(kAllowRetransmission,
rtp_sender_video_.GetStorageType(
header, kRetransmitHigherLayers | kRetransmitBaseLayer,
kDefaultExpectedRetransmissionTimeMs));
}
}
TEST_P(RtpSenderVideoTest, RetransmissionTypesVP9) {
RTPVideoHeader header;
header.codec = kVideoCodecVP9;
auto& vp9_header = header.video_type_header.emplace<RTPVideoHeaderVP9>();
for (int tid = 1; tid <= kMaxTemporalStreams; ++tid) {
vp9_header.temporal_idx = tid;
EXPECT_EQ(kDontRetransmit, rtp_sender_video_.GetStorageType(
header, kRetransmitOff,
kDefaultExpectedRetransmissionTimeMs));
EXPECT_EQ(kDontRetransmit, rtp_sender_video_.GetStorageType(
header, kRetransmitBaseLayer,
kDefaultExpectedRetransmissionTimeMs));
EXPECT_EQ(kAllowRetransmission, rtp_sender_video_.GetStorageType(
header, kRetransmitHigherLayers,
kDefaultExpectedRetransmissionTimeMs));
EXPECT_EQ(kAllowRetransmission,
rtp_sender_video_.GetStorageType(
header, kRetransmitHigherLayers | kRetransmitBaseLayer,
kDefaultExpectedRetransmissionTimeMs));
}
}
TEST_P(RtpSenderVideoTest, ConditionalRetransmit) {
const int64_t kFrameIntervalMs = 33;
const int64_t kRttMs = (kFrameIntervalMs * 3) / 2;
const uint8_t kSettings =
kRetransmitBaseLayer | kConditionallyRetransmitHigherLayers;
// Insert VP8 frames for all temporal layers, but stop before the final index.
RTPVideoHeader header;
header.codec = kVideoCodecVP8;
// Fill averaging window to prevent rounding errors.
constexpr int kNumRepetitions =
(RTPSenderVideo::kTLRateWindowSizeMs + (kFrameIntervalMs / 2)) /
kFrameIntervalMs;
constexpr int kPattern[] = {0, 2, 1, 2};
auto& vp8_header = header.video_type_header.emplace<RTPVideoHeaderVP8>();
for (size_t i = 0; i < arraysize(kPattern) * kNumRepetitions; ++i) {
vp8_header.temporalIdx = kPattern[i % arraysize(kPattern)];
rtp_sender_video_.GetStorageType(header, kSettings, kRttMs);
fake_clock_.AdvanceTimeMilliseconds(kFrameIntervalMs);
}
// Since we're at the start of the pattern, the next expected frame in TL0 is
// right now. We will wait at most one expected retransmission time before
// acknowledging that it did not arrive, which means this frame and the next
// will not be retransmitted.
vp8_header.temporalIdx = 1;
EXPECT_EQ(StorageType::kDontRetransmit,
rtp_sender_video_.GetStorageType(header, kSettings, kRttMs));
fake_clock_.AdvanceTimeMilliseconds(kFrameIntervalMs);
EXPECT_EQ(StorageType::kDontRetransmit,
rtp_sender_video_.GetStorageType(header, kSettings, kRttMs));
fake_clock_.AdvanceTimeMilliseconds(kFrameIntervalMs);
// The TL0 frame did not arrive. So allow retransmission.
EXPECT_EQ(StorageType::kAllowRetransmission,
rtp_sender_video_.GetStorageType(header, kSettings, kRttMs));
fake_clock_.AdvanceTimeMilliseconds(kFrameIntervalMs);
// Insert a frame for TL2. We just had frame in TL1, so the next one there is
// in three frames away. TL0 is still too far in the past. So, allow
// retransmission.
vp8_header.temporalIdx = 2;
EXPECT_EQ(StorageType::kAllowRetransmission,
rtp_sender_video_.GetStorageType(header, kSettings, kRttMs));
fake_clock_.AdvanceTimeMilliseconds(kFrameIntervalMs);
// Another TL2, next in TL1 is two frames away. Allow again.
EXPECT_EQ(StorageType::kAllowRetransmission,
rtp_sender_video_.GetStorageType(header, kSettings, kRttMs));
fake_clock_.AdvanceTimeMilliseconds(kFrameIntervalMs);
// Yet another TL2, next in TL1 is now only one frame away, so don't store
// for retransmission.
EXPECT_EQ(StorageType::kDontRetransmit,
rtp_sender_video_.GetStorageType(header, kSettings, kRttMs));
}
TEST_P(RtpSenderVideoTest, ConditionalRetransmitLimit) {
const int64_t kFrameIntervalMs = 200;
const int64_t kRttMs = (kFrameIntervalMs * 3) / 2;
const int32_t kSettings =
kRetransmitBaseLayer | kConditionallyRetransmitHigherLayers;
// Insert VP8 frames for all temporal layers, but stop before the final index.
RTPVideoHeader header;
header.codec = kVideoCodecVP8;
// Fill averaging window to prevent rounding errors.
constexpr int kNumRepetitions =
(RTPSenderVideo::kTLRateWindowSizeMs + (kFrameIntervalMs / 2)) /
kFrameIntervalMs;
constexpr int kPattern[] = {0, 2, 2, 2};
auto& vp8_header = header.video_type_header.emplace<RTPVideoHeaderVP8>();
for (size_t i = 0; i < arraysize(kPattern) * kNumRepetitions; ++i) {
vp8_header.temporalIdx = kPattern[i % arraysize(kPattern)];
rtp_sender_video_.GetStorageType(header, kSettings, kRttMs);
fake_clock_.AdvanceTimeMilliseconds(kFrameIntervalMs);
}
// Since we're at the start of the pattern, the next expected frame will be
// right now in TL0. Put it in TL1 instead. Regular rules would dictate that
// we don't store for retransmission because we expect a frame in a lower
// layer, but that last frame in TL1 was a long time ago in absolute terms,
// so allow retransmission anyway.
vp8_header.temporalIdx = 1;
EXPECT_EQ(StorageType::kAllowRetransmission,
rtp_sender_video_.GetStorageType(header, kSettings, kRttMs));
}
void RtpSenderVideoTest::PopulateGenericFrameDescriptor(int version) {
const RTPExtensionType ext_type =
(version == 0) ? RTPExtensionType::kRtpExtensionGenericFrameDescriptor00
: RTPExtensionType::kRtpExtensionGenericFrameDescriptor01;
const int ext_id =
(version == 0) ? kGenericDescriptorId00 : kGenericDescriptorId01;
const int64_t kFrameId = 100000;
uint8_t kFrame[100];
EXPECT_EQ(0, rtp_sender_.RegisterRtpHeaderExtension(ext_type, ext_id));
RTPVideoHeader hdr;
RTPVideoHeader::GenericDescriptorInfo& generic = hdr.generic.emplace();
generic.frame_id = kFrameId;
generic.temporal_index = 3;
generic.spatial_index = 2;
generic.higher_spatial_layers.push_back(4);
generic.dependencies.push_back(kFrameId - 1);
generic.dependencies.push_back(kFrameId - 500);
rtp_sender_video_.SendVideo(VideoFrameType::kVideoFrameDelta, kPayload,
kTimestamp, 0, kFrame, sizeof(kFrame), nullptr,
&hdr, kDefaultExpectedRetransmissionTimeMs);
RtpGenericFrameDescriptor descriptor_wire;
EXPECT_EQ(1, transport_.packets_sent());
if (version == 0) {
ASSERT_TRUE(transport_.last_sent_packet()
.GetExtension<RtpGenericFrameDescriptorExtension00>(
&descriptor_wire));
} else {
ASSERT_TRUE(transport_.last_sent_packet()
.GetExtension<RtpGenericFrameDescriptorExtension01>(
&descriptor_wire));
}
EXPECT_EQ(static_cast<uint16_t>(generic.frame_id), descriptor_wire.FrameId());
EXPECT_EQ(generic.temporal_index, descriptor_wire.TemporalLayer());
EXPECT_THAT(descriptor_wire.FrameDependenciesDiffs(), ElementsAre(1, 500));
uint8_t spatial_bitmask = 0x14;
EXPECT_EQ(spatial_bitmask, descriptor_wire.SpatialLayersBitmask());
}
TEST_P(RtpSenderVideoTest, PopulateGenericFrameDescriptor00) {
PopulateGenericFrameDescriptor(0);
}
TEST_P(RtpSenderVideoTest, PopulateGenericFrameDescriptor01) {
PopulateGenericFrameDescriptor(1);
}
void RtpSenderVideoTest::
UsesMinimalVp8DescriptorWhenGenericFrameDescriptorExtensionIsUsed(
int version) {
const int64_t kFrameId = 100000;
const size_t kFrameSize = 100;
uint8_t kFrame[kFrameSize];
if (version == 0) {
ASSERT_TRUE(rtp_sender_.RegisterRtpHeaderExtension(
RtpGenericFrameDescriptorExtension00::kUri, kGenericDescriptorId00));
} else {
ASSERT_TRUE(rtp_sender_.RegisterRtpHeaderExtension(
RtpGenericFrameDescriptorExtension01::kUri, kGenericDescriptorId01));
}
RTPVideoHeader hdr;
hdr.codec = kVideoCodecVP8;
RTPVideoHeaderVP8& vp8 = hdr.video_type_header.emplace<RTPVideoHeaderVP8>();
vp8.pictureId = kFrameId % 0X7FFF;
vp8.tl0PicIdx = 13;
vp8.temporalIdx = 1;
vp8.keyIdx = 2;
RTPVideoHeader::GenericDescriptorInfo& generic = hdr.generic.emplace();
generic.frame_id = kFrameId;
rtp_sender_video_.RegisterPayloadType(kPayload, "vp8", /*raw_payload=*/false);
rtp_sender_video_.SendVideo(VideoFrameType::kVideoFrameDelta, kPayload,
kTimestamp, 0, kFrame, sizeof(kFrame), nullptr,
&hdr, kDefaultExpectedRetransmissionTimeMs);
ASSERT_EQ(transport_.packets_sent(), 1);
// Expect only minimal 1-byte vp8 descriptor was generated.
EXPECT_EQ(transport_.last_sent_packet().payload_size(), 1 + kFrameSize);
}
TEST_P(RtpSenderVideoTest,
UsesMinimalVp8DescriptorWhenGenericFrameDescriptorExtensionIsUsed00) {
UsesMinimalVp8DescriptorWhenGenericFrameDescriptorExtensionIsUsed(0);
}
TEST_P(RtpSenderVideoTest,
UsesMinimalVp8DescriptorWhenGenericFrameDescriptorExtensionIsUsed01) {
UsesMinimalVp8DescriptorWhenGenericFrameDescriptorExtensionIsUsed(1);
}
INSTANTIATE_TEST_SUITE_P(WithAndWithoutOverhead,
RtpSenderVideoTest,
::testing::Bool());
} // namespace webrtc