Wire up statistics in video receive stream of new API
This CL includes Call tests that test both send and receive sides. BUG=2235 R=mflodman@webrtc.org, pbos@webrtc.org Review URL: https://webrtc-codereview.appspot.com/8049004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5499 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
90ce73a0d5
commit
09315705b9
@ -246,13 +246,6 @@ 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.
|
||||
@ -280,13 +273,6 @@ 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.
|
||||
|
@ -41,13 +41,6 @@ struct StreamStats {
|
||||
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.
|
||||
|
@ -11,6 +11,8 @@
|
||||
#ifndef WEBRTC_VIDEO_ENGINE_NEW_INCLUDE_FRAME_CALLBACK_H_
|
||||
#define WEBRTC_VIDEO_ENGINE_NEW_INCLUDE_FRAME_CALLBACK_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "webrtc/common_types.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
@ -1230,6 +1230,244 @@ void CallTest::TestXrReceiverReferenceTimeReport(bool enable_rrtr) {
|
||||
DestroyStreams();
|
||||
}
|
||||
|
||||
class StatsObserver : public test::RtpRtcpObserver, public I420FrameCallback {
|
||||
public:
|
||||
StatsObserver()
|
||||
: test::RtpRtcpObserver(kLongTimeoutMs),
|
||||
receive_stream_(NULL),
|
||||
send_stream_(NULL),
|
||||
expected_receive_ssrc_(),
|
||||
expected_send_ssrcs_(),
|
||||
check_stats_event_(EventWrapper::Create()) {}
|
||||
|
||||
void SetExpectedReceiveSsrc(uint32_t ssrc) { expected_receive_ssrc_ = ssrc; }
|
||||
|
||||
void SetExpectedSendSsrcs(const std::vector<uint32_t>& ssrcs) {
|
||||
for (std::vector<uint32_t>::const_iterator it = ssrcs.begin();
|
||||
it != ssrcs.end();
|
||||
++it) {
|
||||
expected_send_ssrcs_.insert(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void SetExpectedCName(std::string cname) { expected_cname_ = cname; }
|
||||
|
||||
void SetReceiveStream(VideoReceiveStream* stream) {
|
||||
receive_stream_ = stream;
|
||||
}
|
||||
|
||||
void SetSendStream(VideoSendStream* stream) { send_stream_ = stream; }
|
||||
|
||||
void WaitForFilledStats() {
|
||||
Clock* clock = Clock::GetRealTimeClock();
|
||||
int64_t now = clock->TimeInMilliseconds();
|
||||
int64_t stop_time = now + kLongTimeoutMs;
|
||||
bool receive_ok = false;
|
||||
bool send_ok = false;
|
||||
|
||||
while (now < stop_time) {
|
||||
if (!receive_ok)
|
||||
receive_ok = CheckReceiveStats();
|
||||
if (!send_ok)
|
||||
send_ok = CheckSendStats();
|
||||
|
||||
if (receive_ok && send_ok)
|
||||
return;
|
||||
|
||||
int64_t time_until_timout_ = stop_time - now;
|
||||
if (time_until_timout_ > 0)
|
||||
check_stats_event_->Wait(time_until_timout_);
|
||||
now = clock->TimeInMilliseconds();
|
||||
}
|
||||
|
||||
ADD_FAILURE() << "Timed out waiting for filled stats.";
|
||||
for (std::map<std::string, bool>::const_iterator it =
|
||||
receive_stats_filled_.begin();
|
||||
it != receive_stats_filled_.end();
|
||||
++it) {
|
||||
if (!it->second) {
|
||||
ADD_FAILURE() << "Missing receive stats: " << it->first;
|
||||
}
|
||||
}
|
||||
|
||||
for (std::map<std::string, bool>::const_iterator it =
|
||||
send_stats_filled_.begin();
|
||||
it != send_stats_filled_.end();
|
||||
++it) {
|
||||
if (!it->second) {
|
||||
ADD_FAILURE() << "Missing send stats: " << it->first;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
|
||||
check_stats_event_->Set();
|
||||
return SEND_PACKET;
|
||||
}
|
||||
|
||||
virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
|
||||
check_stats_event_->Set();
|
||||
return SEND_PACKET;
|
||||
}
|
||||
|
||||
virtual Action OnReceiveRtp(const uint8_t* packet, size_t length) OVERRIDE {
|
||||
check_stats_event_->Set();
|
||||
return SEND_PACKET;
|
||||
}
|
||||
|
||||
virtual Action OnReceiveRtcp(const uint8_t* packet, size_t length) OVERRIDE {
|
||||
check_stats_event_->Set();
|
||||
return SEND_PACKET;
|
||||
}
|
||||
|
||||
virtual void FrameCallback(I420VideoFrame* video_frame) OVERRIDE {
|
||||
// Ensure that we have at least 5ms send side delay.
|
||||
int64_t render_time = video_frame->render_time_ms();
|
||||
if (render_time > 0)
|
||||
video_frame->set_render_time_ms(render_time - 5);
|
||||
}
|
||||
|
||||
bool CheckReceiveStats() {
|
||||
assert(receive_stream_ != NULL);
|
||||
VideoReceiveStream::Stats stats = receive_stream_->GetStats();
|
||||
EXPECT_EQ(expected_receive_ssrc_, stats.ssrc);
|
||||
|
||||
// Make sure all fields have been populated.
|
||||
|
||||
receive_stats_filled_["IncomingRate"] |=
|
||||
stats.network_frame_rate != 0 || stats.bitrate_bps != 0;
|
||||
|
||||
receive_stats_filled_["FrameCallback"] |= stats.decode_frame_rate != 0;
|
||||
|
||||
receive_stats_filled_["FrameRendered"] |= stats.render_frame_rate != 0;
|
||||
|
||||
receive_stats_filled_["StatisticsUpdated"] |=
|
||||
stats.rtcp_stats.cumulative_lost != 0 ||
|
||||
stats.rtcp_stats.extended_max_sequence_number != 0 ||
|
||||
stats.rtcp_stats.fraction_lost != 0 || stats.rtcp_stats.jitter != 0;
|
||||
|
||||
receive_stats_filled_["DataCountersUpdated"] |=
|
||||
stats.rtp_stats.bytes != 0 || stats.rtp_stats.fec_packets != 0 ||
|
||||
stats.rtp_stats.header_bytes != 0 || stats.rtp_stats.packets != 0 ||
|
||||
stats.rtp_stats.padding_bytes != 0 ||
|
||||
stats.rtp_stats.retransmitted_packets != 0;
|
||||
|
||||
receive_stats_filled_["CodecStats"] |=
|
||||
stats.avg_delay_ms != 0 || stats.discarded_packets != 0 ||
|
||||
stats.key_frames != 0 || stats.delta_frames != 0;
|
||||
|
||||
receive_stats_filled_["CName"] |= stats.c_name == expected_cname_;
|
||||
|
||||
return AllStatsFilled(receive_stats_filled_);
|
||||
}
|
||||
|
||||
bool CheckSendStats() {
|
||||
assert(send_stream_ != NULL);
|
||||
VideoSendStream::Stats stats = send_stream_->GetStats();
|
||||
|
||||
send_stats_filled_["NumStreams"] |=
|
||||
stats.substreams.size() == expected_send_ssrcs_.size();
|
||||
|
||||
send_stats_filled_["Delay"] |=
|
||||
stats.avg_delay_ms != 0 || stats.max_delay_ms != 0;
|
||||
|
||||
receive_stats_filled_["CName"] |= stats.c_name == expected_cname_;
|
||||
|
||||
for (std::map<uint32_t, StreamStats>::const_iterator it =
|
||||
stats.substreams.begin();
|
||||
it != stats.substreams.end();
|
||||
++it) {
|
||||
EXPECT_TRUE(expected_send_ssrcs_.find(it->first) !=
|
||||
expected_send_ssrcs_.end());
|
||||
|
||||
send_stats_filled_[CompoundKey("IncomingRate", it->first)] |=
|
||||
stats.input_frame_rate != 0;
|
||||
|
||||
const StreamStats& stream_stats = it->second;
|
||||
|
||||
send_stats_filled_[CompoundKey("StatisticsUpdated", it->first)] |=
|
||||
stream_stats.rtcp_stats.cumulative_lost != 0 ||
|
||||
stream_stats.rtcp_stats.extended_max_sequence_number != 0 ||
|
||||
stream_stats.rtcp_stats.fraction_lost != 0;
|
||||
|
||||
send_stats_filled_[CompoundKey("DataCountersUpdated", it->first)] |=
|
||||
stream_stats.rtp_stats.fec_packets != 0 ||
|
||||
stream_stats.rtp_stats.padding_bytes != 0 ||
|
||||
stream_stats.rtp_stats.retransmitted_packets != 0 ||
|
||||
stream_stats.rtp_stats.packets != 0;
|
||||
|
||||
send_stats_filled_[CompoundKey("BitrateStatisticsObserver", it->first)] |=
|
||||
stream_stats.bitrate_bps != 0;
|
||||
|
||||
send_stats_filled_[CompoundKey("FrameCountObserver", it->first)] |=
|
||||
stream_stats.delta_frames != 0 || stream_stats.key_frames != 0;
|
||||
|
||||
send_stats_filled_[CompoundKey("OutgoingRate", it->first)] |=
|
||||
stats.encode_frame_rate != 0;
|
||||
}
|
||||
|
||||
return AllStatsFilled(send_stats_filled_);
|
||||
}
|
||||
|
||||
std::string CompoundKey(const char* name, uint32_t ssrc) {
|
||||
std::ostringstream oss;
|
||||
oss << name << "_" << ssrc;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
bool AllStatsFilled(const std::map<std::string, bool>& stats_map) {
|
||||
for (std::map<std::string, bool>::const_iterator it = stats_map.begin();
|
||||
it != stats_map.end();
|
||||
++it) {
|
||||
if (!it->second)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
VideoReceiveStream* receive_stream_;
|
||||
std::map<std::string, bool> receive_stats_filled_;
|
||||
|
||||
VideoSendStream* send_stream_;
|
||||
std::map<std::string, bool> send_stats_filled_;
|
||||
|
||||
uint32_t expected_receive_ssrc_;
|
||||
std::set<uint32_t> expected_send_ssrcs_;
|
||||
std::string expected_cname_;
|
||||
|
||||
scoped_ptr<EventWrapper> check_stats_event_;
|
||||
};
|
||||
|
||||
TEST_F(CallTest, GetStats) {
|
||||
StatsObserver observer;
|
||||
|
||||
CreateCalls(Call::Config(observer.SendTransport()),
|
||||
Call::Config(observer.ReceiveTransport()));
|
||||
|
||||
observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver());
|
||||
|
||||
CreateTestConfigs();
|
||||
send_config_.pre_encode_callback = &observer; // Used to inject delay.
|
||||
send_config_.rtp.c_name = "SomeCName";
|
||||
|
||||
observer.SetExpectedReceiveSsrc(receive_config_.rtp.local_ssrc);
|
||||
observer.SetExpectedSendSsrcs(send_config_.rtp.ssrcs);
|
||||
observer.SetExpectedCName(send_config_.rtp.c_name);
|
||||
|
||||
CreateStreams();
|
||||
observer.SetReceiveStream(receive_stream_);
|
||||
observer.SetSendStream(send_stream_);
|
||||
CreateFrameGenerator();
|
||||
StartSending();
|
||||
|
||||
observer.WaitForFilledStats();
|
||||
|
||||
StopSending();
|
||||
observer.StopSending();
|
||||
DestroyStreams();
|
||||
}
|
||||
|
||||
TEST_F(CallTest, ReceiverReferenceTimeReportEnabled) {
|
||||
TestXrReceiverReferenceTimeReport(true);
|
||||
}
|
||||
|
100
webrtc/video/receive_statistics_proxy.cc
Normal file
100
webrtc/video/receive_statistics_proxy.cc
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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/receive_statistics_proxy.h"
|
||||
|
||||
#include "webrtc/system_wrappers/interface/clock.h"
|
||||
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace internal {
|
||||
|
||||
ReceiveStatisticsProxy::ReceiveStatisticsProxy(uint32_t ssrc,
|
||||
Clock* clock,
|
||||
ViERTP_RTCP* rtp_rtcp,
|
||||
ViECodec* codec,
|
||||
int channel)
|
||||
: channel_(channel),
|
||||
lock_(CriticalSectionWrapper::CreateCriticalSection()),
|
||||
clock_(clock),
|
||||
// 1000ms window, scale 1000 for ms to s.
|
||||
decode_fps_estimator_(1000, 1000),
|
||||
renders_fps_estimator_(1000, 1000),
|
||||
codec_(codec),
|
||||
rtp_rtcp_(rtp_rtcp) {
|
||||
stats_.ssrc = ssrc;
|
||||
}
|
||||
|
||||
ReceiveStatisticsProxy::~ReceiveStatisticsProxy() {}
|
||||
|
||||
VideoReceiveStream::Stats ReceiveStatisticsProxy::GetStats() const {
|
||||
VideoReceiveStream::Stats stats;
|
||||
{
|
||||
CriticalSectionScoped cs(lock_.get());
|
||||
stats = stats_;
|
||||
}
|
||||
stats.c_name = GetCName();
|
||||
codec_->GetReceiveSideDelay(channel_, &stats.avg_delay_ms);
|
||||
stats.discarded_packets = codec_->GetDiscardedPackets(channel_);
|
||||
codec_->GetReceiveCodecStastistics(
|
||||
channel_, stats.key_frames, stats.delta_frames);
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
std::string ReceiveStatisticsProxy::GetCName() const {
|
||||
char rtcp_cname[ViERTP_RTCP::KMaxRTCPCNameLength];
|
||||
if (rtp_rtcp_->GetRemoteRTCPCName(channel_, rtcp_cname) != 0)
|
||||
rtcp_cname[0] = '\0';
|
||||
return rtcp_cname;
|
||||
}
|
||||
|
||||
void ReceiveStatisticsProxy::IncomingRate(const int video_channel,
|
||||
const unsigned int framerate,
|
||||
const unsigned int bitrate) {
|
||||
CriticalSectionScoped cs(lock_.get());
|
||||
stats_.network_frame_rate = framerate;
|
||||
stats_.bitrate_bps = bitrate;
|
||||
}
|
||||
|
||||
void ReceiveStatisticsProxy::StatisticsUpdated(
|
||||
const webrtc::RtcpStatistics& statistics,
|
||||
uint32_t ssrc) {
|
||||
CriticalSectionScoped cs(lock_.get());
|
||||
|
||||
stats_.rtcp_stats = statistics;
|
||||
}
|
||||
|
||||
void ReceiveStatisticsProxy::DataCountersUpdated(
|
||||
const webrtc::StreamDataCounters& counters,
|
||||
uint32_t ssrc) {
|
||||
CriticalSectionScoped cs(lock_.get());
|
||||
|
||||
stats_.rtp_stats = counters;
|
||||
}
|
||||
|
||||
void ReceiveStatisticsProxy::OnDecodedFrame() {
|
||||
uint64_t now = clock_->TimeInMilliseconds();
|
||||
|
||||
CriticalSectionScoped cs(lock_.get());
|
||||
decode_fps_estimator_.Update(1, now);
|
||||
stats_.decode_frame_rate = decode_fps_estimator_.Rate(now);
|
||||
}
|
||||
|
||||
void ReceiveStatisticsProxy::OnRenderedFrame() {
|
||||
uint64_t now = clock_->TimeInMilliseconds();
|
||||
|
||||
CriticalSectionScoped cs(lock_.get());
|
||||
renders_fps_estimator_.Update(1, now);
|
||||
stats_.render_frame_rate = renders_fps_estimator_.Rate(now);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace webrtc
|
87
webrtc/video/receive_statistics_proxy.h
Normal file
87
webrtc/video/receive_statistics_proxy.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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_RECEIVE_STATISTICS_PROXY_H_
|
||||
#define WEBRTC_VIDEO_RECEIVE_STATISTICS_PROXY_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "webrtc/common_types.h"
|
||||
#include "webrtc/frame_callback.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/rate_statistics.h"
|
||||
#include "webrtc/video_engine/include/vie_codec.h"
|
||||
#include "webrtc/video_engine/include/vie_rtp_rtcp.h"
|
||||
#include "webrtc/video_receive_stream.h"
|
||||
#include "webrtc/video_renderer.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class Clock;
|
||||
class CriticalSectionWrapper;
|
||||
class ViECodec;
|
||||
class ViEDecoderObserver;
|
||||
|
||||
namespace internal {
|
||||
|
||||
class ReceiveStatisticsProxy : public ViEDecoderObserver,
|
||||
public RtcpStatisticsCallback,
|
||||
public StreamDataCountersCallback {
|
||||
public:
|
||||
ReceiveStatisticsProxy(uint32_t ssrc,
|
||||
Clock* clock,
|
||||
ViERTP_RTCP* rtp_rtcp,
|
||||
ViECodec* codec,
|
||||
int channel);
|
||||
virtual ~ReceiveStatisticsProxy();
|
||||
|
||||
VideoReceiveStream::Stats GetStats() const;
|
||||
|
||||
void OnDecodedFrame();
|
||||
void OnRenderedFrame();
|
||||
|
||||
// Overrides ViEDecoderObserver.
|
||||
virtual void IncomingCodecChanged(const int video_channel,
|
||||
const VideoCodec& video_codec) OVERRIDE {}
|
||||
virtual void IncomingRate(const int video_channel,
|
||||
const unsigned int framerate,
|
||||
const unsigned int bitrate) OVERRIDE;
|
||||
virtual void DecoderTiming(int decode_ms,
|
||||
int max_decode_ms,
|
||||
int current_delay_ms,
|
||||
int target_delay_ms,
|
||||
int jitter_buffer_ms,
|
||||
int min_playout_delay_ms,
|
||||
int render_delay_ms) OVERRIDE {}
|
||||
virtual void RequestNewKeyFrame(const int video_channel) OVERRIDE {}
|
||||
|
||||
// Overrides RtcpStatisticsBallback.
|
||||
virtual void StatisticsUpdated(const webrtc::RtcpStatistics& statistics,
|
||||
uint32_t ssrc) OVERRIDE;
|
||||
|
||||
// Overrides StreamDataCountersCallback.
|
||||
virtual void DataCountersUpdated(const webrtc::StreamDataCounters& counters,
|
||||
uint32_t ssrc) OVERRIDE;
|
||||
|
||||
private:
|
||||
std::string GetCName() const;
|
||||
|
||||
const int channel_;
|
||||
scoped_ptr<CriticalSectionWrapper> lock_;
|
||||
Clock* clock_;
|
||||
VideoReceiveStream::Stats stats_;
|
||||
RateStatistics decode_fps_estimator_;
|
||||
RateStatistics renders_fps_estimator_;
|
||||
ViECodec* codec_;
|
||||
ViERTP_RTCP* rtp_rtcp_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace webrtc
|
||||
#endif // WEBRTC_VIDEO_RECEIVE_STATISTICS_PROXY_H_
|
@ -18,7 +18,7 @@ namespace webrtc {
|
||||
|
||||
SendStatisticsProxy::SendStatisticsProxy(
|
||||
const VideoSendStream::Config& config,
|
||||
SendStatisticsProxy::StreamStatsProvider* stats_provider)
|
||||
SendStatisticsProxy::StatsProvider* stats_provider)
|
||||
: config_(config),
|
||||
lock_(CriticalSectionWrapper::CreateCriticalSection()),
|
||||
stats_provider_(stats_provider) {}
|
||||
@ -39,8 +39,11 @@ void SendStatisticsProxy::CapturedFrameRate(const int capture_id,
|
||||
}
|
||||
|
||||
VideoSendStream::Stats SendStatisticsProxy::GetStats() const {
|
||||
VideoSendStream::Stats stats = stats_;
|
||||
CriticalSectionScoped cs(lock_.get());
|
||||
VideoSendStream::Stats stats;
|
||||
{
|
||||
CriticalSectionScoped cs(lock_.get());
|
||||
stats = stats_;
|
||||
}
|
||||
stats_provider_->GetSendSideDelay(&stats);
|
||||
stats.c_name = stats_provider_->GetCName();
|
||||
return stats;
|
||||
|
@ -30,17 +30,18 @@ class SendStatisticsProxy : public RtcpStatisticsCallback,
|
||||
public ViEEncoderObserver,
|
||||
public ViECaptureObserver {
|
||||
public:
|
||||
class StreamStatsProvider {
|
||||
public:
|
||||
StreamStatsProvider() {}
|
||||
virtual ~StreamStatsProvider() {}
|
||||
class StatsProvider {
|
||||
protected:
|
||||
StatsProvider() {}
|
||||
virtual ~StatsProvider() {}
|
||||
|
||||
public:
|
||||
virtual bool GetSendSideDelay(VideoSendStream::Stats* stats) = 0;
|
||||
virtual std::string GetCName() = 0;
|
||||
};
|
||||
|
||||
SendStatisticsProxy(const VideoSendStream::Config& config,
|
||||
StreamStatsProvider* stats_provider);
|
||||
StatsProvider* stats_provider);
|
||||
virtual ~SendStatisticsProxy();
|
||||
|
||||
VideoSendStream::Stats GetStats() const;
|
||||
@ -84,7 +85,7 @@ class SendStatisticsProxy : public RtcpStatisticsCallback,
|
||||
const VideoSendStream::Config config_;
|
||||
scoped_ptr<CriticalSectionWrapper> lock_;
|
||||
VideoSendStream::Stats stats_;
|
||||
StreamStatsProvider* stats_provider_;
|
||||
StatsProvider* stats_provider_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -19,9 +19,8 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class SendStatisticsProxyTest
|
||||
: public ::testing::Test,
|
||||
protected SendStatisticsProxy::StreamStatsProvider {
|
||||
class SendStatisticsProxyTest : public ::testing::Test,
|
||||
protected SendStatisticsProxy::StatsProvider {
|
||||
public:
|
||||
SendStatisticsProxyTest() : avg_delay_ms_(0), max_delay_ms_(0) {}
|
||||
virtual ~SendStatisticsProxyTest() {}
|
||||
@ -49,6 +48,45 @@ class SendStatisticsProxyTest
|
||||
|
||||
virtual std::string GetCName() { return cname_; }
|
||||
|
||||
void ExpectEqual(VideoSendStream::Stats one, VideoSendStream::Stats other) {
|
||||
EXPECT_EQ(one.avg_delay_ms, other.avg_delay_ms);
|
||||
EXPECT_EQ(one.input_frame_rate, other.input_frame_rate);
|
||||
EXPECT_EQ(one.encode_frame_rate, other.encode_frame_rate);
|
||||
EXPECT_EQ(one.avg_delay_ms, other.avg_delay_ms);
|
||||
EXPECT_EQ(one.max_delay_ms, other.max_delay_ms);
|
||||
EXPECT_EQ(one.c_name, other.c_name);
|
||||
|
||||
EXPECT_EQ(one.substreams.size(), other.substreams.size());
|
||||
for (std::map<uint32_t, StreamStats>::const_iterator it =
|
||||
one.substreams.begin();
|
||||
it != one.substreams.end();
|
||||
++it) {
|
||||
std::map<uint32_t, StreamStats>::const_iterator corresponding_it =
|
||||
other.substreams.find(it->first);
|
||||
ASSERT_TRUE(corresponding_it != other.substreams.end());
|
||||
const StreamStats& a = it->second;
|
||||
const StreamStats& b = corresponding_it->second;
|
||||
|
||||
EXPECT_EQ(a.key_frames, b.key_frames);
|
||||
EXPECT_EQ(a.delta_frames, b.delta_frames);
|
||||
EXPECT_EQ(a.bitrate_bps, b.bitrate_bps);
|
||||
|
||||
EXPECT_EQ(a.rtp_stats.bytes, b.rtp_stats.bytes);
|
||||
EXPECT_EQ(a.rtp_stats.header_bytes, b.rtp_stats.header_bytes);
|
||||
EXPECT_EQ(a.rtp_stats.padding_bytes, b.rtp_stats.padding_bytes);
|
||||
EXPECT_EQ(a.rtp_stats.packets, b.rtp_stats.packets);
|
||||
EXPECT_EQ(a.rtp_stats.retransmitted_packets,
|
||||
b.rtp_stats.retransmitted_packets);
|
||||
EXPECT_EQ(a.rtp_stats.fec_packets, b.rtp_stats.fec_packets);
|
||||
|
||||
EXPECT_EQ(a.rtcp_stats.fraction_lost, b.rtcp_stats.fraction_lost);
|
||||
EXPECT_EQ(a.rtcp_stats.cumulative_lost, b.rtcp_stats.cumulative_lost);
|
||||
EXPECT_EQ(a.rtcp_stats.extended_max_sequence_number,
|
||||
b.rtcp_stats.extended_max_sequence_number);
|
||||
EXPECT_EQ(a.rtcp_stats.jitter, b.rtcp_stats.jitter);
|
||||
}
|
||||
}
|
||||
|
||||
scoped_ptr<SendStatisticsProxy> statistics_proxy_;
|
||||
VideoSendStream::Config config_;
|
||||
int avg_delay_ms_;
|
||||
@ -76,7 +114,7 @@ TEST_F(SendStatisticsProxyTest, RtcpStatistics) {
|
||||
}
|
||||
|
||||
VideoSendStream::Stats stats = statistics_proxy_->GetStats();
|
||||
EXPECT_EQ(expected_, stats);
|
||||
ExpectEqual(expected_, stats);
|
||||
}
|
||||
|
||||
TEST_F(SendStatisticsProxyTest, FrameRates) {
|
||||
@ -109,7 +147,7 @@ TEST_F(SendStatisticsProxyTest, FrameCounts) {
|
||||
}
|
||||
|
||||
VideoSendStream::Stats stats = statistics_proxy_->GetStats();
|
||||
EXPECT_EQ(expected_, stats);
|
||||
ExpectEqual(expected_, stats);
|
||||
}
|
||||
|
||||
TEST_F(SendStatisticsProxyTest, DataCounters) {
|
||||
@ -131,7 +169,7 @@ TEST_F(SendStatisticsProxyTest, DataCounters) {
|
||||
}
|
||||
|
||||
VideoSendStream::Stats stats = statistics_proxy_->GetStats();
|
||||
EXPECT_EQ(expected_, stats);
|
||||
ExpectEqual(expected_, stats);
|
||||
}
|
||||
|
||||
TEST_F(SendStatisticsProxyTest, Bitrate) {
|
||||
@ -147,7 +185,7 @@ TEST_F(SendStatisticsProxyTest, Bitrate) {
|
||||
}
|
||||
|
||||
VideoSendStream::Stats stats = statistics_proxy_->GetStats();
|
||||
EXPECT_EQ(expected_, stats);
|
||||
ExpectEqual(expected_, stats);
|
||||
}
|
||||
|
||||
TEST_F(SendStatisticsProxyTest, StreamStats) {
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
|
||||
#include "webrtc/system_wrappers/interface/clock.h"
|
||||
#include "webrtc/video/receive_statistics_proxy.h"
|
||||
#include "webrtc/video_engine/include/vie_base.h"
|
||||
#include "webrtc/video_engine/include/vie_capture.h"
|
||||
#include "webrtc/video_engine/include/vie_codec.h"
|
||||
@ -38,6 +39,7 @@ VideoReceiveStream::VideoReceiveStream(webrtc::VideoEngine* video_engine,
|
||||
: transport_adapter_(transport),
|
||||
encoded_frame_proxy_(config.pre_decode_callback),
|
||||
config_(config),
|
||||
clock_(Clock::GetRealTimeClock()),
|
||||
channel_(-1) {
|
||||
video_engine_base_ = ViEBase::GetInterface(video_engine);
|
||||
video_engine_base_->CreateReceiveChannel(channel_, base_channel);
|
||||
@ -105,6 +107,20 @@ VideoReceiveStream::VideoReceiveStream(webrtc::VideoEngine* video_engine,
|
||||
}
|
||||
}
|
||||
|
||||
stats_proxy_.reset(new ReceiveStatisticsProxy(
|
||||
config_.rtp.local_ssrc, clock_, rtp_rtcp_, codec_, channel_));
|
||||
|
||||
if (rtp_rtcp_->RegisterReceiveChannelRtcpStatisticsCallback(
|
||||
channel_, stats_proxy_.get()) != 0)
|
||||
abort();
|
||||
|
||||
if (rtp_rtcp_->RegisterReceiveChannelRtpStatisticsCallback(
|
||||
channel_, stats_proxy_.get()) != 0)
|
||||
abort();
|
||||
|
||||
if (codec_->RegisterDecoderObserver(channel_, *stats_proxy_) != 0)
|
||||
abort();
|
||||
|
||||
external_codec_ = ViEExternalCodec::GetInterface(video_engine);
|
||||
for (size_t i = 0; i < config_.external_decoders.size(); ++i) {
|
||||
ExternalVideoDecoder* decoder = &config_.external_decoders[i];
|
||||
@ -134,14 +150,11 @@ VideoReceiveStream::VideoReceiveStream(webrtc::VideoEngine* video_engine,
|
||||
image_process_->RegisterPreDecodeImageCallback(channel_,
|
||||
&encoded_frame_proxy_);
|
||||
}
|
||||
image_process_->RegisterPreRenderCallback(channel_,
|
||||
config_.pre_render_callback);
|
||||
image_process_->RegisterPreRenderCallback(channel_, this);
|
||||
|
||||
if (config.rtp.rtcp_xr.receiver_reference_time_report) {
|
||||
rtp_rtcp_->SetRtcpXrRrtrStatus(channel_, true);
|
||||
}
|
||||
|
||||
clock_ = Clock::GetRealTimeClock();
|
||||
}
|
||||
|
||||
VideoReceiveStream::~VideoReceiveStream() {
|
||||
@ -161,6 +174,11 @@ VideoReceiveStream::~VideoReceiveStream() {
|
||||
image_process_->Release();
|
||||
video_engine_base_->Release();
|
||||
external_codec_->Release();
|
||||
codec_->DeregisterDecoderObserver(channel_);
|
||||
rtp_rtcp_->DeregisterReceiveChannelRtpStatisticsCallback(channel_,
|
||||
stats_proxy_.get());
|
||||
rtp_rtcp_->DeregisterReceiveChannelRtcpStatisticsCallback(channel_,
|
||||
stats_proxy_.get());
|
||||
codec_->Release();
|
||||
network_->Release();
|
||||
render_->Release();
|
||||
@ -183,6 +201,10 @@ void VideoReceiveStream::StopReceiving() {
|
||||
transport_adapter_.Disable();
|
||||
}
|
||||
|
||||
VideoReceiveStream::Stats VideoReceiveStream::GetStats() {
|
||||
return stats_proxy_->GetStats();
|
||||
}
|
||||
|
||||
void VideoReceiveStream::GetCurrentReceiveCodec(VideoCodec* receive_codec) {
|
||||
// TODO(pbos): Implement
|
||||
}
|
||||
@ -197,13 +219,22 @@ bool VideoReceiveStream::DeliverRtp(const uint8_t* packet, size_t length) {
|
||||
channel_, packet, static_cast<int>(length), PacketTime()) == 0;
|
||||
}
|
||||
|
||||
void VideoReceiveStream::FrameCallback(I420VideoFrame* video_frame) {
|
||||
stats_proxy_->OnDecodedFrame();
|
||||
|
||||
if (config_.pre_render_callback)
|
||||
config_.pre_render_callback->FrameCallback(video_frame);
|
||||
}
|
||||
|
||||
int32_t VideoReceiveStream::RenderFrame(const uint32_t stream_id,
|
||||
I420VideoFrame& video_frame) {
|
||||
if (config_.renderer == NULL)
|
||||
return 0;
|
||||
if (config_.renderer != NULL)
|
||||
config_.renderer->RenderFrame(
|
||||
video_frame,
|
||||
video_frame.render_time_ms() - clock_->TimeInMilliseconds());
|
||||
|
||||
stats_proxy_->OnRenderedFrame();
|
||||
|
||||
config_.renderer->RenderFrame(
|
||||
video_frame, video_frame.render_time_ms() - clock_->TimeInMilliseconds());
|
||||
return 0;
|
||||
}
|
||||
} // namespace internal
|
||||
|
@ -16,7 +16,9 @@
|
||||
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
|
||||
#include "webrtc/modules/video_render/include/video_render_defines.h"
|
||||
#include "webrtc/system_wrappers/interface/clock.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||
#include "webrtc/video/encoded_frame_callback_adapter.h"
|
||||
#include "webrtc/video/receive_statistics_proxy.h"
|
||||
#include "webrtc/video/transport_adapter.h"
|
||||
#include "webrtc/video_engine/include/vie_render.h"
|
||||
#include "webrtc/video_receive_stream.h"
|
||||
@ -36,7 +38,9 @@ class VoiceEngine;
|
||||
namespace internal {
|
||||
|
||||
class VideoReceiveStream : public webrtc::VideoReceiveStream,
|
||||
public I420FrameCallback,
|
||||
public VideoRenderCallback {
|
||||
|
||||
public:
|
||||
VideoReceiveStream(webrtc::VideoEngine* video_engine,
|
||||
const VideoReceiveStream::Config& config,
|
||||
@ -47,9 +51,14 @@ class VideoReceiveStream : public webrtc::VideoReceiveStream,
|
||||
|
||||
virtual void StartReceiving() OVERRIDE;
|
||||
virtual void StopReceiving() OVERRIDE;
|
||||
virtual Stats GetStats() OVERRIDE;
|
||||
|
||||
virtual void GetCurrentReceiveCodec(VideoCodec* receive_codec) OVERRIDE;
|
||||
|
||||
// Overrides I420FrameCallback.
|
||||
virtual void FrameCallback(I420VideoFrame* video_frame) OVERRIDE;
|
||||
|
||||
// Overrides VideoRenderCallback.
|
||||
virtual int32_t RenderFrame(const uint32_t stream_id,
|
||||
I420VideoFrame& video_frame) OVERRIDE;
|
||||
|
||||
@ -71,6 +80,8 @@ class VideoReceiveStream : public webrtc::VideoReceiveStream,
|
||||
ViERTP_RTCP* rtp_rtcp_;
|
||||
ViEImageProcess* image_process_;
|
||||
|
||||
scoped_ptr<ReceiveStatisticsProxy> stats_proxy_;
|
||||
|
||||
int channel_;
|
||||
};
|
||||
} // namespace internal
|
||||
|
@ -36,7 +36,7 @@ namespace internal {
|
||||
|
||||
class VideoSendStream : public webrtc::VideoSendStream,
|
||||
public VideoSendStreamInput,
|
||||
public SendStatisticsProxy::StreamStatsProvider {
|
||||
public SendStatisticsProxy::StatsProvider {
|
||||
public:
|
||||
VideoSendStream(newapi::Transport* transport,
|
||||
CpuOveruseObserver* overuse_observer,
|
||||
|
@ -16,6 +16,8 @@
|
||||
'video/encoded_frame_callback_adapter.h',
|
||||
'video/send_statistics_proxy.cc',
|
||||
'video/send_statistics_proxy.h',
|
||||
'video/receive_statistics_proxy.cc',
|
||||
'video/receive_statistics_proxy.h',
|
||||
'video/transport_adapter.cc',
|
||||
'video/transport_adapter.h',
|
||||
'video/video_receive_stream.cc',
|
||||
|
@ -55,39 +55,22 @@ struct ExternalVideoDecoder {
|
||||
|
||||
class VideoReceiveStream {
|
||||
public:
|
||||
struct Stats {
|
||||
struct Stats : public StreamStats {
|
||||
Stats()
|
||||
: network_frame_rate(0),
|
||||
decode_frame_rate(0),
|
||||
render_frame_rate(0),
|
||||
key_frames(0),
|
||||
delta_frames(0),
|
||||
video_packets(0),
|
||||
retransmitted_packets(0),
|
||||
fec_packets(0),
|
||||
padding_packets(0),
|
||||
avg_delay_ms(0),
|
||||
discarded_packets(0),
|
||||
received_bitrate_bps(0),
|
||||
receive_side_delay_ms(0) {}
|
||||
RtpStatistics rtp_stats;
|
||||
ssrc(0) {}
|
||||
|
||||
int network_frame_rate;
|
||||
int decode_frame_rate;
|
||||
int render_frame_rate;
|
||||
uint32_t key_frames;
|
||||
uint32_t delta_frames;
|
||||
uint32_t video_packets;
|
||||
uint32_t retransmitted_packets;
|
||||
uint32_t fec_packets;
|
||||
uint32_t padding_packets;
|
||||
int avg_delay_ms;
|
||||
uint32_t discarded_packets;
|
||||
int32_t received_bitrate_bps;
|
||||
int receive_side_delay_ms;
|
||||
};
|
||||
|
||||
class StatsCallback {
|
||||
public:
|
||||
virtual ~StatsCallback() {}
|
||||
virtual void ReceiveStats(const Stats& stats) = 0;
|
||||
uint32_t ssrc;
|
||||
std::string c_name;
|
||||
};
|
||||
|
||||
struct Config {
|
||||
@ -186,13 +169,11 @@ class VideoReceiveStream {
|
||||
// Target delay in milliseconds. A positive value indicates this stream is
|
||||
// used for streaming instead of a real-time call.
|
||||
int target_delay_ms;
|
||||
|
||||
// Callback for periodically receiving receiver stats.
|
||||
StatsCallback* stats_callback;
|
||||
};
|
||||
|
||||
virtual void StartReceiving() = 0;
|
||||
virtual void StopReceiving() = 0;
|
||||
virtual Stats GetStats() = 0;
|
||||
|
||||
// TODO(mflodman) Replace this with callback.
|
||||
virtual void GetCurrentReceiveCodec(VideoCodec* receive_codec) = 0;
|
||||
|
@ -51,18 +51,6 @@ class VideoSendStream {
|
||||
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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user