diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h index 0c864cdeb3..a178d54a47 100644 --- a/api/stats/rtcstats_objects.h +++ b/api/stats/rtcstats_objects.h @@ -442,6 +442,7 @@ class RTC_EXPORT RTCInboundRTPStreamStats final : public RTCRTPStreamStats { // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7065 RTCStatsMember gap_discard_rate; RTCStatsMember frames_decoded; + RTCStatsMember key_frames_decoded; // https://henbos.github.io/webrtc-provisional-stats/#dom-rtcinboundrtpstreamstats-contenttype RTCStatsMember content_type; }; @@ -466,6 +467,7 @@ class RTC_EXPORT RTCOutboundRTPStreamStats final : public RTCRTPStreamStats { // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7066 RTCStatsMember target_bitrate; RTCStatsMember frames_encoded; + RTCStatsMember key_frames_encoded; RTCStatsMember total_encode_time; RTCStatsMember total_encoded_bytes_target; // TODO(https://crbug.com/webrtc/10635): This is only implemented for video; diff --git a/media/base/media_channel.h b/media/base/media_channel.h index e98da00adf..8a97022174 100644 --- a/media/base/media_channel.h +++ b/media/base/media_channel.h @@ -564,6 +564,7 @@ struct VideoSenderInfo : public MediaSenderInfo { int avg_encode_ms = 0; int encode_usage_percent = 0; uint32_t frames_encoded = 0; + uint32_t key_frames_encoded = 0; // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-totalencodetime uint64_t total_encode_time_ms = 0; // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-totalencodedbytestarget @@ -597,6 +598,7 @@ struct VideoReceiverInfo : public MediaReceiverInfo { int framerate_render_output = 0; uint32_t frames_received = 0; uint32_t frames_decoded = 0; + uint32_t key_frames_decoded = 0; uint32_t frames_rendered = 0; absl::optional qp_sum; int64_t interframe_delay_max_ms = -1; diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc index b95b40e43a..4561c7f62e 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc @@ -2280,6 +2280,13 @@ VideoSenderInfo WebRtcVideoChannel::WebRtcVideoSendStream::GetVideoSenderInfo( info.avg_encode_ms = stats.avg_encode_time_ms; info.encode_usage_percent = stats.encode_usage_percent; info.frames_encoded = stats.frames_encoded; + // TODO(bugs.webrtc.org/9547): Populate individual outbound-rtp stats objects + // for each simulcast stream, instead of accumulating all keyframes encoded + // over all simulcast streams in the same outbound-rtp stats object. + info.key_frames_encoded = 0; + for (const auto& kv : stats.substreams) { + info.key_frames_encoded += kv.second.frame_counts.key_frames; + } info.total_encode_time_ms = stats.total_encode_time_ms; info.total_encoded_bytes_target = stats.total_encoded_bytes_target; info.qp_sum = stats.qp_sum; @@ -2747,6 +2754,7 @@ WebRtcVideoChannel::WebRtcVideoReceiveStream::GetVideoReceiverInfo( info.frames_received = stats.frame_counts.key_frames + stats.frame_counts.delta_frames; info.frames_decoded = stats.frames_decoded; + info.key_frames_decoded = stats.frame_counts.key_frames; info.frames_rendered = stats.frames_rendered; info.qp_sum = stats.qp_sum; info.last_packet_received_timestamp_ms = diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc index bb989f426a..bedda07535 100644 --- a/media/engine/webrtc_video_engine_unittest.cc +++ b/media/engine/webrtc_video_engine_unittest.cc @@ -4938,6 +4938,21 @@ TEST_F(WebRtcVideoChannelTest, GetStatsReportsFramesEncoded) { EXPECT_EQ(stats.frames_encoded, info.senders[0].frames_encoded); } +TEST_F(WebRtcVideoChannelTest, GetStatsReportsKeyFramesEncoded) { + FakeVideoSendStream* stream = AddSendStream(); + webrtc::VideoSendStream::Stats stats; + stats.substreams[123].frame_counts.key_frames = 10; + stats.substreams[456].frame_counts.key_frames = 87; + stream->SetStats(stats); + + cricket::VideoMediaInfo info; + ASSERT_TRUE(channel_->GetStats(&info)); + // TODO(bugs.webrtc.org/9547): Populate individual outbound-rtp stats objects + // for each simulcast stream, instead of accumulating all keyframes encoded + // over all simulcast streams in the same outbound-rtp stats object. + EXPECT_EQ(97u, info.senders[0].key_frames_encoded); +} + TEST_F(WebRtcVideoChannelTest, GetStatsReportsQpSum) { FakeVideoSendStream* stream = AddSendStream(); webrtc::VideoSendStream::Stats stats; @@ -5095,6 +5110,8 @@ TEST_F(WebRtcVideoChannelTest, GetStatsTranslatesDecodeStatsCorrectly) { info.receivers[0].frames_received); EXPECT_EQ(stats.frames_rendered, info.receivers[0].frames_rendered); EXPECT_EQ(stats.frames_decoded, info.receivers[0].frames_decoded); + EXPECT_EQ(rtc::checked_cast(stats.frame_counts.key_frames), + info.receivers[0].key_frames_decoded); EXPECT_EQ(stats.qp_sum, info.receivers[0].qp_sum); } diff --git a/pc/peer_connection_integrationtest.cc b/pc/peer_connection_integrationtest.cc index c368ec0396..42d6c51e66 100644 --- a/pc/peer_connection_integrationtest.cc +++ b/pc/peer_connection_integrationtest.cc @@ -2708,6 +2708,12 @@ TEST_P(PeerConnectionIntegrationTest, NewGetStatsManyAudioAndManyVideoStreams) { for (const auto& stat : outbound_stream_stats) { ASSERT_TRUE(stat->bytes_sent.is_defined()); EXPECT_LT(0u, *stat->bytes_sent); + if (*stat->kind == "video") { + ASSERT_TRUE(stat->key_frames_encoded.is_defined()); + EXPECT_GT(*stat->key_frames_encoded, 0u); + ASSERT_TRUE(stat->frames_encoded.is_defined()); + EXPECT_GE(*stat->frames_encoded, *stat->key_frames_encoded); + } ASSERT_TRUE(stat->track_id.is_defined()); const auto* track_stat = caller_report->GetAs(*stat->track_id); @@ -2726,6 +2732,12 @@ TEST_P(PeerConnectionIntegrationTest, NewGetStatsManyAudioAndManyVideoStreams) { for (const auto& stat : inbound_stream_stats) { ASSERT_TRUE(stat->bytes_received.is_defined()); EXPECT_LT(0u, *stat->bytes_received); + if (*stat->kind == "video") { + ASSERT_TRUE(stat->key_frames_decoded.is_defined()); + EXPECT_GT(*stat->key_frames_decoded, 0u); + ASSERT_TRUE(stat->frames_decoded.is_defined()); + EXPECT_GE(*stat->frames_decoded, *stat->key_frames_decoded); + } ASSERT_TRUE(stat->track_id.is_defined()); const auto* track_stat = callee_report->GetAs(*stat->track_id); diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc index 5e0986d12e..fb85092221 100644 --- a/pc/rtc_stats_collector.cc +++ b/pc/rtc_stats_collector.cc @@ -309,6 +309,7 @@ void SetInboundRTPStreamStatsFromVideoReceiverInfo( inbound_video->nack_count = static_cast(video_receiver_info.nacks_sent); inbound_video->frames_decoded = video_receiver_info.frames_decoded; + 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; if (video_receiver_info.last_packet_received_timestamp_ms) { @@ -378,6 +379,7 @@ void SetOutboundRTPStreamStatsFromVideoSenderInfo( if (video_sender_info.qp_sum) outbound_video->qp_sum = *video_sender_info.qp_sum; outbound_video->frames_encoded = video_sender_info.frames_encoded; + outbound_video->key_frames_encoded = video_sender_info.key_frames_encoded; outbound_video->total_encode_time = static_cast(video_sender_info.total_encode_time_ms) / rtc::kNumMillisecsPerSec; diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc index 11500d6081..2f0e78bba5 100644 --- a/pc/rtc_stats_collector_unittest.cc +++ b/pc/rtc_stats_collector_unittest.cc @@ -1801,6 +1801,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { video_media_info.receivers[0].plis_sent = 6; video_media_info.receivers[0].nacks_sent = 7; 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].last_packet_received_timestamp_ms = absl::nullopt; @@ -1837,6 +1838,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { expected_video.bytes_received = 3; expected_video.packets_lost = 42; expected_video.frames_decoded = 8; + expected_video.key_frames_decoded = 3; // |expected_video.qp_sum| should be undefined. // |expected_video.last_packet_received_timestamp| should be undefined. // |expected_video.content_type| should be undefined. @@ -1938,6 +1940,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) { video_media_info.senders[0].retransmitted_bytes_sent = 60; video_media_info.senders[0].codec_payload_type = 42; video_media_info.senders[0].frames_encoded = 8; + video_media_info.senders[0].key_frames_encoded = 3; video_media_info.senders[0].total_encode_time_ms = 9000; video_media_info.senders[0].total_encoded_bytes_target = 1234; video_media_info.senders[0].total_packet_send_delay_ms = 10000; @@ -1985,6 +1988,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) { expected_video.bytes_sent = 6; expected_video.retransmitted_bytes_sent = 60; expected_video.frames_encoded = 8; + expected_video.key_frames_encoded = 3; expected_video.total_encode_time = 9.0; expected_video.total_encoded_bytes_target = 1234; expected_video.total_packet_send_delay = 10.0; diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc index 4f10a1d085..79d6faf152 100644 --- a/pc/rtc_stats_integrationtest.cc +++ b/pc/rtc_stats_integrationtest.cc @@ -804,11 +804,13 @@ class RTCStatsReportVerifier { if (inbound_stream.media_type.is_defined() && *inbound_stream.media_type == "video") { verifier.TestMemberIsDefined(inbound_stream.frames_decoded); + verifier.TestMemberIsDefined(inbound_stream.key_frames_decoded); // 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.content_type); } return verifier.ExpectAllMembersSuccessfullyTested(); @@ -838,6 +840,7 @@ class RTCStatsReportVerifier { if (outbound_stream.media_type.is_defined() && *outbound_stream.media_type == "video") { verifier.TestMemberIsDefined(outbound_stream.frames_encoded); + verifier.TestMemberIsDefined(outbound_stream.key_frames_encoded); verifier.TestMemberIsNonNegative( outbound_stream.total_encode_time); verifier.TestMemberIsNonNegative( @@ -850,6 +853,7 @@ class RTCStatsReportVerifier { verifier.MarkMemberTested(outbound_stream.content_type, true); } else { verifier.TestMemberIsUndefined(outbound_stream.frames_encoded); + verifier.TestMemberIsUndefined(outbound_stream.key_frames_encoded); verifier.TestMemberIsUndefined(outbound_stream.total_encode_time); verifier.TestMemberIsUndefined( outbound_stream.total_encoded_bytes_target); diff --git a/stats/rtcstats_objects.cc b/stats/rtcstats_objects.cc index 43c1d231e3..2b62990e1d 100644 --- a/stats/rtcstats_objects.cc +++ b/stats/rtcstats_objects.cc @@ -614,6 +614,7 @@ WEBRTC_RTCSTATS_IMPL( &gap_loss_rate, &gap_discard_rate, &frames_decoded, + &key_frames_decoded, &content_type) // clang-format on @@ -643,6 +644,7 @@ RTCInboundRTPStreamStats::RTCInboundRTPStreamStats(std::string&& id, gap_loss_rate("gapLossRate"), gap_discard_rate("gapDiscardRate"), frames_decoded("framesDecoded"), + key_frames_decoded("keyFramesDecoded"), content_type("contentType") {} RTCInboundRTPStreamStats::RTCInboundRTPStreamStats( @@ -667,6 +669,7 @@ RTCInboundRTPStreamStats::RTCInboundRTPStreamStats( gap_loss_rate(other.gap_loss_rate), gap_discard_rate(other.gap_discard_rate), frames_decoded(other.frames_decoded), + key_frames_decoded(other.key_frames_decoded), content_type(other.content_type) {} RTCInboundRTPStreamStats::~RTCInboundRTPStreamStats() {} @@ -681,6 +684,7 @@ WEBRTC_RTCSTATS_IMPL( &retransmitted_bytes_sent, &target_bitrate, &frames_encoded, + &key_frames_encoded, &total_encode_time, &total_encoded_bytes_target, &total_packet_send_delay, @@ -702,6 +706,7 @@ RTCOutboundRTPStreamStats::RTCOutboundRTPStreamStats(std::string&& id, retransmitted_bytes_sent("retransmittedBytesSent"), target_bitrate("targetBitrate"), frames_encoded("framesEncoded"), + key_frames_encoded("keyFramesEncoded"), total_encode_time("totalEncodeTime"), total_encoded_bytes_target("totalEncodedBytesTarget"), total_packet_send_delay("totalPacketSendDelay"), @@ -718,6 +723,7 @@ RTCOutboundRTPStreamStats::RTCOutboundRTPStreamStats( retransmitted_bytes_sent(other.retransmitted_bytes_sent), target_bitrate(other.target_bitrate), frames_encoded(other.frames_encoded), + key_frames_encoded(other.key_frames_encoded), total_encode_time(other.total_encode_time), total_encoded_bytes_target(other.total_encoded_bytes_target), total_packet_send_delay(other.total_packet_send_delay),