Add field trial to force playout delay

This CL adds the field trial WebRTC-ForcePlayoutDelay with parameters
min_ms and max_ms. If both of these values are set, the playout delay
of any received packet will be overridden by the specified values.

Bug: None
Change-Id: I353282097e3ffa437dfc5affdfdf7780b09474e7
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/174180
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Commit-Queue: Johannes Kron <kron@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31149}
This commit is contained in:
Johannes Kron
2020-04-30 13:50:34 +02:00
committed by Commit Bot
parent 3745d3fc93
commit 14a23a32c4
3 changed files with 97 additions and 35 deletions

View File

@ -237,6 +237,8 @@ RtpVideoStreamReceiver::RtpVideoStreamReceiver(
process_thread_(process_thread),
ntp_estimator_(clock),
rtp_header_extensions_(config_.rtp.extensions),
forced_playout_delay_max_ms_("max_ms", absl::nullopt),
forced_playout_delay_min_ms_("min_ms", absl::nullopt),
rtp_receive_statistics_(rtp_receive_statistics),
ulpfec_receiver_(UlpfecReceiver::Create(config->rtp.remote_ssrc,
this,
@ -290,6 +292,10 @@ RtpVideoStreamReceiver::RtpVideoStreamReceiver(
if (config_.rtp.rtcp_xr.receiver_reference_time_report)
rtp_rtcp_->SetRtcpXrRrtrStatus(true);
ParseFieldTrial(
{&forced_playout_delay_max_ms_, &forced_playout_delay_min_ms_},
field_trial::FindFullName("WebRTC-ForcePlayoutDelay"));
process_thread_->RegisterModule(rtp_rtcp_.get(), RTC_FROM_HERE);
if (config_.rtp.lntf.enabled) {
@ -513,7 +519,12 @@ void RtpVideoStreamReceiver::OnReceivedPayloadData(
rtp_packet.GetExtension<VideoContentTypeExtension>(
&video_header.content_type);
rtp_packet.GetExtension<VideoTimingExtension>(&video_header.video_timing);
if (forced_playout_delay_max_ms_ && forced_playout_delay_min_ms_) {
video_header.playout_delay.max_ms = *forced_playout_delay_max_ms_;
video_header.playout_delay.min_ms = *forced_playout_delay_min_ms_;
} else {
rtp_packet.GetExtension<PlayoutDelayLimits>(&video_header.playout_delay);
}
rtp_packet.GetExtension<FrameMarkingExtension>(&video_header.frame_marking);
ParseGenericDependenciesResult generic_descriptor_state =

View File

@ -43,6 +43,7 @@
#include "modules/video_coding/unique_timestamp_counter.h"
#include "rtc_base/constructor_magic.h"
#include "rtc_base/critical_section.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/numerics/sequence_number_util.h"
#include "rtc_base/synchronization/sequence_checker.h"
#include "rtc_base/thread_annotations.h"
@ -299,6 +300,10 @@ class RtpVideoStreamReceiver : public LossNotificationSender,
RemoteNtpTimeEstimator ntp_estimator_;
RtpHeaderExtensionMap rtp_header_extensions_;
// Set by the field trial WebRTC-ForcePlayoutDelay to override any playout
// delay that is specified in the received packets.
FieldTrialOptional<int> forced_playout_delay_max_ms_;
FieldTrialOptional<int> forced_playout_delay_min_ms_;
ReceiveStatistics* const rtp_receive_statistics_;
std::unique_ptr<UlpfecReceiver> ulpfec_receiver_;

View File

@ -61,6 +61,15 @@ std::vector<uint64_t> GetAbsoluteCaptureTimestamps(
return result;
}
RTPVideoHeader GetGenericVideoHeader(VideoFrameType frame_type) {
RTPVideoHeader video_header;
video_header.is_first_packet_in_frame = true;
video_header.is_last_packet_in_frame = true;
video_header.codec = kVideoCodecGeneric;
video_header.frame_type = frame_type;
return video_header;
}
class MockTransport : public Transport {
public:
MOCK_METHOD3(SendRtp,
@ -358,14 +367,11 @@ TEST_F(RtpVideoStreamReceiverTest, CacheColorSpaceFromLastPacketOfKeyframe) {
TEST_F(RtpVideoStreamReceiverTest, GenericKeyFrame) {
RtpPacketReceived rtp_packet;
RTPVideoHeader video_header;
rtc::CopyOnWriteBuffer data({1, 2, 3, 4});
rtp_packet.SetPayloadType(kPayloadType);
rtp_packet.SetSequenceNumber(1);
video_header.is_first_packet_in_frame = true;
video_header.is_last_packet_in_frame = true;
video_header.codec = kVideoCodecGeneric;
video_header.frame_type = VideoFrameType::kVideoFrameKey;
RTPVideoHeader video_header =
GetGenericVideoHeader(VideoFrameType::kVideoFrameKey);
mock_on_complete_frame_callback_.AppendExpectedBitstream(data.data(),
data.size());
EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_));
@ -381,7 +387,6 @@ TEST_F(RtpVideoStreamReceiverTest, PacketInfoIsPropagatedIntoVideoFrames) {
extension_map.Register<AbsoluteCaptureTimeExtension>(kId0);
RtpPacketReceived rtp_packet(&extension_map);
rtp_packet.SetPayloadType(kPayloadType);
RTPVideoHeader video_header;
rtc::CopyOnWriteBuffer data({1, 2, 3, 4});
rtp_packet.SetSequenceNumber(1);
rtp_packet.SetTimestamp(1);
@ -390,10 +395,8 @@ TEST_F(RtpVideoStreamReceiverTest, PacketInfoIsPropagatedIntoVideoFrames) {
AbsoluteCaptureTime{kAbsoluteCaptureTimestamp,
/*estimated_capture_clock_offset=*/absl::nullopt});
video_header.is_first_packet_in_frame = true;
video_header.is_last_packet_in_frame = true;
video_header.codec = kVideoCodecGeneric;
video_header.frame_type = VideoFrameType::kVideoFrameKey;
RTPVideoHeader video_header =
GetGenericVideoHeader(VideoFrameType::kVideoFrameKey);
mock_on_complete_frame_callback_.AppendExpectedBitstream(data.data(),
data.size());
EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_))
@ -416,7 +419,6 @@ TEST_F(RtpVideoStreamReceiverTest,
RtpPacketReceived rtp_packet(&extension_map);
rtp_packet.SetPayloadType(kPayloadType);
RTPVideoHeader video_header;
rtc::CopyOnWriteBuffer data({1, 2, 3, 4});
uint16_t sequence_number = 1;
uint32_t rtp_timestamp = 1;
@ -427,10 +429,8 @@ TEST_F(RtpVideoStreamReceiverTest,
AbsoluteCaptureTime{kAbsoluteCaptureTimestamp,
/*estimated_capture_clock_offset=*/absl::nullopt});
video_header.is_first_packet_in_frame = true;
video_header.is_last_packet_in_frame = true;
video_header.codec = kVideoCodecGeneric;
video_header.frame_type = VideoFrameType::kVideoFrameKey;
RTPVideoHeader video_header =
GetGenericVideoHeader(VideoFrameType::kVideoFrameKey);
mock_on_complete_frame_callback_.AppendExpectedBitstream(data.data(),
data.size());
EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_));
@ -496,13 +496,10 @@ TEST_F(RtpVideoStreamReceiverTest,
TEST_F(RtpVideoStreamReceiverTest, GenericKeyFrameBitstreamError) {
RtpPacketReceived rtp_packet;
rtp_packet.SetPayloadType(kPayloadType);
RTPVideoHeader video_header;
rtc::CopyOnWriteBuffer data({1, 2, 3, 4});
rtp_packet.SetSequenceNumber(1);
video_header.is_first_packet_in_frame = true;
video_header.is_last_packet_in_frame = true;
video_header.codec = kVideoCodecGeneric;
video_header.frame_type = VideoFrameType::kVideoFrameKey;
RTPVideoHeader video_header =
GetGenericVideoHeader(VideoFrameType::kVideoFrameKey);
constexpr uint8_t expected_bitsteam[] = {1, 2, 3, 0xff};
mock_on_complete_frame_callback_.AppendExpectedBitstream(
expected_bitsteam, sizeof(expected_bitsteam));
@ -658,13 +655,10 @@ TEST_F(RtpVideoStreamReceiverTest, PaddingInMediaStream) {
TEST_F(RtpVideoStreamReceiverTest, RequestKeyframeIfFirstFrameIsDelta) {
RtpPacketReceived rtp_packet;
rtp_packet.SetPayloadType(kPayloadType);
RTPVideoHeader video_header;
rtc::CopyOnWriteBuffer data({1, 2, 3, 4});
rtp_packet.SetSequenceNumber(1);
video_header.is_first_packet_in_frame = true;
video_header.is_last_packet_in_frame = true;
video_header.codec = kVideoCodecGeneric;
video_header.frame_type = VideoFrameType::kVideoFrameDelta;
RTPVideoHeader video_header =
GetGenericVideoHeader(VideoFrameType::kVideoFrameDelta);
EXPECT_CALL(mock_key_frame_request_sender_, RequestKeyFrame());
rtp_video_stream_receiver_->OnReceivedPayloadData(data, rtp_packet,
video_header);
@ -675,13 +669,11 @@ TEST_F(RtpVideoStreamReceiverTest, RequestKeyframeWhenPacketBufferGetsFull) {
RtpPacketReceived rtp_packet;
rtp_packet.SetPayloadType(kPayloadType);
RTPVideoHeader video_header;
rtc::CopyOnWriteBuffer data({1, 2, 3, 4});
video_header.is_first_packet_in_frame = true;
RTPVideoHeader video_header =
GetGenericVideoHeader(VideoFrameType::kVideoFrameDelta);
// Incomplete frames so that the packet buffer is filling up.
video_header.is_last_packet_in_frame = false;
video_header.codec = kVideoCodecGeneric;
video_header.frame_type = VideoFrameType::kVideoFrameDelta;
uint16_t start_sequence_number = 1234;
rtp_packet.SetSequenceNumber(start_sequence_number);
while (rtp_packet.SequenceNumber() - start_sequence_number <
@ -1149,13 +1141,10 @@ TEST_F(RtpVideoStreamReceiverTest, TransformFrame) {
RtpPacketReceived rtp_packet;
rtp_packet.SetPayloadType(kPayloadType);
RTPVideoHeader video_header;
rtc::CopyOnWriteBuffer data({1, 2, 3, 4});
rtp_packet.SetSequenceNumber(1);
video_header.is_first_packet_in_frame = true;
video_header.is_last_packet_in_frame = true;
video_header.codec = kVideoCodecGeneric;
video_header.frame_type = VideoFrameType::kVideoFrameKey;
RTPVideoHeader video_header =
GetGenericVideoHeader(VideoFrameType::kVideoFrameKey);
mock_on_complete_frame_callback_.AppendExpectedBitstream(data.data(),
data.size());
EXPECT_CALL(*mock_frame_transformer,
@ -1168,4 +1157,61 @@ TEST_F(RtpVideoStreamReceiverTest, TransformFrame) {
receiver = nullptr;
}
// Test default behavior and when playout delay is overridden by field trial.
const PlayoutDelay kTransmittedPlayoutDelay = {100, 200};
const PlayoutDelay kForcedPlayoutDelay = {70, 90};
struct PlayoutDelayOptions {
std::string field_trial;
PlayoutDelay expected_delay;
};
const PlayoutDelayOptions kDefaultBehavior = {
/*field_trial=*/"", /*expected_delay=*/kTransmittedPlayoutDelay};
const PlayoutDelayOptions kOverridePlayoutDelay = {
/*field_trial=*/"WebRTC-ForcePlayoutDelay/min_ms:70,max_ms:90/",
/*expected_delay=*/kForcedPlayoutDelay};
class RtpVideoStreamReceiverTestPlayoutDelay
: public RtpVideoStreamReceiverTest,
public ::testing::WithParamInterface<PlayoutDelayOptions> {
protected:
RtpVideoStreamReceiverTestPlayoutDelay()
: RtpVideoStreamReceiverTest(GetParam().field_trial) {}
};
INSTANTIATE_TEST_SUITE_P(PlayoutDelay,
RtpVideoStreamReceiverTestPlayoutDelay,
Values(kDefaultBehavior, kOverridePlayoutDelay));
TEST_P(RtpVideoStreamReceiverTestPlayoutDelay, PlayoutDelay) {
rtc::CopyOnWriteBuffer payload_data({1, 2, 3, 4});
RtpHeaderExtensionMap extension_map;
extension_map.Register<PlayoutDelayLimits>(1);
RtpPacketToSend packet_to_send(&extension_map);
packet_to_send.SetPayloadType(kPayloadType);
packet_to_send.SetSequenceNumber(1);
// Set playout delay on outgoing packet.
EXPECT_TRUE(packet_to_send.SetExtension<PlayoutDelayLimits>(
kTransmittedPlayoutDelay));
uint8_t* payload = packet_to_send.AllocatePayload(payload_data.size());
memcpy(payload, payload_data.data(), payload_data.size());
RtpPacketReceived received_packet(&extension_map);
received_packet.Parse(packet_to_send.data(), packet_to_send.size());
RTPVideoHeader video_header =
GetGenericVideoHeader(VideoFrameType::kVideoFrameKey);
mock_on_complete_frame_callback_.AppendExpectedBitstream(payload_data.data(),
payload_data.size());
// Expect the playout delay of encoded frame to be the same as the transmitted
// playout delay unless it was overridden by a field trial.
EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_))
.WillOnce(Invoke([expected_playout_delay = GetParam().expected_delay](
video_coding::EncodedFrame* frame) {
EXPECT_EQ(frame->EncodedImage().playout_delay_, expected_playout_delay);
}));
rtp_video_stream_receiver_->OnReceivedPayloadData(
received_packet.PayloadBuffer(), received_packet, video_header);
}
} // namespace webrtc