Implement video versions of RTCInboundRtpStreamStats.jitterBuffer{Target,Minimum}Delay

* https://www.w3.org/TR/webrtc-stats/#dom-rtcinboundrtpstreamstats-jitterbuffertargetdelay
* https://www.w3.org/TR/webrtc-stats/#dom-rtcinboundrtpstreamstats-jitterbufferminimumdelay

Tested: https://jsfiddle.net/pfgzj0yo/17/

Bug: webrtc:14244
Change-Id: I3d949ba63c8339b3881f5d00356559d5789d283d
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/304404
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Åsa Persson <asapersson@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40157}
This commit is contained in:
Rasmus Brandt 2023-05-25 09:37:16 +02:00 committed by WebRTC LUCI CQ
parent 9caef2a8b8
commit f0820ffd88
17 changed files with 205 additions and 51 deletions

View File

@ -80,7 +80,11 @@ std::string VideoReceiveStreamInterface::Stats::ToString(
ss << "jitter_delay_ms: " << jitter_buffer_ms << ", ";
ss << "totalAssemblyTime: " << total_assembly_time.seconds<double>() << ", ";
ss << "jitterBufferDelay: " << jitter_buffer_delay.seconds<double>() << ", ";
ss << "jitterBufferTargetDelay: "
<< jitter_buffer_target_delay.seconds<double>() << ", ";
ss << "jitterBufferEmittedCount: " << jitter_buffer_emitted_count << ", ";
ss << "jitterBufferMinimumDelay: "
<< jitter_buffer_minimum_delay.seconds<double>();
ss << "totalDecodeTime: " << total_decode_time.seconds<double>() << ", ";
ss << "totalProcessingDelay: " << total_processing_delay.seconds<double>()
<< ", ";

View File

@ -98,8 +98,12 @@ class VideoReceiveStreamInterface : public MediaReceiveStreamInterface {
int jitter_buffer_ms = 0;
// https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-jitterbufferdelay
TimeDelta jitter_buffer_delay = TimeDelta::Zero();
// https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-jitterbuffertargetdelay
TimeDelta jitter_buffer_target_delay = TimeDelta::Zero();
// https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-jitterbufferemittedcount
uint64_t jitter_buffer_emitted_count = 0;
// https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-jitterbufferminimumdelay
TimeDelta jitter_buffer_minimum_delay = TimeDelta::Zero();
int min_playout_delay_ms = 0;
int render_delay_ms = 10;
int64_t interframe_delay_max_ms = -1;

View File

@ -442,15 +442,11 @@ struct MediaReceiverInfo {
// https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-jitterbufferdelay
double jitter_buffer_delay_seconds = 0.0;
// Target delay for the jitter buffer (cumulative).
// TODO(crbug.com/webrtc/14244): This metric is only implemented for
// audio, it should be implemented for video as well.
// https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-jitterbuffertargetdelay
absl::optional<double> jitter_buffer_target_delay_seconds;
double jitter_buffer_target_delay_seconds = 0.0;
// Minimum obtainable delay for the jitter buffer (cumulative).
// TODO(crbug.com/webrtc/14244): This metric is only implemented for
// audio, it should be implemented for video as well.
// https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-jitterbufferminimumdelay
absl::optional<double> jitter_buffer_minimum_delay_seconds;
double jitter_buffer_minimum_delay_seconds = 0.0;
// Number of observations for cumulative jitter latency.
// https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-jitterbufferemittedcount
uint64_t jitter_buffer_emitted_count = 0;

View File

@ -3394,7 +3394,11 @@ WebRtcVideoChannel::WebRtcVideoReceiveStream::GetVideoReceiverInfo(
info.jitter_buffer_ms = stats.jitter_buffer_ms;
info.jitter_buffer_delay_seconds =
stats.jitter_buffer_delay.seconds<double>();
info.jitter_buffer_target_delay_seconds =
stats.jitter_buffer_target_delay.seconds<double>();
info.jitter_buffer_emitted_count = stats.jitter_buffer_emitted_count;
info.jitter_buffer_minimum_delay_seconds =
stats.jitter_buffer_minimum_delay.seconds<double>();
info.min_playout_delay_ms = stats.min_playout_delay_ms;
info.render_delay_ms = stats.render_delay_ms;
info.frames_received =

View File

@ -6557,7 +6557,9 @@ TEST_F(WebRtcVideoChannelTest, GetStatsTranslatesDecodeStatsCorrectly) {
stats.target_delay_ms = 5;
stats.jitter_buffer_ms = 6;
stats.jitter_buffer_delay = TimeDelta::Seconds(60);
stats.jitter_buffer_target_delay = TimeDelta::Seconds(55);
stats.jitter_buffer_emitted_count = 6;
stats.jitter_buffer_minimum_delay = TimeDelta::Seconds(50);
stats.min_playout_delay_ms = 7;
stats.render_delay_ms = 8;
stats.width = 9;
@ -6591,8 +6593,12 @@ TEST_F(WebRtcVideoChannelTest, GetStatsTranslatesDecodeStatsCorrectly) {
EXPECT_EQ(stats.jitter_buffer_ms, receive_info.receivers[0].jitter_buffer_ms);
EXPECT_EQ(stats.jitter_buffer_delay.seconds<double>(),
receive_info.receivers[0].jitter_buffer_delay_seconds);
EXPECT_EQ(stats.jitter_buffer_target_delay.seconds<double>(),
receive_info.receivers[0].jitter_buffer_target_delay_seconds);
EXPECT_EQ(stats.jitter_buffer_emitted_count,
receive_info.receivers[0].jitter_buffer_emitted_count);
EXPECT_EQ(stats.jitter_buffer_minimum_delay.seconds<double>(),
receive_info.receivers[0].jitter_buffer_minimum_delay_seconds);
EXPECT_EQ(stats.min_playout_delay_ms,
receive_info.receivers[0].min_playout_delay_ms);
EXPECT_EQ(stats.render_delay_ms, receive_info.receivers[0].render_delay_ms);

View File

@ -252,6 +252,13 @@ TimeDelta VCMTiming::TargetDelayInternal() const {
jitter_delay_ + EstimatedMaxDecodeTime() + render_delay_);
}
// TODO(crbug.com/webrtc/15197): Centralize delay arithmetic.
TimeDelta VCMTiming::StatsTargetDelayInternal() const {
TimeDelta stats_target_delay =
TargetDelayInternal() - (EstimatedMaxDecodeTime() + render_delay_);
return std::max(TimeDelta::Zero(), stats_target_delay);
}
VideoFrame::RenderParameters VCMTiming::RenderParameters() const {
MutexLock lock(&mutex_);
return {.use_low_latency_rendering = UseLowLatencyRendering(),
@ -271,12 +278,12 @@ VCMTiming::VideoDelayTimings VCMTiming::GetTimings() const {
MutexLock lock(&mutex_);
return VideoDelayTimings{
.num_decoded_frames = num_decoded_frames_,
.jitter_delay = jitter_delay_,
.minimum_delay = jitter_delay_,
.estimated_max_decode_time = EstimatedMaxDecodeTime(),
.render_delay = render_delay_,
.min_playout_delay = min_playout_delay_,
.max_playout_delay = max_playout_delay_,
.target_delay = TargetDelayInternal(),
.target_delay = StatsTargetDelayInternal(),
.current_delay = current_delay_};
}

View File

@ -31,13 +31,15 @@ class VCMTiming {
public:
struct VideoDelayTimings {
size_t num_decoded_frames;
// Delay added to smooth out frame delay variation ("jitter") caused by
// the network.
TimeDelta jitter_delay;
// Pre-decode delay added to smooth out frame delay variation ("jitter")
// caused by the network. The target delay will be no smaller than this
// delay, thus it is called `minimum_delay`.
TimeDelta minimum_delay;
// Estimated time needed to decode a video frame. Obtained as the 95th
// percentile decode time over a recent time window.
TimeDelta estimated_max_decode_time;
// Estimated time needed to render a frame. Set to a constant.
// Post-decode delay added to smooth out frame delay variation caused by
// decoding and rendering. Set to a constant.
TimeDelta render_delay;
// Minimum total delay used when determining render time for a frame.
// Obtained from API, `playout-delay` RTP header extension, or A/V sync.
@ -45,9 +47,9 @@ class VCMTiming {
// Maximum total delay used when determining render time for a frame.
// Obtained from `playout-delay` RTP header extension.
TimeDelta max_playout_delay;
// Target delay. Obtained from all the elements above.
// Target total delay. Obtained from all the elements above.
TimeDelta target_delay;
// Current delay. Obtained by smoothing out the target delay.
// Current total delay. Obtained by smoothening the `target_delay`.
TimeDelta current_delay;
};
@ -133,6 +135,8 @@ class VCMTiming {
Timestamp RenderTimeInternal(uint32_t frame_timestamp, Timestamp now) const
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
TimeDelta TargetDelayInternal() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
TimeDelta StatsTargetDelayInternal() const
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
bool UseLowLatencyRendering() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
mutable Mutex mutex_;

View File

@ -13,6 +13,7 @@
#include "api/units/frequency.h"
#include "api/units/time_delta.h"
#include "system_wrappers/include/clock.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/scoped_key_value_config.h"
@ -22,9 +23,54 @@ namespace {
constexpr Frequency k25Fps = Frequency::Hertz(25);
constexpr Frequency k90kHz = Frequency::KiloHertz(90);
MATCHER(HasConsistentVideoDelayTimings, "") {
// Delays should be non-negative.
bool p1 = arg.minimum_delay >= TimeDelta::Zero();
bool p2 = arg.estimated_max_decode_time >= TimeDelta::Zero();
bool p3 = arg.render_delay >= TimeDelta::Zero();
bool p4 = arg.min_playout_delay >= TimeDelta::Zero();
bool p5 = arg.max_playout_delay >= TimeDelta::Zero();
bool p6 = arg.target_delay >= TimeDelta::Zero();
bool p7 = arg.current_delay >= TimeDelta::Zero();
*result_listener << "\np: " << p1 << p2 << p3 << p4 << p5 << p6 << p7;
bool p = p1 && p2 && p3 && p4 && p5 && p6 && p7;
// Delays should be internally consistent.
bool m1 = arg.minimum_delay <= arg.target_delay;
if (!m1) {
*result_listener << "\nminimum_delay: " << arg.minimum_delay << ", "
<< "target_delay: " << arg.target_delay << "\n";
}
bool m2 = arg.minimum_delay <= arg.current_delay;
if (!m2) {
*result_listener << "\nminimum_delay: " << arg.minimum_delay << ", "
<< "current_delay: " << arg.current_delay;
}
bool m3 = arg.target_delay >= arg.min_playout_delay;
if (!m3) {
*result_listener << "\ntarget_delay: " << arg.target_delay << ", "
<< "min_playout_delay: " << arg.min_playout_delay << "\n";
}
// TODO(crbug.com/webrtc/15197): Uncomment when this is guaranteed.
// bool m4 = arg.target_delay <= arg.max_playout_delay;
bool m5 = arg.current_delay >= arg.min_playout_delay;
if (!m5) {
*result_listener << "\ncurrent_delay: " << arg.current_delay << ", "
<< "min_playout_delay: " << arg.min_playout_delay << "\n";
}
bool m6 = arg.current_delay <= arg.max_playout_delay;
if (!m6) {
*result_listener << "\ncurrent_delay: " << arg.current_delay << ", "
<< "max_playout_delay: " << arg.max_playout_delay << "\n";
}
bool m = m1 && m2 && m3 && m5 && m6;
return p && m;
}
} // namespace
TEST(ReceiverTimingTest, JitterDelay) {
TEST(VCMTimingTest, JitterDelay) {
test::ScopedKeyValueConfig field_trials;
SimulatedClock clock(0);
VCMTiming timing(&clock, field_trials);
@ -115,9 +161,11 @@ TEST(ReceiverTimingTest, JitterDelay) {
clock.AdvanceTimeMilliseconds(5000);
timestamp += 5 * 90000;
timing.UpdateCurrentDelay(timestamp);
EXPECT_THAT(timing.GetTimings(), HasConsistentVideoDelayTimings());
}
TEST(ReceiverTimingTest, TimestampWrapAround) {
TEST(VCMTimingTest, TimestampWrapAround) {
constexpr auto kStartTime = Timestamp::Millis(1337);
test::ScopedKeyValueConfig field_trials;
SimulatedClock clock(kStartTime);
@ -136,9 +184,11 @@ TEST(ReceiverTimingTest, TimestampWrapAround) {
EXPECT_EQ(kStartTime + 3 / k25Fps + TimeDelta::Millis(1),
timing.RenderTime(89u, clock.CurrentTime()));
}
EXPECT_THAT(timing.GetTimings(), HasConsistentVideoDelayTimings());
}
TEST(ReceiverTimingTest, UseLowLatencyRenderer) {
TEST(VCMTimingTest, UseLowLatencyRenderer) {
test::ScopedKeyValueConfig field_trials;
SimulatedClock clock(0);
VCMTiming timing(&clock, field_trials);
@ -161,9 +211,11 @@ TEST(ReceiverTimingTest, UseLowLatencyRenderer) {
// False if max playout delay > 500 ms.
timing.set_max_playout_delay(TimeDelta::Millis(501));
EXPECT_FALSE(timing.RenderParameters().use_low_latency_rendering);
EXPECT_THAT(timing.GetTimings(), HasConsistentVideoDelayTimings());
}
TEST(ReceiverTimingTest, MaxWaitingTimeIsZeroForZeroRenderTime) {
TEST(VCMTimingTest, MaxWaitingTimeIsZeroForZeroRenderTime) {
// This is the default path when the RTP playout delay header extension is set
// to min==0 and max==0.
constexpr int64_t kStartTimeUs = 3.15e13; // About one year in us.
@ -197,9 +249,11 @@ TEST(ReceiverTimingTest, MaxWaitingTimeIsZeroForZeroRenderTime) {
EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTime, now,
/*too_many_frames_queued=*/false),
TimeDelta::Zero());
EXPECT_THAT(timing.GetTimings(), HasConsistentVideoDelayTimings());
}
TEST(ReceiverTimingTest, MaxWaitingTimeZeroDelayPacingExperiment) {
TEST(VCMTimingTest, MaxWaitingTimeZeroDelayPacingExperiment) {
// The minimum pacing is enabled by a field trial and active if the RTP
// playout delay header extension is set to min==0.
constexpr TimeDelta kMinPacing = TimeDelta::Millis(3);
@ -247,9 +301,11 @@ TEST(ReceiverTimingTest, MaxWaitingTimeZeroDelayPacingExperiment) {
EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now,
/*too_many_frames_queued=*/false),
kMinPacing);
EXPECT_THAT(timing.GetTimings(), HasConsistentVideoDelayTimings());
}
TEST(ReceiverTimingTest, DefaultMaxWaitingTimeUnaffectedByPacingExperiment) {
TEST(VCMTimingTest, DefaultMaxWaitingTimeUnaffectedByPacingExperiment) {
// The minimum pacing is enabled by a field trial but should not have any
// effect if render_time_ms is greater than 0;
test::ScopedKeyValueConfig field_trials(
@ -277,9 +333,11 @@ TEST(ReceiverTimingTest, DefaultMaxWaitingTimeUnaffectedByPacingExperiment) {
/*too_many_frames_queued=*/false),
render_time - now - estimated_processing_delay);
}
EXPECT_THAT(timing.GetTimings(), HasConsistentVideoDelayTimings());
}
TEST(ReceiverTimingTest, MaxWaitingTimeReturnsZeroIfTooManyFramesQueuedIsTrue) {
TEST(VCMTimingTest, MaxWaitingTimeReturnsZeroIfTooManyFramesQueuedIsTrue) {
// The minimum pacing is enabled by a field trial and active if the RTP
// playout delay header extension is set to min==0.
constexpr TimeDelta kMinPacing = TimeDelta::Millis(3);
@ -314,9 +372,11 @@ TEST(ReceiverTimingTest, MaxWaitingTimeReturnsZeroIfTooManyFramesQueuedIsTrue) {
EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now_ms,
/*too_many_frames_queued=*/true),
TimeDelta::Zero());
EXPECT_THAT(timing.GetTimings(), HasConsistentVideoDelayTimings());
}
TEST(ReceiverTimingTest, UpdateCurrentDelayCapsWhenOffByMicroseconds) {
TEST(VCMTimingTest, UpdateCurrentDelayCapsWhenOffByMicroseconds) {
test::ScopedKeyValueConfig field_trials;
SimulatedClock clock(0);
VCMTiming timing(&clock, field_trials);
@ -334,6 +394,52 @@ TEST(ReceiverTimingTest, UpdateCurrentDelayCapsWhenOffByMicroseconds) {
decode_time + TimeDelta::Millis(10) + TimeDelta::Micros(37);
timing.UpdateCurrentDelay(render_time, decode_time);
EXPECT_EQ(timing.GetTimings().current_delay, timing.TargetVideoDelay());
// TODO(crbug.com/webrtc/15197): Fix this.
// EXPECT_THAT(timing.GetTimings(), HasConsistentVideoDelayTimings());
}
TEST(VCMTimingTest, GetTimings) {
test::ScopedKeyValueConfig field_trials;
SimulatedClock clock(33);
VCMTiming timing(&clock, field_trials);
timing.Reset();
// Setup.
TimeDelta render_delay = TimeDelta::Millis(11);
timing.set_render_delay(render_delay);
TimeDelta min_playout_delay = TimeDelta::Millis(50);
timing.set_min_playout_delay(min_playout_delay);
TimeDelta max_playout_delay = TimeDelta::Millis(500);
timing.set_max_playout_delay(max_playout_delay);
// On complete.
timing.IncomingTimestamp(3000, clock.CurrentTime());
clock.AdvanceTimeMilliseconds(1);
// On decodable.
Timestamp render_time =
timing.RenderTime(/*next_temporal_unit_rtp=*/3000, clock.CurrentTime());
TimeDelta minimum_delay = TimeDelta::Millis(123);
timing.SetJitterDelay(minimum_delay);
timing.UpdateCurrentDelay(render_time, clock.CurrentTime());
clock.AdvanceTimeMilliseconds(100);
// On decoded.
TimeDelta decode_time = TimeDelta::Millis(4);
timing.StopDecodeTimer(decode_time, clock.CurrentTime());
VCMTiming::VideoDelayTimings timings = timing.GetTimings();
EXPECT_EQ(timings.num_decoded_frames, 1u);
EXPECT_EQ(timings.minimum_delay, minimum_delay);
// A single decoded frame is not enough to calculate p95.
EXPECT_EQ(timings.estimated_max_decode_time, TimeDelta::Zero());
EXPECT_EQ(timings.render_delay, render_delay);
EXPECT_EQ(timings.min_playout_delay, min_playout_delay);
EXPECT_EQ(timings.max_playout_delay, max_playout_delay);
EXPECT_EQ(timings.target_delay, minimum_delay);
EXPECT_EQ(timings.current_delay, minimum_delay);
EXPECT_THAT(timings, HasConsistentVideoDelayTimings());
}
} // namespace webrtc

View File

@ -416,14 +416,10 @@ void SetInboundRTPStreamStatsFromMediaReceiverInfo(
static_cast<int32_t>(media_receiver_info.packets_lost);
inbound_stats->jitter_buffer_delay =
media_receiver_info.jitter_buffer_delay_seconds;
if (media_receiver_info.jitter_buffer_target_delay_seconds.has_value()) {
inbound_stats->jitter_buffer_target_delay =
*media_receiver_info.jitter_buffer_target_delay_seconds;
}
if (media_receiver_info.jitter_buffer_minimum_delay_seconds.has_value()) {
inbound_stats->jitter_buffer_minimum_delay =
*media_receiver_info.jitter_buffer_minimum_delay_seconds;
}
inbound_stats->jitter_buffer_target_delay =
media_receiver_info.jitter_buffer_target_delay_seconds;
inbound_stats->jitter_buffer_minimum_delay =
media_receiver_info.jitter_buffer_minimum_delay_seconds;
inbound_stats->jitter_buffer_emitted_count =
media_receiver_info.jitter_buffer_emitted_count;
if (media_receiver_info.nacks_sent.has_value()) {

View File

@ -634,6 +634,10 @@ class RTCStatsReportVerifier {
inbound_stream.jitter_buffer_delay);
verifier.TestMemberIsNonNegative<uint64_t>(
inbound_stream.jitter_buffer_emitted_count);
verifier.TestMemberIsNonNegative<double>(
inbound_stream.jitter_buffer_target_delay);
verifier.TestMemberIsNonNegative<double>(
inbound_stream.jitter_buffer_minimum_delay);
if (inbound_stream.kind.is_defined() && *inbound_stream.kind == "video") {
verifier.TestMemberIsUndefined(inbound_stream.total_samples_received);
verifier.TestMemberIsUndefined(inbound_stream.concealed_samples);
@ -643,9 +647,6 @@ class RTCStatsReportVerifier {
inbound_stream.inserted_samples_for_deceleration);
verifier.TestMemberIsUndefined(
inbound_stream.removed_samples_for_acceleration);
verifier.TestMemberIsUndefined(inbound_stream.jitter_buffer_target_delay);
verifier.TestMemberIsUndefined(
inbound_stream.jitter_buffer_minimum_delay);
verifier.TestMemberIsUndefined(inbound_stream.audio_level);
verifier.TestMemberIsUndefined(inbound_stream.total_audio_energy);
verifier.TestMemberIsUndefined(inbound_stream.total_samples_duration);

View File

@ -129,8 +129,12 @@ TEST_F(StatsEndToEndTest, GetStats) {
receive_stats_filled_["JitterBufferDelay"] =
stats.jitter_buffer_delay > TimeDelta::Zero();
receive_stats_filled_["JitterBufferTargetDelay"] =
stats.jitter_buffer_target_delay > TimeDelta::Zero();
receive_stats_filled_["JitterBufferEmittedCount"] =
stats.jitter_buffer_emitted_count != 0;
receive_stats_filled_["JitterBufferMinimumDelay"] =
stats.jitter_buffer_minimum_delay > TimeDelta::Zero();
receive_stats_filled_["CName"] |= !stats.c_name.empty();

View File

@ -531,12 +531,15 @@ void ReceiveStatisticsProxy::OnDecoderInfo(
}));
}
void ReceiveStatisticsProxy::OnDecodableFrame(TimeDelta jitter_buffer_delay) {
void ReceiveStatisticsProxy::OnDecodableFrame(TimeDelta jitter_buffer_delay,
TimeDelta target_delay,
TimeDelta minimum_delay) {
RTC_DCHECK_RUN_ON(&main_thread_);
// Cumulative stats exposed through standardized GetStats.
// TODO(crbug.com/webrtc/14244): Implement targetDelay and minimumDelay here.
stats_.jitter_buffer_delay += jitter_buffer_delay;
stats_.jitter_buffer_target_delay += target_delay;
++stats_.jitter_buffer_emitted_count;
stats_.jitter_buffer_minimum_delay += minimum_delay;
}
void ReceiveStatisticsProxy::OnFrameBufferTimingsUpdated(

View File

@ -89,7 +89,9 @@ class ReceiveStatisticsProxy : public VideoStreamBufferControllerStatsObserver,
size_t size_bytes,
VideoContentType content_type) override;
void OnDroppedFrames(uint32_t frames_dropped) override;
void OnDecodableFrame(TimeDelta jitter_buffer_delay) override;
void OnDecodableFrame(TimeDelta jitter_buffer_delay,
TimeDelta target_delay,
TimeDelta minimum_delay) override;
void OnFrameBufferTimingsUpdated(int estimated_max_decode_time_ms,
int current_delay_ms,
int target_delay_ms,

View File

@ -560,35 +560,45 @@ TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsOnDroppedFrame) {
TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsDecodeTimingStats) {
const int kMaxDecodeMs = 2;
const int kCurrentDelayMs = 3;
const int kTargetDelayMs = 4;
const TimeDelta kTargetDelay = TimeDelta::Millis(4);
const int kJitterDelayMs = 5;
const int kMinPlayoutDelayMs = 6;
const int kRenderDelayMs = 7;
const int64_t kRttMs = 8;
const int kJitterBufferDelayMs = 9;
const TimeDelta kJitterBufferDelay = TimeDelta::Millis(9);
const TimeDelta kMinimumDelay = TimeDelta::Millis(1);
statistics_proxy_->OnRttUpdate(kRttMs);
statistics_proxy_->OnFrameBufferTimingsUpdated(
kMaxDecodeMs, kCurrentDelayMs, kTargetDelayMs, kJitterDelayMs,
kMaxDecodeMs, kCurrentDelayMs, kTargetDelay.ms(), kJitterDelayMs,
kMinPlayoutDelayMs, kRenderDelayMs);
statistics_proxy_->OnDecodableFrame(TimeDelta::Millis(kJitterBufferDelayMs));
statistics_proxy_->OnDecodableFrame(kJitterBufferDelay, kTargetDelay,
kMinimumDelay);
VideoReceiveStreamInterface::Stats stats = FlushAndGetStats();
EXPECT_EQ(kMaxDecodeMs, stats.max_decode_ms);
EXPECT_EQ(kCurrentDelayMs, stats.current_delay_ms);
EXPECT_EQ(kTargetDelayMs, stats.target_delay_ms);
EXPECT_EQ(kTargetDelay.ms(), stats.target_delay_ms);
EXPECT_EQ(kJitterDelayMs, stats.jitter_buffer_ms);
EXPECT_EQ(kMinPlayoutDelayMs, stats.min_playout_delay_ms);
EXPECT_EQ(kRenderDelayMs, stats.render_delay_ms);
EXPECT_EQ(kJitterBufferDelayMs, stats.jitter_buffer_delay.ms());
EXPECT_EQ(kJitterBufferDelay, stats.jitter_buffer_delay);
EXPECT_EQ(kTargetDelay, stats.jitter_buffer_target_delay);
EXPECT_EQ(1u, stats.jitter_buffer_emitted_count);
EXPECT_EQ(kMinimumDelay, stats.jitter_buffer_minimum_delay);
}
TEST_F(ReceiveStatisticsProxyTest, CumulativeDecodeGetStatsAccumulate) {
const int kJitterBufferDelayMs = 3;
statistics_proxy_->OnDecodableFrame(TimeDelta::Millis(kJitterBufferDelayMs));
statistics_proxy_->OnDecodableFrame(TimeDelta::Millis(kJitterBufferDelayMs));
const TimeDelta kJitterBufferDelay = TimeDelta::Millis(3);
const TimeDelta kTargetDelay = TimeDelta::Millis(2);
const TimeDelta kMinimumDelay = TimeDelta::Millis(1);
statistics_proxy_->OnDecodableFrame(kJitterBufferDelay, kTargetDelay,
kMinimumDelay);
statistics_proxy_->OnDecodableFrame(kJitterBufferDelay, kTargetDelay,
kMinimumDelay);
VideoReceiveStreamInterface::Stats stats = FlushAndGetStats();
EXPECT_EQ(2 * kJitterBufferDelayMs, stats.jitter_buffer_delay.ms());
EXPECT_EQ(2 * kJitterBufferDelay, stats.jitter_buffer_delay);
EXPECT_EQ(2 * kTargetDelay, stats.jitter_buffer_target_delay);
EXPECT_EQ(2u, stats.jitter_buffer_emitted_count);
EXPECT_EQ(2 * kMinimumDelay, stats.jitter_buffer_minimum_delay);
}
TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsRtcpPacketTypeCounts) {

View File

@ -336,7 +336,7 @@ void VideoStreamBufferController::UpdateFrameBufferTimings(
if (timings.num_decoded_frames) {
stats_proxy_->OnFrameBufferTimingsUpdated(
timings.estimated_max_decode_time.ms(), timings.current_delay.ms(),
timings.target_delay.ms(), timings.jitter_delay.ms(),
timings.target_delay.ms(), timings.minimum_delay.ms(),
timings.min_playout_delay.ms(), timings.render_delay.ms());
}
@ -351,7 +351,8 @@ void VideoStreamBufferController::UpdateFrameBufferTimings(
// https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-jitterbufferdelay
TimeDelta jitter_buffer_delay =
std::max(TimeDelta::Zero(), now - min_receive_time);
stats_proxy_->OnDecodableFrame(jitter_buffer_delay);
stats_proxy_->OnDecodableFrame(jitter_buffer_delay, timings.target_delay,
timings.minimum_delay);
}
void VideoStreamBufferController::UpdateTimingFrameInfo() {

View File

@ -45,8 +45,12 @@ class VideoStreamBufferControllerStatsObserver {
virtual void OnDroppedFrames(uint32_t frames_dropped) = 0;
// Actual delay experienced by a single frame.
virtual void OnDecodableFrame(TimeDelta jitter_buffer_delay) = 0;
// `jitter_buffer_delay` is the delay experienced by a single frame,
// whereas `target_delay` and `minimum_delay` are the current delays
// applied by the jitter buffer.
virtual void OnDecodableFrame(TimeDelta jitter_buffer_delay,
TimeDelta target_delay,
TimeDelta minimum_delay) = 0;
// Various jitter buffer delays determined by VCMTiming.
virtual void OnFrameBufferTimingsUpdated(int estimated_max_decode_time_ms,

View File

@ -107,7 +107,9 @@ class VideoStreamBufferControllerStatsObserverMock
MOCK_METHOD(void, OnDroppedFrames, (uint32_t num_dropped), (override));
MOCK_METHOD(void,
OnDecodableFrame,
(TimeDelta jitter_buffer_delay),
(TimeDelta jitter_buffer_delay,
TimeDelta target_delay,
TimeDelta minimum_delay),
(override));
MOCK_METHOD(void,
OnFrameBufferTimingsUpdated,