summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsprang@webrtc.org <sprang@webrtc.org@4adac7df-926f-26a2-2b94-8c16560cd09d>2014-01-07 09:54:34 +0000
committersprang@webrtc.org <sprang@webrtc.org@4adac7df-926f-26a2-2b94-8c16560cd09d>2014-01-07 09:54:34 +0000
commit49812e6680a6392d53897b82b1b8dbfca6dd4faa (patch)
treea5f2d0931cb826a06cc543d28cca0f37d13d1c13
parentc902d880d3ea38dd395f6b896e0fbc904b6678dd (diff)
downloadwebrtc-49812e6680a6392d53897b82b1b8dbfca6dd4faa.tar.gz
Wire up statistics in video send stream of new video engine api
Note, this CL does not contain any tests. Those are implemeted as call tests and will be submitted when the receive stream is wired up as well. BUG=2235 R=mflodman@webrtc.org, pbos@webrtc.org Review URL: https://webrtc-codereview.appspot.com/5559006 git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@5344 4adac7df-926f-26a2-2b94-8c16560cd09d
-rw-r--r--common_types.h14
-rw-r--r--config.h17
-rw-r--r--video/send_statistics_proxy.cc114
-rw-r--r--video/send_statistics_proxy.h91
-rw-r--r--video/send_statistics_proxy_unittest.cc191
-rw-r--r--video/video_send_stream.cc39
-rw-r--r--video/video_send_stream.h29
-rw-r--r--video/video_send_stream_tests.cc65
-rw-r--r--video/webrtc_video.gypi2
-rw-r--r--video_engine/vie_channel.cc2
-rw-r--r--video_engine/vie_channel.h1
-rw-r--r--video_send_stream.h54
-rw-r--r--webrtc_tests.gypi1
13 files changed, 575 insertions, 45 deletions
diff --git a/common_types.h b/common_types.h
index 3d47b861..0ae01a62 100644
--- a/common_types.h
+++ b/common_types.h
@@ -246,6 +246,13 @@ struct RtcpStatistics {
uint32_t cumulative_lost;
uint32_t extended_max_sequence_number;
uint32_t jitter;
+
+ bool operator==(const RtcpStatistics& other) const {
+ return fraction_lost == other.fraction_lost &&
+ cumulative_lost == other.cumulative_lost &&
+ extended_max_sequence_number == other.extended_max_sequence_number &&
+ jitter == other.jitter;
+ }
};
// Callback, called whenever a new rtcp report block is transmitted.
@@ -273,6 +280,13 @@ struct StreamDataCounters {
uint32_t packets; // Number of packets.
uint32_t retransmitted_packets; // Number of retransmitted packets.
uint32_t fec_packets; // Number of redundancy packets.
+
+ bool operator==(const StreamDataCounters& other) const {
+ return bytes == other.bytes && header_bytes == other.header_bytes &&
+ padding_bytes == other.padding_bytes && packets == other.packets &&
+ retransmitted_packets == other.retransmitted_packets &&
+ fec_packets == other.fec_packets;
+ }
};
// Callback, called whenever byte/packet counts have been updated.
diff --git a/config.h b/config.h
index 3ff3bb86..f42895ae 100644
--- a/config.h
+++ b/config.h
@@ -16,6 +16,7 @@
#include <string>
#include <vector>
+#include "webrtc/common_types.h"
#include "webrtc/typedefs.h"
namespace webrtc {
@@ -33,6 +34,22 @@ struct RtpStatistics {
std::string c_name;
};
+struct StreamStats {
+ StreamStats() : key_frames(0), delta_frames(0), bitrate_bps(0) {}
+ uint32_t key_frames;
+ uint32_t delta_frames;
+ int32_t bitrate_bps;
+ StreamDataCounters rtp_stats;
+ RtcpStatistics rtcp_stats;
+
+ bool operator==(const StreamStats& other) const {
+ return key_frames == other.key_frames &&
+ delta_frames == other.delta_frames &&
+ bitrate_bps == other.bitrate_bps && rtp_stats == other.rtp_stats &&
+ rtcp_stats == other.rtcp_stats;
+ }
+};
+
// Settings for NACK, see RFC 4585 for details.
struct NackConfig {
NackConfig() : rtp_history_ms(0) {}
diff --git a/video/send_statistics_proxy.cc b/video/send_statistics_proxy.cc
new file mode 100644
index 00000000..423d7146
--- /dev/null
+++ b/video/send_statistics_proxy.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/video/send_statistics_proxy.h"
+
+#include <map>
+
+#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
+
+namespace webrtc {
+
+SendStatisticsProxy::SendStatisticsProxy(
+ const VideoSendStream::Config& config,
+ SendStatisticsProxy::StreamStatsProvider* stats_provider)
+ : config_(config),
+ lock_(CriticalSectionWrapper::CreateCriticalSection()),
+ stats_provider_(stats_provider) {}
+
+SendStatisticsProxy::~SendStatisticsProxy() {}
+
+void SendStatisticsProxy::OutgoingRate(const int video_channel,
+ const unsigned int framerate,
+ const unsigned int bitrate) {
+ CriticalSectionScoped cs(lock_.get());
+ stats_.encode_frame_rate = framerate;
+}
+
+void SendStatisticsProxy::CapturedFrameRate(const int capture_id,
+ const unsigned char frame_rate) {
+ CriticalSectionScoped cs(lock_.get());
+ stats_.input_frame_rate = frame_rate;
+}
+
+VideoSendStream::Stats SendStatisticsProxy::GetStats() const {
+ VideoSendStream::Stats stats = stats_;
+ CriticalSectionScoped cs(lock_.get());
+ stats_provider_->GetSendSideDelay(&stats);
+ stats.c_name = stats_provider_->GetCName();
+ return stats;
+}
+
+StreamStats* SendStatisticsProxy::GetStatsEntry(uint32_t ssrc) {
+ std::map<uint32_t, StreamStats>::iterator it = stats_.substreams.find(ssrc);
+ if (it != stats_.substreams.end())
+ return &it->second;
+
+ if (std::find(config_.rtp.ssrcs.begin(), config_.rtp.ssrcs.end(), ssrc) ==
+ config_.rtp.ssrcs.end())
+ return NULL;
+
+ return &stats_.substreams[ssrc]; // Insert new entry and return ptr.
+}
+
+void SendStatisticsProxy::StatisticsUpdated(const RtcpStatistics& statistics,
+ uint32_t ssrc) {
+ CriticalSectionScoped cs(lock_.get());
+ StreamStats* stats = GetStatsEntry(ssrc);
+ if (stats == NULL)
+ return;
+
+ stats->rtcp_stats = statistics;
+}
+
+void SendStatisticsProxy::DataCountersUpdated(
+ const StreamDataCounters& counters,
+ uint32_t ssrc) {
+ CriticalSectionScoped cs(lock_.get());
+ StreamStats* stats = GetStatsEntry(ssrc);
+ if (stats == NULL)
+ return;
+
+ stats->rtp_stats = counters;
+}
+
+void SendStatisticsProxy::Notify(const BitrateStatistics& bitrate,
+ uint32_t ssrc) {
+ CriticalSectionScoped cs(lock_.get());
+ StreamStats* stats = GetStatsEntry(ssrc);
+ if (stats == NULL)
+ return;
+
+ stats->bitrate_bps = bitrate.bitrate_bps;
+}
+
+void SendStatisticsProxy::FrameCountUpdated(FrameType frame_type,
+ uint32_t frame_count,
+ const unsigned int ssrc) {
+ CriticalSectionScoped cs(lock_.get());
+ StreamStats* stats = GetStatsEntry(ssrc);
+ if (stats == NULL)
+ return;
+
+ switch (frame_type) {
+ case kVideoFrameDelta:
+ stats->delta_frames = frame_count;
+ break;
+ case kVideoFrameKey:
+ stats->key_frames = frame_count;
+ break;
+ case kFrameEmpty:
+ case kAudioFrameSpeech:
+ case kAudioFrameCN:
+ break;
+ }
+}
+
+} // namespace webrtc
diff --git a/video/send_statistics_proxy.h b/video/send_statistics_proxy.h
new file mode 100644
index 00000000..4f2fafbe
--- /dev/null
+++ b/video/send_statistics_proxy.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_VIDEO_SEND_STATISTICS_PROXY_H_
+#define WEBRTC_VIDEO_SEND_STATISTICS_PROXY_H_
+
+#include <string>
+
+#include "webrtc/common_types.h"
+#include "webrtc/video_engine/include/vie_codec.h"
+#include "webrtc/video_engine/include/vie_capture.h"
+#include "webrtc/video_send_stream.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+
+namespace webrtc {
+
+class CriticalSectionWrapper;
+
+class SendStatisticsProxy : public RtcpStatisticsCallback,
+ public StreamDataCountersCallback,
+ public BitrateStatisticsObserver,
+ public FrameCountObserver,
+ public ViEEncoderObserver,
+ public ViECaptureObserver {
+ public:
+ class StreamStatsProvider {
+ public:
+ StreamStatsProvider() {}
+ virtual ~StreamStatsProvider() {}
+
+ virtual bool GetSendSideDelay(VideoSendStream::Stats* stats) = 0;
+ virtual std::string GetCName() = 0;
+ };
+
+ SendStatisticsProxy(const VideoSendStream::Config& config,
+ StreamStatsProvider* stats_provider);
+ virtual ~SendStatisticsProxy();
+
+ VideoSendStream::Stats GetStats() const;
+
+ protected:
+ // From RtcpStatisticsCallback.
+ virtual void StatisticsUpdated(const RtcpStatistics& statistics,
+ uint32_t ssrc) OVERRIDE;
+ // From StreamDataCountersCallback.
+ virtual void DataCountersUpdated(const StreamDataCounters& counters,
+ uint32_t ssrc) OVERRIDE;
+
+ // From BitrateStatisticsObserver.
+ virtual void Notify(const BitrateStatistics& stats, uint32_t ssrc) OVERRIDE;
+
+ // From FrameCountObserver.
+ virtual void FrameCountUpdated(FrameType frame_type,
+ uint32_t frame_count,
+ const unsigned int ssrc) OVERRIDE;
+
+ // From ViEEncoderObserver.
+ virtual void OutgoingRate(const int video_channel,
+ const unsigned int framerate,
+ const unsigned int bitrate) OVERRIDE;
+
+ virtual void SuspendChange(int video_channel, bool is_suspended) OVERRIDE {}
+
+ // From ViECaptureObserver.
+ virtual void BrightnessAlarm(const int capture_id,
+ const Brightness brightness) OVERRIDE {}
+
+ virtual void CapturedFrameRate(const int capture_id,
+ const unsigned char frame_rate) OVERRIDE;
+
+ virtual void NoPictureAlarm(const int capture_id,
+ const CaptureAlarm alarm) OVERRIDE {}
+
+ private:
+ StreamStats* GetStatsEntry(uint32_t ssrc);
+
+ const VideoSendStream::Config config_;
+ scoped_ptr<CriticalSectionWrapper> lock_;
+ VideoSendStream::Stats stats_;
+ StreamStatsProvider* stats_provider_;
+};
+
+} // namespace webrtc
+#endif // WEBRTC_VIDEO_SEND_STATISTICS_PROXY_H_
diff --git a/video/send_statistics_proxy_unittest.cc b/video/send_statistics_proxy_unittest.cc
new file mode 100644
index 00000000..6e921928
--- /dev/null
+++ b/video/send_statistics_proxy_unittest.cc
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// This file includes unit tests for SendStatisticsProxy.
+#include "webrtc/video/send_statistics_proxy.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace webrtc {
+
+class SendStatisticsProxyTest
+ : public ::testing::Test,
+ protected SendStatisticsProxy::StreamStatsProvider {
+ public:
+ SendStatisticsProxyTest() : avg_delay_ms_(0), max_delay_ms_(0) {}
+ virtual ~SendStatisticsProxyTest() {}
+
+ protected:
+ virtual void SetUp() {
+ statistics_proxy_.reset(
+ new SendStatisticsProxy(GetTestConfig(), this));
+ config_ = GetTestConfig();
+ expected_ = VideoSendStream::Stats();
+ }
+
+ VideoSendStream::Config GetTestConfig() {
+ VideoSendStream::Config config;
+ config.rtp.ssrcs.push_back(17);
+ config.rtp.ssrcs.push_back(42);
+ return config;
+ }
+
+ virtual bool GetSendSideDelay(VideoSendStream::Stats* stats) OVERRIDE {
+ stats->avg_delay_ms = avg_delay_ms_;
+ stats->max_delay_ms = max_delay_ms_;
+ return true;
+ }
+
+ virtual std::string GetCName() { return cname_; }
+
+ scoped_ptr<SendStatisticsProxy> statistics_proxy_;
+ VideoSendStream::Config config_;
+ int avg_delay_ms_;
+ int max_delay_ms_;
+ std::string cname_;
+ VideoSendStream::Stats expected_;
+ typedef std::map<uint32_t, StreamStats>::const_iterator StreamIterator;
+};
+
+TEST_F(SendStatisticsProxyTest, RtcpStatistics) {
+ RtcpStatisticsCallback* callback = statistics_proxy_.get();
+ for (std::vector<uint32_t>::const_iterator it = config_.rtp.ssrcs.begin();
+ it != config_.rtp.ssrcs.end();
+ ++it) {
+ const uint32_t ssrc = *it;
+ StreamStats& ssrc_stats = expected_.substreams[ssrc];
+
+ // Add statistics with some arbitrary, but unique, numbers.
+ uint32_t offset = ssrc * sizeof(RtcpStatistics);
+ ssrc_stats.rtcp_stats.cumulative_lost = offset;
+ ssrc_stats.rtcp_stats.extended_max_sequence_number = offset + 1;
+ ssrc_stats.rtcp_stats.fraction_lost = offset + 2;
+ ssrc_stats.rtcp_stats.jitter = offset + 3;
+ callback->StatisticsUpdated(ssrc_stats.rtcp_stats, ssrc);
+ }
+
+ VideoSendStream::Stats stats = statistics_proxy_->GetStats();
+ EXPECT_EQ(expected_, stats);
+}
+
+TEST_F(SendStatisticsProxyTest, FrameRates) {
+ const int capture_fps = 31;
+ const int encode_fps = 29;
+
+ ViECaptureObserver* capture_observer = statistics_proxy_.get();
+ capture_observer->CapturedFrameRate(0, capture_fps);
+ ViEEncoderObserver* encoder_observer = statistics_proxy_.get();
+ encoder_observer->OutgoingRate(0, encode_fps, 0);
+
+ VideoSendStream::Stats stats = statistics_proxy_->GetStats();
+ EXPECT_EQ(capture_fps, stats.input_frame_rate);
+ EXPECT_EQ(encode_fps, stats.encode_frame_rate);
+}
+
+TEST_F(SendStatisticsProxyTest, FrameCounts) {
+ FrameCountObserver* observer = statistics_proxy_.get();
+ for (std::vector<uint32_t>::const_iterator it = config_.rtp.ssrcs.begin();
+ it != config_.rtp.ssrcs.end();
+ ++it) {
+ const uint32_t ssrc = *it;
+ // Add statistics with some arbitrary, but unique, numbers.
+ StreamStats& stats = expected_.substreams[ssrc];
+ uint32_t offset = ssrc * sizeof(StreamStats);
+ stats.key_frames = offset;
+ stats.delta_frames = offset + 1;
+ observer->FrameCountUpdated(kVideoFrameKey, stats.key_frames, ssrc);
+ observer->FrameCountUpdated(kVideoFrameDelta, stats.delta_frames, ssrc);
+ }
+
+ VideoSendStream::Stats stats = statistics_proxy_->GetStats();
+ EXPECT_EQ(expected_, stats);
+}
+
+TEST_F(SendStatisticsProxyTest, DataCounters) {
+ StreamDataCountersCallback* callback = statistics_proxy_.get();
+ for (std::vector<uint32_t>::const_iterator it = config_.rtp.ssrcs.begin();
+ it != config_.rtp.ssrcs.end();
+ ++it) {
+ const uint32_t ssrc = *it;
+ StreamDataCounters& counters = expected_.substreams[ssrc].rtp_stats;
+ // Add statistics with some arbitrary, but unique, numbers.
+ uint32_t offset = ssrc * sizeof(StreamDataCounters);
+ counters.bytes = offset;
+ counters.header_bytes = offset + 1;
+ counters.fec_packets = offset + 2;
+ counters.padding_bytes = offset + 3;
+ counters.retransmitted_packets = offset + 4;
+ counters.packets = offset + 5;
+ callback->DataCountersUpdated(counters, ssrc);
+ }
+
+ VideoSendStream::Stats stats = statistics_proxy_->GetStats();
+ EXPECT_EQ(expected_, stats);
+}
+
+TEST_F(SendStatisticsProxyTest, Bitrate) {
+ BitrateStatisticsObserver* observer = statistics_proxy_.get();
+ for (std::vector<uint32_t>::const_iterator it = config_.rtp.ssrcs.begin();
+ it != config_.rtp.ssrcs.end();
+ ++it) {
+ const uint32_t ssrc = *it;
+ BitrateStatistics bitrate;
+ bitrate.bitrate_bps = ssrc;
+ observer->Notify(bitrate, ssrc);
+ expected_.substreams[ssrc].bitrate_bps = ssrc;
+ }
+
+ VideoSendStream::Stats stats = statistics_proxy_->GetStats();
+ EXPECT_EQ(expected_, stats);
+}
+
+TEST_F(SendStatisticsProxyTest, StreamStats) {
+ avg_delay_ms_ = 1;
+ max_delay_ms_ = 2;
+ cname_ = "qwertyuiop";
+
+ VideoSendStream::Stats stats = statistics_proxy_->GetStats();
+
+ EXPECT_EQ(avg_delay_ms_, stats.avg_delay_ms);
+ EXPECT_EQ(max_delay_ms_, stats.max_delay_ms);
+ EXPECT_EQ(cname_, stats.c_name);
+}
+
+TEST_F(SendStatisticsProxyTest, NoSubstreams) {
+ uint32_t exluded_ssrc =
+ *std::max_element(config_.rtp.ssrcs.begin(), config_.rtp.ssrcs.end()) + 1;
+ // From RtcpStatisticsCallback.
+ RtcpStatistics rtcp_stats;
+ RtcpStatisticsCallback* rtcp_callback = statistics_proxy_.get();
+ rtcp_callback->StatisticsUpdated(rtcp_stats, exluded_ssrc);
+
+ // From StreamDataCountersCallback.
+ StreamDataCounters rtp_stats;
+ StreamDataCountersCallback* rtp_callback = statistics_proxy_.get();
+ rtp_callback->DataCountersUpdated(rtp_stats, exluded_ssrc);
+
+ // From BitrateStatisticsObserver.
+ BitrateStatistics bitrate;
+ BitrateStatisticsObserver* bitrate_observer = statistics_proxy_.get();
+ bitrate_observer->Notify(bitrate, exluded_ssrc);
+
+ // From FrameCountObserver.
+ FrameCountObserver* fps_observer = statistics_proxy_.get();
+ fps_observer->FrameCountUpdated(kVideoFrameKey, 1, exluded_ssrc);
+
+ VideoSendStream::Stats stats = statistics_proxy_->GetStats();
+ EXPECT_TRUE(stats.substreams.empty());
+}
+
+} // namespace webrtc
diff --git a/video/video_send_stream.cc b/video/video_send_stream.cc
index e18b3462..0ab70391 100644
--- a/video/video_send_stream.cc
+++ b/video/video_send_stream.cc
@@ -186,9 +186,32 @@ VideoSendStream::VideoSendStream(newapi::Transport* transport,
if (config.suspend_below_min_bitrate) {
codec_->SuspendBelowMinBitrate(channel_);
}
+
+ stats_proxy_.reset(
+ new SendStatisticsProxy(config, this));
+
+ rtp_rtcp_->RegisterSendChannelRtcpStatisticsCallback(channel_,
+ stats_proxy_.get());
+ rtp_rtcp_->RegisterSendChannelRtpStatisticsCallback(channel_,
+ stats_proxy_.get());
+ rtp_rtcp_->RegisterSendBitrateObserver(channel_, stats_proxy_.get());
+ rtp_rtcp_->RegisterSendFrameCountObserver(channel_, stats_proxy_.get());
+
+ codec_->RegisterEncoderObserver(channel_, *stats_proxy_);
+ capture_->RegisterObserver(capture_id_, *stats_proxy_);
}
VideoSendStream::~VideoSendStream() {
+ capture_->DeregisterObserver(capture_id_);
+ codec_->DeregisterEncoderObserver(channel_);
+
+ rtp_rtcp_->DeregisterSendFrameCountObserver(channel_, stats_proxy_.get());
+ rtp_rtcp_->DeregisterSendBitrateObserver(channel_, stats_proxy_.get());
+ rtp_rtcp_->DeregisterSendChannelRtpStatisticsCallback(channel_,
+ stats_proxy_.get());
+ rtp_rtcp_->DeregisterSendChannelRtcpStatisticsCallback(channel_,
+ stats_proxy_.get());
+
image_process_->DeRegisterPreEncodeCallback(channel_);
network_->DeregisterSendTransport(channel_);
@@ -293,5 +316,21 @@ bool VideoSendStream::DeliverRtcp(const uint8_t* packet, size_t length) {
return network_->ReceivedRTCPPacket(
channel_, packet, static_cast<int>(length)) == 0;
}
+
+VideoSendStream::Stats VideoSendStream::GetStats() const {
+ return stats_proxy_->GetStats();
+}
+
+bool VideoSendStream::GetSendSideDelay(VideoSendStream::Stats* stats) {
+ return codec_->GetSendSideDelay(
+ channel_, &stats->avg_delay_ms, &stats->max_delay_ms);
+}
+
+std::string VideoSendStream::GetCName() {
+ char rtcp_cname[ViERTP_RTCP::KMaxRTCPCNameLength];
+ rtp_rtcp_->GetRTCPCName(channel_, rtcp_cname);
+ return rtcp_cname;
+}
+
} // namespace internal
} // namespace webrtc
diff --git a/video/video_send_stream.h b/video/video_send_stream.h
index 7433e8a3..05c481f3 100644
--- a/video/video_send_stream.h
+++ b/video/video_send_stream.h
@@ -11,10 +11,9 @@
#ifndef WEBRTC_VIDEO_VIDEO_SEND_STREAM_H_
#define WEBRTC_VIDEO_VIDEO_SEND_STREAM_H_
-#include <vector>
-
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
#include "webrtc/video/encoded_frame_callback_adapter.h"
+#include "webrtc/video/send_statistics_proxy.h"
#include "webrtc/video/transport_adapter.h"
#include "webrtc/video_receive_stream.h"
#include "webrtc/video_send_stream.h"
@@ -37,7 +36,8 @@ namespace internal {
class ResolutionAdaptor;
class VideoSendStream : public webrtc::VideoSendStream,
- public VideoSendStreamInput {
+ public VideoSendStreamInput,
+ public SendStatisticsProxy::StreamStatsProvider {
public:
VideoSendStream(newapi::Transport* transport,
bool overuse_detection,
@@ -47,12 +47,6 @@ class VideoSendStream : public webrtc::VideoSendStream,
virtual ~VideoSendStream();
- virtual void PutFrame(const I420VideoFrame& frame) OVERRIDE;
-
- virtual void SwapFrame(I420VideoFrame* frame) OVERRIDE;
-
- virtual VideoSendStreamInput* Input() OVERRIDE;
-
virtual void StartSending() OVERRIDE;
virtual void StopSending() OVERRIDE;
@@ -60,9 +54,22 @@ class VideoSendStream : public webrtc::VideoSendStream,
virtual bool SetCodec(const VideoCodec& codec) OVERRIDE;
virtual VideoCodec GetCodec() OVERRIDE;
- public:
+ virtual Stats GetStats() const OVERRIDE;
+
bool DeliverRtcp(const uint8_t* packet, size_t length);
+ // From VideoSendStreamInput.
+ virtual void PutFrame(const I420VideoFrame& frame) OVERRIDE;
+ virtual void SwapFrame(I420VideoFrame* frame) OVERRIDE;
+
+ // From webrtc::VideoSendStream.
+ virtual VideoSendStreamInput* Input() OVERRIDE;
+
+ protected:
+ // From SendStatisticsProxy::StreamStatsProvider.
+ virtual bool GetSendSideDelay(VideoSendStream::Stats* stats) OVERRIDE;
+ virtual std::string GetCName() OVERRIDE;
+
private:
I420VideoFrame input_frame_;
TransportAdapter transport_adapter_;
@@ -82,6 +89,8 @@ class VideoSendStream : public webrtc::VideoSendStream,
int channel_;
int capture_id_;
scoped_ptr<ResolutionAdaptor> overuse_observer_;
+
+ scoped_ptr<SendStatisticsProxy> stats_proxy_;
};
} // namespace internal
} // namespace webrtc
diff --git a/video/video_send_stream_tests.cc b/video/video_send_stream_tests.cc
index 42259e38..908632bb 100644
--- a/video/video_send_stream_tests.cc
+++ b/video/video_send_stream_tests.cc
@@ -35,7 +35,8 @@ namespace webrtc {
class VideoSendStreamTest : public ::testing::Test {
public:
- VideoSendStreamTest() : fake_encoder_(Clock::GetRealTimeClock()) {}
+ VideoSendStreamTest()
+ : send_stream_(NULL), fake_encoder_(Clock::GetRealTimeClock()) {}
protected:
void RunSendTest(Call* call,
@@ -190,7 +191,7 @@ void VideoSendStreamTest::SendsSetSsrcs(size_t num_ssrcs,
frame_generator_capturer->Stop();
send_stream_->StopSending();
call->DestroyVideoSendStream(send_stream_);
-};
+}
TEST_F(VideoSendStreamTest, SendsSetSsrc) { SendsSetSsrcs(1, false); }
@@ -936,4 +937,64 @@ TEST_F(VideoSendStreamTest, NoPaddingWhenVideoIsMuted) {
call->DestroyVideoSendStream(send_stream_);
}
+TEST_F(VideoSendStreamTest, ProducesStats) {
+ static std::string kCName = "PjQatC14dGfbVwGPUOA9IH7RlsFDbWl4AhXEiDsBizo=";
+ class StatsObserver : public test::RtpRtcpObserver {
+ public:
+ StatsObserver() : RtpRtcpObserver(30 * 1000), stream_(NULL) {}
+
+ virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
+ VideoSendStream::Stats stats = stream_->GetStats();
+ // Check that all applicable data sources have been used.
+ if (stats.input_frame_rate > 0 && stats.encode_frame_rate > 0 &&
+ stats.avg_delay_ms > 0 && stats.c_name == kCName &&
+ !stats.substreams.empty()) {
+ uint32_t ssrc = stats.substreams.begin()->first;
+ EXPECT_NE(
+ config_.rtp.ssrcs.end(),
+ std::find(
+ config_.rtp.ssrcs.begin(), config_.rtp.ssrcs.end(), ssrc));
+ // Check for data populated by various sources. RTCP excluded as this
+ // data is received from remote side. Tested in call tests instead.
+ StreamStats& entry = stats.substreams[ssrc];
+ if (entry.key_frames > 0u && entry.bitrate_bps > 0 &&
+ entry.rtp_stats.packets > 0u) {
+ observation_complete_->Set();
+ }
+ }
+
+ return SEND_PACKET;
+ }
+
+ void SetConfig(const VideoSendStream::Config& config) { config_ = config; }
+
+ void SetSendStream(VideoSendStream* stream) { stream_ = stream; }
+
+ VideoSendStream* stream_;
+ VideoSendStream::Config config_;
+ } observer;
+
+ Call::Config call_config(observer.SendTransport());
+ scoped_ptr<Call> call(Call::Create(call_config));
+
+ VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
+ send_config.rtp.c_name = kCName;
+ observer.SetConfig(send_config);
+
+ send_stream_ = call->CreateVideoSendStream(send_config);
+ observer.SetSendStream(send_stream_);
+ scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer(
+ test::FrameGeneratorCapturer::Create(
+ send_stream_->Input(), 320, 240, 30, Clock::GetRealTimeClock()));
+ send_stream_->StartSending();
+ frame_generator_capturer->Start();
+
+ EXPECT_EQ(kEventSignaled, observer.Wait());
+
+ observer.StopSending();
+ frame_generator_capturer->Stop();
+ send_stream_->StopSending();
+ call->DestroyVideoSendStream(send_stream_);
+}
+
} // namespace webrtc
diff --git a/video/webrtc_video.gypi b/video/webrtc_video.gypi
index 5f7784d3..0d83a514 100644
--- a/video/webrtc_video.gypi
+++ b/video/webrtc_video.gypi
@@ -14,6 +14,8 @@
'video/call.cc',
'video/encoded_frame_callback_adapter.cc',
'video/encoded_frame_callback_adapter.h',
+ 'video/send_statistics_proxy.cc',
+ 'video/send_statistics_proxy.h',
'video/transport_adapter.cc',
'video/transport_adapter.h',
'video/video_receive_stream.cc',
diff --git a/video_engine/vie_channel.cc b/video_engine/vie_channel.cc
index 54731775..6e58313b 100644
--- a/video_engine/vie_channel.cc
+++ b/video_engine/vie_channel.cc
@@ -345,8 +345,6 @@ int32_t ViEChannel::SetSendCodec(const VideoCodec& video_codec,
}
rtp_rtcp->SetSendingStatus(rtp_rtcp_->Sending());
rtp_rtcp->SetSendingMediaStatus(rtp_rtcp_->SendingMedia());
- rtp_rtcp->RegisterSendChannelRtcpStatisticsCallback(
- rtp_rtcp_->GetSendChannelRtcpStatisticsCallback());
simulcast_rtp_rtcp_.push_back(rtp_rtcp);
}
// Remove last in list if we have too many.
diff --git a/video_engine/vie_channel.h b/video_engine/vie_channel.h
index dd602eed..9313fdc5 100644
--- a/video_engine/vie_channel.h
+++ b/video_engine/vie_channel.h
@@ -400,7 +400,6 @@ class ViEChannel
scoped_ptr<RtcpBandwidthObserver> bandwidth_observer_;
int send_timestamp_extension_id_;
int absolute_send_time_extension_id_;
- bool using_packet_spread_;
Transport* external_transport_;
diff --git a/video_send_stream.h b/video_send_stream.h
index 7c1a6fd0..e59ef60d 100644
--- a/video_send_stream.h
+++ b/video_send_stream.h
@@ -11,8 +11,8 @@
#ifndef WEBRTC_VIDEO_SEND_STREAM_H_
#define WEBRTC_VIDEO_SEND_STREAM_H_
+#include <map>
#include <string>
-#include <vector>
#include "webrtc/common_types.h"
#include "webrtc/config.h"
@@ -41,32 +41,28 @@ class VideoSendStream {
struct Stats {
Stats()
: input_frame_rate(0),
- encode_frame(0),
- key_frames(0),
- delta_frames(0),
- video_packets(0),
- retransmitted_packets(0),
- fec_packets(0),
- padding_packets(0),
- send_bitrate_bps(0),
- delay_ms(0) {}
- RtpStatistics rtp;
- int input_frame_rate;
- int encode_frame;
- uint32_t key_frames;
- uint32_t delta_frames;
- uint32_t video_packets;
- uint32_t retransmitted_packets;
- uint32_t fec_packets;
- uint32_t padding_packets;
- int32_t send_bitrate_bps;
- int delay_ms;
- };
+ encode_frame_rate(0),
+ avg_delay_ms(0),
+ max_delay_ms(0) {}
- class StatsCallback {
- public:
- virtual ~StatsCallback() {}
- virtual void ReceiveStats(const std::vector<Stats>& stats) = 0;
+ int input_frame_rate;
+ int encode_frame_rate;
+ int avg_delay_ms;
+ int max_delay_ms;
+ std::string c_name;
+ std::map<uint32_t, StreamStats> substreams;
+
+ bool operator==(const Stats& other) const {
+ if (input_frame_rate != other.input_frame_rate ||
+ encode_frame_rate != other.encode_frame_rate ||
+ avg_delay_ms != other.avg_delay_ms ||
+ max_delay_ms != other.max_delay_ms || c_name != other.c_name ||
+ substreams.size() != other.substreams.size()) {
+ return false;
+ }
+ return std::equal(
+ substreams.begin(), substreams.end(), other.substreams.begin());
+ }
};
struct Config {
@@ -79,7 +75,6 @@ class VideoSendStream {
internal_source(false),
target_delay_ms(0),
pacing(false),
- stats_callback(NULL),
suspend_below_min_bitrate(false) {}
VideoCodec codec;
@@ -140,9 +135,6 @@ class VideoSendStream {
// packets onto the network.
bool pacing;
- // Callback for periodically receiving send stats.
- StatsCallback* stats_callback;
-
// True if the stream should be suspended when the available bitrate fall
// below the minimum configured bitrate. If this variable is false, the
// stream may send at a rate higher than the estimated available bitrate.
@@ -161,6 +153,8 @@ class VideoSendStream {
virtual bool SetCodec(const VideoCodec& codec) = 0;
virtual VideoCodec GetCodec() = 0;
+ virtual Stats GetStats() const = 0;
+
protected:
virtual ~VideoSendStream() {}
};
diff --git a/webrtc_tests.gypi b/webrtc_tests.gypi
index 0d2b30ee..b60660ac 100644
--- a/webrtc_tests.gypi
+++ b/webrtc_tests.gypi
@@ -35,6 +35,7 @@
'sources': [
'video/bitrate_estimator_tests.cc',
'video/call_tests.cc',
+ 'video/send_statistics_proxy_unittest.cc',
'video/video_send_stream_tests.cc',
'test/common_unittest.cc',
'test/testsupport/metrics/video_metrics_unittest.cc',