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:

committed by
Commit Bot

parent
3745d3fc93
commit
14a23a32c4
@ -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);
|
||||
rtp_packet.GetExtension<PlayoutDelayLimits>(&video_header.playout_delay);
|
||||
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 =
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user