Expose video freeze metrics in GetStats.
This adds the following non-standardized metrics to video receiver stats: - freezeCount - pauseCount - totalFreezesDuration - totalPausesDuration - totalFramesDuration - sumOfSquaredFrameDurations For description of these metrics see https://henbos.github.io/webrtc-provisional-stats/#RTCVideoReceiverStats-dict* Bug: webrtc:10145 Change-Id: I4c76d5651102e73b1592ffed561e6224f2badeb6 Reviewed-on: https://webrtc-review.googlesource.com/c/114840 Reviewed-by: Stefan Holmer <stefan@webrtc.org> Reviewed-by: Henrik Boström <hbos@webrtc.org> Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Commit-Queue: Sergey Silkin <ssilkin@webrtc.org> Cr-Commit-Position: refs/heads/master@{#26523}
This commit is contained in:
committed by
Commit Bot
parent
1266487631
commit
0237106559
@ -314,6 +314,14 @@ class RTC_EXPORT RTCMediaStreamTrackStats final : public RTCStats {
|
|||||||
// TODO(kuddai): Add description to standard. crbug.com/webrtc/10042
|
// TODO(kuddai): Add description to standard. crbug.com/webrtc/10042
|
||||||
RTCNonStandardStatsMember<uint64_t> jitter_buffer_flushes;
|
RTCNonStandardStatsMember<uint64_t> jitter_buffer_flushes;
|
||||||
RTCNonStandardStatsMember<uint64_t> delayed_packet_outage_samples;
|
RTCNonStandardStatsMember<uint64_t> delayed_packet_outage_samples;
|
||||||
|
// Non-standard video-only members.
|
||||||
|
// https://henbos.github.io/webrtc-provisional-stats/#RTCVideoReceiverStats-dict*
|
||||||
|
RTCNonStandardStatsMember<uint32_t> freeze_count;
|
||||||
|
RTCNonStandardStatsMember<uint32_t> pause_count;
|
||||||
|
RTCNonStandardStatsMember<double> total_freezes_duration;
|
||||||
|
RTCNonStandardStatsMember<double> total_pauses_duration;
|
||||||
|
RTCNonStandardStatsMember<double> total_frames_duration;
|
||||||
|
RTCNonStandardStatsMember<double> sum_squared_frame_durations;
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://w3c.github.io/webrtc-stats/#pcstats-dict*
|
// https://w3c.github.io/webrtc-stats/#pcstats-dict*
|
||||||
|
|||||||
@ -90,6 +90,13 @@ class VideoReceiveStream {
|
|||||||
int width = 0;
|
int width = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
|
|
||||||
|
uint32_t freeze_count = 0;
|
||||||
|
uint32_t pause_count = 0;
|
||||||
|
uint32_t total_freezes_duration_ms = 0;
|
||||||
|
uint32_t total_pauses_duration_ms = 0;
|
||||||
|
uint32_t total_frames_duration_ms = 0;
|
||||||
|
double sum_squared_frame_durations = 0.0;
|
||||||
|
|
||||||
VideoContentType content_type = VideoContentType::UNSPECIFIED;
|
VideoContentType content_type = VideoContentType::UNSPECIFIED;
|
||||||
|
|
||||||
int sync_offset_ms = std::numeric_limits<int>::max();
|
int sync_offset_ms = std::numeric_limits<int>::max();
|
||||||
|
|||||||
@ -533,6 +533,12 @@ struct VideoReceiverInfo : public MediaReceiverInfo {
|
|||||||
uint32_t frames_rendered = 0;
|
uint32_t frames_rendered = 0;
|
||||||
absl::optional<uint64_t> qp_sum;
|
absl::optional<uint64_t> qp_sum;
|
||||||
int64_t interframe_delay_max_ms = -1;
|
int64_t interframe_delay_max_ms = -1;
|
||||||
|
uint32_t freeze_count = 0;
|
||||||
|
uint32_t pause_count = 0;
|
||||||
|
uint32_t total_freezes_duration_ms = 0;
|
||||||
|
uint32_t total_pauses_duration_ms = 0;
|
||||||
|
uint32_t total_frames_duration_ms = 0;
|
||||||
|
double sum_squared_frame_durations = 0.0;
|
||||||
|
|
||||||
webrtc::VideoContentType content_type = webrtc::VideoContentType::UNSPECIFIED;
|
webrtc::VideoContentType content_type = webrtc::VideoContentType::UNSPECIFIED;
|
||||||
|
|
||||||
|
|||||||
@ -2515,6 +2515,12 @@ WebRtcVideoChannel::WebRtcVideoReceiveStream::GetVideoReceiverInfo(
|
|||||||
info.first_frame_received_to_decoded_ms =
|
info.first_frame_received_to_decoded_ms =
|
||||||
stats.first_frame_received_to_decoded_ms;
|
stats.first_frame_received_to_decoded_ms;
|
||||||
info.interframe_delay_max_ms = stats.interframe_delay_max_ms;
|
info.interframe_delay_max_ms = stats.interframe_delay_max_ms;
|
||||||
|
info.freeze_count = stats.freeze_count;
|
||||||
|
info.pause_count = stats.pause_count;
|
||||||
|
info.total_freezes_duration_ms = stats.total_freezes_duration_ms;
|
||||||
|
info.total_pauses_duration_ms = stats.total_pauses_duration_ms;
|
||||||
|
info.total_frames_duration_ms = stats.total_frames_duration_ms;
|
||||||
|
info.sum_squared_frame_durations = stats.sum_squared_frame_durations;
|
||||||
|
|
||||||
info.content_type = stats.content_type;
|
info.content_type = stats.content_type;
|
||||||
|
|
||||||
|
|||||||
@ -523,6 +523,20 @@ ProduceMediaStreamTrackStatsFromVideoReceiverInfo(
|
|||||||
video_receiver_info.frames_rendered);
|
video_receiver_info.frames_rendered);
|
||||||
video_track_stats->frames_dropped = video_receiver_info.frames_received -
|
video_track_stats->frames_dropped = video_receiver_info.frames_received -
|
||||||
video_receiver_info.frames_rendered;
|
video_receiver_info.frames_rendered;
|
||||||
|
video_track_stats->freeze_count = video_receiver_info.freeze_count;
|
||||||
|
video_track_stats->pause_count = video_receiver_info.pause_count;
|
||||||
|
video_track_stats->total_freezes_duration =
|
||||||
|
static_cast<double>(video_receiver_info.total_freezes_duration_ms) /
|
||||||
|
rtc::kNumMillisecsPerSec;
|
||||||
|
video_track_stats->total_pauses_duration =
|
||||||
|
static_cast<double>(video_receiver_info.total_pauses_duration_ms) /
|
||||||
|
rtc::kNumMillisecsPerSec;
|
||||||
|
video_track_stats->total_frames_duration =
|
||||||
|
static_cast<double>(video_receiver_info.total_frames_duration_ms) /
|
||||||
|
rtc::kNumMillisecsPerSec;
|
||||||
|
video_track_stats->sum_squared_frame_durations =
|
||||||
|
video_receiver_info.sum_squared_frame_durations;
|
||||||
|
|
||||||
return video_track_stats;
|
return video_track_stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1552,6 +1552,12 @@ TEST_F(RTCStatsCollectorTest,
|
|||||||
video_receiver_info_ssrc3.frames_received = 1000;
|
video_receiver_info_ssrc3.frames_received = 1000;
|
||||||
video_receiver_info_ssrc3.frames_decoded = 995;
|
video_receiver_info_ssrc3.frames_decoded = 995;
|
||||||
video_receiver_info_ssrc3.frames_rendered = 990;
|
video_receiver_info_ssrc3.frames_rendered = 990;
|
||||||
|
video_receiver_info_ssrc3.freeze_count = 3;
|
||||||
|
video_receiver_info_ssrc3.pause_count = 2;
|
||||||
|
video_receiver_info_ssrc3.total_freezes_duration_ms = 1000;
|
||||||
|
video_receiver_info_ssrc3.total_pauses_duration_ms = 10000;
|
||||||
|
video_receiver_info_ssrc3.total_frames_duration_ms = 15000;
|
||||||
|
video_receiver_info_ssrc3.sum_squared_frame_durations = 1.5;
|
||||||
|
|
||||||
stats_->CreateMockRtpSendersReceiversAndChannels(
|
stats_->CreateMockRtpSendersReceiversAndChannels(
|
||||||
{}, {}, {},
|
{}, {}, {},
|
||||||
@ -1591,6 +1597,13 @@ TEST_F(RTCStatsCollectorTest,
|
|||||||
expected_remote_video_track_ssrc3.frames_received = 1000;
|
expected_remote_video_track_ssrc3.frames_received = 1000;
|
||||||
expected_remote_video_track_ssrc3.frames_decoded = 995;
|
expected_remote_video_track_ssrc3.frames_decoded = 995;
|
||||||
expected_remote_video_track_ssrc3.frames_dropped = 1000 - 990;
|
expected_remote_video_track_ssrc3.frames_dropped = 1000 - 990;
|
||||||
|
expected_remote_video_track_ssrc3.freeze_count = 3;
|
||||||
|
expected_remote_video_track_ssrc3.pause_count = 2;
|
||||||
|
expected_remote_video_track_ssrc3.total_freezes_duration = 1;
|
||||||
|
expected_remote_video_track_ssrc3.total_pauses_duration = 10;
|
||||||
|
expected_remote_video_track_ssrc3.total_frames_duration = 15;
|
||||||
|
expected_remote_video_track_ssrc3.sum_squared_frame_durations = 1.5;
|
||||||
|
|
||||||
ASSERT_TRUE(report->Get(expected_remote_video_track_ssrc3.id()));
|
ASSERT_TRUE(report->Get(expected_remote_video_track_ssrc3.id()));
|
||||||
EXPECT_EQ(expected_remote_video_track_ssrc3,
|
EXPECT_EQ(expected_remote_video_track_ssrc3,
|
||||||
report->Get(expected_remote_video_track_ssrc3.id())
|
report->Get(expected_remote_video_track_ssrc3.id())
|
||||||
|
|||||||
@ -560,6 +560,18 @@ class RTCStatsReportVerifier {
|
|||||||
media_stream_track.frames_decoded);
|
media_stream_track.frames_decoded);
|
||||||
verifier.TestMemberIsNonNegative<uint32_t>(
|
verifier.TestMemberIsNonNegative<uint32_t>(
|
||||||
media_stream_track.frames_dropped);
|
media_stream_track.frames_dropped);
|
||||||
|
verifier.TestMemberIsNonNegative<uint32_t>(
|
||||||
|
media_stream_track.freeze_count);
|
||||||
|
verifier.TestMemberIsNonNegative<uint32_t>(
|
||||||
|
media_stream_track.pause_count);
|
||||||
|
verifier.TestMemberIsNonNegative<double>(
|
||||||
|
media_stream_track.total_freezes_duration);
|
||||||
|
verifier.TestMemberIsNonNegative<double>(
|
||||||
|
media_stream_track.total_pauses_duration);
|
||||||
|
verifier.TestMemberIsNonNegative<double>(
|
||||||
|
media_stream_track.total_frames_duration);
|
||||||
|
verifier.TestMemberIsNonNegative<double>(
|
||||||
|
media_stream_track.sum_squared_frame_durations);
|
||||||
} else {
|
} else {
|
||||||
verifier.TestMemberIsNonNegative<uint32_t>(
|
verifier.TestMemberIsNonNegative<uint32_t>(
|
||||||
media_stream_track.frames_sent);
|
media_stream_track.frames_sent);
|
||||||
@ -568,6 +580,16 @@ class RTCStatsReportVerifier {
|
|||||||
verifier.TestMemberIsUndefined(media_stream_track.frames_received);
|
verifier.TestMemberIsUndefined(media_stream_track.frames_received);
|
||||||
verifier.TestMemberIsUndefined(media_stream_track.frames_decoded);
|
verifier.TestMemberIsUndefined(media_stream_track.frames_decoded);
|
||||||
verifier.TestMemberIsUndefined(media_stream_track.frames_dropped);
|
verifier.TestMemberIsUndefined(media_stream_track.frames_dropped);
|
||||||
|
verifier.TestMemberIsUndefined(media_stream_track.freeze_count);
|
||||||
|
verifier.TestMemberIsUndefined(media_stream_track.pause_count);
|
||||||
|
verifier.TestMemberIsUndefined(
|
||||||
|
media_stream_track.total_freezes_duration);
|
||||||
|
verifier.TestMemberIsUndefined(
|
||||||
|
media_stream_track.total_pauses_duration);
|
||||||
|
verifier.TestMemberIsUndefined(
|
||||||
|
media_stream_track.total_frames_duration);
|
||||||
|
verifier.TestMemberIsUndefined(
|
||||||
|
media_stream_track.sum_squared_frame_durations);
|
||||||
}
|
}
|
||||||
verifier.TestMemberIsUndefined(media_stream_track.frames_corrupted);
|
verifier.TestMemberIsUndefined(media_stream_track.frames_corrupted);
|
||||||
verifier.TestMemberIsUndefined(media_stream_track.partial_frames_lost);
|
verifier.TestMemberIsUndefined(media_stream_track.partial_frames_lost);
|
||||||
@ -593,6 +615,13 @@ class RTCStatsReportVerifier {
|
|||||||
verifier.TestMemberIsUndefined(media_stream_track.frames_corrupted);
|
verifier.TestMemberIsUndefined(media_stream_track.frames_corrupted);
|
||||||
verifier.TestMemberIsUndefined(media_stream_track.partial_frames_lost);
|
verifier.TestMemberIsUndefined(media_stream_track.partial_frames_lost);
|
||||||
verifier.TestMemberIsUndefined(media_stream_track.full_frames_lost);
|
verifier.TestMemberIsUndefined(media_stream_track.full_frames_lost);
|
||||||
|
verifier.TestMemberIsUndefined(media_stream_track.freeze_count);
|
||||||
|
verifier.TestMemberIsUndefined(media_stream_track.pause_count);
|
||||||
|
verifier.TestMemberIsUndefined(media_stream_track.total_freezes_duration);
|
||||||
|
verifier.TestMemberIsUndefined(media_stream_track.total_pauses_duration);
|
||||||
|
verifier.TestMemberIsUndefined(media_stream_track.total_frames_duration);
|
||||||
|
verifier.TestMemberIsUndefined(
|
||||||
|
media_stream_track.sum_squared_frame_durations);
|
||||||
// Audio-only members
|
// Audio-only members
|
||||||
verifier.TestMemberIsNonNegative<double>(media_stream_track.audio_level);
|
verifier.TestMemberIsNonNegative<double>(media_stream_track.audio_level);
|
||||||
verifier.TestMemberIsNonNegative<double>(
|
verifier.TestMemberIsNonNegative<double>(
|
||||||
|
|||||||
@ -57,6 +57,13 @@ absl::optional<int> SampleCounter::Max() const {
|
|||||||
return max_;
|
return max_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
absl::optional<int64_t> SampleCounter::Sum(int64_t min_required_samples) const {
|
||||||
|
RTC_DCHECK_GT(min_required_samples, 0);
|
||||||
|
if (num_samples_ < min_required_samples)
|
||||||
|
return absl::nullopt;
|
||||||
|
return sum_;
|
||||||
|
}
|
||||||
|
|
||||||
int64_t SampleCounter::NumSamples() const {
|
int64_t SampleCounter::NumSamples() const {
|
||||||
return num_samples_;
|
return num_samples_;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,6 +26,7 @@ class SampleCounter {
|
|||||||
void Add(int sample);
|
void Add(int sample);
|
||||||
absl::optional<int> Avg(int64_t min_required_samples) const;
|
absl::optional<int> Avg(int64_t min_required_samples) const;
|
||||||
absl::optional<int> Max() const;
|
absl::optional<int> Max() const;
|
||||||
|
absl::optional<int64_t> Sum(int64_t min_required_samples) const;
|
||||||
int64_t NumSamples() const;
|
int64_t NumSamples() const;
|
||||||
void Reset();
|
void Reset();
|
||||||
// Adds all the samples from the |other| SampleCounter as if they were all
|
// Adds all the samples from the |other| SampleCounter as if they were all
|
||||||
|
|||||||
@ -33,6 +33,7 @@ TEST(SampleCounterTest, NotEnoughSamples) {
|
|||||||
counter.Add(value);
|
counter.Add(value);
|
||||||
}
|
}
|
||||||
EXPECT_THAT(counter.Avg(kMinSamples), Eq(absl::nullopt));
|
EXPECT_THAT(counter.Avg(kMinSamples), Eq(absl::nullopt));
|
||||||
|
EXPECT_THAT(counter.Sum(kMinSamples), Eq(absl::nullopt));
|
||||||
EXPECT_THAT(counter.Max(), Eq(5));
|
EXPECT_THAT(counter.Max(), Eq(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,6 +44,7 @@ TEST(SampleCounterTest, EnoughSamples) {
|
|||||||
counter.Add(value);
|
counter.Add(value);
|
||||||
}
|
}
|
||||||
EXPECT_THAT(counter.Avg(kMinSamples), Eq(3));
|
EXPECT_THAT(counter.Avg(kMinSamples), Eq(3));
|
||||||
|
EXPECT_THAT(counter.Sum(kMinSamples), Eq(15));
|
||||||
EXPECT_THAT(counter.Max(), Eq(5));
|
EXPECT_THAT(counter.Max(), Eq(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -381,7 +381,13 @@ WEBRTC_RTCSTATS_IMPL(RTCMediaStreamTrackStats, RTCStats, "track",
|
|||||||
&concealed_samples,
|
&concealed_samples,
|
||||||
&concealment_events,
|
&concealment_events,
|
||||||
&jitter_buffer_flushes,
|
&jitter_buffer_flushes,
|
||||||
&delayed_packet_outage_samples);
|
&delayed_packet_outage_samples,
|
||||||
|
&freeze_count,
|
||||||
|
&pause_count,
|
||||||
|
&total_freezes_duration,
|
||||||
|
&total_pauses_duration,
|
||||||
|
&total_frames_duration,
|
||||||
|
&sum_squared_frame_durations);
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
RTCMediaStreamTrackStats::RTCMediaStreamTrackStats(const std::string& id,
|
RTCMediaStreamTrackStats::RTCMediaStreamTrackStats(const std::string& id,
|
||||||
@ -420,7 +426,13 @@ RTCMediaStreamTrackStats::RTCMediaStreamTrackStats(std::string&& id,
|
|||||||
concealed_samples("concealedSamples"),
|
concealed_samples("concealedSamples"),
|
||||||
concealment_events("concealmentEvents"),
|
concealment_events("concealmentEvents"),
|
||||||
jitter_buffer_flushes("jitterBufferFlushes"),
|
jitter_buffer_flushes("jitterBufferFlushes"),
|
||||||
delayed_packet_outage_samples("delayedPacketOutageSamples") {
|
delayed_packet_outage_samples("delayedPacketOutageSamples"),
|
||||||
|
freeze_count("freezeCount"),
|
||||||
|
pause_count("pauseCount"),
|
||||||
|
total_freezes_duration("totalFreezesDuration"),
|
||||||
|
total_pauses_duration("totalPausesDuration"),
|
||||||
|
total_frames_duration("totalFramesDuration"),
|
||||||
|
sum_squared_frame_durations("sumOfSquaredFramesDuration") {
|
||||||
RTC_DCHECK(kind == RTCMediaStreamTrackKind::kAudio ||
|
RTC_DCHECK(kind == RTCMediaStreamTrackKind::kAudio ||
|
||||||
kind == RTCMediaStreamTrackKind::kVideo);
|
kind == RTCMediaStreamTrackKind::kVideo);
|
||||||
}
|
}
|
||||||
@ -455,7 +467,13 @@ RTCMediaStreamTrackStats::RTCMediaStreamTrackStats(
|
|||||||
concealed_samples(other.concealed_samples),
|
concealed_samples(other.concealed_samples),
|
||||||
concealment_events(other.concealment_events),
|
concealment_events(other.concealment_events),
|
||||||
jitter_buffer_flushes(other.jitter_buffer_flushes),
|
jitter_buffer_flushes(other.jitter_buffer_flushes),
|
||||||
delayed_packet_outage_samples(other.delayed_packet_outage_samples) {}
|
delayed_packet_outage_samples(other.delayed_packet_outage_samples),
|
||||||
|
freeze_count(other.freeze_count),
|
||||||
|
pause_count(other.pause_count),
|
||||||
|
total_freezes_duration(other.total_freezes_duration),
|
||||||
|
total_pauses_duration(other.total_pauses_duration),
|
||||||
|
total_frames_duration(other.total_frames_duration),
|
||||||
|
sum_squared_frame_durations(other.sum_squared_frame_durations) {}
|
||||||
|
|
||||||
RTCMediaStreamTrackStats::~RTCMediaStreamTrackStats() {}
|
RTCMediaStreamTrackStats::~RTCMediaStreamTrackStats() {}
|
||||||
|
|
||||||
|
|||||||
@ -582,8 +582,18 @@ VideoReceiveStream::Stats ReceiveStatisticsProxy::GetStats() const {
|
|||||||
static_cast<int>(total_byte_tracker_.ComputeRate() * 8);
|
static_cast<int>(total_byte_tracker_.ComputeRate() * 8);
|
||||||
stats_.interframe_delay_max_ms =
|
stats_.interframe_delay_max_ms =
|
||||||
interframe_delay_max_moving_.Max(now_ms).value_or(-1);
|
interframe_delay_max_moving_.Max(now_ms).value_or(-1);
|
||||||
stats_.timing_frame_info = timing_frame_info_counter_.Max(now_ms);
|
stats_.freeze_count = video_quality_observer_->NumFreezes();
|
||||||
|
stats_.pause_count = video_quality_observer_->NumPauses();
|
||||||
|
stats_.total_freezes_duration_ms =
|
||||||
|
video_quality_observer_->TotalFreezesDurationMs();
|
||||||
|
stats_.total_pauses_duration_ms =
|
||||||
|
video_quality_observer_->TotalPausesDurationMs();
|
||||||
|
stats_.total_frames_duration_ms =
|
||||||
|
video_quality_observer_->TotalFramesDurationMs();
|
||||||
|
stats_.sum_squared_frame_durations =
|
||||||
|
video_quality_observer_->SumSquaredFrameDurationsSec();
|
||||||
stats_.content_type = last_content_type_;
|
stats_.content_type = last_content_type_;
|
||||||
|
stats_.timing_frame_info = timing_frame_info_counter_.Max(now_ms);
|
||||||
return stats_;
|
return stats_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,8 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "api/scoped_refptr.h"
|
#include "api/scoped_refptr.h"
|
||||||
#include "api/video/i420_buffer.h"
|
#include "api/video/i420_buffer.h"
|
||||||
@ -30,12 +32,10 @@ const uint32_t kRemoteSsrc = 456;
|
|||||||
const int kMinRequiredSamples = 200;
|
const int kMinRequiredSamples = 200;
|
||||||
const int kWidth = 1280;
|
const int kWidth = 1280;
|
||||||
const int kHeight = 720;
|
const int kHeight = 720;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// TODO(sakal): ReceiveStatisticsProxy is lacking unittesting.
|
// TODO(sakal): ReceiveStatisticsProxy is lacking unittesting.
|
||||||
class ReceiveStatisticsProxyTest
|
class ReceiveStatisticsProxyTest : public ::testing::Test {
|
||||||
: public ::testing::TestWithParam<webrtc::VideoContentType> {
|
|
||||||
public:
|
public:
|
||||||
ReceiveStatisticsProxyTest() : fake_clock_(1234), config_(GetTestConfig()) {}
|
ReceiveStatisticsProxyTest() : fake_clock_(1234), config_(GetTestConfig()) {}
|
||||||
virtual ~ReceiveStatisticsProxyTest() {}
|
virtual ~ReceiveStatisticsProxyTest() {}
|
||||||
@ -169,7 +169,8 @@ TEST_F(ReceiveStatisticsProxyTest, ReportsContentType) {
|
|||||||
EXPECT_EQ(kRealtimeString, videocontenttypehelpers::ToString(
|
EXPECT_EQ(kRealtimeString, videocontenttypehelpers::ToString(
|
||||||
statistics_proxy_->GetStats().content_type));
|
statistics_proxy_->GetStats().content_type));
|
||||||
statistics_proxy_->OnDecodedFrame(frame, 3u, VideoContentType::SCREENSHARE);
|
statistics_proxy_->OnDecodedFrame(frame, 3u, VideoContentType::SCREENSHARE);
|
||||||
EXPECT_EQ(kScreenshareString, videocontenttypehelpers::ToString(
|
EXPECT_EQ(kScreenshareString,
|
||||||
|
videocontenttypehelpers::ToString(
|
||||||
statistics_proxy_->GetStats().content_type));
|
statistics_proxy_->GetStats().content_type));
|
||||||
statistics_proxy_->OnDecodedFrame(frame, 3u, VideoContentType::UNSPECIFIED);
|
statistics_proxy_->OnDecodedFrame(frame, 3u, VideoContentType::UNSPECIFIED);
|
||||||
EXPECT_EQ(kRealtimeString, videocontenttypehelpers::ToString(
|
EXPECT_EQ(kRealtimeString, videocontenttypehelpers::ToString(
|
||||||
@ -237,6 +238,107 @@ TEST_F(ReceiveStatisticsProxyTest, ReportInterframeDelayInWindow) {
|
|||||||
statistics_proxy_->GetStats().interframe_delay_max_ms);
|
statistics_proxy_->GetStats().interframe_delay_max_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ReceiveStatisticsProxyTest, ReportsFreezeMetrics) {
|
||||||
|
const int64_t kFreezeDurationMs = 1000;
|
||||||
|
|
||||||
|
VideoReceiveStream::Stats stats = statistics_proxy_->GetStats();
|
||||||
|
EXPECT_EQ(0u, stats.freeze_count);
|
||||||
|
EXPECT_FALSE(stats.total_freezes_duration_ms);
|
||||||
|
|
||||||
|
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||||
|
for (size_t i = 0; i < VideoQualityObserver::kMinFrameSamplesToDetectFreeze;
|
||||||
|
++i) {
|
||||||
|
fake_clock_.AdvanceTimeMilliseconds(30);
|
||||||
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Freeze.
|
||||||
|
fake_clock_.AdvanceTimeMilliseconds(kFreezeDurationMs);
|
||||||
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
|
|
||||||
|
stats = statistics_proxy_->GetStats();
|
||||||
|
EXPECT_EQ(1u, stats.freeze_count);
|
||||||
|
EXPECT_EQ(kFreezeDurationMs, stats.total_freezes_duration_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ReceiveStatisticsProxyTest, ReportsPauseMetrics) {
|
||||||
|
VideoReceiveStream::Stats stats = statistics_proxy_->GetStats();
|
||||||
|
ASSERT_EQ(0u, stats.pause_count);
|
||||||
|
ASSERT_EQ(0u, stats.total_pauses_duration_ms);
|
||||||
|
|
||||||
|
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||||
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
|
|
||||||
|
// Pause.
|
||||||
|
fake_clock_.AdvanceTimeMilliseconds(5432);
|
||||||
|
statistics_proxy_->OnStreamInactive();
|
||||||
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
|
|
||||||
|
stats = statistics_proxy_->GetStats();
|
||||||
|
EXPECT_EQ(1u, stats.pause_count);
|
||||||
|
EXPECT_EQ(5432u, stats.total_pauses_duration_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ReceiveStatisticsProxyTest, PauseBeforeFirstAndAfterLastFrameIgnored) {
|
||||||
|
VideoReceiveStream::Stats stats = statistics_proxy_->GetStats();
|
||||||
|
ASSERT_EQ(0u, stats.pause_count);
|
||||||
|
ASSERT_EQ(0u, stats.total_pauses_duration_ms);
|
||||||
|
|
||||||
|
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||||
|
|
||||||
|
// Pause -> Frame -> Pause
|
||||||
|
fake_clock_.AdvanceTimeMilliseconds(5000);
|
||||||
|
statistics_proxy_->OnStreamInactive();
|
||||||
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
|
|
||||||
|
fake_clock_.AdvanceTimeMilliseconds(30);
|
||||||
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
|
|
||||||
|
fake_clock_.AdvanceTimeMilliseconds(5000);
|
||||||
|
statistics_proxy_->OnStreamInactive();
|
||||||
|
|
||||||
|
stats = statistics_proxy_->GetStats();
|
||||||
|
EXPECT_EQ(0u, stats.pause_count);
|
||||||
|
EXPECT_EQ(0u, stats.total_pauses_duration_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ReceiveStatisticsProxyTest, ReportsFramesDuration) {
|
||||||
|
VideoReceiveStream::Stats stats = statistics_proxy_->GetStats();
|
||||||
|
ASSERT_EQ(0u, stats.total_frames_duration_ms);
|
||||||
|
|
||||||
|
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||||
|
|
||||||
|
// Emulate delay before first frame is rendered. This is needed to ensure
|
||||||
|
// that frame duration only covers time since first frame is rendered and
|
||||||
|
// not the total time.
|
||||||
|
fake_clock_.AdvanceTimeMilliseconds(5432);
|
||||||
|
|
||||||
|
for (int i = 0; i <= 10; ++i) {
|
||||||
|
fake_clock_.AdvanceTimeMilliseconds(30);
|
||||||
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
stats = statistics_proxy_->GetStats();
|
||||||
|
EXPECT_EQ(10 * 30u, stats.total_frames_duration_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ReceiveStatisticsProxyTest, ReportsSumSquaredFrameDurations) {
|
||||||
|
VideoReceiveStream::Stats stats = statistics_proxy_->GetStats();
|
||||||
|
ASSERT_EQ(0u, stats.sum_squared_frame_durations);
|
||||||
|
|
||||||
|
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||||
|
for (int i = 0; i <= 10; ++i) {
|
||||||
|
fake_clock_.AdvanceTimeMilliseconds(30);
|
||||||
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
stats = statistics_proxy_->GetStats();
|
||||||
|
const double kExpectedSumSquaredFrameDurationsSecs =
|
||||||
|
10 * (30 / 1000.0 * 30 / 1000.0);
|
||||||
|
EXPECT_EQ(kExpectedSumSquaredFrameDurationsSecs,
|
||||||
|
stats.sum_squared_frame_durations);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ReceiveStatisticsProxyTest, OnDecodedFrameWithoutQpQpSumWontExist) {
|
TEST_F(ReceiveStatisticsProxyTest, OnDecodedFrameWithoutQpQpSumWontExist) {
|
||||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||||
EXPECT_EQ(absl::nullopt, statistics_proxy_->GetStats().qp_sum);
|
EXPECT_EQ(absl::nullopt, statistics_proxy_->GetStats().qp_sum);
|
||||||
@ -928,30 +1030,89 @@ TEST_F(ReceiveStatisticsProxyTest, RtcpHistogramsAreUpdated) {
|
|||||||
kNackPackets * 60 / metrics::kMinRunTimeInSeconds));
|
kNackPackets * 60 / metrics::kMinRunTimeInSeconds));
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(ContentTypes,
|
class ReceiveStatisticsProxyTestWithFreezeDuration
|
||||||
ReceiveStatisticsProxyTest,
|
: public ReceiveStatisticsProxyTest,
|
||||||
|
public testing::WithParamInterface<
|
||||||
|
std::tuple<uint32_t, uint32_t, uint32_t>> {
|
||||||
|
protected:
|
||||||
|
const uint32_t frame_duration_ms_ = {std::get<0>(GetParam())};
|
||||||
|
const uint32_t freeze_duration_ms_ = {std::get<1>(GetParam())};
|
||||||
|
const uint32_t expected_freeze_count_ = {std::get<2>(GetParam())};
|
||||||
|
};
|
||||||
|
|
||||||
|
// It is a freeze if:
|
||||||
|
// frame_duration_ms >= max(3 * avg_frame_duration, avg_frame_duration + 150)
|
||||||
|
// where avg_frame_duration is average duration of last 30 frames including
|
||||||
|
// the current one.
|
||||||
|
//
|
||||||
|
// Condition 1: 3 * avg_frame_duration > avg_frame_duration + 150
|
||||||
|
const auto kFreezeDetectionCond1Freeze = std::make_tuple(150, 483, 1);
|
||||||
|
const auto kFreezeDetectionCond1NotFreeze = std::make_tuple(150, 482, 0);
|
||||||
|
// Condition 2: 3 * avg_frame_duration < avg_frame_duration + 150
|
||||||
|
const auto kFreezeDetectionCond2Freeze = std::make_tuple(30, 185, 1);
|
||||||
|
const auto kFreezeDetectionCond2NotFreeze = std::make_tuple(30, 184, 0);
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(_,
|
||||||
|
ReceiveStatisticsProxyTestWithFreezeDuration,
|
||||||
|
::testing::Values(kFreezeDetectionCond1Freeze,
|
||||||
|
kFreezeDetectionCond1NotFreeze,
|
||||||
|
kFreezeDetectionCond2Freeze,
|
||||||
|
kFreezeDetectionCond2NotFreeze));
|
||||||
|
|
||||||
|
TEST_P(ReceiveStatisticsProxyTestWithFreezeDuration, FreezeDetection) {
|
||||||
|
VideoReceiveStream::Stats stats = statistics_proxy_->GetStats();
|
||||||
|
EXPECT_EQ(0u, stats.freeze_count);
|
||||||
|
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||||
|
|
||||||
|
// Add a very long frame. This is need to verify that average frame
|
||||||
|
// duration, which is supposed to be calculated as mean of durations of
|
||||||
|
// last 30 frames, is calculated correctly.
|
||||||
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
|
fake_clock_.AdvanceTimeMilliseconds(2000);
|
||||||
|
|
||||||
|
for (size_t i = 0;
|
||||||
|
i <= VideoQualityObserver::kAvgInterframeDelaysWindowSizeFrames; ++i) {
|
||||||
|
fake_clock_.AdvanceTimeMilliseconds(frame_duration_ms_);
|
||||||
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
fake_clock_.AdvanceTimeMilliseconds(freeze_duration_ms_);
|
||||||
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
|
|
||||||
|
stats = statistics_proxy_->GetStats();
|
||||||
|
EXPECT_EQ(stats.freeze_count, expected_freeze_count_);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReceiveStatisticsProxyTestWithContent
|
||||||
|
: public ReceiveStatisticsProxyTest,
|
||||||
|
public ::testing::WithParamInterface<webrtc::VideoContentType> {
|
||||||
|
protected:
|
||||||
|
const webrtc::VideoContentType content_type_{GetParam()};
|
||||||
|
};
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(ContentTypes,
|
||||||
|
ReceiveStatisticsProxyTestWithContent,
|
||||||
::testing::Values(VideoContentType::UNSPECIFIED,
|
::testing::Values(VideoContentType::UNSPECIFIED,
|
||||||
VideoContentType::SCREENSHARE));
|
VideoContentType::SCREENSHARE));
|
||||||
|
|
||||||
TEST_P(ReceiveStatisticsProxyTest, InterFrameDelaysAreReported) {
|
TEST_P(ReceiveStatisticsProxyTestWithContent, InterFrameDelaysAreReported) {
|
||||||
const VideoContentType content_type = GetParam();
|
|
||||||
const int kInterFrameDelayMs = 33;
|
const int kInterFrameDelayMs = 33;
|
||||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||||
|
|
||||||
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
||||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||||
}
|
}
|
||||||
// One extra with double the interval.
|
// One extra with double the interval.
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||||
|
|
||||||
statistics_proxy_.reset();
|
statistics_proxy_.reset();
|
||||||
const int kExpectedInterFrame =
|
const int kExpectedInterFrame =
|
||||||
(kInterFrameDelayMs * (kMinRequiredSamples - 1) +
|
(kInterFrameDelayMs * (kMinRequiredSamples - 1) +
|
||||||
kInterFrameDelayMs * 2) /
|
kInterFrameDelayMs * 2) /
|
||||||
kMinRequiredSamples;
|
kMinRequiredSamples;
|
||||||
if (videocontenttypehelpers::IsScreenshare(content_type)) {
|
if (videocontenttypehelpers::IsScreenshare(content_type_)) {
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
kExpectedInterFrame,
|
kExpectedInterFrame,
|
||||||
metrics::MinSample("WebRTC.Video.Screenshare.InterframeDelayInMs"));
|
metrics::MinSample("WebRTC.Video.Screenshare.InterframeDelayInMs"));
|
||||||
@ -966,28 +1127,28 @@ TEST_P(ReceiveStatisticsProxyTest, InterFrameDelaysAreReported) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ReceiveStatisticsProxyTest, InterFrameDelaysPercentilesAreReported) {
|
TEST_P(ReceiveStatisticsProxyTestWithContent,
|
||||||
const VideoContentType content_type = GetParam();
|
InterFrameDelaysPercentilesAreReported) {
|
||||||
const int kInterFrameDelayMs = 33;
|
const int kInterFrameDelayMs = 33;
|
||||||
const int kLastFivePercentsSamples = kMinRequiredSamples * 5 / 100;
|
const int kLastFivePercentsSamples = kMinRequiredSamples * 5 / 100;
|
||||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||||
|
|
||||||
for (int i = 0; i <= kMinRequiredSamples - kLastFivePercentsSamples; ++i) {
|
for (int i = 0; i <= kMinRequiredSamples - kLastFivePercentsSamples; ++i) {
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||||
}
|
}
|
||||||
// Last 5% of intervals are double in size.
|
// Last 5% of intervals are double in size.
|
||||||
for (int i = 0; i < kLastFivePercentsSamples; ++i) {
|
for (int i = 0; i < kLastFivePercentsSamples; ++i) {
|
||||||
fake_clock_.AdvanceTimeMilliseconds(2 * kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(2 * kInterFrameDelayMs);
|
||||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||||
}
|
}
|
||||||
// Final sample is outlier and 10 times as big.
|
// Final sample is outlier and 10 times as big.
|
||||||
fake_clock_.AdvanceTimeMilliseconds(10 * kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(10 * kInterFrameDelayMs);
|
||||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||||
|
|
||||||
statistics_proxy_.reset();
|
statistics_proxy_.reset();
|
||||||
const int kExpectedInterFrame = kInterFrameDelayMs * 2;
|
const int kExpectedInterFrame = kInterFrameDelayMs * 2;
|
||||||
if (videocontenttypehelpers::IsScreenshare(content_type)) {
|
if (videocontenttypehelpers::IsScreenshare(content_type_)) {
|
||||||
EXPECT_EQ(kExpectedInterFrame,
|
EXPECT_EQ(kExpectedInterFrame,
|
||||||
metrics::MinSample(
|
metrics::MinSample(
|
||||||
"WebRTC.Video.Screenshare.InterframeDelay95PercentileInMs"));
|
"WebRTC.Video.Screenshare.InterframeDelay95PercentileInMs"));
|
||||||
@ -998,13 +1159,13 @@ TEST_P(ReceiveStatisticsProxyTest, InterFrameDelaysPercentilesAreReported) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ReceiveStatisticsProxyTest, MaxInterFrameDelayOnlyWithValidAverage) {
|
TEST_P(ReceiveStatisticsProxyTestWithContent,
|
||||||
const VideoContentType content_type = GetParam();
|
MaxInterFrameDelayOnlyWithValidAverage) {
|
||||||
const int kInterFrameDelayMs = 33;
|
const int kInterFrameDelayMs = 33;
|
||||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||||
|
|
||||||
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
||||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1019,13 +1180,12 @@ TEST_P(ReceiveStatisticsProxyTest, MaxInterFrameDelayOnlyWithValidAverage) {
|
|||||||
"WebRTC.Video.Screenshare.InterframeDelayMaxInMs"));
|
"WebRTC.Video.Screenshare.InterframeDelayMaxInMs"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ReceiveStatisticsProxyTest, MaxInterFrameDelayOnlyWithPause) {
|
TEST_P(ReceiveStatisticsProxyTestWithContent, MaxInterFrameDelayOnlyWithPause) {
|
||||||
const VideoContentType content_type = GetParam();
|
|
||||||
const int kInterFrameDelayMs = 33;
|
const int kInterFrameDelayMs = 33;
|
||||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||||
|
|
||||||
for (int i = 0; i <= kMinRequiredSamples; ++i) {
|
for (int i = 0; i <= kMinRequiredSamples; ++i) {
|
||||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1036,12 +1196,12 @@ TEST_P(ReceiveStatisticsProxyTest, MaxInterFrameDelayOnlyWithPause) {
|
|||||||
|
|
||||||
// Insert two more frames. The interval during the pause should be disregarded
|
// Insert two more frames. The interval during the pause should be disregarded
|
||||||
// in the stats.
|
// in the stats.
|
||||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||||
|
|
||||||
statistics_proxy_.reset();
|
statistics_proxy_.reset();
|
||||||
if (videocontenttypehelpers::IsScreenshare(content_type)) {
|
if (videocontenttypehelpers::IsScreenshare(content_type_)) {
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
1, metrics::NumSamples("WebRTC.Video.Screenshare.InterframeDelayInMs"));
|
1, metrics::NumSamples("WebRTC.Video.Screenshare.InterframeDelayInMs"));
|
||||||
EXPECT_EQ(1, metrics::NumSamples(
|
EXPECT_EQ(1, metrics::NumSamples(
|
||||||
@ -1062,8 +1222,7 @@ TEST_P(ReceiveStatisticsProxyTest, MaxInterFrameDelayOnlyWithPause) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ReceiveStatisticsProxyTest, FreezesAreReported) {
|
TEST_P(ReceiveStatisticsProxyTestWithContent, FreezesAreReported) {
|
||||||
const VideoContentType content_type = GetParam();
|
|
||||||
const int kInterFrameDelayMs = 33;
|
const int kInterFrameDelayMs = 33;
|
||||||
const int kFreezeDelayMs = 200;
|
const int kFreezeDelayMs = 200;
|
||||||
const int kCallDurationMs =
|
const int kCallDurationMs =
|
||||||
@ -1071,20 +1230,20 @@ TEST_P(ReceiveStatisticsProxyTest, FreezesAreReported) {
|
|||||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||||
|
|
||||||
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
||||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||||
statistics_proxy_->OnRenderedFrame(frame);
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||||
}
|
}
|
||||||
// Add extra freeze.
|
// Add extra freeze.
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kFreezeDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kFreezeDelayMs);
|
||||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||||
statistics_proxy_->OnRenderedFrame(frame);
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
|
|
||||||
statistics_proxy_.reset();
|
statistics_proxy_.reset();
|
||||||
const int kExpectedTimeBetweenFreezes =
|
const int kExpectedTimeBetweenFreezes =
|
||||||
kInterFrameDelayMs * (kMinRequiredSamples - 1);
|
kInterFrameDelayMs * (kMinRequiredSamples - 1);
|
||||||
const int kExpectedNumberFreezesPerMinute = 60 * 1000 / kCallDurationMs;
|
const int kExpectedNumberFreezesPerMinute = 60 * 1000 / kCallDurationMs;
|
||||||
if (videocontenttypehelpers::IsScreenshare(content_type)) {
|
if (videocontenttypehelpers::IsScreenshare(content_type_)) {
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
kFreezeDelayMs + kInterFrameDelayMs,
|
kFreezeDelayMs + kInterFrameDelayMs,
|
||||||
metrics::MinSample("WebRTC.Video.Screenshare.MeanFreezeDurationMs"));
|
metrics::MinSample("WebRTC.Video.Screenshare.MeanFreezeDurationMs"));
|
||||||
@ -1104,8 +1263,7 @@ TEST_P(ReceiveStatisticsProxyTest, FreezesAreReported) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ReceiveStatisticsProxyTest, HarmonicFrameRateIsReported) {
|
TEST_P(ReceiveStatisticsProxyTestWithContent, HarmonicFrameRateIsReported) {
|
||||||
const VideoContentType content_type = GetParam();
|
|
||||||
const int kInterFrameDelayMs = 33;
|
const int kInterFrameDelayMs = 33;
|
||||||
const int kFreezeDelayMs = 200;
|
const int kFreezeDelayMs = 200;
|
||||||
const int kCallDurationMs =
|
const int kCallDurationMs =
|
||||||
@ -1114,12 +1272,12 @@ TEST_P(ReceiveStatisticsProxyTest, HarmonicFrameRateIsReported) {
|
|||||||
|
|
||||||
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||||
statistics_proxy_->OnRenderedFrame(frame);
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
}
|
}
|
||||||
// Add extra freeze.
|
// Add extra freeze.
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kFreezeDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kFreezeDelayMs);
|
||||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||||
statistics_proxy_->OnRenderedFrame(frame);
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
|
|
||||||
statistics_proxy_.reset();
|
statistics_proxy_.reset();
|
||||||
@ -1130,7 +1288,7 @@ TEST_P(ReceiveStatisticsProxyTest, HarmonicFrameRateIsReported) {
|
|||||||
kFreezeDelayMs / 1000.0 * kFreezeDelayMs / 1000.0;
|
kFreezeDelayMs / 1000.0 * kFreezeDelayMs / 1000.0;
|
||||||
const int kExpectedHarmonicFrameRateFps =
|
const int kExpectedHarmonicFrameRateFps =
|
||||||
std::round(kCallDurationMs / (1000 * kSumSquaredInterframeDelaysSecs));
|
std::round(kCallDurationMs / (1000 * kSumSquaredInterframeDelaysSecs));
|
||||||
if (videocontenttypehelpers::IsScreenshare(content_type)) {
|
if (videocontenttypehelpers::IsScreenshare(content_type_)) {
|
||||||
EXPECT_EQ(kExpectedHarmonicFrameRateFps,
|
EXPECT_EQ(kExpectedHarmonicFrameRateFps,
|
||||||
metrics::MinSample("WebRTC.Video.Screenshare.HarmonicFrameRate"));
|
metrics::MinSample("WebRTC.Video.Screenshare.HarmonicFrameRate"));
|
||||||
} else {
|
} else {
|
||||||
@ -1139,14 +1297,13 @@ TEST_P(ReceiveStatisticsProxyTest, HarmonicFrameRateIsReported) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ReceiveStatisticsProxyTest, PausesAreIgnored) {
|
TEST_P(ReceiveStatisticsProxyTestWithContent, PausesAreIgnored) {
|
||||||
const VideoContentType content_type = GetParam();
|
|
||||||
const int kInterFrameDelayMs = 33;
|
const int kInterFrameDelayMs = 33;
|
||||||
const int kPauseDurationMs = 10000;
|
const int kPauseDurationMs = 10000;
|
||||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||||
|
|
||||||
for (int i = 0; i <= kMinRequiredSamples; ++i) {
|
for (int i = 0; i <= kMinRequiredSamples; ++i) {
|
||||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||||
statistics_proxy_->OnRenderedFrame(frame);
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||||
}
|
}
|
||||||
@ -1156,7 +1313,7 @@ TEST_P(ReceiveStatisticsProxyTest, PausesAreIgnored) {
|
|||||||
|
|
||||||
// Second playback interval with triple the length.
|
// Second playback interval with triple the length.
|
||||||
for (int i = 0; i <= kMinRequiredSamples * 3; ++i) {
|
for (int i = 0; i <= kMinRequiredSamples * 3; ++i) {
|
||||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||||
statistics_proxy_->OnRenderedFrame(frame);
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||||
}
|
}
|
||||||
@ -1165,7 +1322,7 @@ TEST_P(ReceiveStatisticsProxyTest, PausesAreIgnored) {
|
|||||||
// Average of two playback intervals.
|
// Average of two playback intervals.
|
||||||
const int kExpectedTimeBetweenFreezes =
|
const int kExpectedTimeBetweenFreezes =
|
||||||
kInterFrameDelayMs * kMinRequiredSamples * 2;
|
kInterFrameDelayMs * kMinRequiredSamples * 2;
|
||||||
if (videocontenttypehelpers::IsScreenshare(content_type)) {
|
if (videocontenttypehelpers::IsScreenshare(content_type_)) {
|
||||||
EXPECT_EQ(-1, metrics::MinSample(
|
EXPECT_EQ(-1, metrics::MinSample(
|
||||||
"WebRTC.Video.Screenshare.MeanFreezeDurationMs"));
|
"WebRTC.Video.Screenshare.MeanFreezeDurationMs"));
|
||||||
EXPECT_EQ(kExpectedTimeBetweenFreezes,
|
EXPECT_EQ(kExpectedTimeBetweenFreezes,
|
||||||
@ -1178,26 +1335,25 @@ TEST_P(ReceiveStatisticsProxyTest, PausesAreIgnored) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ReceiveStatisticsProxyTest, ManyPausesAtTheBeginning) {
|
TEST_P(ReceiveStatisticsProxyTestWithContent, ManyPausesAtTheBeginning) {
|
||||||
const VideoContentType content_type = GetParam();
|
|
||||||
const int kInterFrameDelayMs = 33;
|
const int kInterFrameDelayMs = 33;
|
||||||
const int kPauseDurationMs = 10000;
|
const int kPauseDurationMs = 10000;
|
||||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||||
|
|
||||||
for (int i = 0; i <= kMinRequiredSamples; ++i) {
|
for (int i = 0; i <= kMinRequiredSamples; ++i) {
|
||||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||||
|
|
||||||
statistics_proxy_->OnStreamInactive();
|
statistics_proxy_->OnStreamInactive();
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kPauseDurationMs);
|
fake_clock_.AdvanceTimeMilliseconds(kPauseDurationMs);
|
||||||
|
|
||||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
statistics_proxy_.reset();
|
statistics_proxy_.reset();
|
||||||
// No freezes should be detected, as all long inter-frame delays were pauses.
|
// No freezes should be detected, as all long inter-frame delays were pauses.
|
||||||
if (videocontenttypehelpers::IsScreenshare(content_type)) {
|
if (videocontenttypehelpers::IsScreenshare(content_type_)) {
|
||||||
EXPECT_EQ(-1, metrics::MinSample(
|
EXPECT_EQ(-1, metrics::MinSample(
|
||||||
"WebRTC.Video.Screenshare.MeanFreezeDurationMs"));
|
"WebRTC.Video.Screenshare.MeanFreezeDurationMs"));
|
||||||
} else {
|
} else {
|
||||||
@ -1205,21 +1361,20 @@ TEST_P(ReceiveStatisticsProxyTest, ManyPausesAtTheBeginning) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ReceiveStatisticsProxyTest, TimeInHdReported) {
|
TEST_P(ReceiveStatisticsProxyTestWithContent, TimeInHdReported) {
|
||||||
const VideoContentType content_type = GetParam();
|
|
||||||
const int kInterFrameDelayMs = 20;
|
const int kInterFrameDelayMs = 20;
|
||||||
webrtc::VideoFrame frame_hd = CreateFrame(1280, 720);
|
webrtc::VideoFrame frame_hd = CreateFrame(1280, 720);
|
||||||
webrtc::VideoFrame frame_sd = CreateFrame(640, 360);
|
webrtc::VideoFrame frame_sd = CreateFrame(640, 360);
|
||||||
|
|
||||||
// HD frames.
|
// HD frames.
|
||||||
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
||||||
statistics_proxy_->OnDecodedFrame(frame_hd, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame_hd, absl::nullopt, content_type_);
|
||||||
statistics_proxy_->OnRenderedFrame(frame_hd);
|
statistics_proxy_->OnRenderedFrame(frame_hd);
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||||
}
|
}
|
||||||
// SD frames.
|
// SD frames.
|
||||||
for (int i = 0; i < 2 * kMinRequiredSamples; ++i) {
|
for (int i = 0; i < 2 * kMinRequiredSamples; ++i) {
|
||||||
statistics_proxy_->OnDecodedFrame(frame_sd, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame_sd, absl::nullopt, content_type_);
|
||||||
statistics_proxy_->OnRenderedFrame(frame_sd);
|
statistics_proxy_->OnRenderedFrame(frame_sd);
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||||
}
|
}
|
||||||
@ -1228,7 +1383,7 @@ TEST_P(ReceiveStatisticsProxyTest, TimeInHdReported) {
|
|||||||
|
|
||||||
statistics_proxy_.reset();
|
statistics_proxy_.reset();
|
||||||
const int kExpectedTimeInHdPercents = 33;
|
const int kExpectedTimeInHdPercents = 33;
|
||||||
if (videocontenttypehelpers::IsScreenshare(content_type)) {
|
if (videocontenttypehelpers::IsScreenshare(content_type_)) {
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
kExpectedTimeInHdPercents,
|
kExpectedTimeInHdPercents,
|
||||||
metrics::MinSample("WebRTC.Video.Screenshare.TimeInHdPercentage"));
|
metrics::MinSample("WebRTC.Video.Screenshare.TimeInHdPercentage"));
|
||||||
@ -1238,8 +1393,7 @@ TEST_P(ReceiveStatisticsProxyTest, TimeInHdReported) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ReceiveStatisticsProxyTest, TimeInBlockyVideoReported) {
|
TEST_P(ReceiveStatisticsProxyTestWithContent, TimeInBlockyVideoReported) {
|
||||||
const VideoContentType content_type = GetParam();
|
|
||||||
const int kInterFrameDelayMs = 20;
|
const int kInterFrameDelayMs = 20;
|
||||||
const int kHighQp = 80;
|
const int kHighQp = 80;
|
||||||
const int kLowQp = 30;
|
const int kLowQp = 30;
|
||||||
@ -1247,23 +1401,23 @@ TEST_P(ReceiveStatisticsProxyTest, TimeInBlockyVideoReported) {
|
|||||||
|
|
||||||
// High quality frames.
|
// High quality frames.
|
||||||
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
||||||
statistics_proxy_->OnDecodedFrame(frame, kLowQp, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, kLowQp, content_type_);
|
||||||
statistics_proxy_->OnRenderedFrame(frame);
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||||
}
|
}
|
||||||
// Blocky frames.
|
// Blocky frames.
|
||||||
for (int i = 0; i < 2 * kMinRequiredSamples; ++i) {
|
for (int i = 0; i < 2 * kMinRequiredSamples; ++i) {
|
||||||
statistics_proxy_->OnDecodedFrame(frame, kHighQp, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, kHighQp, content_type_);
|
||||||
statistics_proxy_->OnRenderedFrame(frame);
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||||
}
|
}
|
||||||
// Extra last frame.
|
// Extra last frame.
|
||||||
statistics_proxy_->OnDecodedFrame(frame, kHighQp, content_type);
|
statistics_proxy_->OnDecodedFrame(frame, kHighQp, content_type_);
|
||||||
statistics_proxy_->OnRenderedFrame(frame);
|
statistics_proxy_->OnRenderedFrame(frame);
|
||||||
|
|
||||||
statistics_proxy_.reset();
|
statistics_proxy_.reset();
|
||||||
const int kExpectedTimeInHdPercents = 66;
|
const int kExpectedTimeInHdPercents = 66;
|
||||||
if (videocontenttypehelpers::IsScreenshare(content_type)) {
|
if (videocontenttypehelpers::IsScreenshare(content_type_)) {
|
||||||
EXPECT_EQ(kExpectedTimeInHdPercents,
|
EXPECT_EQ(kExpectedTimeInHdPercents,
|
||||||
metrics::MinSample(
|
metrics::MinSample(
|
||||||
"WebRTC.Video.Screenshare.TimeInBlockyVideoPercentage"));
|
"WebRTC.Video.Screenshare.TimeInBlockyVideoPercentage"));
|
||||||
@ -1273,8 +1427,7 @@ TEST_P(ReceiveStatisticsProxyTest, TimeInBlockyVideoReported) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ReceiveStatisticsProxyTest, DownscalesReported) {
|
TEST_P(ReceiveStatisticsProxyTestWithContent, DownscalesReported) {
|
||||||
const VideoContentType content_type = GetParam();
|
|
||||||
const int kInterFrameDelayMs = 2000; // To ensure long enough call duration.
|
const int kInterFrameDelayMs = 2000; // To ensure long enough call duration.
|
||||||
|
|
||||||
webrtc::VideoFrame frame_hd = CreateFrame(1280, 720);
|
webrtc::VideoFrame frame_hd = CreateFrame(1280, 720);
|
||||||
@ -1282,7 +1435,7 @@ TEST_P(ReceiveStatisticsProxyTest, DownscalesReported) {
|
|||||||
webrtc::VideoFrame frame_ld = CreateFrame(320, 180);
|
webrtc::VideoFrame frame_ld = CreateFrame(320, 180);
|
||||||
|
|
||||||
// Call once to pass content type.
|
// Call once to pass content type.
|
||||||
statistics_proxy_->OnDecodedFrame(frame_hd, absl::nullopt, content_type);
|
statistics_proxy_->OnDecodedFrame(frame_hd, absl::nullopt, content_type_);
|
||||||
|
|
||||||
statistics_proxy_->OnRenderedFrame(frame_hd);
|
statistics_proxy_->OnRenderedFrame(frame_hd);
|
||||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||||
@ -1297,7 +1450,7 @@ TEST_P(ReceiveStatisticsProxyTest, DownscalesReported) {
|
|||||||
|
|
||||||
statistics_proxy_.reset();
|
statistics_proxy_.reset();
|
||||||
const int kExpectedDownscales = 30; // 2 per 4 seconds = 30 per minute.
|
const int kExpectedDownscales = 30; // 2 per 4 seconds = 30 per minute.
|
||||||
if (videocontenttypehelpers::IsScreenshare(content_type)) {
|
if (videocontenttypehelpers::IsScreenshare(content_type_)) {
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
kExpectedDownscales,
|
kExpectedDownscales,
|
||||||
metrics::MinSample(
|
metrics::MinSample(
|
||||||
@ -1309,9 +1462,10 @@ TEST_P(ReceiveStatisticsProxyTest, DownscalesReported) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(ReceiveStatisticsProxyTest, StatsAreSlicedOnSimulcastAndExperiment) {
|
TEST_P(ReceiveStatisticsProxyTestWithContent,
|
||||||
VideoContentType content_type = GetParam();
|
StatsAreSlicedOnSimulcastAndExperiment) {
|
||||||
const uint8_t experiment_id = 1;
|
const uint8_t experiment_id = 1;
|
||||||
|
webrtc::VideoContentType content_type = content_type_;
|
||||||
videocontenttypehelpers::SetExperimentId(&content_type, experiment_id);
|
videocontenttypehelpers::SetExperimentId(&content_type, experiment_id);
|
||||||
const int kInterFrameDelayMs1 = 30;
|
const int kInterFrameDelayMs1 = 30;
|
||||||
const int kInterFrameDelayMs2 = 50;
|
const int kInterFrameDelayMs2 = 50;
|
||||||
|
|||||||
@ -20,12 +20,13 @@
|
|||||||
#include "system_wrappers/include/metrics.h"
|
#include "system_wrappers/include/metrics.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
const uint32_t VideoQualityObserver::kMinFrameSamplesToDetectFreeze = 5;
|
||||||
|
const uint32_t VideoQualityObserver::kMinIncreaseForFreezeMs = 150;
|
||||||
|
const uint32_t VideoQualityObserver::kAvgInterframeDelaysWindowSizeFrames = 30;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int kMinFrameSamplesToDetectFreeze = 5;
|
|
||||||
constexpr int kMinVideoDurationMs = 3000;
|
constexpr int kMinVideoDurationMs = 3000;
|
||||||
constexpr int kMinRequiredSamples = 1;
|
constexpr int kMinRequiredSamples = 1;
|
||||||
constexpr int kMinIncreaseForFreezeMs = 150;
|
|
||||||
constexpr int kPixelsInHighResolution =
|
constexpr int kPixelsInHighResolution =
|
||||||
960 * 540; // CPU-adapted HD still counts.
|
960 * 540; // CPU-adapted HD still counts.
|
||||||
constexpr int kPixelsInMediumResolution = 640 * 360;
|
constexpr int kPixelsInMediumResolution = 640 * 360;
|
||||||
@ -41,7 +42,8 @@ VideoQualityObserver::VideoQualityObserver(VideoContentType content_type)
|
|||||||
first_frame_rendered_ms_(-1),
|
first_frame_rendered_ms_(-1),
|
||||||
last_frame_pixels_(0),
|
last_frame_pixels_(0),
|
||||||
is_last_frame_blocky_(false),
|
is_last_frame_blocky_(false),
|
||||||
last_unfreeze_time_(0),
|
last_unfreeze_time_ms_(0),
|
||||||
|
render_interframe_delays_(kAvgInterframeDelaysWindowSizeFrames),
|
||||||
sum_squared_interframe_delays_secs_(0.0),
|
sum_squared_interframe_delays_secs_(0.0),
|
||||||
time_in_resolution_ms_(3, 0),
|
time_in_resolution_ms_(3, 0),
|
||||||
current_resolution_(Resolution::Low),
|
current_resolution_(Resolution::Low),
|
||||||
@ -63,9 +65,9 @@ void VideoQualityObserver::UpdateHistograms() {
|
|||||||
char log_stream_buf[2 * 1024];
|
char log_stream_buf[2 * 1024];
|
||||||
rtc::SimpleStringBuilder log_stream(log_stream_buf);
|
rtc::SimpleStringBuilder log_stream(log_stream_buf);
|
||||||
|
|
||||||
if (last_frame_rendered_ms_ > last_unfreeze_time_) {
|
if (last_frame_rendered_ms_ > last_unfreeze_time_ms_) {
|
||||||
smooth_playback_durations_.Add(last_frame_rendered_ms_ -
|
smooth_playback_durations_.Add(last_frame_rendered_ms_ -
|
||||||
last_unfreeze_time_);
|
last_unfreeze_time_ms_);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string uma_prefix = videocontenttypehelpers::IsScreenshare(content_type_)
|
std::string uma_prefix = videocontenttypehelpers::IsScreenshare(content_type_)
|
||||||
@ -135,32 +137,38 @@ void VideoQualityObserver::UpdateHistograms() {
|
|||||||
|
|
||||||
void VideoQualityObserver::OnRenderedFrame(const VideoFrame& frame,
|
void VideoQualityObserver::OnRenderedFrame(const VideoFrame& frame,
|
||||||
int64_t now_ms) {
|
int64_t now_ms) {
|
||||||
if (num_frames_rendered_ == 0) {
|
RTC_DCHECK_LE(last_frame_rendered_ms_, now_ms);
|
||||||
first_frame_rendered_ms_ = last_unfreeze_time_ = now_ms;
|
RTC_DCHECK_LE(last_unfreeze_time_ms_, now_ms);
|
||||||
}
|
|
||||||
|
|
||||||
++num_frames_rendered_;
|
if (num_frames_rendered_ == 0) {
|
||||||
|
first_frame_rendered_ms_ = last_unfreeze_time_ms_ = now_ms;
|
||||||
|
}
|
||||||
|
|
||||||
auto blocky_frame_it = blocky_frames_.find(frame.timestamp());
|
auto blocky_frame_it = blocky_frames_.find(frame.timestamp());
|
||||||
|
|
||||||
if (!is_paused_ && num_frames_rendered_ > 1) {
|
if (!is_paused_ && num_frames_rendered_ > 0) {
|
||||||
// Process inter-frame delay.
|
// Process inter-frame delay.
|
||||||
const int64_t interframe_delay_ms = now_ms - last_frame_rendered_ms_;
|
const int64_t interframe_delay_ms = now_ms - last_frame_rendered_ms_;
|
||||||
const float interframe_delays_secs = interframe_delay_ms / 1000.0;
|
const double interframe_delays_secs = interframe_delay_ms / 1000.0;
|
||||||
sum_squared_interframe_delays_secs_ +=
|
sum_squared_interframe_delays_secs_ +=
|
||||||
interframe_delays_secs * interframe_delays_secs;
|
interframe_delays_secs * interframe_delays_secs;
|
||||||
render_interframe_delays_.Add(interframe_delay_ms);
|
render_interframe_delays_.AddSample(interframe_delay_ms);
|
||||||
absl::optional<int> avg_interframe_delay =
|
|
||||||
render_interframe_delays_.Avg(kMinFrameSamplesToDetectFreeze);
|
bool was_freeze = false;
|
||||||
// Check if it was a freeze.
|
if (render_interframe_delays_.Size() >= kMinFrameSamplesToDetectFreeze) {
|
||||||
if (avg_interframe_delay &&
|
const absl::optional<int64_t> avg_interframe_delay =
|
||||||
interframe_delay_ms >=
|
render_interframe_delays_.GetAverageRoundedDown();
|
||||||
|
RTC_DCHECK(avg_interframe_delay);
|
||||||
|
was_freeze = interframe_delay_ms >=
|
||||||
std::max(3 * *avg_interframe_delay,
|
std::max(3 * *avg_interframe_delay,
|
||||||
*avg_interframe_delay + kMinIncreaseForFreezeMs)) {
|
*avg_interframe_delay + kMinIncreaseForFreezeMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (was_freeze) {
|
||||||
freezes_durations_.Add(interframe_delay_ms);
|
freezes_durations_.Add(interframe_delay_ms);
|
||||||
smooth_playback_durations_.Add(last_frame_rendered_ms_ -
|
smooth_playback_durations_.Add(last_frame_rendered_ms_ -
|
||||||
last_unfreeze_time_);
|
last_unfreeze_time_ms_);
|
||||||
last_unfreeze_time_ = now_ms;
|
last_unfreeze_time_ms_ = now_ms;
|
||||||
} else {
|
} else {
|
||||||
// Count spatial metrics if there were no freeze.
|
// Count spatial metrics if there were no freeze.
|
||||||
time_in_resolution_ms_[current_resolution_] += interframe_delay_ms;
|
time_in_resolution_ms_[current_resolution_] += interframe_delay_ms;
|
||||||
@ -176,11 +184,15 @@ void VideoQualityObserver::OnRenderedFrame(const VideoFrame& frame,
|
|||||||
// pause toward smooth playback. Explicitly count the part before it and
|
// pause toward smooth playback. Explicitly count the part before it and
|
||||||
// start the new smooth playback interval from this frame.
|
// start the new smooth playback interval from this frame.
|
||||||
is_paused_ = false;
|
is_paused_ = false;
|
||||||
if (last_frame_rendered_ms_ > last_unfreeze_time_) {
|
if (last_frame_rendered_ms_ > last_unfreeze_time_ms_) {
|
||||||
smooth_playback_durations_.Add(last_frame_rendered_ms_ -
|
smooth_playback_durations_.Add(last_frame_rendered_ms_ -
|
||||||
last_unfreeze_time_);
|
last_unfreeze_time_ms_);
|
||||||
|
}
|
||||||
|
last_unfreeze_time_ms_ = now_ms;
|
||||||
|
|
||||||
|
if (num_frames_rendered_ > 0) {
|
||||||
|
pauses_durations_.Add(now_ms - last_frame_rendered_ms_);
|
||||||
}
|
}
|
||||||
last_unfreeze_time_ = now_ms;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t pixels = frame.width() * frame.height();
|
int64_t pixels = frame.width() * frame.height();
|
||||||
@ -203,6 +215,8 @@ void VideoQualityObserver::OnRenderedFrame(const VideoFrame& frame,
|
|||||||
if (is_last_frame_blocky_) {
|
if (is_last_frame_blocky_) {
|
||||||
blocky_frames_.erase(blocky_frames_.begin(), ++blocky_frame_it);
|
blocky_frames_.erase(blocky_frames_.begin(), ++blocky_frame_it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++num_frames_rendered_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoQualityObserver::OnDecodedFrame(const VideoFrame& frame,
|
void VideoQualityObserver::OnDecodedFrame(const VideoFrame& frame,
|
||||||
@ -241,4 +255,29 @@ void VideoQualityObserver::OnDecodedFrame(const VideoFrame& frame,
|
|||||||
void VideoQualityObserver::OnStreamInactive() {
|
void VideoQualityObserver::OnStreamInactive() {
|
||||||
is_paused_ = true;
|
is_paused_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t VideoQualityObserver::NumFreezes() {
|
||||||
|
return freezes_durations_.NumSamples();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t VideoQualityObserver::NumPauses() {
|
||||||
|
return pauses_durations_.NumSamples();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t VideoQualityObserver::TotalFreezesDurationMs() {
|
||||||
|
return freezes_durations_.Sum(kMinRequiredSamples).value_or(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t VideoQualityObserver::TotalPausesDurationMs() {
|
||||||
|
return pauses_durations_.Sum(kMinRequiredSamples).value_or(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t VideoQualityObserver::TotalFramesDurationMs() {
|
||||||
|
return last_frame_rendered_ms_ - first_frame_rendered_ms_;
|
||||||
|
}
|
||||||
|
|
||||||
|
double VideoQualityObserver::SumSquaredFrameDurationsSec() {
|
||||||
|
return sum_squared_interframe_delays_secs_;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
#include "api/video/video_codec_type.h"
|
#include "api/video/video_codec_type.h"
|
||||||
#include "api/video/video_content_type.h"
|
#include "api/video/video_content_type.h"
|
||||||
#include "api/video/video_frame.h"
|
#include "api/video/video_frame.h"
|
||||||
|
#include "rtc_base/numerics/moving_average.h"
|
||||||
#include "rtc_base/numerics/sample_counter.h"
|
#include "rtc_base/numerics/sample_counter.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
@ -40,6 +41,17 @@ class VideoQualityObserver {
|
|||||||
|
|
||||||
void OnStreamInactive();
|
void OnStreamInactive();
|
||||||
|
|
||||||
|
uint32_t NumFreezes();
|
||||||
|
uint32_t NumPauses();
|
||||||
|
uint32_t TotalFreezesDurationMs();
|
||||||
|
uint32_t TotalPausesDurationMs();
|
||||||
|
uint32_t TotalFramesDurationMs();
|
||||||
|
double SumSquaredFrameDurationsSec();
|
||||||
|
|
||||||
|
static const uint32_t kMinFrameSamplesToDetectFreeze;
|
||||||
|
static const uint32_t kMinIncreaseForFreezeMs;
|
||||||
|
static const uint32_t kAvgInterframeDelaysWindowSizeFrames;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void UpdateHistograms();
|
void UpdateHistograms();
|
||||||
|
|
||||||
@ -55,12 +67,13 @@ class VideoQualityObserver {
|
|||||||
int64_t last_frame_pixels_;
|
int64_t last_frame_pixels_;
|
||||||
bool is_last_frame_blocky_;
|
bool is_last_frame_blocky_;
|
||||||
// Decoded timestamp of the last delayed frame.
|
// Decoded timestamp of the last delayed frame.
|
||||||
int64_t last_unfreeze_time_;
|
int64_t last_unfreeze_time_ms_;
|
||||||
rtc::SampleCounter render_interframe_delays_;
|
rtc::MovingAverage render_interframe_delays_;
|
||||||
double sum_squared_interframe_delays_secs_;
|
double sum_squared_interframe_delays_secs_;
|
||||||
// An inter-frame delay is counted as a freeze if it's significantly longer
|
// An inter-frame delay is counted as a freeze if it's significantly longer
|
||||||
// than average inter-frame delay.
|
// than average inter-frame delay.
|
||||||
rtc::SampleCounter freezes_durations_;
|
rtc::SampleCounter freezes_durations_;
|
||||||
|
rtc::SampleCounter pauses_durations_;
|
||||||
// Time between freezes.
|
// Time between freezes.
|
||||||
rtc::SampleCounter smooth_playback_durations_;
|
rtc::SampleCounter smooth_playback_durations_;
|
||||||
// Counters for time spent in different resolutions. Time between each two
|
// Counters for time spent in different resolutions. Time between each two
|
||||||
|
|||||||
Reference in New Issue
Block a user