Add decoder-timing stats to VideoReceiveStream.

Also breaks out SsrcStats from VideoReceiveStream::Stats as they don't
have that much overlap.

R=mflodman@webrtc.org, stefan@webrtc.org
BUG=1667, 1788

Review URL: https://webrtc-codereview.appspot.com/40819004

Cr-Commit-Position: refs/heads/master@{#8501}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8501 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
pbos@webrtc.org 2015-02-25 10:42:16 +00:00
parent c5558b7021
commit 09c77b95bb
11 changed files with 142 additions and 104 deletions

View File

@ -1766,21 +1766,20 @@ WebRtcVideoChannel2::WebRtcVideoSendStream::GetVideoSenderInfo() {
info.send_frame_width = 0;
info.send_frame_height = 0;
for (std::map<uint32_t, webrtc::SsrcStats>::iterator it =
for (std::map<uint32_t, webrtc::VideoSendStream::StreamStats>::iterator it =
stats.substreams.begin();
it != stats.substreams.end();
++it) {
it != stats.substreams.end(); ++it) {
// TODO(pbos): Wire up additional stats, such as padding bytes.
webrtc::SsrcStats stream_stats = it->second;
webrtc::VideoSendStream::StreamStats stream_stats = it->second;
info.bytes_sent += stream_stats.rtp_stats.transmitted.payload_bytes +
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_lost += stream_stats.rtcp_stats.cumulative_lost;
if (stream_stats.sent_width > info.send_frame_width)
info.send_frame_width = stream_stats.sent_width;
if (stream_stats.sent_height > info.send_frame_height)
info.send_frame_height = stream_stats.sent_height;
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;
@ -1788,7 +1787,8 @@ WebRtcVideoChannel2::WebRtcVideoSendStream::GetVideoSenderInfo() {
if (!stats.substreams.empty()) {
// TODO(pbos): Report fraction lost per SSRC.
webrtc::SsrcStats first_stream_stats = stats.substreams.begin()->second;
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);
@ -1804,10 +1804,9 @@ void WebRtcVideoChannel2::WebRtcVideoSendStream::FillBandwidthEstimationInfo(
return;
}
webrtc::VideoSendStream::Stats stats = stream_->GetStats();
for (std::map<uint32_t, webrtc::SsrcStats>::iterator it =
for (std::map<uint32_t, webrtc::VideoSendStream::StreamStats>::iterator it =
stats.substreams.begin();
it != stats.substreams.end();
++it) {
it != stats.substreams.end(); ++it) {
bwe_info->transmit_bitrate += it->second.total_bitrate_bps;
bwe_info->retransmit_bitrate += it->second.retransmit_bitrate_bps;
}
@ -2043,6 +2042,14 @@ WebRtcVideoChannel2::WebRtcVideoReceiveStream::GetVideoReceiverInfo() {
info.capture_start_ntp_time_ms = estimated_remote_start_ntp_time_ms_;
}
info.decode_ms = stats.decode_ms;
info.max_decode_ms = stats.max_decode_ms;
info.current_delay_ms = stats.current_delay_ms;
info.target_delay_ms = stats.target_delay_ms;
info.jitter_buffer_ms = stats.jitter_buffer_ms;
info.min_playout_delay_ms = stats.min_playout_delay_ms;
info.render_delay_ms = stats.render_delay_ms;
info.firs_sent = stats.rtcp_packet_type_counts.fir_packets;
info.plis_sent = stats.rtcp_packet_type_counts.pli_packets;
info.nacks_sent = stats.rtcp_packet_type_counts.nack_packets;

View File

@ -2041,12 +2041,12 @@ TEST_F(WebRtcVideoChannel2Test, OnReadyToSendSignalsNetworkState) {
TEST_F(WebRtcVideoChannel2Test, GetStatsReportsUpperResolution) {
FakeVideoSendStream* stream = AddSendStream();
webrtc::VideoSendStream::Stats stats;
stats.substreams[17].sent_width = 123;
stats.substreams[17].sent_height = 40;
stats.substreams[42].sent_width = 80;
stats.substreams[42].sent_height = 31;
stats.substreams[11].sent_width = 20;
stats.substreams[11].sent_height = 90;
stats.substreams[17].width = 123;
stats.substreams[17].height = 40;
stats.substreams[42].width = 80;
stats.substreams[42].height = 31;
stats.substreams[11].width = 20;
stats.substreams[11].height = 90;
stream->SetStats(stats);
cricket::VideoMediaInfo info;
@ -2096,6 +2096,29 @@ TEST_F(WebRtcVideoChannel2Test,
info.receivers[0].plis_sent);
}
TEST_F(WebRtcVideoChannel2Test, GetStatsTranslatesDecodeStatsCorrectly) {
FakeVideoReceiveStream* stream = AddRecvStream();
webrtc::VideoReceiveStream::Stats stats;
stats.decode_ms = 2;
stats.max_decode_ms = 3;
stats.current_delay_ms = 4;
stats.target_delay_ms = 5;
stats.jitter_buffer_ms = 6;
stats.min_playout_delay_ms = 7;
stats.render_delay_ms = 8;
stream->SetStats(stats);
cricket::VideoMediaInfo info;
ASSERT_TRUE(channel_->GetStats(cricket::StatsOptions(), &info));
EXPECT_EQ(stats.decode_ms, info.receivers[0].decode_ms);
EXPECT_EQ(stats.max_decode_ms, info.receivers[0].max_decode_ms);
EXPECT_EQ(stats.current_delay_ms, info.receivers[0].current_delay_ms);
EXPECT_EQ(stats.target_delay_ms, info.receivers[0].target_delay_ms);
EXPECT_EQ(stats.jitter_buffer_ms, info.receivers[0].jitter_buffer_ms);
EXPECT_EQ(stats.min_playout_delay_ms, info.receivers[0].min_playout_delay_ms);
EXPECT_EQ(stats.render_delay_ms, info.receivers[0].render_delay_ms);
}
TEST_F(WebRtcVideoChannel2Test, TranslatesCallStatsCorrectly) {
AddSendStream();
AddSendStream();

View File

@ -21,27 +21,6 @@
namespace webrtc {
struct SsrcStats {
SsrcStats()
: sent_width(0),
sent_height(0),
total_bitrate_bps(0),
retransmit_bitrate_bps(0),
avg_delay_ms(0),
max_delay_ms(0) {}
FrameCounts frame_counts;
int sent_width;
int sent_height;
// TODO(holmer): Move bitrate_bps out to the webrtc::Call layer.
int total_bitrate_bps;
int retransmit_bitrate_bps;
int avg_delay_ms;
int max_delay_ms;
StreamDataCounters rtp_stats;
RtcpPacketTypeCounter rtcp_packet_type_counts;
RtcpStatistics rtcp_stats;
};
// Settings for NACK, see RFC 4585 for details.
struct NackConfig {
NackConfig() : rtp_history_ms(0) {}

View File

@ -1630,7 +1630,7 @@ TEST_F(EndToEndTest, GetStats) {
stats.rtp_stats.retransmitted.packets != 0;
receive_stats_filled_["CodecStats"] |=
stats.avg_delay_ms != 0 || stats.discarded_packets != 0;
stats.target_delay_ms != 0 || stats.discarded_packets != 0;
receive_stats_filled_["FrameCounts"] |=
stats.frame_counts.key_frames != 0 ||
@ -1656,17 +1656,16 @@ TEST_F(EndToEndTest, GetStats) {
send_stats_filled_["NumStreams"] |=
stats.substreams.size() == expected_send_ssrcs_.size();
for (std::map<uint32_t, SsrcStats>::const_iterator it =
for (std::map<uint32_t, VideoSendStream::StreamStats>::const_iterator it =
stats.substreams.begin();
it != stats.substreams.end();
++it) {
it != stats.substreams.end(); ++it) {
EXPECT_TRUE(expected_send_ssrcs_.find(it->first) !=
expected_send_ssrcs_.end());
send_stats_filled_[CompoundKey("CapturedFrameRate", it->first)] |=
stats.input_frame_rate != 0;
const SsrcStats& stream_stats = it->second;
const VideoSendStream::StreamStats& stream_stats = it->second;
send_stats_filled_[CompoundKey("StatisticsUpdated", it->first)] |=
stream_stats.rtcp_stats.cumulative_lost != 0 ||

View File

@ -47,7 +47,13 @@ void ReceiveStatisticsProxy::DecoderTiming(int decode_ms,
int min_playout_delay_ms,
int render_delay_ms) {
CriticalSectionScoped lock(crit_.get());
stats_.avg_delay_ms = target_delay_ms;
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;
}
void ReceiveStatisticsProxy::RtcpPacketTypesCounterUpdated(

View File

@ -57,20 +57,23 @@ VideoSendStream::Stats SendStatisticsProxy::GetStats() {
void SendStatisticsProxy::PurgeOldStats() {
int64_t current_time_ms = clock_->TimeInMilliseconds();
for (std::map<uint32_t, SsrcStats>::iterator it = stats_.substreams.begin();
for (std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
stats_.substreams.begin();
it != stats_.substreams.end(); ++it) {
uint32_t ssrc = it->first;
if (update_times_[ssrc].resolution_update_ms + kStatsTimeoutMs >
current_time_ms)
continue;
it->second.sent_width = 0;
it->second.sent_height = 0;
it->second.width = 0;
it->second.height = 0;
}
}
SsrcStats* SendStatisticsProxy::GetStatsEntry(uint32_t ssrc) {
std::map<uint32_t, SsrcStats>::iterator it = stats_.substreams.find(ssrc);
VideoSendStream::StreamStats* SendStatisticsProxy::GetStatsEntry(
uint32_t ssrc) {
std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
stats_.substreams.find(ssrc);
if (it != stats_.substreams.end())
return &it->second;
@ -98,12 +101,12 @@ void SendStatisticsProxy::OnSendEncodedImage(
uint32_t ssrc = config_.rtp.ssrcs[simulcast_idx];
CriticalSectionScoped lock(crit_.get());
SsrcStats* stats = GetStatsEntry(ssrc);
VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
if (stats == NULL)
return;
stats->sent_width = encoded_image._encodedWidth;
stats->sent_height = encoded_image._encodedHeight;
stats->width = encoded_image._encodedWidth;
stats->height = encoded_image._encodedHeight;
update_times_[ssrc].resolution_update_ms = clock_->TimeInMilliseconds();
}
@ -111,7 +114,7 @@ void SendStatisticsProxy::RtcpPacketTypesCounterUpdated(
uint32_t ssrc,
const RtcpPacketTypeCounter& packet_counter) {
CriticalSectionScoped lock(crit_.get());
SsrcStats* stats = GetStatsEntry(ssrc);
VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
if (stats == NULL)
return;
@ -121,7 +124,7 @@ void SendStatisticsProxy::RtcpPacketTypesCounterUpdated(
void SendStatisticsProxy::StatisticsUpdated(const RtcpStatistics& statistics,
uint32_t ssrc) {
CriticalSectionScoped lock(crit_.get());
SsrcStats* stats = GetStatsEntry(ssrc);
VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
if (stats == NULL)
return;
@ -135,11 +138,9 @@ void SendStatisticsProxy::DataCountersUpdated(
const StreamDataCounters& counters,
uint32_t ssrc) {
CriticalSectionScoped lock(crit_.get());
SsrcStats* stats = GetStatsEntry(ssrc);
VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
DCHECK(stats != NULL) << "DataCountersUpdated reported for unknown ssrc: "
<< ssrc;
if (stats == NULL)
return;
stats->rtp_stats = counters;
}
@ -148,7 +149,7 @@ void SendStatisticsProxy::Notify(const BitrateStatistics& total_stats,
const BitrateStatistics& retransmit_stats,
uint32_t ssrc) {
CriticalSectionScoped lock(crit_.get());
SsrcStats* stats = GetStatsEntry(ssrc);
VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
if (stats == NULL)
return;
@ -159,7 +160,7 @@ void SendStatisticsProxy::Notify(const BitrateStatistics& total_stats,
void SendStatisticsProxy::FrameCountUpdated(const FrameCounts& frame_counts,
uint32_t ssrc) {
CriticalSectionScoped lock(crit_.get());
SsrcStats* stats = GetStatsEntry(ssrc);
VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
if (stats == NULL)
return;
@ -170,7 +171,7 @@ void SendStatisticsProxy::SendSideDelayUpdated(int avg_delay_ms,
int max_delay_ms,
uint32_t ssrc) {
CriticalSectionScoped lock(crit_.get());
SsrcStats* stats = GetStatsEntry(ssrc);
VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
if (stats == NULL)
return;
stats->avg_delay_ms = avg_delay_ms;

View File

@ -94,7 +94,8 @@ class SendStatisticsProxy : public RtcpStatisticsCallback,
int64_t resolution_update_ms;
};
void PurgeOldStats() EXCLUSIVE_LOCKS_REQUIRED(crit_);
SsrcStats* GetStatsEntry(uint32_t ssrc) EXCLUSIVE_LOCKS_REQUIRED(crit_);
VideoSendStream::StreamStats* GetStatsEntry(uint32_t ssrc)
EXCLUSIVE_LOCKS_REQUIRED(crit_);
Clock* const clock_;
const VideoSendStream::Config config_;

View File

@ -49,15 +49,14 @@ class SendStatisticsProxyTest : public ::testing::Test {
EXPECT_EQ(one.suspended, other.suspended);
EXPECT_EQ(one.substreams.size(), other.substreams.size());
for (std::map<uint32_t, SsrcStats>::const_iterator it =
for (std::map<uint32_t, VideoSendStream::StreamStats>::const_iterator it =
one.substreams.begin();
it != one.substreams.end();
++it) {
std::map<uint32_t, SsrcStats>::const_iterator corresponding_it =
other.substreams.find(it->first);
it != one.substreams.end(); ++it) {
std::map<uint32_t, VideoSendStream::StreamStats>::const_iterator
corresponding_it = other.substreams.find(it->first);
ASSERT_TRUE(corresponding_it != other.substreams.end());
const SsrcStats& a = it->second;
const SsrcStats& b = corresponding_it->second;
const VideoSendStream::StreamStats& a = it->second;
const VideoSendStream::StreamStats& b = corresponding_it->second;
EXPECT_EQ(a.frame_counts.key_frames, b.frame_counts.key_frames);
EXPECT_EQ(a.frame_counts.delta_frames, b.frame_counts.delta_frames);
@ -91,7 +90,8 @@ class SendStatisticsProxyTest : public ::testing::Test {
int avg_delay_ms_;
int max_delay_ms_;
VideoSendStream::Stats expected_;
typedef std::map<uint32_t, SsrcStats>::const_iterator StreamIterator;
typedef std::map<uint32_t, VideoSendStream::StreamStats>::const_iterator
StreamIterator;
};
TEST_F(SendStatisticsProxyTest, RtcpStatistics) {
@ -100,7 +100,7 @@ TEST_F(SendStatisticsProxyTest, RtcpStatistics) {
it != config_.rtp.ssrcs.end();
++it) {
const uint32_t ssrc = *it;
SsrcStats& ssrc_stats = expected_.substreams[ssrc];
VideoSendStream::StreamStats& ssrc_stats = expected_.substreams[ssrc];
// Add statistics with some arbitrary, but unique, numbers.
uint32_t offset = ssrc * sizeof(RtcpStatistics);
@ -114,7 +114,7 @@ TEST_F(SendStatisticsProxyTest, RtcpStatistics) {
it != config_.rtp.rtx.ssrcs.end();
++it) {
const uint32_t ssrc = *it;
SsrcStats& ssrc_stats = expected_.substreams[ssrc];
VideoSendStream::StreamStats& ssrc_stats = expected_.substreams[ssrc];
// Add statistics with some arbitrary, but unique, numbers.
uint32_t offset = ssrc * sizeof(RtcpStatistics);
@ -171,8 +171,8 @@ TEST_F(SendStatisticsProxyTest, FrameCounts) {
++it) {
const uint32_t ssrc = *it;
// Add statistics with some arbitrary, but unique, numbers.
SsrcStats& stats = expected_.substreams[ssrc];
uint32_t offset = ssrc * sizeof(SsrcStats);
VideoSendStream::StreamStats& stats = expected_.substreams[ssrc];
uint32_t offset = ssrc * sizeof(VideoSendStream::StreamStats);
FrameCounts frame_counts;
frame_counts.key_frames = offset;
frame_counts.delta_frames = offset + 1;
@ -184,8 +184,8 @@ TEST_F(SendStatisticsProxyTest, FrameCounts) {
++it) {
const uint32_t ssrc = *it;
// Add statistics with some arbitrary, but unique, numbers.
SsrcStats& stats = expected_.substreams[ssrc];
uint32_t offset = ssrc * sizeof(SsrcStats);
VideoSendStream::StreamStats& stats = expected_.substreams[ssrc];
uint32_t offset = ssrc * sizeof(VideoSendStream::StreamStats);
FrameCounts frame_counts;
frame_counts.key_frames = offset;
frame_counts.delta_frames = offset + 1;
@ -342,16 +342,16 @@ TEST_F(SendStatisticsProxyTest, EncodedResolutionTimesOut) {
statistics_proxy_->OnSendEncodedImage(encoded_image, &rtp_video_header);
VideoSendStream::Stats stats = statistics_proxy_->GetStats();
EXPECT_EQ(kEncodedWidth, stats.substreams[config_.rtp.ssrcs[0]].sent_width);
EXPECT_EQ(kEncodedHeight, stats.substreams[config_.rtp.ssrcs[0]].sent_height);
EXPECT_EQ(kEncodedWidth, stats.substreams[config_.rtp.ssrcs[1]].sent_width);
EXPECT_EQ(kEncodedHeight, stats.substreams[config_.rtp.ssrcs[1]].sent_height);
EXPECT_EQ(kEncodedWidth, stats.substreams[config_.rtp.ssrcs[0]].width);
EXPECT_EQ(kEncodedHeight, stats.substreams[config_.rtp.ssrcs[0]].height);
EXPECT_EQ(kEncodedWidth, stats.substreams[config_.rtp.ssrcs[1]].width);
EXPECT_EQ(kEncodedHeight, stats.substreams[config_.rtp.ssrcs[1]].height);
// Forward almost to timeout, this should not have removed stats.
fake_clock_.AdvanceTimeMilliseconds(SendStatisticsProxy::kStatsTimeoutMs - 1);
stats = statistics_proxy_->GetStats();
EXPECT_EQ(kEncodedWidth, stats.substreams[config_.rtp.ssrcs[0]].sent_width);
EXPECT_EQ(kEncodedHeight, stats.substreams[config_.rtp.ssrcs[0]].sent_height);
EXPECT_EQ(kEncodedWidth, stats.substreams[config_.rtp.ssrcs[0]].width);
EXPECT_EQ(kEncodedHeight, stats.substreams[config_.rtp.ssrcs[0]].height);
// Update the first SSRC with bogus RTCP stats to make sure that encoded
// resolution still times out (no global timeout for all stats).
@ -368,10 +368,10 @@ TEST_F(SendStatisticsProxyTest, EncodedResolutionTimesOut) {
// reported, but substream 1 should.
fake_clock_.AdvanceTimeMilliseconds(1);
stats = statistics_proxy_->GetStats();
EXPECT_EQ(0, stats.substreams[config_.rtp.ssrcs[0]].sent_width);
EXPECT_EQ(0, stats.substreams[config_.rtp.ssrcs[0]].sent_height);
EXPECT_EQ(kEncodedWidth, stats.substreams[config_.rtp.ssrcs[1]].sent_width);
EXPECT_EQ(kEncodedHeight, stats.substreams[config_.rtp.ssrcs[1]].sent_height);
EXPECT_EQ(0, stats.substreams[config_.rtp.ssrcs[0]].width);
EXPECT_EQ(0, stats.substreams[config_.rtp.ssrcs[0]].height);
EXPECT_EQ(kEncodedWidth, stats.substreams[config_.rtp.ssrcs[1]].width);
EXPECT_EQ(kEncodedHeight, stats.substreams[config_.rtp.ssrcs[1]].height);
}
} // namespace webrtc

View File

@ -1673,9 +1673,10 @@ TEST_F(VideoSendStreamTest, ReportsSentResolution) {
stats.substreams.end())
<< "No stats for SSRC: " << kSendSsrcs[i]
<< ", stats should exist as soon as frames have been encoded.";
SsrcStats ssrc_stats = stats.substreams[kSendSsrcs[i]];
EXPECT_EQ(kEncodedResolution[i].width, ssrc_stats.sent_width);
EXPECT_EQ(kEncodedResolution[i].height, ssrc_stats.sent_height);
VideoSendStream::StreamStats ssrc_stats =
stats.substreams[kSendSsrcs[i]];
EXPECT_EQ(kEncodedResolution[i].width, ssrc_stats.width);
EXPECT_EQ(kEncodedResolution[i].height, ssrc_stats.height);
}
}

View File

@ -64,22 +64,29 @@ class VideoReceiveStream {
int expected_delay_ms;
};
struct Stats : public SsrcStats {
Stats()
: network_frame_rate(0),
decode_frame_rate(0),
render_frame_rate(0),
avg_delay_ms(0),
discarded_packets(0),
ssrc(0) {}
struct Stats {
int network_frame_rate = 0;
int decode_frame_rate = 0;
int render_frame_rate = 0;
int network_frame_rate;
int decode_frame_rate;
int render_frame_rate;
int avg_delay_ms;
int discarded_packets;
uint32_t ssrc;
// Decoder stats.
FrameCounts frame_counts;
int decode_ms = 0;
int max_decode_ms = 0;
int current_delay_ms = 0;
int target_delay_ms = 0;
int jitter_buffer_ms = 0;
int min_playout_delay_ms = 0;
int render_delay_ms = 0;
int total_bitrate_bps = 0;
int discarded_packets = 0;
uint32_t ssrc = 0;
std::string c_name;
StreamDataCounters rtp_stats;
RtcpPacketTypeCounter rtcp_packet_type_counts;
RtcpStatistics rtcp_stats;
};
struct Config {

View File

@ -37,6 +37,20 @@ class VideoSendStreamInput {
class VideoSendStream {
public:
struct StreamStats {
FrameCounts frame_counts;
int width = 0;
int height = 0;
// TODO(holmer): Move bitrate_bps out to the webrtc::Call layer.
int total_bitrate_bps = 0;
int retransmit_bitrate_bps = 0;
int avg_delay_ms = 0;
int max_delay_ms = 0;
StreamDataCounters rtp_stats;
RtcpPacketTypeCounter rtcp_packet_type_counts;
RtcpStatistics rtcp_stats;
};
struct Stats {
Stats()
: input_frame_rate(0),
@ -47,7 +61,7 @@ class VideoSendStream {
int encode_frame_rate;
int media_bitrate_bps;
bool suspended;
std::map<uint32_t, SsrcStats> substreams;
std::map<uint32_t, StreamStats> substreams;
};
struct Config {