Add totalDecodeTime to RTCInboundRTPStreamStats
Pull request to WebRTC stats specification: https://github.com/w3c/webrtc-stats/pull/450 Bug: webrtc:10775 Change-Id: Id032cb324724329fee284ebc84595b9c39208ab8 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/144035 Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org> Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Reviewed-by: Henrik Boström <hbos@webrtc.org> Reviewed-by: Niels Moller <nisse@webrtc.org> Cr-Commit-Position: refs/heads/master@{#28440}
This commit is contained in:
parent
419aae2ed3
commit
bfd343b9be
@ -443,6 +443,7 @@ class RTC_EXPORT RTCInboundRTPStreamStats final : public RTCRTPStreamStats {
|
||||
RTCStatsMember<double> gap_discard_rate;
|
||||
RTCStatsMember<uint32_t> frames_decoded;
|
||||
RTCStatsMember<uint32_t> key_frames_decoded;
|
||||
RTCStatsMember<double> total_decode_time;
|
||||
// https://henbos.github.io/webrtc-provisional-stats/#dom-rtcinboundrtpstreamstats-contenttype
|
||||
RTCStatsMember<std::string> content_type;
|
||||
};
|
||||
|
||||
@ -85,6 +85,8 @@ class VideoReceiveStream {
|
||||
int render_delay_ms = 10;
|
||||
int64_t interframe_delay_max_ms = -1;
|
||||
uint32_t frames_decoded = 0;
|
||||
// https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-totaldecodetime
|
||||
uint64_t total_decode_time_ms = 0;
|
||||
int64_t first_frame_received_to_decoded_ms = -1;
|
||||
absl::optional<uint64_t> qp_sum;
|
||||
|
||||
|
||||
@ -601,6 +601,8 @@ struct VideoReceiverInfo : public MediaReceiverInfo {
|
||||
uint32_t key_frames_decoded = 0;
|
||||
uint32_t frames_rendered = 0;
|
||||
absl::optional<uint64_t> qp_sum;
|
||||
// https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-totaldecodetime
|
||||
uint64_t total_decode_time_ms = 0;
|
||||
int64_t interframe_delay_max_ms = -1;
|
||||
uint32_t freeze_count = 0;
|
||||
uint32_t pause_count = 0;
|
||||
|
||||
@ -2762,6 +2762,7 @@ WebRtcVideoChannel::WebRtcVideoReceiveStream::GetVideoReceiverInfo(
|
||||
info.key_frames_decoded = stats.frame_counts.key_frames;
|
||||
info.frames_rendered = stats.frames_rendered;
|
||||
info.qp_sum = stats.qp_sum;
|
||||
info.total_decode_time_ms = stats.total_decode_time_ms;
|
||||
info.last_packet_received_timestamp_ms =
|
||||
stats.rtp_stats.last_packet_received_timestamp_ms;
|
||||
info.first_frame_received_to_decoded_ms =
|
||||
|
||||
@ -5086,6 +5086,7 @@ TEST_F(WebRtcVideoChannelTest, GetStatsTranslatesDecodeStatsCorrectly) {
|
||||
stats.frames_rendered = 13;
|
||||
stats.frames_decoded = 14;
|
||||
stats.qp_sum = 15;
|
||||
stats.total_decode_time_ms = 16;
|
||||
stream->SetStats(stats);
|
||||
|
||||
cricket::VideoMediaInfo info;
|
||||
@ -5113,6 +5114,7 @@ TEST_F(WebRtcVideoChannelTest, GetStatsTranslatesDecodeStatsCorrectly) {
|
||||
EXPECT_EQ(rtc::checked_cast<unsigned int>(stats.frame_counts.key_frames),
|
||||
info.receivers[0].key_frames_decoded);
|
||||
EXPECT_EQ(stats.qp_sum, info.receivers[0].qp_sum);
|
||||
EXPECT_EQ(stats.total_decode_time_ms, info.receivers[0].total_decode_time_ms);
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVideoChannelTest, GetStatsTranslatesReceivePacketStatsCorrectly) {
|
||||
|
||||
@ -699,19 +699,18 @@ void FrameBuffer::UpdateJitterDelay() {
|
||||
if (!stats_callback_)
|
||||
return;
|
||||
|
||||
int decode_ms;
|
||||
int max_decode_ms;
|
||||
int current_delay_ms;
|
||||
int target_delay_ms;
|
||||
int jitter_buffer_ms;
|
||||
int min_playout_delay_ms;
|
||||
int render_delay_ms;
|
||||
if (timing_->GetTimings(&decode_ms, &max_decode_ms, ¤t_delay_ms,
|
||||
&target_delay_ms, &jitter_buffer_ms,
|
||||
&min_playout_delay_ms, &render_delay_ms)) {
|
||||
if (timing_->GetTimings(&max_decode_ms, ¤t_delay_ms, &target_delay_ms,
|
||||
&jitter_buffer_ms, &min_playout_delay_ms,
|
||||
&render_delay_ms)) {
|
||||
stats_callback_->OnFrameBufferTimingsUpdated(
|
||||
decode_ms, max_decode_ms, current_delay_ms, target_delay_ms,
|
||||
jitter_buffer_ms, min_playout_delay_ms, render_delay_ms);
|
||||
max_decode_ms, current_delay_ms, target_delay_ms, jitter_buffer_ms,
|
||||
min_playout_delay_ms, render_delay_ms);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -59,8 +59,7 @@ class VCMTimingFake : public VCMTiming {
|
||||
return render_time_ms - now_ms - kDecodeTime;
|
||||
}
|
||||
|
||||
bool GetTimings(int* decode_ms,
|
||||
int* max_decode_ms,
|
||||
bool GetTimings(int* max_decode_ms,
|
||||
int* current_delay_ms,
|
||||
int* target_delay_ms,
|
||||
int* jitter_buffer_ms,
|
||||
@ -70,16 +69,15 @@ class VCMTimingFake : public VCMTiming {
|
||||
}
|
||||
|
||||
int GetCurrentJitter() {
|
||||
int decode_ms;
|
||||
int max_decode_ms;
|
||||
int current_delay_ms;
|
||||
int target_delay_ms;
|
||||
int jitter_buffer_ms;
|
||||
int min_playout_delay_ms;
|
||||
int render_delay_ms;
|
||||
VCMTiming::GetTimings(&decode_ms, &max_decode_ms, ¤t_delay_ms,
|
||||
&target_delay_ms, &jitter_buffer_ms,
|
||||
&min_playout_delay_ms, &render_delay_ms);
|
||||
VCMTiming::GetTimings(&max_decode_ms, ¤t_delay_ms, &target_delay_ms,
|
||||
&jitter_buffer_ms, &min_playout_delay_ms,
|
||||
&render_delay_ms);
|
||||
return jitter_buffer_ms;
|
||||
}
|
||||
|
||||
@ -115,9 +113,8 @@ class VCMReceiveStatisticsCallbackMock : public VCMReceiveStatisticsCallback {
|
||||
VideoContentType content_type));
|
||||
MOCK_METHOD1(OnDiscardedPacketsUpdated, void(int discarded_packets));
|
||||
MOCK_METHOD1(OnFrameCountsUpdated, void(const FrameCounts& frame_counts));
|
||||
MOCK_METHOD7(OnFrameBufferTimingsUpdated,
|
||||
void(int decode_ms,
|
||||
int max_decode_ms,
|
||||
MOCK_METHOD6(OnFrameBufferTimingsUpdated,
|
||||
void(int max_decode_ms,
|
||||
int current_delay_ms,
|
||||
int target_delay_ms,
|
||||
int jitter_buffer_ms,
|
||||
@ -548,8 +545,7 @@ TEST_F(TestFrameBuffer2, StatsCallback) {
|
||||
|
||||
EXPECT_CALL(stats_callback_,
|
||||
OnCompleteFrame(true, kFrameSize, VideoContentType::UNSPECIFIED));
|
||||
EXPECT_CALL(stats_callback_,
|
||||
OnFrameBufferTimingsUpdated(_, _, _, _, _, _, _));
|
||||
EXPECT_CALL(stats_callback_, OnFrameBufferTimingsUpdated(_, _, _, _, _, _));
|
||||
|
||||
{
|
||||
std::unique_ptr<FrameObjectFake> frame(new FrameObjectFake());
|
||||
|
||||
@ -91,8 +91,7 @@ void VCMDecodedFrameCallback::Decoded(VideoFrame& decodedImage,
|
||||
if (!decode_time_ms) {
|
||||
decode_time_ms = now_ms - frameInfo->decodeStartTimeMs;
|
||||
}
|
||||
_timing->StopDecodeTimer(decodedImage.timestamp(), *decode_time_ms, now_ms,
|
||||
frameInfo->renderTimeMs);
|
||||
_timing->StopDecodeTimer(*decode_time_ms, now_ms);
|
||||
|
||||
// Report timing information.
|
||||
TimingFrameInfo timing_frame_info;
|
||||
@ -147,7 +146,8 @@ void VCMDecodedFrameCallback::Decoded(VideoFrame& decodedImage,
|
||||
|
||||
decodedImage.set_timestamp_us(frameInfo->renderTimeMs *
|
||||
rtc::kNumMicrosecsPerMillisec);
|
||||
_receiveCallback->FrameToRender(decodedImage, qp, frameInfo->content_type);
|
||||
_receiveCallback->FrameToRender(decodedImage, qp, *decode_time_ms,
|
||||
frameInfo->content_type);
|
||||
}
|
||||
|
||||
void VCMDecodedFrameCallback::OnDecoderImplementationName(
|
||||
|
||||
@ -30,6 +30,7 @@ class ReceiveCallback : public VCMReceiveCallback {
|
||||
public:
|
||||
int32_t FrameToRender(VideoFrame& videoFrame, // NOLINT
|
||||
absl::optional<uint8_t> qp,
|
||||
int32_t decode_time_ms,
|
||||
VideoContentType content_type) override {
|
||||
{
|
||||
rtc::CritScope cs(&lock_);
|
||||
|
||||
@ -27,8 +27,9 @@ class MockVCMReceiveCallback : public VCMReceiveCallback {
|
||||
MockVCMReceiveCallback() {}
|
||||
virtual ~MockVCMReceiveCallback() {}
|
||||
|
||||
MOCK_METHOD3(FrameToRender,
|
||||
int32_t(VideoFrame&, absl::optional<uint8_t>, VideoContentType));
|
||||
MOCK_METHOD4(
|
||||
FrameToRender,
|
||||
int32_t(VideoFrame&, absl::optional<uint8_t>, int32_t, VideoContentType));
|
||||
MOCK_METHOD1(OnIncomingPayloadType, void(int));
|
||||
MOCK_METHOD1(OnDecoderImplementationName, void(const char*));
|
||||
};
|
||||
|
||||
@ -51,9 +51,21 @@ enum VCMVideoProtection {
|
||||
// rendered.
|
||||
class VCMReceiveCallback {
|
||||
public:
|
||||
// TODO(kron): Remove once downstream projects are updated.
|
||||
virtual int32_t FrameToRender(VideoFrame& videoFrame, // NOLINT
|
||||
absl::optional<uint8_t> qp,
|
||||
VideoContentType content_type) = 0;
|
||||
VideoContentType content_type) {
|
||||
// Cannot be pure virtual since this should be removed from derived
|
||||
// classes.
|
||||
return FrameToRender(videoFrame, qp, 0, content_type);
|
||||
}
|
||||
|
||||
virtual int32_t FrameToRender(VideoFrame& videoFrame, // NOLINT
|
||||
absl::optional<uint8_t> qp,
|
||||
int32_t decode_time_ms,
|
||||
VideoContentType content_type) {
|
||||
return FrameToRender(videoFrame, qp, content_type);
|
||||
}
|
||||
|
||||
// Called when the current receive codec changes.
|
||||
virtual void OnIncomingPayloadType(int payload_type);
|
||||
@ -71,8 +83,7 @@ class VCMReceiveStatisticsCallback {
|
||||
size_t size_bytes,
|
||||
VideoContentType content_type) = 0;
|
||||
|
||||
virtual void OnFrameBufferTimingsUpdated(int decode_ms,
|
||||
int max_decode_ms,
|
||||
virtual void OnFrameBufferTimingsUpdated(int max_decode_ms,
|
||||
int current_delay_ms,
|
||||
int target_delay_ms,
|
||||
int jitter_buffer_ms,
|
||||
|
||||
@ -454,7 +454,7 @@ TEST_F(VCMReceiverTimingTest, FrameForDecodingPreferLateDecoding) {
|
||||
int render_delay_ms;
|
||||
int max_decode_ms;
|
||||
int dummy;
|
||||
timing_.GetTimings(&dummy, &max_decode_ms, &dummy, &dummy, &dummy, &dummy,
|
||||
timing_.GetTimings(&max_decode_ms, &dummy, &dummy, &dummy, &dummy,
|
||||
&render_delay_ms);
|
||||
|
||||
// Construct test samples.
|
||||
|
||||
@ -28,7 +28,6 @@ VCMTiming::VCMTiming(Clock* clock, VCMTiming* master_timing)
|
||||
max_playout_delay_ms_(10000),
|
||||
jitter_delay_ms_(0),
|
||||
current_delay_ms_(0),
|
||||
last_decode_ms_(0),
|
||||
prev_frame_timestamp_(0),
|
||||
timing_frame_info_(),
|
||||
num_decoded_frames_(0) {
|
||||
@ -150,14 +149,17 @@ void VCMTiming::UpdateCurrentDelay(int64_t render_time_ms,
|
||||
}
|
||||
}
|
||||
|
||||
void VCMTiming::StopDecodeTimer(uint32_t time_stamp,
|
||||
void VCMTiming::StopDecodeTimer(uint32_t /*time_stamp*/,
|
||||
int32_t decode_time_ms,
|
||||
int64_t now_ms,
|
||||
int64_t render_time_ms) {
|
||||
int64_t /*render_time_ms*/) {
|
||||
StopDecodeTimer(decode_time_ms, now_ms);
|
||||
}
|
||||
|
||||
void VCMTiming::StopDecodeTimer(int32_t decode_time_ms, int64_t now_ms) {
|
||||
rtc::CritScope cs(&crit_sect_);
|
||||
codec_timer_->AddTiming(decode_time_ms, now_ms);
|
||||
assert(decode_time_ms >= 0);
|
||||
last_decode_ms_ = decode_time_ms;
|
||||
++num_decoded_frames_;
|
||||
}
|
||||
|
||||
@ -217,15 +219,13 @@ int VCMTiming::TargetDelayInternal() const {
|
||||
jitter_delay_ms_ + RequiredDecodeTimeMs() + render_delay_ms_);
|
||||
}
|
||||
|
||||
bool VCMTiming::GetTimings(int* decode_ms,
|
||||
int* max_decode_ms,
|
||||
bool VCMTiming::GetTimings(int* max_decode_ms,
|
||||
int* current_delay_ms,
|
||||
int* target_delay_ms,
|
||||
int* jitter_buffer_ms,
|
||||
int* min_playout_delay_ms,
|
||||
int* render_delay_ms) const {
|
||||
rtc::CritScope cs(&crit_sect_);
|
||||
*decode_ms = last_decode_ms_;
|
||||
*max_decode_ms = RequiredDecodeTimeMs();
|
||||
*current_delay_ms = current_delay_ms_;
|
||||
*target_delay_ms = TargetDelayInternal();
|
||||
|
||||
@ -61,6 +61,9 @@ class VCMTiming {
|
||||
|
||||
// Stops the decoder timer, should be called when the decoder returns a frame
|
||||
// or when the decoded frame callback is called.
|
||||
void StopDecodeTimer(int32_t decode_time_ms, int64_t now_ms);
|
||||
// TODO(kron): Remove once downstream projects has been changed to use the
|
||||
// above function.
|
||||
void StopDecodeTimer(uint32_t time_stamp,
|
||||
int32_t decode_time_ms,
|
||||
int64_t now_ms,
|
||||
@ -85,8 +88,7 @@ class VCMTiming {
|
||||
|
||||
// Return current timing information. Returns true if the first frame has been
|
||||
// decoded, false otherwise.
|
||||
virtual bool GetTimings(int* decode_ms,
|
||||
int* max_decode_ms,
|
||||
virtual bool GetTimings(int* max_decode_ms,
|
||||
int* current_delay_ms,
|
||||
int* target_delay_ms,
|
||||
int* jitter_buffer_ms,
|
||||
@ -121,7 +123,6 @@ class VCMTiming {
|
||||
int max_playout_delay_ms_ RTC_GUARDED_BY(crit_sect_);
|
||||
int jitter_delay_ms_ RTC_GUARDED_BY(crit_sect_);
|
||||
int current_delay_ms_ RTC_GUARDED_BY(crit_sect_);
|
||||
int last_decode_ms_ RTC_GUARDED_BY(crit_sect_);
|
||||
uint32_t prev_frame_timestamp_ RTC_GUARDED_BY(crit_sect_);
|
||||
absl::optional<TimingFrameInfo> timing_frame_info_ RTC_GUARDED_BY(crit_sect_);
|
||||
size_t num_decoded_frames_ RTC_GUARDED_BY(crit_sect_);
|
||||
|
||||
@ -75,9 +75,7 @@ TEST(ReceiverTiming, Tests) {
|
||||
const uint32_t kDecodeTimeMs = 10;
|
||||
for (int i = 0; i < kFps; i++) {
|
||||
clock.AdvanceTimeMilliseconds(kDecodeTimeMs);
|
||||
timing.StopDecodeTimer(
|
||||
timestamp, kDecodeTimeMs, clock.TimeInMilliseconds(),
|
||||
timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()));
|
||||
timing.StopDecodeTimer(kDecodeTimeMs, clock.TimeInMilliseconds());
|
||||
timestamp += 90000 / kFps;
|
||||
clock.AdvanceTimeMilliseconds(1000 / kFps - kDecodeTimeMs);
|
||||
timing.IncomingTimestamp(timestamp, clock.TimeInMilliseconds());
|
||||
|
||||
@ -312,6 +312,9 @@ void SetInboundRTPStreamStatsFromVideoReceiverInfo(
|
||||
inbound_video->key_frames_decoded = video_receiver_info.key_frames_decoded;
|
||||
if (video_receiver_info.qp_sum)
|
||||
inbound_video->qp_sum = *video_receiver_info.qp_sum;
|
||||
inbound_video->total_decode_time =
|
||||
static_cast<double>(video_receiver_info.total_decode_time_ms) /
|
||||
rtc::kNumMillisecsPerSec;
|
||||
if (video_receiver_info.last_packet_received_timestamp_ms) {
|
||||
inbound_video->last_packet_received_timestamp =
|
||||
static_cast<double>(
|
||||
|
||||
@ -1803,6 +1803,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) {
|
||||
video_media_info.receivers[0].frames_decoded = 8;
|
||||
video_media_info.receivers[0].key_frames_decoded = 3;
|
||||
video_media_info.receivers[0].qp_sum = absl::nullopt;
|
||||
video_media_info.receivers[0].total_decode_time_ms = 9000;
|
||||
video_media_info.receivers[0].last_packet_received_timestamp_ms =
|
||||
absl::nullopt;
|
||||
video_media_info.receivers[0].content_type = VideoContentType::UNSPECIFIED;
|
||||
@ -1840,6 +1841,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) {
|
||||
expected_video.frames_decoded = 8;
|
||||
expected_video.key_frames_decoded = 3;
|
||||
// |expected_video.qp_sum| should be undefined.
|
||||
expected_video.total_decode_time = 9.0;
|
||||
// |expected_video.last_packet_received_timestamp| should be undefined.
|
||||
// |expected_video.content_type| should be undefined.
|
||||
|
||||
|
||||
@ -805,12 +805,15 @@ class RTCStatsReportVerifier {
|
||||
*inbound_stream.media_type == "video") {
|
||||
verifier.TestMemberIsDefined(inbound_stream.frames_decoded);
|
||||
verifier.TestMemberIsDefined(inbound_stream.key_frames_decoded);
|
||||
verifier.TestMemberIsNonNegative<double>(
|
||||
inbound_stream.total_decode_time);
|
||||
// The integration test is not set up to test screen share; don't require
|
||||
// this to be present.
|
||||
verifier.MarkMemberTested(inbound_stream.content_type, true);
|
||||
} else {
|
||||
verifier.TestMemberIsUndefined(inbound_stream.frames_decoded);
|
||||
verifier.TestMemberIsUndefined(inbound_stream.key_frames_decoded);
|
||||
verifier.TestMemberIsUndefined(inbound_stream.total_decode_time);
|
||||
verifier.TestMemberIsUndefined(inbound_stream.content_type);
|
||||
}
|
||||
return verifier.ExpectAllMembersSuccessfullyTested();
|
||||
|
||||
@ -615,6 +615,7 @@ WEBRTC_RTCSTATS_IMPL(
|
||||
&gap_discard_rate,
|
||||
&frames_decoded,
|
||||
&key_frames_decoded,
|
||||
&total_decode_time,
|
||||
&content_type)
|
||||
// clang-format on
|
||||
|
||||
@ -645,6 +646,7 @@ RTCInboundRTPStreamStats::RTCInboundRTPStreamStats(std::string&& id,
|
||||
gap_discard_rate("gapDiscardRate"),
|
||||
frames_decoded("framesDecoded"),
|
||||
key_frames_decoded("keyFramesDecoded"),
|
||||
total_decode_time("totalDecodeTime"),
|
||||
content_type("contentType") {}
|
||||
|
||||
RTCInboundRTPStreamStats::RTCInboundRTPStreamStats(
|
||||
@ -670,6 +672,7 @@ RTCInboundRTPStreamStats::RTCInboundRTPStreamStats(
|
||||
gap_discard_rate(other.gap_discard_rate),
|
||||
frames_decoded(other.frames_decoded),
|
||||
key_frames_decoded(other.key_frames_decoded),
|
||||
total_decode_time(other.total_decode_time),
|
||||
content_type(other.content_type) {}
|
||||
|
||||
RTCInboundRTPStreamStats::~RTCInboundRTPStreamStats() {}
|
||||
|
||||
@ -615,7 +615,6 @@ void ReceiveStatisticsProxy::OnDecoderImplementationName(
|
||||
}
|
||||
|
||||
void ReceiveStatisticsProxy::OnFrameBufferTimingsUpdated(
|
||||
int decode_ms,
|
||||
int max_decode_ms,
|
||||
int current_delay_ms,
|
||||
int target_delay_ms,
|
||||
@ -623,14 +622,12 @@ void ReceiveStatisticsProxy::OnFrameBufferTimingsUpdated(
|
||||
int min_playout_delay_ms,
|
||||
int render_delay_ms) {
|
||||
rtc::CritScope lock(&crit_);
|
||||
stats_.decode_ms = decode_ms;
|
||||
stats_.max_decode_ms = max_decode_ms;
|
||||
stats_.current_delay_ms = current_delay_ms;
|
||||
stats_.target_delay_ms = target_delay_ms;
|
||||
stats_.jitter_buffer_ms = jitter_buffer_ms;
|
||||
stats_.min_playout_delay_ms = min_playout_delay_ms;
|
||||
stats_.render_delay_ms = render_delay_ms;
|
||||
decode_time_counter_.Add(decode_ms);
|
||||
jitter_buffer_delay_counter_.Add(jitter_buffer_ms);
|
||||
target_delay_counter_.Add(target_delay_ms);
|
||||
current_delay_counter_.Add(current_delay_ms);
|
||||
@ -723,6 +720,7 @@ void ReceiveStatisticsProxy::DataCountersUpdated(
|
||||
|
||||
void ReceiveStatisticsProxy::OnDecodedFrame(const VideoFrame& frame,
|
||||
absl::optional<uint8_t> qp,
|
||||
int32_t decode_time_ms,
|
||||
VideoContentType content_type) {
|
||||
rtc::CritScope lock(&crit_);
|
||||
|
||||
@ -745,7 +743,6 @@ void ReceiveStatisticsProxy::OnDecodedFrame(const VideoFrame& frame,
|
||||
if (stats_.frames_decoded != 1) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "Frames decoded was not 1 when first qp value was received.";
|
||||
stats_.frames_decoded = 1;
|
||||
}
|
||||
stats_.qp_sum = 0;
|
||||
}
|
||||
@ -754,8 +751,10 @@ void ReceiveStatisticsProxy::OnDecodedFrame(const VideoFrame& frame,
|
||||
} else if (stats_.qp_sum) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "QP sum was already set and no QP was given for a frame.";
|
||||
stats_.qp_sum = absl::nullopt;
|
||||
}
|
||||
decode_time_counter_.Add(decode_time_ms);
|
||||
stats_.decode_ms = decode_time_ms;
|
||||
stats_.total_decode_time_ms += decode_time_ms;
|
||||
last_content_type_ = content_type;
|
||||
decode_fps_estimator_.Update(1, now_ms);
|
||||
if (last_decoded_frame_time_ms_) {
|
||||
|
||||
@ -51,6 +51,7 @@ class ReceiveStatisticsProxy : public VCMReceiveStatisticsCallback,
|
||||
|
||||
void OnDecodedFrame(const VideoFrame& frame,
|
||||
absl::optional<uint8_t> qp,
|
||||
int32_t decode_time_ms,
|
||||
VideoContentType content_type);
|
||||
void OnSyncOffsetUpdated(int64_t sync_offset_ms, double estimated_freq_khz);
|
||||
void OnRenderedFrame(const VideoFrame& frame);
|
||||
@ -68,8 +69,7 @@ class ReceiveStatisticsProxy : public VCMReceiveStatisticsCallback,
|
||||
void OnCompleteFrame(bool is_keyframe,
|
||||
size_t size_bytes,
|
||||
VideoContentType content_type) override;
|
||||
void OnFrameBufferTimingsUpdated(int decode_ms,
|
||||
int max_decode_ms,
|
||||
void OnFrameBufferTimingsUpdated(int max_decode_ms,
|
||||
int current_delay_ms,
|
||||
int target_delay_ms,
|
||||
int jitter_buffer_ms,
|
||||
|
||||
@ -88,7 +88,7 @@ TEST_F(ReceiveStatisticsProxyTest, OnDecodedFrameIncreasesFramesDecoded) {
|
||||
EXPECT_EQ(0u, statistics_proxy_->GetStats().frames_decoded);
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
for (uint32_t i = 1; i <= 3; ++i) {
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
EXPECT_EQ(i, statistics_proxy_->GetStats().frames_decoded);
|
||||
}
|
||||
@ -99,7 +99,7 @@ TEST_F(ReceiveStatisticsProxyTest, DecodedFpsIsReported) {
|
||||
const int kRequiredSamples = metrics::kMinRunTimeInSeconds * kFps;
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
for (int i = 0; i < kRequiredSamples; ++i) {
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
fake_clock_.AdvanceTimeMilliseconds(1000 / kFps);
|
||||
}
|
||||
@ -113,7 +113,7 @@ TEST_F(ReceiveStatisticsProxyTest, DecodedFpsIsNotReportedForTooFewSamples) {
|
||||
const int kRequiredSamples = metrics::kMinRunTimeInSeconds * kFps;
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
for (int i = 0; i < kRequiredSamples - 1; ++i) {
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
fake_clock_.AdvanceTimeMilliseconds(1000 / kFps);
|
||||
}
|
||||
@ -121,58 +121,67 @@ TEST_F(ReceiveStatisticsProxyTest, DecodedFpsIsNotReportedForTooFewSamples) {
|
||||
EXPECT_EQ(0, metrics::NumSamples("WebRTC.Video.DecodedFramesPerSecond"));
|
||||
}
|
||||
|
||||
TEST_F(ReceiveStatisticsProxyTest, DecodedFpsIsReportedWithQpReset) {
|
||||
const int kFps1 = 10;
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
for (int i = 0; i < metrics::kMinRunTimeInSeconds * kFps1; ++i) {
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
fake_clock_.AdvanceTimeMilliseconds(1000 / kFps1);
|
||||
}
|
||||
// First QP value received, resets frames decoded.
|
||||
const int kFps2 = 20;
|
||||
for (int i = 0; i < metrics::kMinRunTimeInSeconds * kFps2; ++i) {
|
||||
statistics_proxy_->OnDecodedFrame(frame, 1u, VideoContentType::UNSPECIFIED);
|
||||
fake_clock_.AdvanceTimeMilliseconds(1000 / kFps2);
|
||||
}
|
||||
statistics_proxy_.reset();
|
||||
EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.DecodedFramesPerSecond"));
|
||||
EXPECT_EQ(1,
|
||||
metrics::NumEvents("WebRTC.Video.DecodedFramesPerSecond", kFps2));
|
||||
}
|
||||
|
||||
TEST_F(ReceiveStatisticsProxyTest, OnDecodedFrameWithQpResetsFramesDecoded) {
|
||||
TEST_F(ReceiveStatisticsProxyTest,
|
||||
OnDecodedFrameWithQpDoesNotResetFramesDecodedOrTotalDecodeTime) {
|
||||
EXPECT_EQ(0u, statistics_proxy_->GetStats().frames_decoded);
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
unsigned int expected_total_decode_time_ms = 0;
|
||||
unsigned int expected_frames_decoded = 0;
|
||||
for (uint32_t i = 1; i <= 3; ++i) {
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 1,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
EXPECT_EQ(i, statistics_proxy_->GetStats().frames_decoded);
|
||||
expected_total_decode_time_ms += 1;
|
||||
++expected_frames_decoded;
|
||||
EXPECT_EQ(expected_frames_decoded,
|
||||
statistics_proxy_->GetStats().frames_decoded);
|
||||
EXPECT_EQ(expected_total_decode_time_ms,
|
||||
statistics_proxy_->GetStats().total_decode_time_ms);
|
||||
}
|
||||
statistics_proxy_->OnDecodedFrame(frame, 1u, VideoContentType::UNSPECIFIED);
|
||||
EXPECT_EQ(1u, statistics_proxy_->GetStats().frames_decoded);
|
||||
statistics_proxy_->OnDecodedFrame(frame, 1u, 3,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
++expected_frames_decoded;
|
||||
expected_total_decode_time_ms += 3;
|
||||
EXPECT_EQ(expected_frames_decoded,
|
||||
statistics_proxy_->GetStats().frames_decoded);
|
||||
EXPECT_EQ(expected_total_decode_time_ms,
|
||||
statistics_proxy_->GetStats().total_decode_time_ms);
|
||||
}
|
||||
|
||||
TEST_F(ReceiveStatisticsProxyTest, OnDecodedFrameIncreasesQpSum) {
|
||||
EXPECT_EQ(absl::nullopt, statistics_proxy_->GetStats().qp_sum);
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
statistics_proxy_->OnDecodedFrame(frame, 3u, VideoContentType::UNSPECIFIED);
|
||||
statistics_proxy_->OnDecodedFrame(frame, 3u, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
EXPECT_EQ(3u, statistics_proxy_->GetStats().qp_sum);
|
||||
statistics_proxy_->OnDecodedFrame(frame, 127u, VideoContentType::UNSPECIFIED);
|
||||
statistics_proxy_->OnDecodedFrame(frame, 127u, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
EXPECT_EQ(130u, statistics_proxy_->GetStats().qp_sum);
|
||||
}
|
||||
|
||||
TEST_F(ReceiveStatisticsProxyTest, OnDecodedFrameIncreasesTotalDecodeTime) {
|
||||
EXPECT_EQ(absl::nullopt, statistics_proxy_->GetStats().qp_sum);
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
statistics_proxy_->OnDecodedFrame(frame, 3u, 4,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
EXPECT_EQ(4u, statistics_proxy_->GetStats().total_decode_time_ms);
|
||||
statistics_proxy_->OnDecodedFrame(frame, 127u, 7,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
EXPECT_EQ(11u, statistics_proxy_->GetStats().total_decode_time_ms);
|
||||
}
|
||||
|
||||
TEST_F(ReceiveStatisticsProxyTest, ReportsContentType) {
|
||||
const std::string kRealtimeString("realtime");
|
||||
const std::string kScreenshareString("screen");
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
EXPECT_EQ(kRealtimeString, videocontenttypehelpers::ToString(
|
||||
statistics_proxy_->GetStats().content_type));
|
||||
statistics_proxy_->OnDecodedFrame(frame, 3u, VideoContentType::SCREENSHARE);
|
||||
statistics_proxy_->OnDecodedFrame(frame, 3u, 0,
|
||||
VideoContentType::SCREENSHARE);
|
||||
EXPECT_EQ(kScreenshareString,
|
||||
videocontenttypehelpers::ToString(
|
||||
statistics_proxy_->GetStats().content_type));
|
||||
statistics_proxy_->OnDecodedFrame(frame, 3u, VideoContentType::UNSPECIFIED);
|
||||
statistics_proxy_->OnDecodedFrame(frame, 3u, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
EXPECT_EQ(kRealtimeString, videocontenttypehelpers::ToString(
|
||||
statistics_proxy_->GetStats().content_type));
|
||||
}
|
||||
@ -183,24 +192,24 @@ TEST_F(ReceiveStatisticsProxyTest, ReportsMaxInterframeDelay) {
|
||||
const int64_t kInterframeDelayMs2 = 200;
|
||||
const int64_t kInterframeDelayMs3 = 100;
|
||||
EXPECT_EQ(-1, statistics_proxy_->GetStats().interframe_delay_max_ms);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
EXPECT_EQ(-1, statistics_proxy_->GetStats().interframe_delay_max_ms);
|
||||
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs1);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
EXPECT_EQ(kInterframeDelayMs1,
|
||||
statistics_proxy_->GetStats().interframe_delay_max_ms);
|
||||
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs2);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
EXPECT_EQ(kInterframeDelayMs2,
|
||||
statistics_proxy_->GetStats().interframe_delay_max_ms);
|
||||
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs3);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
// kInterframeDelayMs3 is smaller than kInterframeDelayMs2.
|
||||
EXPECT_EQ(kInterframeDelayMs2,
|
||||
@ -213,25 +222,25 @@ TEST_F(ReceiveStatisticsProxyTest, ReportInterframeDelayInWindow) {
|
||||
const int64_t kInterframeDelayMs2 = 750;
|
||||
const int64_t kInterframeDelayMs3 = 700;
|
||||
EXPECT_EQ(-1, statistics_proxy_->GetStats().interframe_delay_max_ms);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
EXPECT_EQ(-1, statistics_proxy_->GetStats().interframe_delay_max_ms);
|
||||
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs1);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
EXPECT_EQ(kInterframeDelayMs1,
|
||||
statistics_proxy_->GetStats().interframe_delay_max_ms);
|
||||
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs2);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
// Still first delay is the maximum
|
||||
EXPECT_EQ(kInterframeDelayMs1,
|
||||
statistics_proxy_->GetStats().interframe_delay_max_ms);
|
||||
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs3);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
// Now the first sample is out of the window, so the second is the maximum.
|
||||
EXPECT_EQ(kInterframeDelayMs2,
|
||||
@ -342,19 +351,20 @@ TEST_F(ReceiveStatisticsProxyTest, ReportsSumSquaredFrameDurations) {
|
||||
TEST_F(ReceiveStatisticsProxyTest, OnDecodedFrameWithoutQpQpSumWontExist) {
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
EXPECT_EQ(absl::nullopt, statistics_proxy_->GetStats().qp_sum);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
EXPECT_EQ(absl::nullopt, statistics_proxy_->GetStats().qp_sum);
|
||||
}
|
||||
|
||||
TEST_F(ReceiveStatisticsProxyTest, OnDecodedFrameWithoutQpResetsQpSum) {
|
||||
TEST_F(ReceiveStatisticsProxyTest, OnDecodedFrameWithoutQpDoesNotResetQpSum) {
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
EXPECT_EQ(absl::nullopt, statistics_proxy_->GetStats().qp_sum);
|
||||
statistics_proxy_->OnDecodedFrame(frame, 3u, VideoContentType::UNSPECIFIED);
|
||||
EXPECT_EQ(3u, statistics_proxy_->GetStats().qp_sum);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, 3u, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
EXPECT_EQ(absl::nullopt, statistics_proxy_->GetStats().qp_sum);
|
||||
EXPECT_EQ(3u, statistics_proxy_->GetStats().qp_sum);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
EXPECT_EQ(3u, statistics_proxy_->GetStats().qp_sum);
|
||||
}
|
||||
|
||||
TEST_F(ReceiveStatisticsProxyTest, OnRenderedFrameIncreasesFramesRendered) {
|
||||
@ -394,7 +404,6 @@ TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsOnCompleteFrame) {
|
||||
}
|
||||
|
||||
TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsDecodeTimingStats) {
|
||||
const int kDecodeMs = 1;
|
||||
const int kMaxDecodeMs = 2;
|
||||
const int kCurrentDelayMs = 3;
|
||||
const int kTargetDelayMs = 4;
|
||||
@ -404,10 +413,9 @@ TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsDecodeTimingStats) {
|
||||
const int64_t kRttMs = 8;
|
||||
statistics_proxy_->OnRttUpdate(kRttMs, 0);
|
||||
statistics_proxy_->OnFrameBufferTimingsUpdated(
|
||||
kDecodeMs, kMaxDecodeMs, kCurrentDelayMs, kTargetDelayMs, kJitterBufferMs,
|
||||
kMaxDecodeMs, kCurrentDelayMs, kTargetDelayMs, kJitterBufferMs,
|
||||
kMinPlayoutDelayMs, kRenderDelayMs);
|
||||
VideoReceiveStream::Stats stats = statistics_proxy_->GetStats();
|
||||
EXPECT_EQ(kDecodeMs, stats.decode_ms);
|
||||
EXPECT_EQ(kMaxDecodeMs, stats.max_decode_ms);
|
||||
EXPECT_EQ(kCurrentDelayMs, stats.current_delay_ms);
|
||||
EXPECT_EQ(kTargetDelayMs, stats.target_delay_ms);
|
||||
@ -774,7 +782,6 @@ TEST_F(ReceiveStatisticsProxyTest, KeyFrameHistogramIsUpdated) {
|
||||
}
|
||||
|
||||
TEST_F(ReceiveStatisticsProxyTest, TimingHistogramsNotUpdatedForTooFewSamples) {
|
||||
const int kDecodeMs = 1;
|
||||
const int kMaxDecodeMs = 2;
|
||||
const int kCurrentDelayMs = 3;
|
||||
const int kTargetDelayMs = 4;
|
||||
@ -784,8 +791,8 @@ TEST_F(ReceiveStatisticsProxyTest, TimingHistogramsNotUpdatedForTooFewSamples) {
|
||||
|
||||
for (int i = 0; i < kMinRequiredSamples - 1; ++i) {
|
||||
statistics_proxy_->OnFrameBufferTimingsUpdated(
|
||||
kDecodeMs, kMaxDecodeMs, kCurrentDelayMs, kTargetDelayMs,
|
||||
kJitterBufferMs, kMinPlayoutDelayMs, kRenderDelayMs);
|
||||
kMaxDecodeMs, kCurrentDelayMs, kTargetDelayMs, kJitterBufferMs,
|
||||
kMinPlayoutDelayMs, kRenderDelayMs);
|
||||
}
|
||||
|
||||
statistics_proxy_.reset();
|
||||
@ -797,7 +804,6 @@ TEST_F(ReceiveStatisticsProxyTest, TimingHistogramsNotUpdatedForTooFewSamples) {
|
||||
}
|
||||
|
||||
TEST_F(ReceiveStatisticsProxyTest, TimingHistogramsAreUpdated) {
|
||||
const int kDecodeMs = 1;
|
||||
const int kMaxDecodeMs = 2;
|
||||
const int kCurrentDelayMs = 3;
|
||||
const int kTargetDelayMs = 4;
|
||||
@ -807,18 +813,16 @@ TEST_F(ReceiveStatisticsProxyTest, TimingHistogramsAreUpdated) {
|
||||
|
||||
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
||||
statistics_proxy_->OnFrameBufferTimingsUpdated(
|
||||
kDecodeMs, kMaxDecodeMs, kCurrentDelayMs, kTargetDelayMs,
|
||||
kJitterBufferMs, kMinPlayoutDelayMs, kRenderDelayMs);
|
||||
kMaxDecodeMs, kCurrentDelayMs, kTargetDelayMs, kJitterBufferMs,
|
||||
kMinPlayoutDelayMs, kRenderDelayMs);
|
||||
}
|
||||
|
||||
statistics_proxy_.reset();
|
||||
EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.DecodeTimeInMs"));
|
||||
EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.JitterBufferDelayInMs"));
|
||||
EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.TargetDelayInMs"));
|
||||
EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.CurrentDelayInMs"));
|
||||
EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.OnewayDelayInMs"));
|
||||
|
||||
EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.DecodeTimeInMs", kDecodeMs));
|
||||
EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.JitterBufferDelayInMs",
|
||||
kJitterBufferMs));
|
||||
EXPECT_EQ(1,
|
||||
@ -837,7 +841,7 @@ TEST_F(ReceiveStatisticsProxyTest, DoesNotReportStaleFramerates) {
|
||||
// Since OnRenderedFrame is never called the fps in each sample will be 0,
|
||||
// i.e. bad
|
||||
frame.set_ntp_time_ms(fake_clock_.CurrentNtpInMilliseconds());
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
statistics_proxy_->OnRenderedFrame(frame);
|
||||
fake_clock_.AdvanceTimeMilliseconds(1000 / kDefaultFps);
|
||||
@ -893,7 +897,7 @@ TEST_F(ReceiveStatisticsProxyTest, ReceivedFrameHistogramsAreUpdated) {
|
||||
|
||||
TEST_F(ReceiveStatisticsProxyTest, ZeroDelayReportedIfFrameNotDelayed) {
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
|
||||
// Frame not delayed, delayed frames to render: 0%.
|
||||
@ -912,7 +916,7 @@ TEST_F(ReceiveStatisticsProxyTest, ZeroDelayReportedIfFrameNotDelayed) {
|
||||
TEST_F(ReceiveStatisticsProxyTest,
|
||||
DelayedFrameHistogramsAreNotUpdatedIfMinRuntimeHasNotPassed) {
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
|
||||
// Frame not delayed, delayed frames to render: 0%.
|
||||
@ -931,7 +935,7 @@ TEST_F(ReceiveStatisticsProxyTest,
|
||||
TEST_F(ReceiveStatisticsProxyTest,
|
||||
DelayedFramesHistogramsAreNotUpdatedIfNoRenderedFrames) {
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
|
||||
// Min run time has passed. No rendered frames.
|
||||
@ -944,7 +948,7 @@ TEST_F(ReceiveStatisticsProxyTest,
|
||||
|
||||
TEST_F(ReceiveStatisticsProxyTest, DelayReportedIfFrameIsDelayed) {
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
|
||||
// Frame delayed 1 ms, delayed frames to render: 100%.
|
||||
@ -964,7 +968,7 @@ TEST_F(ReceiveStatisticsProxyTest, DelayReportedIfFrameIsDelayed) {
|
||||
|
||||
TEST_F(ReceiveStatisticsProxyTest, AverageDelayOfDelayedFramesIsReported) {
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt,
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
|
||||
VideoContentType::UNSPECIFIED);
|
||||
|
||||
// Two frames delayed (6 ms, 10 ms), delayed frames to render: 50%.
|
||||
@ -1099,12 +1103,12 @@ TEST_P(ReceiveStatisticsProxyTestWithContent, InterFrameDelaysAreReported) {
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
|
||||
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||
}
|
||||
// One extra with double the interval.
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
|
||||
|
||||
statistics_proxy_.reset();
|
||||
const int kExpectedInterFrame =
|
||||
@ -1134,16 +1138,16 @@ TEST_P(ReceiveStatisticsProxyTestWithContent,
|
||||
|
||||
for (int i = 0; i <= kMinRequiredSamples - kLastFivePercentsSamples; ++i) {
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
|
||||
}
|
||||
// Last 5% of intervals are double in size.
|
||||
for (int i = 0; i < kLastFivePercentsSamples; ++i) {
|
||||
fake_clock_.AdvanceTimeMilliseconds(2 * kInterFrameDelayMs);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
|
||||
}
|
||||
// Final sample is outlier and 10 times as big.
|
||||
fake_clock_.AdvanceTimeMilliseconds(10 * kInterFrameDelayMs);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
|
||||
|
||||
statistics_proxy_.reset();
|
||||
const int kExpectedInterFrame = kInterFrameDelayMs * 2;
|
||||
@ -1164,7 +1168,7 @@ TEST_P(ReceiveStatisticsProxyTestWithContent,
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
|
||||
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||
}
|
||||
|
||||
@ -1184,7 +1188,7 @@ TEST_P(ReceiveStatisticsProxyTestWithContent, MaxInterFrameDelayOnlyWithPause) {
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
|
||||
for (int i = 0; i <= kMinRequiredSamples; ++i) {
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||
}
|
||||
|
||||
@ -1195,9 +1199,9 @@ TEST_P(ReceiveStatisticsProxyTestWithContent, MaxInterFrameDelayOnlyWithPause) {
|
||||
|
||||
// Insert two more frames. The interval during the pause should be disregarded
|
||||
// in the stats.
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
|
||||
|
||||
statistics_proxy_.reset();
|
||||
if (videocontenttypehelpers::IsScreenshare(content_type_)) {
|
||||
@ -1229,13 +1233,13 @@ TEST_P(ReceiveStatisticsProxyTestWithContent, FreezesAreReported) {
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
|
||||
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
|
||||
statistics_proxy_->OnRenderedFrame(frame);
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||
}
|
||||
// Add extra freeze.
|
||||
fake_clock_.AdvanceTimeMilliseconds(kFreezeDelayMs);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
|
||||
statistics_proxy_->OnRenderedFrame(frame);
|
||||
|
||||
statistics_proxy_.reset();
|
||||
@ -1272,20 +1276,20 @@ TEST_P(ReceiveStatisticsProxyTestWithContent, HarmonicFrameRateIsReported) {
|
||||
|
||||
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
||||
fake_clock_.AdvanceTimeMilliseconds(kFrameDurationMs);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
|
||||
statistics_proxy_->OnRenderedFrame(frame);
|
||||
}
|
||||
|
||||
// Freezes and pauses should be included into harmonic frame rate.
|
||||
// Add freeze.
|
||||
fake_clock_.AdvanceTimeMilliseconds(kFreezeDurationMs);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
|
||||
statistics_proxy_->OnRenderedFrame(frame);
|
||||
|
||||
// Add pause.
|
||||
fake_clock_.AdvanceTimeMilliseconds(kPauseDurationMs);
|
||||
statistics_proxy_->OnStreamInactive();
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
|
||||
statistics_proxy_->OnRenderedFrame(frame);
|
||||
|
||||
statistics_proxy_.reset();
|
||||
@ -1313,7 +1317,7 @@ TEST_P(ReceiveStatisticsProxyTestWithContent, PausesAreIgnored) {
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
|
||||
for (int i = 0; i <= kMinRequiredSamples; ++i) {
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
|
||||
statistics_proxy_->OnRenderedFrame(frame);
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||
}
|
||||
@ -1323,7 +1327,7 @@ TEST_P(ReceiveStatisticsProxyTestWithContent, PausesAreIgnored) {
|
||||
|
||||
// Second playback interval with triple the length.
|
||||
for (int i = 0; i <= kMinRequiredSamples * 3; ++i) {
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
|
||||
statistics_proxy_->OnRenderedFrame(frame);
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||
}
|
||||
@ -1351,13 +1355,13 @@ TEST_P(ReceiveStatisticsProxyTestWithContent, ManyPausesAtTheBeginning) {
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
|
||||
for (int i = 0; i <= kMinRequiredSamples; ++i) {
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||
|
||||
statistics_proxy_->OnStreamInactive();
|
||||
fake_clock_.AdvanceTimeMilliseconds(kPauseDurationMs);
|
||||
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||
}
|
||||
|
||||
@ -1378,13 +1382,15 @@ TEST_P(ReceiveStatisticsProxyTestWithContent, TimeInHdReported) {
|
||||
|
||||
// HD frames.
|
||||
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
||||
statistics_proxy_->OnDecodedFrame(frame_hd, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame_hd, absl::nullopt, 0,
|
||||
content_type_);
|
||||
statistics_proxy_->OnRenderedFrame(frame_hd);
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||
}
|
||||
// SD frames.
|
||||
for (int i = 0; i < 2 * kMinRequiredSamples; ++i) {
|
||||
statistics_proxy_->OnDecodedFrame(frame_sd, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame_sd, absl::nullopt, 0,
|
||||
content_type_);
|
||||
statistics_proxy_->OnRenderedFrame(frame_sd);
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||
}
|
||||
@ -1411,18 +1417,18 @@ TEST_P(ReceiveStatisticsProxyTestWithContent, TimeInBlockyVideoReported) {
|
||||
|
||||
// High quality frames.
|
||||
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
||||
statistics_proxy_->OnDecodedFrame(frame, kLowQp, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, kLowQp, 0, content_type_);
|
||||
statistics_proxy_->OnRenderedFrame(frame);
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||
}
|
||||
// Blocky frames.
|
||||
for (int i = 0; i < 2 * kMinRequiredSamples; ++i) {
|
||||
statistics_proxy_->OnDecodedFrame(frame, kHighQp, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, kHighQp, 0, content_type_);
|
||||
statistics_proxy_->OnRenderedFrame(frame);
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||
}
|
||||
// Extra last frame.
|
||||
statistics_proxy_->OnDecodedFrame(frame, kHighQp, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame, kHighQp, 0, content_type_);
|
||||
statistics_proxy_->OnRenderedFrame(frame);
|
||||
|
||||
statistics_proxy_.reset();
|
||||
@ -1445,7 +1451,7 @@ TEST_P(ReceiveStatisticsProxyTestWithContent, DownscalesReported) {
|
||||
webrtc::VideoFrame frame_ld = CreateFrame(320, 180);
|
||||
|
||||
// Call once to pass content type.
|
||||
statistics_proxy_->OnDecodedFrame(frame_hd, absl::nullopt, content_type_);
|
||||
statistics_proxy_->OnDecodedFrame(frame_hd, absl::nullopt, 0, content_type_);
|
||||
|
||||
statistics_proxy_->OnRenderedFrame(frame_hd);
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||
@ -1472,6 +1478,21 @@ TEST_P(ReceiveStatisticsProxyTestWithContent, DownscalesReported) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ReceiveStatisticsProxyTestWithContent, DecodeTimeReported) {
|
||||
const int kInterFrameDelayMs = 20;
|
||||
const int kLowQp = 30;
|
||||
const int kDecodeMs = 7;
|
||||
|
||||
webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
|
||||
|
||||
for (int i = 0; i < kMinRequiredSamples; ++i) {
|
||||
statistics_proxy_->OnDecodedFrame(frame, kLowQp, kDecodeMs, content_type_);
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
|
||||
}
|
||||
statistics_proxy_.reset();
|
||||
EXPECT_EQ(1, metrics::NumEvents("WebRTC.Video.DecodeTimeInMs", kDecodeMs));
|
||||
}
|
||||
|
||||
TEST_P(ReceiveStatisticsProxyTestWithContent,
|
||||
StatsAreSlicedOnSimulcastAndExperiment) {
|
||||
const uint8_t experiment_id = 1;
|
||||
@ -1484,13 +1505,13 @@ TEST_P(ReceiveStatisticsProxyTestWithContent,
|
||||
videocontenttypehelpers::SetSimulcastId(&content_type, 1);
|
||||
for (int i = 0; i <= kMinRequiredSamples; ++i) {
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs1);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type);
|
||||
}
|
||||
|
||||
videocontenttypehelpers::SetSimulcastId(&content_type, 2);
|
||||
for (int i = 0; i <= kMinRequiredSamples; ++i) {
|
||||
fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs2);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, content_type);
|
||||
statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type);
|
||||
}
|
||||
statistics_proxy_.reset();
|
||||
|
||||
|
||||
@ -44,8 +44,10 @@ VideoStreamDecoder::~VideoStreamDecoder() {
|
||||
// Release. Acquiring the same lock in the path of decode callback can deadlock.
|
||||
int32_t VideoStreamDecoder::FrameToRender(VideoFrame& video_frame,
|
||||
absl::optional<uint8_t> qp,
|
||||
int32_t decode_time_ms,
|
||||
VideoContentType content_type) {
|
||||
receive_stats_callback_->OnDecodedFrame(video_frame, qp, content_type);
|
||||
receive_stats_callback_->OnDecodedFrame(video_frame, qp, decode_time_ms,
|
||||
content_type);
|
||||
incoming_video_stream_->OnFrame(video_frame);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -42,6 +42,7 @@ class VideoStreamDecoder : public VCMReceiveCallback {
|
||||
// Implements VCMReceiveCallback.
|
||||
int32_t FrameToRender(VideoFrame& video_frame,
|
||||
absl::optional<uint8_t> qp,
|
||||
int32_t decode_time_ms,
|
||||
VideoContentType content_type) override;
|
||||
void OnIncomingPayloadType(int payload_type) override;
|
||||
void OnDecoderImplementationName(const char* implementation_name) override;
|
||||
|
||||
@ -277,8 +277,7 @@ void VideoStreamDecoderImpl::Decoded(VideoFrame& decoded_image,
|
||||
absl::optional<int> casted_decode_time_ms(decode_time_ms.value_or(
|
||||
decode_stop_time_ms - frame_timestamps->decode_start_time_ms));
|
||||
|
||||
timing_.StopDecodeTimer(0, *casted_decode_time_ms, decode_stop_time_ms,
|
||||
frame_timestamps->render_time_us / 1000);
|
||||
timing_.StopDecodeTimer(*casted_decode_time_ms, decode_stop_time_ms);
|
||||
|
||||
VideoFrame copy = decoded_image;
|
||||
copy.set_timestamp_us(frame_timestamps->render_time_us);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user