Reland "Improve outbound-rtp statistics for simulcast" This reverts commit 9a925c9ce33a6ccdd11b545b11ba68e985c2a65d. Reason for revert: The original CL is updated in PS #2 to fix the googRtt issue which was that when the legacy sender stats were put in "aggregated_senders" we forgot to update rtt_ms the same way that we do it for "senders". Original change's description: > Revert "Improve outbound-rtp statistics for simulcast" > > This reverts commit da6cda839dac7d9d18eba8d365188fa94831e0b1. > > Reason for revert: Breaks googRtt in legacy getStats API > > Original change's description: > > Improve outbound-rtp statistics for simulcast > > > > Bug: webrtc:9547 > > Change-Id: Iec4eb976aa11ee743805425bedb77dcea7c2c9be > > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/168120 > > Reviewed-by: Sebastian Jansson <srte@webrtc.org> > > Reviewed-by: Erik Språng <sprang@webrtc.org> > > Reviewed-by: Henrik Boström <hbos@webrtc.org> > > Reviewed-by: Harald Alvestrand <hta@webrtc.org> > > Commit-Queue: Eldar Rello <elrello@microsoft.com> > > Cr-Commit-Position: refs/heads/master@{#31097} > > TBR=hbos@webrtc.org,sprang@webrtc.org,stefan@webrtc.org,srte@webrtc.org,hta@webrtc.org,elrello@microsoft.com > > # Not skipping CQ checks because original CL landed > 1 day ago. > > Bug: webrtc:9547 > Change-Id: I06673328c2a5293a7eef03b3aaf2ded9d13df1b3 > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/174443 > Reviewed-by: Henrik Boström <hbos@webrtc.org> > Commit-Queue: Henrik Boström <hbos@webrtc.org> > Cr-Commit-Position: refs/heads/master@{#31165} TBR=hbos@webrtc.org,sprang@webrtc.org,stefan@webrtc.org,srte@webrtc.org,hta@webrtc.org,elrello@microsoft.com # Not skipping CQ checks because this is a reland. Bug: webrtc:9547 Change-Id: I723744c496c3c65f95ab6a8940862c8b9f544338 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/174480 Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Henrik Boström <hbos@webrtc.org> Commit-Queue: Henrik Boström <hbos@webrtc.org> Cr-Commit-Position: refs/heads/master@{#31169}
diff --git a/api/peer_connection_interface.h b/api/peer_connection_interface.h index 0ae47b2..1d81de7 100644 --- a/api/peer_connection_interface.h +++ b/api/peer_connection_interface.h
@@ -666,6 +666,8 @@ // Whether network condition based codec switching is allowed. absl::optional<bool> allow_codec_switching; + bool enable_simulcast_stats = true; + // // Don't forget to update operator== if adding something. //
diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h index e83c83d..28d841d 100644 --- a/api/stats/rtcstats_objects.h +++ b/api/stats/rtcstats_objects.h
@@ -469,6 +469,7 @@ RTCStatsMember<std::string> media_source_id; RTCStatsMember<std::string> remote_id; + RTCStatsMember<std::string> rid; RTCStatsMember<uint32_t> packets_sent; RTCStatsMember<uint64_t> retransmitted_packets_sent; RTCStatsMember<uint64_t> bytes_sent; @@ -480,6 +481,11 @@ RTCStatsMember<uint32_t> key_frames_encoded; RTCStatsMember<double> total_encode_time; RTCStatsMember<uint64_t> total_encoded_bytes_target; + RTCStatsMember<uint32_t> frame_width; + RTCStatsMember<uint32_t> frame_height; + RTCStatsMember<double> frames_per_second; + RTCStatsMember<uint32_t> frames_sent; + RTCStatsMember<uint32_t> huge_frames_sent; // TODO(https://crbug.com/webrtc/10635): This is only implemented for video; // implement it for audio as well. RTCStatsMember<double> total_packet_send_delay;
diff --git a/call/rtp_config.cc b/call/rtp_config.cc index d1db867..c84a63e 100644 --- a/call/rtp_config.cc +++ b/call/rtp_config.cc
@@ -189,4 +189,15 @@ return media_ssrc; } +absl::optional<std::string> RtpConfig::GetRidForSsrc(uint32_t ssrc) const { + auto it = std::find(ssrcs.begin(), ssrcs.end(), ssrc); + if (it != ssrcs.end()) { + size_t ssrc_index = std::distance(ssrcs.begin(), it); + if (ssrc_index < rids.size()) { + return rids[ssrc_index]; + } + } + return absl::nullopt; +} + } // namespace webrtc
diff --git a/call/rtp_config.h b/call/rtp_config.h index d9caeb0..2986449 100644 --- a/call/rtp_config.h +++ b/call/rtp_config.h
@@ -166,6 +166,7 @@ uint32_t media_ssrc) const; uint32_t GetMediaSsrcAssociatedWithRtxSsrc(uint32_t rtx_ssrc) const; uint32_t GetMediaSsrcAssociatedWithFlexfecSsrc(uint32_t flexfec_ssrc) const; + absl::optional<std::string> GetRidForSsrc(uint32_t ssrc) const; }; } // namespace webrtc #endif // CALL_RTP_CONFIG_H_
diff --git a/call/video_send_stream.h b/call/video_send_stream.h index 86f3102..392c955 100644 --- a/call/video_send_stream.h +++ b/call/video_send_stream.h
@@ -83,6 +83,12 @@ // A snapshot of the most recent Report Block with additional data of // interest to statistics. Used to implement RTCRemoteInboundRtpStreamStats. absl::optional<ReportBlockData> report_block_data; + double encode_frame_rate = 0.0; + int frames_encoded = 0; + absl::optional<uint64_t> qp_sum; + uint64_t total_encode_time_ms = 0; + uint64_t total_encoded_bytes_target = 0; + uint32_t huge_frames_sent = 0; }; struct Stats { @@ -104,7 +110,6 @@ uint32_t frames_dropped_by_rate_limiter = 0; uint32_t frames_dropped_by_congestion_window = 0; uint32_t frames_dropped_by_encoder = 0; - absl::optional<uint64_t> qp_sum; // Bitrate the encoder is currently configured to use due to bandwidth // limitations. int target_media_bitrate_bps = 0; @@ -130,6 +135,7 @@ std::map<uint32_t, StreamStats> substreams; webrtc::VideoContentType content_type = webrtc::VideoContentType::UNSPECIFIED; + uint32_t frames_sent = 0; uint32_t huge_frames_sent = 0; };
diff --git a/media/base/media_channel.h b/media/base/media_channel.h index 4758cf5..d71ec91 100644 --- a/media/base/media_channel.h +++ b/media/base/media_channel.h
@@ -569,6 +569,7 @@ int send_frame_height = 0; int framerate_input = 0; int framerate_sent = 0; + int aggregated_framerate_sent = 0; int nominal_bitrate = 0; int adapt_reason = 0; int adapt_changes = 0; @@ -592,8 +593,11 @@ bool has_entered_low_resolution = false; absl::optional<uint64_t> qp_sum; webrtc::VideoContentType content_type = webrtc::VideoContentType::UNSPECIFIED; + uint32_t frames_sent = 0; // https://w3c.github.io/webrtc-stats/#dom-rtcvideosenderstats-hugeframessent uint32_t huge_frames_sent = 0; + uint32_t aggregated_huge_frames_sent = 0; + absl::optional<std::string> rid; }; struct VideoReceiverInfo : public MediaReceiverInfo { @@ -713,11 +717,20 @@ ~VideoMediaInfo(); void Clear() { senders.clear(); + aggregated_senders.clear(); receivers.clear(); send_codecs.clear(); receive_codecs.clear(); } + // Each sender info represents one "outbound-rtp" stream.In non - simulcast, + // this means one info per RtpSender but if simulcast is used this means + // one info per simulcast layer. std::vector<VideoSenderInfo> senders; + // Used for legacy getStats() API's "ssrc" stats and modern getStats() API's + // "track" stats. If simulcast is used, instead of having one sender info per + // simulcast layer, the metrics of all layers of an RtpSender are aggregated + // into a single sender info per RtpSender. + std::vector<VideoSenderInfo> aggregated_senders; std::vector<VideoReceiverInfo> receivers; RtpCodecParametersMap send_codecs; RtpCodecParametersMap receive_codecs;
diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc index 092fb67..71a0939 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc
@@ -1570,6 +1570,9 @@ for (size_t i = 0; i < info->senders.size(); ++i) { info->senders[i].rtt_ms = stats.rtt_ms; } + for (size_t i = 0; i < info->aggregated_senders.size(); ++i) { + info->aggregated_senders[i].rtt_ms = stats.rtt_ms; + } } if (log_stats) @@ -1583,8 +1586,12 @@ for (std::map<uint32_t, WebRtcVideoSendStream*>::iterator it = send_streams_.begin(); it != send_streams_.end(); ++it) { - video_media_info->senders.push_back( - it->second->GetVideoSenderInfo(log_stats)); + auto infos = it->second->GetPerLayerVideoSenderInfos(log_stats); + video_media_info->aggregated_senders.push_back( + it->second->GetAggregatedVideoSenderInfo(infos)); + for (auto&& info : infos) { + video_media_info->senders.push_back(info); + } } } @@ -2474,108 +2481,161 @@ }); } } - -VideoSenderInfo WebRtcVideoChannel::WebRtcVideoSendStream::GetVideoSenderInfo( +std::vector<VideoSenderInfo> +WebRtcVideoChannel::WebRtcVideoSendStream::GetPerLayerVideoSenderInfos( bool log_stats) { - VideoSenderInfo info; RTC_DCHECK_RUN_ON(&thread_checker_); - for (uint32_t ssrc : parameters_.config.rtp.ssrcs) - info.add_ssrc(ssrc); - + VideoSenderInfo common_info; if (parameters_.codec_settings) { - info.codec_name = parameters_.codec_settings->codec.name; - info.codec_payload_type = parameters_.codec_settings->codec.id; + common_info.codec_name = parameters_.codec_settings->codec.name; + common_info.codec_payload_type = parameters_.codec_settings->codec.id; } + std::vector<VideoSenderInfo> infos; + webrtc::VideoSendStream::Stats stats; + if (stream_ == nullptr) { + for (uint32_t ssrc : parameters_.config.rtp.ssrcs) { + common_info.add_ssrc(ssrc); + } + infos.push_back(common_info); + return infos; + } else { + stats = stream_->GetStats(); + if (log_stats) + RTC_LOG(LS_INFO) << stats.ToString(rtc::TimeMillis()); - if (stream_ == NULL) - return info; + // Metrics that are in common for all substreams. + common_info.adapt_changes = stats.number_of_cpu_adapt_changes; + common_info.adapt_reason = + stats.cpu_limited_resolution ? ADAPTREASON_CPU : ADAPTREASON_NONE; + common_info.has_entered_low_resolution = stats.has_entered_low_resolution; - webrtc::VideoSendStream::Stats stats = stream_->GetStats(); + // Get bandwidth limitation info from stream_->GetStats(). + // Input resolution (output from video_adapter) can be further scaled down + // or higher video layer(s) can be dropped due to bitrate constraints. + // Note, adapt_changes only include changes from the video_adapter. + if (stats.bw_limited_resolution) + common_info.adapt_reason |= ADAPTREASON_BANDWIDTH; - if (log_stats) - RTC_LOG(LS_INFO) << stats.ToString(rtc::TimeMillis()); + common_info.quality_limitation_reason = stats.quality_limitation_reason; + common_info.quality_limitation_durations_ms = + stats.quality_limitation_durations_ms; + common_info.quality_limitation_resolution_changes = + stats.quality_limitation_resolution_changes; + common_info.encoder_implementation_name = stats.encoder_implementation_name; + common_info.ssrc_groups = ssrc_groups_; + common_info.framerate_input = stats.input_frame_rate; + common_info.avg_encode_ms = stats.avg_encode_time_ms; + common_info.encode_usage_percent = stats.encode_usage_percent; + common_info.nominal_bitrate = stats.media_bitrate_bps; + common_info.content_type = stats.content_type; + common_info.aggregated_framerate_sent = stats.encode_frame_rate; + common_info.aggregated_huge_frames_sent = stats.huge_frames_sent; - info.adapt_changes = stats.number_of_cpu_adapt_changes; - info.adapt_reason = - stats.cpu_limited_resolution ? ADAPTREASON_CPU : ADAPTREASON_NONE; - info.has_entered_low_resolution = stats.has_entered_low_resolution; - - // Get bandwidth limitation info from stream_->GetStats(). - // Input resolution (output from video_adapter) can be further scaled down or - // higher video layer(s) can be dropped due to bitrate constraints. - // Note, adapt_changes only include changes from the video_adapter. - if (stats.bw_limited_resolution) - info.adapt_reason |= ADAPTREASON_BANDWIDTH; - - info.quality_limitation_reason = stats.quality_limitation_reason; - info.quality_limitation_durations_ms = stats.quality_limitation_durations_ms; - info.quality_limitation_resolution_changes = - stats.quality_limitation_resolution_changes; - info.encoder_implementation_name = stats.encoder_implementation_name; - info.ssrc_groups = ssrc_groups_; - info.framerate_input = stats.input_frame_rate; - info.framerate_sent = stats.encode_frame_rate; - 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; + // If we don't have any substreams, get the remaining metrics from |stats|. + // Otherwise, these values are obtained from |sub_stream| below. + if (stats.substreams.empty()) { + for (uint32_t ssrc : parameters_.config.rtp.ssrcs) { + common_info.add_ssrc(ssrc); + } + common_info.framerate_sent = stats.encode_frame_rate; + common_info.frames_encoded = stats.frames_encoded; + common_info.total_encode_time_ms = stats.total_encode_time_ms; + common_info.total_encoded_bytes_target = stats.total_encoded_bytes_target; + common_info.frames_sent = stats.frames_encoded; + common_info.huge_frames_sent = stats.huge_frames_sent; + infos.push_back(common_info); + return infos; + } } - 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; - - info.nominal_bitrate = stats.media_bitrate_bps; - - info.content_type = stats.content_type; - info.huge_frames_sent = stats.huge_frames_sent; - - info.send_frame_width = 0; - info.send_frame_height = 0; - info.total_packet_send_delay_ms = 0; - std::map<uint32_t, webrtc::VideoSendStream::StreamStats> - outbound_rtp_substreams = - MergeInfoAboutOutboundRtpSubstreams(stats.substreams); + auto outbound_rtp_substreams = + MergeInfoAboutOutboundRtpSubstreams(stats.substreams); for (const auto& pair : outbound_rtp_substreams) { - // TODO(pbos): Wire up additional stats, such as padding bytes. - const webrtc::VideoSendStream::StreamStats& stream_stats = pair.second; + auto info = common_info; + info.add_ssrc(pair.first); + info.rid = parameters_.config.rtp.GetRidForSsrc(pair.first); + auto stream_stats = pair.second; RTC_DCHECK_EQ(stream_stats.type, webrtc::VideoSendStream::StreamStats::StreamType::kMedia); - info.payload_bytes_sent += stream_stats.rtp_stats.transmitted.payload_bytes; - info.header_and_padding_bytes_sent += + info.payload_bytes_sent = stream_stats.rtp_stats.transmitted.payload_bytes; + info.header_and_padding_bytes_sent = stream_stats.rtp_stats.transmitted.header_bytes + stream_stats.rtp_stats.transmitted.padding_bytes; - info.packets_sent += stream_stats.rtp_stats.transmitted.packets; + info.packets_sent = stream_stats.rtp_stats.transmitted.packets; info.total_packet_send_delay_ms += stream_stats.total_packet_send_delay_ms; - info.retransmitted_bytes_sent += + info.send_frame_width = stream_stats.width; + info.send_frame_height = stream_stats.height; + info.key_frames_encoded = stream_stats.frame_counts.key_frames; + info.framerate_sent = stream_stats.encode_frame_rate; + info.frames_encoded = stream_stats.frames_encoded; + info.frames_sent = stream_stats.frames_encoded; + info.retransmitted_bytes_sent = stream_stats.rtp_stats.retransmitted.payload_bytes; - info.retransmitted_packets_sent += + info.retransmitted_packets_sent = stream_stats.rtp_stats.retransmitted.packets; - info.packets_lost += stream_stats.rtcp_stats.packets_lost; - if (stream_stats.width > info.send_frame_width) - info.send_frame_width = stream_stats.width; - if (stream_stats.height > info.send_frame_height) - info.send_frame_height = stream_stats.height; - info.firs_rcvd += stream_stats.rtcp_packet_type_counts.fir_packets; - info.nacks_rcvd += stream_stats.rtcp_packet_type_counts.nack_packets; - info.plis_rcvd += stream_stats.rtcp_packet_type_counts.pli_packets; + info.packets_lost = stream_stats.rtcp_stats.packets_lost; + info.firs_rcvd = stream_stats.rtcp_packet_type_counts.fir_packets; + info.nacks_rcvd = stream_stats.rtcp_packet_type_counts.nack_packets; + info.plis_rcvd = stream_stats.rtcp_packet_type_counts.pli_packets; if (stream_stats.report_block_data.has_value()) { info.report_block_datas.push_back(stream_stats.report_block_data.value()); } - } - if (!stats.substreams.empty()) { - // TODO(pbos): Report fraction lost per SSRC. - webrtc::VideoSendStream::StreamStats first_stream_stats = - stats.substreams.begin()->second; info.fraction_lost = - static_cast<float>(first_stream_stats.rtcp_stats.fraction_lost) / - (1 << 8); + static_cast<float>(stream_stats.rtcp_stats.fraction_lost) / (1 << 8); + info.qp_sum = stream_stats.qp_sum; + info.total_encode_time_ms = stream_stats.total_encode_time_ms; + info.total_encoded_bytes_target = stream_stats.total_encoded_bytes_target; + info.huge_frames_sent = stream_stats.huge_frames_sent; + infos.push_back(info); } + return infos; +} +VideoSenderInfo +WebRtcVideoChannel::WebRtcVideoSendStream::GetAggregatedVideoSenderInfo( + const std::vector<VideoSenderInfo>& infos) const { + RTC_DCHECK_RUN_ON(&thread_checker_); + RTC_DCHECK(!infos.empty()); + if (infos.size() == 1) { + return infos[0]; + } + VideoSenderInfo info = infos[0]; + info.local_stats.clear(); + for (uint32_t ssrc : parameters_.config.rtp.ssrcs) { + info.add_ssrc(ssrc); + } + info.framerate_sent = info.aggregated_framerate_sent; + info.huge_frames_sent = info.aggregated_huge_frames_sent; + + for (size_t i = 1; i < infos.size(); i++) { + info.key_frames_encoded += infos[i].key_frames_encoded; + info.payload_bytes_sent += infos[i].payload_bytes_sent; + info.header_and_padding_bytes_sent += + infos[i].header_and_padding_bytes_sent; + info.packets_sent += infos[i].packets_sent; + info.total_packet_send_delay_ms += infos[i].total_packet_send_delay_ms; + info.retransmitted_bytes_sent += infos[i].retransmitted_bytes_sent; + info.retransmitted_packets_sent += infos[i].retransmitted_packets_sent; + info.packets_lost += infos[i].packets_lost; + if (infos[i].send_frame_width > info.send_frame_width) + info.send_frame_width = infos[i].send_frame_width; + if (infos[i].send_frame_height > info.send_frame_height) + info.send_frame_height = infos[i].send_frame_height; + info.firs_rcvd += infos[i].firs_rcvd; + info.nacks_rcvd += infos[i].nacks_rcvd; + info.plis_rcvd += infos[i].plis_rcvd; + if (infos[i].report_block_datas.size()) + info.report_block_datas.push_back(infos[i].report_block_datas[0]); + if (infos[i].qp_sum) { + if (!info.qp_sum) { + info.qp_sum = 0; + } + info.qp_sum = *info.qp_sum + *infos[i].qp_sum; + } + info.frames_encoded += infos[i].frames_encoded; + info.frames_sent += infos[i].frames_sent; + info.total_encode_time_ms += infos[i].total_encode_time_ms; + info.total_encoded_bytes_target += infos[i].total_encoded_bytes_target; + } return info; }
diff --git a/media/engine/webrtc_video_engine.h b/media/engine/webrtc_video_engine.h index 6ed556e..00d2495 100644 --- a/media/engine/webrtc_video_engine.h +++ b/media/engine/webrtc_video_engine.h
@@ -357,7 +357,12 @@ void SetSend(bool send); const std::vector<uint32_t>& GetSsrcs() const; - VideoSenderInfo GetVideoSenderInfo(bool log_stats); + // Returns per ssrc VideoSenderInfos. Useful for simulcast scenario. + std::vector<VideoSenderInfo> GetPerLayerVideoSenderInfos(bool log_stats); + // Aggregates per ssrc VideoSenderInfos to single VideoSenderInfo for + // legacy reasons. Used in old GetStats API and track stats. + VideoSenderInfo GetAggregatedVideoSenderInfo( + const std::vector<VideoSenderInfo>& infos) const; void FillBitrateInfo(BandwidthEstimationInfo* bwe_info); void SetEncoderToPacketizerFrameTransformer(
diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc index 27206db..4a33c51 100644 --- a/media/engine/webrtc_video_engine_unittest.cc +++ b/media/engine/webrtc_video_engine_unittest.cc
@@ -2425,6 +2425,18 @@ ASSERT_TRUE(channel_->SetSendParameters(send_parameters_)); } + cricket::VideoCodec GetEngineCodec(const std::string& name) { + for (const cricket::VideoCodec& engine_codec : engine_.send_codecs()) { + if (absl::EqualsIgnoreCase(name, engine_codec.name)) + return engine_codec; + } + // This point should never be reached. + ADD_FAILURE() << "Unrecognized codec name: " << name; + return cricket::VideoCodec(); + } + + cricket::VideoCodec DefaultCodec() { return GetEngineCodec("VP8"); } + protected: FakeVideoSendStream* AddSendStream() { return AddSendStream(StreamParams::CreateLegacy(++last_ssrc_)); @@ -5218,21 +5230,369 @@ 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); + EXPECT_EQ(info.senders.size(), 2u); + EXPECT_EQ(10u, info.senders[0].key_frames_encoded); + EXPECT_EQ(87u, info.senders[1].key_frames_encoded); + EXPECT_EQ(97u, info.aggregated_senders[0].key_frames_encoded); } -TEST_F(WebRtcVideoChannelTest, GetStatsReportsQpSum) { +TEST_F(WebRtcVideoChannelTest, GetStatsReportsPerLayerQpSum) { FakeVideoSendStream* stream = AddSendStream(); webrtc::VideoSendStream::Stats stats; - stats.qp_sum = 13; + stats.substreams[123].qp_sum = 15; + stats.substreams[456].qp_sum = 11; stream->SetStats(stats); cricket::VideoMediaInfo info; ASSERT_TRUE(channel_->GetStats(&info)); - EXPECT_EQ(stats.qp_sum, info.senders[0].qp_sum); + EXPECT_EQ(info.senders.size(), 2u); + EXPECT_EQ(stats.substreams[123].qp_sum, info.senders[0].qp_sum); + EXPECT_EQ(stats.substreams[456].qp_sum, info.senders[1].qp_sum); + EXPECT_EQ(*info.aggregated_senders[0].qp_sum, 26u); +} + +webrtc::VideoSendStream::Stats GetInitialisedStats() { + webrtc::VideoSendStream::Stats stats; + stats.encoder_implementation_name = "vp"; + stats.input_frame_rate = 1; + stats.encode_frame_rate = 2; + stats.avg_encode_time_ms = 3; + stats.encode_usage_percent = 4; + stats.frames_encoded = 5; + stats.total_encode_time_ms = 6; + stats.frames_dropped_by_capturer = 7; + stats.frames_dropped_by_encoder_queue = 8; + stats.frames_dropped_by_rate_limiter = 9; + stats.frames_dropped_by_congestion_window = 10; + stats.frames_dropped_by_encoder = 11; + stats.target_media_bitrate_bps = 13; + stats.media_bitrate_bps = 14; + stats.suspended = true; + stats.bw_limited_resolution = true; + stats.cpu_limited_resolution = true; + // Not wired. + stats.bw_limited_framerate = true; + // Not wired. + stats.cpu_limited_framerate = true; + stats.quality_limitation_reason = webrtc::QualityLimitationReason::kCpu; + stats.quality_limitation_durations_ms[webrtc::QualityLimitationReason::kCpu] = + 15; + stats.quality_limitation_resolution_changes = 16; + stats.number_of_cpu_adapt_changes = 17; + stats.number_of_quality_adapt_changes = 18; + stats.has_entered_low_resolution = true; + stats.content_type = webrtc::VideoContentType::SCREENSHARE; + stats.frames_sent = 19; + stats.huge_frames_sent = 20; + + return stats; +} + +TEST_F(WebRtcVideoChannelTest, GetAggregatedStatsReportWithoutSubStreams) { + FakeVideoSendStream* stream = AddSendStream(); + auto stats = GetInitialisedStats(); + stream->SetStats(stats); + cricket::VideoMediaInfo video_media_info; + ASSERT_TRUE(channel_->GetStats(&video_media_info)); + EXPECT_EQ(video_media_info.aggregated_senders.size(), 1u); + auto& sender = video_media_info.aggregated_senders[0]; + + // MediaSenderInfo + + EXPECT_EQ(sender.payload_bytes_sent, 0); + EXPECT_EQ(sender.header_and_padding_bytes_sent, 0); + EXPECT_EQ(sender.retransmitted_bytes_sent, 0u); + EXPECT_EQ(sender.packets_sent, 0); + EXPECT_EQ(sender.retransmitted_packets_sent, 0u); + EXPECT_EQ(sender.packets_lost, 0); + EXPECT_EQ(sender.fraction_lost, 0.0f); + EXPECT_EQ(sender.rtt_ms, 0); + EXPECT_EQ(sender.codec_name, DefaultCodec().name); + EXPECT_EQ(sender.codec_payload_type, DefaultCodec().id); + EXPECT_EQ(sender.local_stats.size(), 1u); + EXPECT_EQ(sender.local_stats[0].ssrc, last_ssrc_); + EXPECT_EQ(sender.local_stats[0].timestamp, 0.0f); + EXPECT_EQ(sender.remote_stats.size(), 0u); + EXPECT_EQ(sender.report_block_datas.size(), 0u); + + // VideoSenderInfo + + EXPECT_EQ(sender.ssrc_groups.size(), 0u); + EXPECT_EQ(sender.encoder_implementation_name, + stats.encoder_implementation_name); + // Comes from substream only. + EXPECT_EQ(sender.firs_rcvd, 0); + EXPECT_EQ(sender.plis_rcvd, 0); + EXPECT_EQ(sender.nacks_rcvd, 0); + EXPECT_EQ(sender.send_frame_width, 0); + EXPECT_EQ(sender.send_frame_height, 0); + + EXPECT_EQ(sender.framerate_input, stats.input_frame_rate); + EXPECT_EQ(sender.framerate_sent, stats.encode_frame_rate); + EXPECT_EQ(sender.nominal_bitrate, stats.media_bitrate_bps); + EXPECT_NE(sender.adapt_reason & WebRtcVideoChannel::ADAPTREASON_CPU, 0); + EXPECT_NE(sender.adapt_reason & WebRtcVideoChannel::ADAPTREASON_BANDWIDTH, 0); + EXPECT_EQ(sender.adapt_changes, stats.number_of_cpu_adapt_changes); + EXPECT_EQ(sender.quality_limitation_reason, stats.quality_limitation_reason); + EXPECT_EQ(sender.quality_limitation_durations_ms, + stats.quality_limitation_durations_ms); + EXPECT_EQ(sender.quality_limitation_resolution_changes, + stats.quality_limitation_resolution_changes); + EXPECT_EQ(sender.avg_encode_ms, stats.avg_encode_time_ms); + EXPECT_EQ(sender.encode_usage_percent, stats.encode_usage_percent); + EXPECT_EQ(sender.frames_encoded, stats.frames_encoded); + // Comes from substream only. + EXPECT_EQ(sender.key_frames_encoded, 0u); + + EXPECT_EQ(sender.total_encode_time_ms, stats.total_encode_time_ms); + EXPECT_EQ(sender.total_encoded_bytes_target, + stats.total_encoded_bytes_target); + // Comes from substream only. + EXPECT_EQ(sender.total_packet_send_delay_ms, 0u); + EXPECT_EQ(sender.qp_sum, absl::nullopt); + + EXPECT_EQ(sender.has_entered_low_resolution, + stats.has_entered_low_resolution); + EXPECT_EQ(sender.content_type, webrtc::VideoContentType::SCREENSHARE); + EXPECT_EQ(sender.frames_sent, stats.frames_encoded); + EXPECT_EQ(sender.huge_frames_sent, stats.huge_frames_sent); + EXPECT_EQ(sender.rid, absl::nullopt); +} + +TEST_F(WebRtcVideoChannelTest, GetAggregatedStatsReportForSubStreams) { + FakeVideoSendStream* stream = AddSendStream(); + auto stats = GetInitialisedStats(); + + const uint32_t ssrc_1 = 123u; + const uint32_t ssrc_2 = 456u; + + auto& substream = stats.substreams[ssrc_1]; + substream.frame_counts.key_frames = 1; + substream.frame_counts.delta_frames = 2; + substream.width = 3; + substream.height = 4; + substream.total_bitrate_bps = 5; + substream.retransmit_bitrate_bps = 6; + substream.avg_delay_ms = 7; + substream.max_delay_ms = 8; + substream.total_packet_send_delay_ms = 9; + substream.rtp_stats.transmitted.header_bytes = 10; + substream.rtp_stats.transmitted.padding_bytes = 11; + substream.rtp_stats.retransmitted.payload_bytes = 12; + substream.rtp_stats.retransmitted.packets = 13; + substream.rtcp_packet_type_counts.fir_packets = 14; + substream.rtcp_packet_type_counts.nack_packets = 15; + substream.rtcp_packet_type_counts.pli_packets = 16; + substream.rtcp_stats.packets_lost = 17; + substream.rtcp_stats.fraction_lost = 18; + webrtc::ReportBlockData report_block_data; + report_block_data.AddRoundTripTimeSample(19); + substream.report_block_data = report_block_data; + substream.encode_frame_rate = 20.0; + substream.frames_encoded = 21; + substream.qp_sum = 22; + substream.total_encode_time_ms = 23; + substream.total_encoded_bytes_target = 24; + substream.huge_frames_sent = 25; + + stats.substreams[ssrc_2] = substream; + + stream->SetStats(stats); + + cricket::VideoMediaInfo video_media_info; + ASSERT_TRUE(channel_->GetStats(&video_media_info)); + EXPECT_EQ(video_media_info.aggregated_senders.size(), 1u); + auto& sender = video_media_info.aggregated_senders[0]; + + // MediaSenderInfo + + EXPECT_EQ( + sender.payload_bytes_sent, + static_cast<int64_t>(2u * substream.rtp_stats.transmitted.payload_bytes)); + EXPECT_EQ(sender.header_and_padding_bytes_sent, + static_cast<int64_t>( + 2u * (substream.rtp_stats.transmitted.header_bytes + + substream.rtp_stats.transmitted.padding_bytes))); + EXPECT_EQ(sender.retransmitted_bytes_sent, + 2u * substream.rtp_stats.retransmitted.payload_bytes); + EXPECT_EQ(sender.packets_sent, + static_cast<int>(2 * substream.rtp_stats.transmitted.packets)); + EXPECT_EQ(sender.retransmitted_packets_sent, + 2u * substream.rtp_stats.retransmitted.packets); + EXPECT_EQ(sender.packets_lost, 2 * substream.rtcp_stats.packets_lost); + EXPECT_EQ(sender.fraction_lost, + static_cast<float>(substream.rtcp_stats.fraction_lost) / (1 << 8)); + EXPECT_EQ(sender.rtt_ms, 0); + EXPECT_EQ(sender.codec_name, DefaultCodec().name); + EXPECT_EQ(sender.codec_payload_type, DefaultCodec().id); + EXPECT_EQ(sender.local_stats.size(), 1u); + EXPECT_EQ(sender.local_stats[0].ssrc, last_ssrc_); + EXPECT_EQ(sender.local_stats[0].timestamp, 0.0f); + EXPECT_EQ(sender.remote_stats.size(), 0u); + EXPECT_EQ(sender.report_block_datas.size(), 2u * 1); + + // VideoSenderInfo + + EXPECT_EQ(sender.ssrc_groups.size(), 0u); + EXPECT_EQ(sender.encoder_implementation_name, + stats.encoder_implementation_name); + EXPECT_EQ( + sender.firs_rcvd, + static_cast<int>(2 * substream.rtcp_packet_type_counts.fir_packets)); + EXPECT_EQ( + sender.plis_rcvd, + static_cast<int>(2 * substream.rtcp_packet_type_counts.pli_packets)); + EXPECT_EQ( + sender.nacks_rcvd, + static_cast<int>(2 * substream.rtcp_packet_type_counts.nack_packets)); + EXPECT_EQ(sender.send_frame_width, substream.width); + EXPECT_EQ(sender.send_frame_height, substream.height); + + EXPECT_EQ(sender.framerate_input, stats.input_frame_rate); + EXPECT_EQ(sender.framerate_sent, stats.encode_frame_rate); + EXPECT_EQ(sender.nominal_bitrate, stats.media_bitrate_bps); + EXPECT_NE(sender.adapt_reason & WebRtcVideoChannel::ADAPTREASON_CPU, 0); + EXPECT_NE(sender.adapt_reason & WebRtcVideoChannel::ADAPTREASON_BANDWIDTH, 0); + EXPECT_EQ(sender.adapt_changes, stats.number_of_cpu_adapt_changes); + EXPECT_EQ(sender.quality_limitation_reason, stats.quality_limitation_reason); + EXPECT_EQ(sender.quality_limitation_durations_ms, + stats.quality_limitation_durations_ms); + EXPECT_EQ(sender.quality_limitation_resolution_changes, + stats.quality_limitation_resolution_changes); + EXPECT_EQ(sender.avg_encode_ms, stats.avg_encode_time_ms); + EXPECT_EQ(sender.encode_usage_percent, stats.encode_usage_percent); + EXPECT_EQ(sender.frames_encoded, 2u * substream.frames_encoded); + EXPECT_EQ(sender.key_frames_encoded, 2u * substream.frame_counts.key_frames); + EXPECT_EQ(sender.total_encode_time_ms, 2u * substream.total_encode_time_ms); + EXPECT_EQ(sender.total_encoded_bytes_target, + 2u * substream.total_encoded_bytes_target); + EXPECT_EQ(sender.total_packet_send_delay_ms, + 2u * substream.total_packet_send_delay_ms); + EXPECT_EQ(sender.has_entered_low_resolution, + stats.has_entered_low_resolution); + EXPECT_EQ(sender.qp_sum, 2u * *substream.qp_sum); + EXPECT_EQ(sender.content_type, webrtc::VideoContentType::SCREENSHARE); + EXPECT_EQ(sender.frames_sent, 2u * substream.frames_encoded); + EXPECT_EQ(sender.huge_frames_sent, stats.huge_frames_sent); + EXPECT_EQ(sender.rid, absl::nullopt); +} + +TEST_F(WebRtcVideoChannelTest, GetPerLayerStatsReportForSubStreams) { + FakeVideoSendStream* stream = AddSendStream(); + auto stats = GetInitialisedStats(); + + const uint32_t ssrc_1 = 123u; + const uint32_t ssrc_2 = 456u; + + auto& substream = stats.substreams[ssrc_1]; + substream.frame_counts.key_frames = 1; + substream.frame_counts.delta_frames = 2; + substream.width = 3; + substream.height = 4; + substream.total_bitrate_bps = 5; + substream.retransmit_bitrate_bps = 6; + substream.avg_delay_ms = 7; + substream.max_delay_ms = 8; + substream.total_packet_send_delay_ms = 9; + substream.rtp_stats.transmitted.header_bytes = 10; + substream.rtp_stats.transmitted.padding_bytes = 11; + substream.rtp_stats.retransmitted.payload_bytes = 12; + substream.rtp_stats.retransmitted.packets = 13; + substream.rtcp_packet_type_counts.fir_packets = 14; + substream.rtcp_packet_type_counts.nack_packets = 15; + substream.rtcp_packet_type_counts.pli_packets = 16; + substream.rtcp_stats.packets_lost = 17; + substream.rtcp_stats.fraction_lost = 18; + webrtc::ReportBlockData report_block_data; + report_block_data.AddRoundTripTimeSample(19); + substream.report_block_data = report_block_data; + substream.encode_frame_rate = 20.0; + substream.frames_encoded = 21; + substream.qp_sum = 22; + substream.total_encode_time_ms = 23; + substream.total_encoded_bytes_target = 24; + substream.huge_frames_sent = 25; + + stats.substreams[ssrc_2] = substream; + + stream->SetStats(stats); + + cricket::VideoMediaInfo video_media_info; + ASSERT_TRUE(channel_->GetStats(&video_media_info)); + EXPECT_EQ(video_media_info.senders.size(), 2u); + auto& sender = video_media_info.senders[0]; + + // MediaSenderInfo + + EXPECT_EQ( + sender.payload_bytes_sent, + static_cast<int64_t>(substream.rtp_stats.transmitted.payload_bytes)); + EXPECT_EQ( + sender.header_and_padding_bytes_sent, + static_cast<int64_t>(substream.rtp_stats.transmitted.header_bytes + + substream.rtp_stats.transmitted.padding_bytes)); + EXPECT_EQ(sender.retransmitted_bytes_sent, + substream.rtp_stats.retransmitted.payload_bytes); + EXPECT_EQ(sender.packets_sent, + static_cast<int>(substream.rtp_stats.transmitted.packets)); + EXPECT_EQ(sender.retransmitted_packets_sent, + substream.rtp_stats.retransmitted.packets); + EXPECT_EQ(sender.packets_lost, substream.rtcp_stats.packets_lost); + EXPECT_EQ(sender.fraction_lost, + static_cast<float>(substream.rtcp_stats.fraction_lost) / (1 << 8)); + EXPECT_EQ(sender.rtt_ms, 0); + EXPECT_EQ(sender.codec_name, DefaultCodec().name); + EXPECT_EQ(sender.codec_payload_type, DefaultCodec().id); + EXPECT_EQ(sender.local_stats.size(), 1u); + EXPECT_EQ(sender.local_stats[0].ssrc, ssrc_1); + EXPECT_EQ(sender.local_stats[0].timestamp, 0.0f); + EXPECT_EQ(sender.remote_stats.size(), 0u); + EXPECT_EQ(sender.report_block_datas.size(), 1u); + + // VideoSenderInfo + + EXPECT_EQ(sender.ssrc_groups.size(), 0u); + EXPECT_EQ(sender.encoder_implementation_name, + stats.encoder_implementation_name); + EXPECT_EQ(sender.firs_rcvd, + static_cast<int>(substream.rtcp_packet_type_counts.fir_packets)); + EXPECT_EQ(sender.plis_rcvd, + static_cast<int>(substream.rtcp_packet_type_counts.pli_packets)); + EXPECT_EQ(sender.nacks_rcvd, + static_cast<int>(substream.rtcp_packet_type_counts.nack_packets)); + EXPECT_EQ(sender.send_frame_width, substream.width); + EXPECT_EQ(sender.send_frame_height, substream.height); + + EXPECT_EQ(sender.framerate_input, stats.input_frame_rate); + EXPECT_EQ(sender.framerate_sent, substream.encode_frame_rate); + EXPECT_EQ(sender.nominal_bitrate, stats.media_bitrate_bps); + EXPECT_NE(sender.adapt_reason & WebRtcVideoChannel::ADAPTREASON_CPU, 0); + EXPECT_NE(sender.adapt_reason & WebRtcVideoChannel::ADAPTREASON_BANDWIDTH, 0); + EXPECT_EQ(sender.adapt_changes, stats.number_of_cpu_adapt_changes); + EXPECT_EQ(sender.quality_limitation_reason, stats.quality_limitation_reason); + EXPECT_EQ(sender.quality_limitation_durations_ms, + stats.quality_limitation_durations_ms); + EXPECT_EQ(sender.quality_limitation_resolution_changes, + stats.quality_limitation_resolution_changes); + EXPECT_EQ(sender.avg_encode_ms, stats.avg_encode_time_ms); + EXPECT_EQ(sender.encode_usage_percent, stats.encode_usage_percent); + EXPECT_EQ(sender.frames_encoded, + static_cast<uint32_t>(substream.frames_encoded)); + EXPECT_EQ(sender.key_frames_encoded, + static_cast<uint32_t>(substream.frame_counts.key_frames)); + EXPECT_EQ(sender.total_encode_time_ms, substream.total_encode_time_ms); + EXPECT_EQ(sender.total_encoded_bytes_target, + substream.total_encoded_bytes_target); + EXPECT_EQ(sender.total_packet_send_delay_ms, + substream.total_packet_send_delay_ms); + EXPECT_EQ(sender.has_entered_low_resolution, + stats.has_entered_low_resolution); + EXPECT_EQ(sender.qp_sum, *substream.qp_sum); + EXPECT_EQ(sender.content_type, webrtc::VideoContentType::SCREENSHARE); + EXPECT_EQ(sender.frames_sent, + static_cast<uint32_t>(substream.frames_encoded)); + EXPECT_EQ(sender.huge_frames_sent, substream.huge_frames_sent); + EXPECT_EQ(sender.rid, absl::nullopt); } TEST_F(WebRtcVideoChannelTest, GetStatsReportsUpperResolution) { @@ -5248,9 +5608,16 @@ cricket::VideoMediaInfo info; ASSERT_TRUE(channel_->GetStats(&info)); - ASSERT_EQ(1u, info.senders.size()); - EXPECT_EQ(123, info.senders[0].send_frame_width); + ASSERT_EQ(1u, info.aggregated_senders.size()); + ASSERT_EQ(3u, info.senders.size()); + EXPECT_EQ(123, info.senders[1].send_frame_width); + EXPECT_EQ(40, info.senders[1].send_frame_height); + EXPECT_EQ(80, info.senders[2].send_frame_width); + EXPECT_EQ(31, info.senders[2].send_frame_height); + EXPECT_EQ(20, info.senders[0].send_frame_width); EXPECT_EQ(90, info.senders[0].send_frame_height); + EXPECT_EQ(123, info.aggregated_senders[0].send_frame_width); + EXPECT_EQ(90, info.aggregated_senders[0].send_frame_height); } TEST_F(WebRtcVideoChannelTest, GetStatsReportsCpuAdaptationStats) { @@ -5448,19 +5815,18 @@ cricket::VideoMediaInfo info; ASSERT_TRUE(channel_->GetStats(&info)); - // TODO(https://crbug.com/webrtc/9547): Populate individual VideoSenderInfo - // objects for each simulcast stream, instead of accumulating all layers into - // a single VideoSenderInfo. When this is fixed, this test should expect that - // there are two VideoSenderInfo, where the first info accounts for the first - // RTX and the second info accounts for the second RTX. In order for the test - // to be set up correctly, it may need to be updated such that the - // relationship between RTP and RTX streams are known. See also - // https://crbug.com/webrtc/11439. - EXPECT_EQ(60u, info.senders[0].header_and_padding_bytes_sent); - EXPECT_EQ(107u, info.senders[0].payload_bytes_sent); - EXPECT_EQ(20, info.senders[0].packets_sent); - EXPECT_EQ(30u, info.senders[0].retransmitted_bytes_sent); - EXPECT_EQ(5u, info.senders[0].retransmitted_packets_sent); + EXPECT_EQ(info.senders.size(), 2u); + EXPECT_EQ(15u, info.senders[0].header_and_padding_bytes_sent); + EXPECT_EQ(30u, info.senders[0].payload_bytes_sent); + EXPECT_EQ(4, info.senders[0].packets_sent); + EXPECT_EQ(10u, info.senders[0].retransmitted_bytes_sent); + EXPECT_EQ(1u, info.senders[0].retransmitted_packets_sent); + + EXPECT_EQ(45u, info.senders[1].header_and_padding_bytes_sent); + EXPECT_EQ(77u, info.senders[1].payload_bytes_sent); + EXPECT_EQ(16, info.senders[1].packets_sent); + EXPECT_EQ(20u, info.senders[1].retransmitted_bytes_sent); + EXPECT_EQ(4u, info.senders[1].retransmitted_packets_sent); } TEST_F(WebRtcVideoChannelTest, @@ -5492,9 +5858,17 @@ cricket::VideoMediaInfo info; ASSERT_TRUE(channel_->GetStats(&info)); - EXPECT_EQ(7, info.senders[0].firs_rcvd); - EXPECT_EQ(10, info.senders[0].nacks_rcvd); - EXPECT_EQ(13, info.senders[0].plis_rcvd); + EXPECT_EQ(2, info.senders[0].firs_rcvd); + EXPECT_EQ(3, info.senders[0].nacks_rcvd); + EXPECT_EQ(4, info.senders[0].plis_rcvd); + + EXPECT_EQ(5, info.senders[1].firs_rcvd); + EXPECT_EQ(7, info.senders[1].nacks_rcvd); + EXPECT_EQ(9, info.senders[1].plis_rcvd); + + EXPECT_EQ(7, info.aggregated_senders[0].firs_rcvd); + EXPECT_EQ(10, info.aggregated_senders[0].nacks_rcvd); + EXPECT_EQ(13, info.aggregated_senders[0].plis_rcvd); } TEST_F(WebRtcVideoChannelTest, @@ -5639,13 +6013,16 @@ cricket::VideoMediaInfo info; ASSERT_TRUE(channel_->GetStats(&info)); - ASSERT_EQ(2u, info.senders.size()); + ASSERT_EQ(2u, info.aggregated_senders.size()); + ASSERT_EQ(4u, info.senders.size()); BandwidthEstimationInfo bwe_info; channel_->FillBitrateInfo(&bwe_info); // Assuming stream and stream2 corresponds to senders[0] and [1] respectively // is OK as std::maps are sorted and AddSendStream() gives increasing SSRCs. - EXPECT_EQ(stats.media_bitrate_bps, info.senders[0].nominal_bitrate); - EXPECT_EQ(stats2.media_bitrate_bps, info.senders[1].nominal_bitrate); + EXPECT_EQ(stats.media_bitrate_bps, + info.aggregated_senders[0].nominal_bitrate); + EXPECT_EQ(stats2.media_bitrate_bps, + info.aggregated_senders[1].nominal_bitrate); EXPECT_EQ(stats.target_media_bitrate_bps + stats2.target_media_bitrate_bps, bwe_info.target_enc_bitrate); EXPECT_EQ(stats.media_bitrate_bps + stats2.media_bitrate_bps,
diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc index 14281eb..05e7b95 100644 --- a/pc/peer_connection.cc +++ b/pc/peer_connection.cc
@@ -973,7 +973,8 @@ offer_extmap_allow_mixed == o.offer_extmap_allow_mixed && turn_logging_id == o.turn_logging_id && enable_implicit_rollback == o.enable_implicit_rollback && - allow_codec_switching == o.allow_codec_switching; + allow_codec_switching == o.allow_codec_switching && + enable_simulcast_stats == o.enable_simulcast_stats; } bool PeerConnectionInterface::RTCConfiguration::operator!=(
diff --git a/pc/peer_connection_integrationtest.cc b/pc/peer_connection_integrationtest.cc index 063c24f..f3b4f28 100644 --- a/pc/peer_connection_integrationtest.cc +++ b/pc/peer_connection_integrationtest.cc
@@ -3059,7 +3059,7 @@ ASSERT_TRUE(caller_report); auto outbound_stream_stats = caller_report->GetStatsOfType<webrtc::RTCOutboundRTPStreamStats>(); - ASSERT_EQ(4u, outbound_stream_stats.size()); + ASSERT_EQ(outbound_stream_stats.size(), 4u); std::vector<std::string> outbound_track_ids; for (const auto& stat : outbound_stream_stats) { ASSERT_TRUE(stat->bytes_sent.is_defined());
diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc index 01799b4..0e2f170 100644 --- a/pc/rtc_stats_collector.cc +++ b/pc/rtc_stats_collector.cc
@@ -397,6 +397,7 @@ void SetOutboundRTPStreamStatsFromVideoSenderInfo( const std::string& mid, const cricket::VideoSenderInfo& video_sender_info, + bool enable_simulcast_stats, RTCOutboundRTPStreamStats* outbound_video) { SetOutboundRTPStreamStatsFromMediaSenderInfo(video_sender_info, outbound_video); @@ -421,6 +422,21 @@ rtc::kNumMillisecsPerSec; outbound_video->total_encoded_bytes_target = video_sender_info.total_encoded_bytes_target; + if (enable_simulcast_stats) { + if (video_sender_info.send_frame_width > 0) { + outbound_video->frame_width = + static_cast<uint32_t>(video_sender_info.send_frame_width); + } + if (video_sender_info.send_frame_height > 0) { + outbound_video->frame_height = + static_cast<uint32_t>(video_sender_info.send_frame_height); + } + if (video_sender_info.framerate_sent > 0) { + outbound_video->frames_per_second = video_sender_info.framerate_sent; + } + outbound_video->frames_sent = video_sender_info.frames_sent; + outbound_video->huge_frames_sent = video_sender_info.huge_frames_sent; + } outbound_video->total_packet_send_delay = static_cast<double>(video_sender_info.total_packet_send_delay_ms) / rtc::kNumMillisecsPerSec; @@ -437,6 +453,9 @@ outbound_video->encoder_implementation = video_sender_info.encoder_implementation_name; } + if (video_sender_info.rid) { + outbound_video->rid = *video_sender_info.rid; + } } std::unique_ptr<RTCRemoteInboundRtpStreamStats> @@ -968,6 +987,7 @@ RTC_DCHECK_GE(cache_lifetime_us_, 0); pc_->SignalDataChannelCreated().connect( this, &RTCStatsCollector::OnDataChannelCreated); + enable_simulcast_stats_ = pc_->GetConfiguration().enable_simulcast_stats; } RTCStatsCollector::~RTCStatsCollector() { @@ -1643,14 +1663,16 @@ // Outbound std::map<std::string, RTCOutboundRTPStreamStats*> video_outbound_rtps; for (const cricket::VideoSenderInfo& video_sender_info : - track_media_info_map.video_media_info()->senders) { + enable_simulcast_stats_ + ? track_media_info_map.video_media_info()->senders + : track_media_info_map.video_media_info()->aggregated_senders) { if (!video_sender_info.connected()) continue; auto outbound_video = std::make_unique<RTCOutboundRTPStreamStats>( RTCOutboundRTPStreamStatsIDFromSSRC(false, video_sender_info.ssrc()), timestamp_us); - SetOutboundRTPStreamStatsFromVideoSenderInfo(mid, video_sender_info, - outbound_video.get()); + SetOutboundRTPStreamStatsFromVideoSenderInfo( + mid, video_sender_info, enable_simulcast_stats_, outbound_video.get()); rtc::scoped_refptr<VideoTrackInterface> video_track = track_media_info_map.GetVideoTrack(video_sender_info); if (video_track) {
diff --git a/pc/rtc_stats_collector.h b/pc/rtc_stats_collector.h index cd5ec21..7c85a35 100644 --- a/pc/rtc_stats_collector.h +++ b/pc/rtc_stats_collector.h
@@ -288,6 +288,7 @@ std::set<uintptr_t> opened_data_channels; }; InternalRecord internal_record_; + bool enable_simulcast_stats_ = false; }; const char* CandidateTypeToRTCIceCandidateTypeForTesting(
diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc index 12f6059..d3114f3 100644 --- a/pc/rtc_stats_collector_unittest.cc +++ b/pc/rtc_stats_collector_unittest.cc
@@ -519,6 +519,7 @@ MediaStreamTrackInterface::kVideoKind); video_media_info.senders.push_back(video_sender_info); + video_media_info.aggregated_senders.push_back(video_sender_info); rtc::scoped_refptr<MockRtpSenderInternal> rtp_sender = CreateMockSender( cricket::MEDIA_TYPE_VIDEO, rtc::scoped_refptr<MediaStreamTrackInterface>(local_video_track), @@ -641,6 +642,7 @@ cricket::SsrcSenderInfo()); video_media_info.senders[0].local_stats[0].ssrc = 3; video_media_info.senders[0].codec_payload_type = send_codec.payload_type; + video_media_info.aggregated_senders.push_back(video_media_info.senders[0]); // inbound-rtp graph.inbound_rtp_id = "RTCInboundRTPVideoStream_4"; video_media_info.receivers.push_back(cricket::VideoReceiverInfo()); @@ -2014,7 +2016,12 @@ video_media_info.senders[0].qp_sum = absl::nullopt; video_media_info.senders[0].content_type = VideoContentType::UNSPECIFIED; video_media_info.senders[0].encoder_implementation_name = ""; - + video_media_info.senders[0].send_frame_width = 200; + video_media_info.senders[0].send_frame_height = 100; + video_media_info.senders[0].framerate_sent = 10; + video_media_info.senders[0].frames_sent = 5; + video_media_info.senders[0].huge_frames_sent = 2; + video_media_info.aggregated_senders.push_back(video_media_info.senders[0]); RtpCodecParameters codec_parameters; codec_parameters.payload_type = 42; codec_parameters.kind = cricket::MEDIA_TYPE_AUDIO; @@ -2062,6 +2069,13 @@ expected_video.total_packet_send_delay = 10.0; expected_video.quality_limitation_reason = "bandwidth"; expected_video.quality_limitation_resolution_changes = 56u; + if (pc_->GetConfiguration().enable_simulcast_stats) { + expected_video.frame_width = 200u; + expected_video.frame_height = 100u; + expected_video.frames_per_second = 10.0; + expected_video.frames_sent = 5; + expected_video.huge_frames_sent = 2; + } // |expected_video.content_type| should be undefined. // |expected_video.qp_sum| should be undefined. // |expected_video.encoder_implementation| should be undefined. @@ -2077,6 +2091,7 @@ video_media_info.senders[0].content_type = VideoContentType::SCREENSHARE; expected_video.content_type = "screenshare"; video_media_info.senders[0].encoder_implementation_name = "libfooencoder"; + video_media_info.aggregated_senders[0] = video_media_info.senders[0]; expected_video.encoder_implementation = "libfooencoder"; video_media_channel->SetStats(video_media_info); @@ -2390,10 +2405,15 @@ const int kVideoSourceHeight = 34; cricket::VideoMediaInfo video_media_info; + video_media_info.aggregated_senders.push_back(cricket::VideoSenderInfo()); video_media_info.senders.push_back(cricket::VideoSenderInfo()); video_media_info.senders[0].local_stats.push_back(cricket::SsrcSenderInfo()); video_media_info.senders[0].local_stats[0].ssrc = kSsrc; video_media_info.senders[0].framerate_input = 29; + video_media_info.aggregated_senders[0].local_stats.push_back( + cricket::SsrcSenderInfo()); + video_media_info.aggregated_senders[0].local_stats[0].ssrc = kSsrc; + video_media_info.aggregated_senders[0].framerate_input = 29; auto* video_media_channel = pc_->AddVideoChannel("VideoMid", "TransportName"); video_media_channel->SetStats(video_media_info); @@ -2572,6 +2592,8 @@ } video_media_info.senders[0].report_block_datas.push_back( report_block_data); + video_media_info.aggregated_senders.push_back( + video_media_info.senders[0]); auto* video_media_channel = pc_->AddVideoChannel("mid", transport_name); video_media_channel->SetStats(video_media_info); return;
diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc index fa7d56a..d6d5c6f 100644 --- a/pc/rtc_stats_integrationtest.cc +++ b/pc/rtc_stats_integrationtest.cc
@@ -352,7 +352,8 @@ explicit RTCStatsReportVerifier(const RTCStatsReport* report) : report_(report) {} - void VerifyReport(std::vector<const char*> allowed_missing_stats) { + void VerifyReport(std::vector<const char*> allowed_missing_stats, + bool enable_simulcast_stats) { std::set<const char*> missing_stats = StatsTypes(); bool verify_successful = true; std::vector<const RTCTransportStats*> transport_stats = @@ -395,7 +396,7 @@ stats.cast_to<RTCInboundRTPStreamStats>()); } else if (stats.type() == RTCOutboundRTPStreamStats::kType) { verify_successful &= VerifyRTCOutboundRTPStreamStats( - stats.cast_to<RTCOutboundRTPStreamStats>()); + stats.cast_to<RTCOutboundRTPStreamStats>(), enable_simulcast_stats); } else if (stats.type() == RTCRemoteInboundRtpStreamStats::kType) { verify_successful &= VerifyRTCRemoteInboundRtpStreamStats( stats.cast_to<RTCRemoteInboundRtpStreamStats>()); @@ -865,14 +866,19 @@ } bool VerifyRTCOutboundRTPStreamStats( - const RTCOutboundRTPStreamStats& outbound_stream) { + const RTCOutboundRTPStreamStats& outbound_stream, + bool enable_simulcast_stats) { RTCStatsVerifier verifier(report_, &outbound_stream); VerifyRTCRTPStreamStats(outbound_stream, &verifier); if (outbound_stream.media_type.is_defined() && *outbound_stream.media_type == "video") { verifier.TestMemberIsIDReference(outbound_stream.media_source_id, RTCVideoSourceStats::kType); - verifier.TestMemberIsNonNegative<uint64_t>(outbound_stream.qp_sum); + if (*outbound_stream.frames_encoded > 0) { + verifier.TestMemberIsNonNegative<uint64_t>(outbound_stream.qp_sum); + } else { + verifier.TestMemberIsUndefined(outbound_stream.qp_sum); + } } else { verifier.TestMemberIsIDReference(outbound_stream.media_source_id, RTCAudioSourceStats::kType); @@ -906,6 +912,24 @@ // this to be present. verifier.MarkMemberTested(outbound_stream.content_type, true); verifier.TestMemberIsDefined(outbound_stream.encoder_implementation); + if (enable_simulcast_stats) { + verifier.TestMemberIsNonNegative<double>( + outbound_stream.frames_per_second); + verifier.TestMemberIsNonNegative<uint32_t>( + outbound_stream.frame_height); + verifier.TestMemberIsNonNegative<uint32_t>(outbound_stream.frame_width); + verifier.TestMemberIsNonNegative<uint32_t>(outbound_stream.frames_sent); + verifier.TestMemberIsNonNegative<uint32_t>( + outbound_stream.huge_frames_sent); + verifier.MarkMemberTested(outbound_stream.rid, true); + } else { + verifier.TestMemberIsUndefined(outbound_stream.frames_per_second); + verifier.TestMemberIsUndefined(outbound_stream.frame_height); + verifier.TestMemberIsUndefined(outbound_stream.frame_width); + verifier.TestMemberIsUndefined(outbound_stream.frames_sent); + verifier.TestMemberIsUndefined(outbound_stream.huge_frames_sent); + verifier.TestMemberIsUndefined(outbound_stream.rid); + } } else { verifier.TestMemberIsUndefined(outbound_stream.frames_encoded); verifier.TestMemberIsUndefined(outbound_stream.key_frames_encoded); @@ -920,6 +944,12 @@ verifier.TestMemberIsUndefined(outbound_stream.content_type); // TODO(hbos): Implement for audio as well. verifier.TestMemberIsUndefined(outbound_stream.encoder_implementation); + verifier.TestMemberIsUndefined(outbound_stream.rid); + verifier.TestMemberIsUndefined(outbound_stream.frames_per_second); + verifier.TestMemberIsUndefined(outbound_stream.frame_height); + verifier.TestMemberIsUndefined(outbound_stream.frame_width); + verifier.TestMemberIsUndefined(outbound_stream.frames_sent); + verifier.TestMemberIsUndefined(outbound_stream.huge_frames_sent); } return verifier.ExpectAllMembersSuccessfullyTested(); } @@ -1014,9 +1044,11 @@ StartCall(); rtc::scoped_refptr<const RTCStatsReport> report = GetStatsFromCaller(); - RTCStatsReportVerifier(report.get()).VerifyReport({}); + RTCStatsReportVerifier(report.get()) + .VerifyReport({}, + caller_->pc()->GetConfiguration().enable_simulcast_stats); - #if RTC_TRACE_EVENTS_ENABLED +#if RTC_TRACE_EVENTS_ENABLED EXPECT_EQ(report->ToJson(), RTCStatsReportTraceListener::last_trace()); #endif } @@ -1025,9 +1057,11 @@ StartCall(); rtc::scoped_refptr<const RTCStatsReport> report = GetStatsFromCallee(); - RTCStatsReportVerifier(report.get()).VerifyReport({}); + RTCStatsReportVerifier(report.get()) + .VerifyReport({}, + caller_->pc()->GetConfiguration().enable_simulcast_stats); - #if RTC_TRACE_EVENTS_ENABLED +#if RTC_TRACE_EVENTS_ENABLED EXPECT_EQ(report->ToJson(), RTCStatsReportTraceListener::last_trace()); #endif } @@ -1049,7 +1083,9 @@ RTCMediaStreamStats::kType, RTCDataChannelStats::kType, }; - RTCStatsReportVerifier(report.get()).VerifyReport(allowed_missing_stats); + RTCStatsReportVerifier(report.get()) + .VerifyReport(allowed_missing_stats, + caller_->pc()->GetConfiguration().enable_simulcast_stats); EXPECT_TRUE(report->size()); } @@ -1068,7 +1104,9 @@ RTCMediaStreamStats::kType, RTCDataChannelStats::kType, }; - RTCStatsReportVerifier(report.get()).VerifyReport(allowed_missing_stats); + RTCStatsReportVerifier(report.get()) + .VerifyReport(allowed_missing_stats, + caller_->pc()->GetConfiguration().enable_simulcast_stats); EXPECT_TRUE(report->size()); }
diff --git a/pc/stats_collector.cc b/pc/stats_collector.cc index b447b8f..0509c6d 100644 --- a/pc/stats_collector.cc +++ b/pc/stats_collector.cc
@@ -1028,7 +1028,7 @@ void ExtractStats(StatsCollector* collector) const override { ExtractSenderReceiverStats(collector, video_media_info.receivers, - video_media_info.senders); + video_media_info.aggregated_senders); } bool HasRemoteAudio() const override { return false; }
diff --git a/pc/stats_collector_unittest.cc b/pc/stats_collector_unittest.cc index 471f382..ab46972 100644 --- a/pc/stats_collector_unittest.cc +++ b/pc/stats_collector_unittest.cc
@@ -912,7 +912,7 @@ video_sender_info.payload_bytes_sent = kBytesSent; video_sender_info.header_and_padding_bytes_sent = 0; VideoMediaInfo video_info; - video_info.senders.push_back(video_sender_info); + video_info.aggregated_senders.push_back(video_sender_info); auto* video_media_channel = pc->AddVideoChannel("video", "transport"); video_media_channel->SetStats(video_info); @@ -995,7 +995,7 @@ video_sender_info.header_and_padding_bytes_sent = 12; VideoMediaInfo video_info; - video_info.senders.push_back(video_sender_info); + video_info.aggregated_senders.push_back(video_sender_info); auto* video_media_channel = pc->AddVideoChannel("video", "transport"); video_media_channel->SetStats(video_info); @@ -1093,7 +1093,7 @@ video_sender_info.payload_bytes_sent = kBytesSent - 12; video_sender_info.header_and_padding_bytes_sent = 12; VideoMediaInfo video_info; - video_info.senders.push_back(video_sender_info); + video_info.aggregated_senders.push_back(video_sender_info); auto* video_media_channel = pc->AddVideoChannel("video", "transport"); video_media_channel->SetStats(video_info); @@ -1148,7 +1148,7 @@ video_sender_info.payload_bytes_sent = kBytesSent - 12; video_sender_info.header_and_padding_bytes_sent = 12; VideoMediaInfo video_info; - video_info.senders.push_back(video_sender_info); + video_info.aggregated_senders.push_back(video_sender_info); auto* video_media_channel = pc->AddVideoChannel("video", "transport"); video_media_channel->SetStats(video_info); @@ -1211,7 +1211,7 @@ video_sender_info.add_ssrc(kSsrcOfTrack); video_sender_info.remote_stats.push_back(remote_ssrc_stats); VideoMediaInfo video_info; - video_info.senders.push_back(video_sender_info); + video_info.aggregated_senders.push_back(video_sender_info); auto* video_media_channel = pc->AddVideoChannel("video", "transport"); video_media_channel->SetStats(video_info); @@ -1853,7 +1853,7 @@ video_sender_info.frames_encoded = 10; video_sender_info.qp_sum = 11; VideoMediaInfo video_info; - video_info.senders.push_back(video_sender_info); + video_info.aggregated_senders.push_back(video_sender_info); auto* video_media_channel = pc->AddVideoChannel("video", "transport"); video_media_channel->SetStats(video_info);
diff --git a/pc/track_media_info_map.cc b/pc/track_media_info_map.cc index 781737a..ca923a0 100644 --- a/pc/track_media_info_map.cc +++ b/pc/track_media_info_map.cc
@@ -10,6 +10,7 @@ #include "pc/track_media_info_map.h" +#include <set> #include <string> #include <utility> @@ -170,19 +171,36 @@ } if (video_media_info_) { for (auto& sender_info : video_media_info_->senders) { - VideoTrackInterface* associated_track = - FindValueOrNull(local_video_track_by_ssrc, sender_info.ssrc()); - if (associated_track) { - // One sender is associated with at most one track. - // One track may be associated with multiple senders. - video_track_by_sender_info_[&sender_info] = associated_track; - video_infos_by_local_track_[associated_track].push_back(&sender_info); + std::set<uint32_t> ssrcs; + ssrcs.insert(sender_info.ssrc()); + for (auto& ssrc_group : sender_info.ssrc_groups) { + for (auto ssrc : ssrc_group.ssrcs) { + ssrcs.insert(ssrc); + } } + for (auto ssrc : ssrcs) { + VideoTrackInterface* associated_track = + FindValueOrNull(local_video_track_by_ssrc, ssrc); + if (associated_track) { + // One sender is associated with at most one track. + // One track may be associated with multiple senders. + video_track_by_sender_info_[&sender_info] = associated_track; + video_infos_by_local_track_[associated_track].push_back(&sender_info); + break; + } + } + } + for (auto& sender_info : video_media_info_->aggregated_senders) { if (sender_info.ssrc() == 0) continue; // Unconnected SSRC. bugs.webrtc.org/8673 RTC_DCHECK(video_info_by_sender_ssrc_.count(sender_info.ssrc()) == 0) << "Duplicate video sender SSRC: " << sender_info.ssrc(); video_info_by_sender_ssrc_[sender_info.ssrc()] = &sender_info; + VideoTrackInterface* associated_track = + FindValueOrNull(local_video_track_by_ssrc, sender_info.ssrc()); + if (associated_track) { + video_track_by_sender_info_[&sender_info] = associated_track; + } } for (auto& receiver_info : video_media_info_->receivers) { VideoTrackInterface* associated_track =
diff --git a/pc/track_media_info_map_unittest.cc b/pc/track_media_info_map_unittest.cc index 4fa8a4a..c487ab8 100644 --- a/pc/track_media_info_map_unittest.cc +++ b/pc/track_media_info_map_unittest.cc
@@ -131,6 +131,7 @@ video_sender_info.local_stats[i++].ssrc = ssrc; } video_media_info_->senders.push_back(video_sender_info); + video_media_info_->aggregated_senders.push_back(video_sender_info); } }
diff --git a/stats/rtcstats_objects.cc b/stats/rtcstats_objects.cc index d99c9e7..453acce 100644 --- a/stats/rtcstats_objects.cc +++ b/stats/rtcstats_objects.cc
@@ -698,6 +698,7 @@ RTCOutboundRTPStreamStats, RTCRTPStreamStats, "outbound-rtp", &media_source_id, &remote_id, + &rid, &packets_sent, &retransmitted_packets_sent, &bytes_sent, @@ -708,6 +709,11 @@ &key_frames_encoded, &total_encode_time, &total_encoded_bytes_target, + &frame_width, + &frame_height, + &frames_per_second, + &frames_sent, + &huge_frames_sent, &total_packet_send_delay, &quality_limitation_reason, &quality_limitation_resolution_changes, @@ -724,6 +730,7 @@ : RTCRTPStreamStats(std::move(id), timestamp_us), media_source_id("mediaSourceId"), remote_id("remoteId"), + rid("rid"), packets_sent("packetsSent"), retransmitted_packets_sent("retransmittedPacketsSent"), bytes_sent("bytesSent"), @@ -734,6 +741,11 @@ key_frames_encoded("keyFramesEncoded"), total_encode_time("totalEncodeTime"), total_encoded_bytes_target("totalEncodedBytesTarget"), + frame_width("frameWidth"), + frame_height("frameHeight"), + frames_per_second("framesPerSecond"), + frames_sent("framesSent"), + huge_frames_sent("hugeFramesSent"), total_packet_send_delay("totalPacketSendDelay"), quality_limitation_reason("qualityLimitationReason"), quality_limitation_resolution_changes( @@ -746,6 +758,7 @@ : RTCRTPStreamStats(other), media_source_id(other.media_source_id), remote_id(other.remote_id), + rid(other.rid), packets_sent(other.packets_sent), retransmitted_packets_sent(other.retransmitted_packets_sent), bytes_sent(other.bytes_sent), @@ -756,6 +769,11 @@ key_frames_encoded(other.key_frames_encoded), total_encode_time(other.total_encode_time), total_encoded_bytes_target(other.total_encoded_bytes_target), + frame_width(other.frame_width), + frame_height(other.frame_height), + frames_per_second(other.frames_per_second), + frames_sent(other.frames_sent), + huge_frames_sent(other.huge_frames_sent), total_packet_send_delay(other.total_packet_send_delay), quality_limitation_reason(other.quality_limitation_reason), quality_limitation_resolution_changes(
diff --git a/video/send_statistics_proxy.cc b/video/send_statistics_proxy.cc index 1cb059d..f8d768f 100644 --- a/video/send_statistics_proxy.cc +++ b/video/send_statistics_proxy.cc
@@ -958,7 +958,15 @@ VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc); if (!stats) return; - + if (encoded_frame_rate_trackers_.count(simulcast_idx) == 0) { + encoded_frame_rate_trackers_[simulcast_idx] = + std::make_unique<rtc::RateTracker>(kBucketSizeMs, kBucketCount); + } + stats->encode_frame_rate = + encoded_frame_rate_trackers_[simulcast_idx]->ComputeRate(); + stats->frames_encoded++; + stats->total_encode_time_ms += encoded_image.timing_.encode_finish_ms - + encoded_image.timing_.encode_start_ms; // Report resolution of top spatial layer in case of VP9 SVC. bool is_svc_low_spatial_layer = (codec_info && codec_info->codecType == kVideoCodecVP9) @@ -975,9 +983,9 @@ VideoFrameType::kVideoFrameKey); if (encoded_image.qp_ != -1) { - if (!stats_.qp_sum) - stats_.qp_sum = 0; - *stats_.qp_sum += encoded_image.qp_; + if (!stats->qp_sum) + stats->qp_sum = 0; + *stats->qp_sum += encoded_image.qp_; if (codec_info) { if (codec_info->codecType == kVideoCodecVP8) { @@ -997,6 +1005,7 @@ // as a single difficult input frame. // https://w3c.github.io/webrtc-stats/#dom-rtcvideosenderstats-hugeframessent if (encoded_image.timing_.flags & VideoSendTiming::kTriggeredBySize) { + ++stats->huge_frames_sent; if (!last_outlier_timestamp_ || *last_outlier_timestamp_ < encoded_image.capture_time_ms_) { last_outlier_timestamp_.emplace(encoded_image.capture_time_ms_); @@ -1007,6 +1016,7 @@ media_byte_rate_tracker_.AddSamples(encoded_image.size()); if (uma_container_->InsertEncodedFrame(encoded_image, simulcast_idx)) { + encoded_frame_rate_trackers_[simulcast_idx]->AddSamples(1); encoded_frame_rate_tracker_.AddSamples(1); }
diff --git a/video/send_statistics_proxy.h b/video/send_statistics_proxy.h index 08717ca..1d2fd21 100644 --- a/video/send_statistics_proxy.h +++ b/video/send_statistics_proxy.h
@@ -285,6 +285,8 @@ RTC_GUARDED_BY(crit_); rtc::RateTracker media_byte_rate_tracker_ RTC_GUARDED_BY(crit_); rtc::RateTracker encoded_frame_rate_tracker_ RTC_GUARDED_BY(crit_); + std::map<uint32_t, std::unique_ptr<rtc::RateTracker>> + encoded_frame_rate_trackers_ RTC_GUARDED_BY(crit_); absl::optional<int64_t> last_outlier_timestamp_ RTC_GUARDED_BY(crit_);
diff --git a/video/send_statistics_proxy_unittest.cc b/video/send_statistics_proxy_unittest.cc index b69dfad..ab5b491 100644 --- a/video/send_statistics_proxy_unittest.cc +++ b/video/send_statistics_proxy_unittest.cc
@@ -372,22 +372,27 @@ TEST_F(SendStatisticsProxyTest, OnSendEncodedImageIncreasesQpSum) { EncodedImage encoded_image; CodecSpecificInfo codec_info; - EXPECT_EQ(absl::nullopt, statistics_proxy_->GetStats().qp_sum); + auto ssrc = config_.rtp.ssrcs[0]; + EXPECT_EQ(absl::nullopt, + statistics_proxy_->GetStats().substreams[ssrc].qp_sum); encoded_image.qp_ = 3; statistics_proxy_->OnSendEncodedImage(encoded_image, &codec_info); - EXPECT_EQ(3u, statistics_proxy_->GetStats().qp_sum); + EXPECT_EQ(3u, statistics_proxy_->GetStats().substreams[ssrc].qp_sum); encoded_image.qp_ = 127; statistics_proxy_->OnSendEncodedImage(encoded_image, &codec_info); - EXPECT_EQ(130u, statistics_proxy_->GetStats().qp_sum); + EXPECT_EQ(130u, statistics_proxy_->GetStats().substreams[ssrc].qp_sum); } TEST_F(SendStatisticsProxyTest, OnSendEncodedImageWithoutQpQpSumWontExist) { EncodedImage encoded_image; CodecSpecificInfo codec_info; + auto ssrc = config_.rtp.ssrcs[0]; encoded_image.qp_ = -1; - EXPECT_EQ(absl::nullopt, statistics_proxy_->GetStats().qp_sum); + EXPECT_EQ(absl::nullopt, + statistics_proxy_->GetStats().substreams[ssrc].qp_sum); statistics_proxy_->OnSendEncodedImage(encoded_image, &codec_info); - EXPECT_EQ(absl::nullopt, statistics_proxy_->GetStats().qp_sum); + EXPECT_EQ(absl::nullopt, + statistics_proxy_->GetStats().substreams[ssrc].qp_sum); } TEST_F(SendStatisticsProxyTest, TotalEncodedBytesTargetFirstFrame) { @@ -442,6 +447,29 @@ EXPECT_EQ(kTargetBytesPerSecond / 10, delta_encoded_bytes_target); } +TEST_F(SendStatisticsProxyTest, EncodeFrameRateInSubStream) { + const int kInterframeDelayMs = 100; + auto ssrc = config_.rtp.ssrcs[0]; + rtc::ScopedFakeClock fake_global_clock; + fake_global_clock.SetTime( + Timestamp::Millis(fake_clock_.TimeInMilliseconds())); + + EncodedImage encoded_image; + + // First frame + statistics_proxy_->OnSendEncodedImage(encoded_image, nullptr); + // Second frame + fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs); + fake_global_clock.SetTime( + Timestamp::Millis(fake_clock_.TimeInMilliseconds())); + encoded_image.SetTimestamp(encoded_image.Timestamp() + + 90 * kInterframeDelayMs); + statistics_proxy_->OnSendEncodedImage(encoded_image, nullptr); + + auto stats = statistics_proxy_->GetStats(); + EXPECT_EQ(stats.substreams[ssrc].encode_frame_rate, 10); +} + TEST_F(SendStatisticsProxyTest, GetCpuAdaptationStats) { VideoAdaptationCounters cpu_counts; VideoAdaptationCounters quality_counts;