Add callbacks for receive channel RTCP statistics.
This allows a listener to receive new statistics as it is generated - avoiding the need to poll. This also makes handling stats from multiple RTP streams more tractable. The change is primarily targeted at the new video engine API. TEST=Unit test in ReceiveStatisticsTest. Integration tests to follow as call tests when fully wired up. BUG=2235 R=henrika@webrtc.org, pbos@webrtc.org, stefan@webrtc.org Review URL: https://webrtc-codereview.appspot.com/5089004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5323 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
@@ -23,24 +23,9 @@ class Clock;
|
|||||||
|
|
||||||
class StreamStatistician {
|
class StreamStatistician {
|
||||||
public:
|
public:
|
||||||
struct Statistics {
|
|
||||||
Statistics()
|
|
||||||
: fraction_lost(0),
|
|
||||||
cumulative_lost(0),
|
|
||||||
extended_max_sequence_number(0),
|
|
||||||
jitter(0),
|
|
||||||
max_jitter(0) {}
|
|
||||||
|
|
||||||
uint8_t fraction_lost;
|
|
||||||
uint32_t cumulative_lost;
|
|
||||||
uint32_t extended_max_sequence_number;
|
|
||||||
uint32_t jitter;
|
|
||||||
uint32_t max_jitter;
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual ~StreamStatistician();
|
virtual ~StreamStatistician();
|
||||||
|
|
||||||
virtual bool GetStatistics(Statistics* statistics, bool reset) = 0;
|
virtual bool GetStatistics(RtcpStatistics* statistics, bool reset) = 0;
|
||||||
virtual void GetDataCounters(uint32_t* bytes_received,
|
virtual void GetDataCounters(uint32_t* bytes_received,
|
||||||
uint32_t* packets_received) const = 0;
|
uint32_t* packets_received) const = 0;
|
||||||
virtual uint32_t BitrateReceived() const = 0;
|
virtual uint32_t BitrateReceived() const = 0;
|
||||||
@@ -78,6 +63,10 @@ class ReceiveStatistics : public Module {
|
|||||||
|
|
||||||
// Sets the max reordering threshold in number of packets.
|
// Sets the max reordering threshold in number of packets.
|
||||||
virtual void SetMaxReorderingThreshold(int max_reordering_threshold) = 0;
|
virtual void SetMaxReorderingThreshold(int max_reordering_threshold) = 0;
|
||||||
|
|
||||||
|
// Called on new RTCP stats creation.
|
||||||
|
virtual void RegisterRtcpStatisticsCallback(
|
||||||
|
RtcpStatisticsCallback* callback) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NullReceiveStatistics : public ReceiveStatistics {
|
class NullReceiveStatistics : public ReceiveStatistics {
|
||||||
@@ -89,6 +78,8 @@ class NullReceiveStatistics : public ReceiveStatistics {
|
|||||||
virtual int32_t TimeUntilNextProcess() OVERRIDE;
|
virtual int32_t TimeUntilNextProcess() OVERRIDE;
|
||||||
virtual int32_t Process() OVERRIDE;
|
virtual int32_t Process() OVERRIDE;
|
||||||
virtual void SetMaxReorderingThreshold(int max_reordering_threshold) OVERRIDE;
|
virtual void SetMaxReorderingThreshold(int max_reordering_threshold) OVERRIDE;
|
||||||
|
virtual void RegisterRtcpStatisticsCallback(RtcpStatisticsCallback* callback)
|
||||||
|
OVERRIDE;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@@ -24,14 +24,15 @@ const int kStatisticsProcessIntervalMs = 1000;
|
|||||||
|
|
||||||
StreamStatistician::~StreamStatistician() {}
|
StreamStatistician::~StreamStatistician() {}
|
||||||
|
|
||||||
StreamStatisticianImpl::StreamStatisticianImpl(Clock* clock)
|
StreamStatisticianImpl::StreamStatisticianImpl(
|
||||||
|
Clock* clock,
|
||||||
|
RtcpStatisticsCallback* rtcp_callback)
|
||||||
: clock_(clock),
|
: clock_(clock),
|
||||||
crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
|
crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
|
||||||
incoming_bitrate_(clock, NULL),
|
incoming_bitrate_(clock, NULL),
|
||||||
ssrc_(0),
|
ssrc_(0),
|
||||||
max_reordering_threshold_(kDefaultMaxReorderingThreshold),
|
max_reordering_threshold_(kDefaultMaxReorderingThreshold),
|
||||||
jitter_q4_(0),
|
jitter_q4_(0),
|
||||||
jitter_max_q4_(0),
|
|
||||||
cumulative_loss_(0),
|
cumulative_loss_(0),
|
||||||
jitter_q4_transmission_time_offset_(0),
|
jitter_q4_transmission_time_offset_(0),
|
||||||
last_receive_time_ms_(0),
|
last_receive_time_ms_(0),
|
||||||
@@ -50,7 +51,8 @@ StreamStatisticianImpl::StreamStatisticianImpl(Clock* clock)
|
|||||||
last_report_inorder_packets_(0),
|
last_report_inorder_packets_(0),
|
||||||
last_report_old_packets_(0),
|
last_report_old_packets_(0),
|
||||||
last_report_seq_max_(0),
|
last_report_seq_max_(0),
|
||||||
last_reported_statistics_() {}
|
last_reported_statistics_(),
|
||||||
|
rtcp_callback_(rtcp_callback) {}
|
||||||
|
|
||||||
void StreamStatisticianImpl::ResetStatistics() {
|
void StreamStatisticianImpl::ResetStatistics() {
|
||||||
CriticalSectionScoped cs(crit_sect_.get());
|
CriticalSectionScoped cs(crit_sect_.get());
|
||||||
@@ -59,7 +61,6 @@ void StreamStatisticianImpl::ResetStatistics() {
|
|||||||
last_report_seq_max_ = 0;
|
last_report_seq_max_ = 0;
|
||||||
memset(&last_reported_statistics_, 0, sizeof(last_reported_statistics_));
|
memset(&last_reported_statistics_, 0, sizeof(last_reported_statistics_));
|
||||||
jitter_q4_ = 0;
|
jitter_q4_ = 0;
|
||||||
jitter_max_q4_ = 0;
|
|
||||||
cumulative_loss_ = 0;
|
cumulative_loss_ = 0;
|
||||||
jitter_q4_transmission_time_offset_ = 0;
|
jitter_q4_transmission_time_offset_ = 0;
|
||||||
received_seq_wraps_ = 0;
|
received_seq_wraps_ = 0;
|
||||||
@@ -173,7 +174,8 @@ void StreamStatisticianImpl::SetMaxReorderingThreshold(
|
|||||||
max_reordering_threshold_ = max_reordering_threshold;
|
max_reordering_threshold_ = max_reordering_threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StreamStatisticianImpl::GetStatistics(Statistics* statistics, bool reset) {
|
bool StreamStatisticianImpl::GetStatistics(RtcpStatistics* statistics,
|
||||||
|
bool reset) {
|
||||||
CriticalSectionScoped cs(crit_sect_.get());
|
CriticalSectionScoped cs(crit_sect_.get());
|
||||||
if (received_seq_first_ == 0 && received_byte_count_ == 0) {
|
if (received_seq_first_ == 0 && received_byte_count_ == 0) {
|
||||||
// We have not received anything.
|
// We have not received anything.
|
||||||
@@ -235,16 +237,11 @@ bool StreamStatisticianImpl::GetStatistics(Statistics* statistics, bool reset) {
|
|||||||
|
|
||||||
// We need a counter for cumulative loss too.
|
// We need a counter for cumulative loss too.
|
||||||
cumulative_loss_ += missing;
|
cumulative_loss_ += missing;
|
||||||
|
|
||||||
if (jitter_q4_ > jitter_max_q4_) {
|
|
||||||
jitter_max_q4_ = jitter_q4_;
|
|
||||||
}
|
|
||||||
statistics->cumulative_lost = cumulative_loss_;
|
statistics->cumulative_lost = cumulative_loss_;
|
||||||
statistics->extended_max_sequence_number = (received_seq_wraps_ << 16) +
|
statistics->extended_max_sequence_number = (received_seq_wraps_ << 16) +
|
||||||
received_seq_max_;
|
received_seq_max_;
|
||||||
// Note: internal jitter value is in Q4 and needs to be scaled by 1/16.
|
// Note: internal jitter value is in Q4 and needs to be scaled by 1/16.
|
||||||
statistics->jitter = jitter_q4_ >> 4;
|
statistics->jitter = jitter_q4_ >> 4;
|
||||||
statistics->max_jitter = jitter_max_q4_ >> 4;
|
|
||||||
if (reset) {
|
if (reset) {
|
||||||
// Store this report.
|
// Store this report.
|
||||||
last_reported_statistics_ = *statistics;
|
last_reported_statistics_ = *statistics;
|
||||||
@@ -254,6 +251,8 @@ bool StreamStatisticianImpl::GetStatistics(Statistics* statistics, bool reset) {
|
|||||||
last_report_old_packets_ = received_retransmitted_packets_;
|
last_report_old_packets_ = received_retransmitted_packets_;
|
||||||
last_report_seq_max_ = received_seq_max_;
|
last_report_seq_max_ = received_seq_max_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rtcp_callback_->StatisticsUpdated(last_reported_statistics_, ssrc_);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,7 +348,8 @@ ReceiveStatistics* ReceiveStatistics::Create(Clock* clock) {
|
|||||||
ReceiveStatisticsImpl::ReceiveStatisticsImpl(Clock* clock)
|
ReceiveStatisticsImpl::ReceiveStatisticsImpl(Clock* clock)
|
||||||
: clock_(clock),
|
: clock_(clock),
|
||||||
crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
|
crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
|
||||||
last_rate_update_ms_(0) {}
|
last_rate_update_ms_(0),
|
||||||
|
rtcp_stats_callback_(NULL) {}
|
||||||
|
|
||||||
ReceiveStatisticsImpl::~ReceiveStatisticsImpl() {
|
ReceiveStatisticsImpl::~ReceiveStatisticsImpl() {
|
||||||
while (!statisticians_.empty()) {
|
while (!statisticians_.empty()) {
|
||||||
@@ -365,7 +365,7 @@ void ReceiveStatisticsImpl::IncomingPacket(const RTPHeader& header,
|
|||||||
if (it == statisticians_.end()) {
|
if (it == statisticians_.end()) {
|
||||||
std::pair<StatisticianImplMap::iterator, uint32_t> insert_result =
|
std::pair<StatisticianImplMap::iterator, uint32_t> insert_result =
|
||||||
statisticians_.insert(std::make_pair(
|
statisticians_.insert(std::make_pair(
|
||||||
header.ssrc, new StreamStatisticianImpl(clock_)));
|
header.ssrc, new StreamStatisticianImpl(clock_, this)));
|
||||||
it = insert_result.first;
|
it = insert_result.first;
|
||||||
}
|
}
|
||||||
statisticians_[header.ssrc]->IncomingPacket(header, bytes, old_packet);
|
statisticians_[header.ssrc]->IncomingPacket(header, bytes, old_packet);
|
||||||
@@ -433,6 +433,21 @@ int32_t ReceiveStatisticsImpl::TimeUntilNextProcess() {
|
|||||||
return std::max(kStatisticsProcessIntervalMs - time_since_last_update, 0);
|
return std::max(kStatisticsProcessIntervalMs - time_since_last_update, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ReceiveStatisticsImpl::RegisterRtcpStatisticsCallback(
|
||||||
|
RtcpStatisticsCallback* callback) {
|
||||||
|
CriticalSectionScoped cs(crit_sect_.get());
|
||||||
|
if (callback != NULL)
|
||||||
|
assert(rtcp_stats_callback_ == NULL);
|
||||||
|
rtcp_stats_callback_ = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReceiveStatisticsImpl::StatisticsUpdated(const RtcpStatistics& statistics,
|
||||||
|
uint32_t ssrc) {
|
||||||
|
CriticalSectionScoped cs(crit_sect_.get());
|
||||||
|
if (rtcp_stats_callback_) {
|
||||||
|
rtcp_stats_callback_->StatisticsUpdated(statistics, ssrc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NullReceiveStatistics::IncomingPacket(const RTPHeader& rtp_header,
|
void NullReceiveStatistics::IncomingPacket(const RTPHeader& rtp_header,
|
||||||
size_t bytes,
|
size_t bytes,
|
||||||
@@ -454,4 +469,7 @@ int32_t NullReceiveStatistics::TimeUntilNextProcess() { return 0; }
|
|||||||
|
|
||||||
int32_t NullReceiveStatistics::Process() { return 0; }
|
int32_t NullReceiveStatistics::Process() { return 0; }
|
||||||
|
|
||||||
|
void NullReceiveStatistics::RegisterRtcpStatisticsCallback(
|
||||||
|
RtcpStatisticsCallback* callback) {}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@@ -25,11 +25,10 @@ class CriticalSectionWrapper;
|
|||||||
|
|
||||||
class StreamStatisticianImpl : public StreamStatistician {
|
class StreamStatisticianImpl : public StreamStatistician {
|
||||||
public:
|
public:
|
||||||
explicit StreamStatisticianImpl(Clock* clock);
|
StreamStatisticianImpl(Clock* clock, RtcpStatisticsCallback* rtcp_callback);
|
||||||
|
|
||||||
virtual ~StreamStatisticianImpl() {}
|
virtual ~StreamStatisticianImpl() {}
|
||||||
|
|
||||||
virtual bool GetStatistics(Statistics* statistics, bool reset) OVERRIDE;
|
virtual bool GetStatistics(RtcpStatistics* statistics, bool reset) OVERRIDE;
|
||||||
virtual void GetDataCounters(uint32_t* bytes_received,
|
virtual void GetDataCounters(uint32_t* bytes_received,
|
||||||
uint32_t* packets_received) const OVERRIDE;
|
uint32_t* packets_received) const OVERRIDE;
|
||||||
virtual uint32_t BitrateReceived() const OVERRIDE;
|
virtual uint32_t BitrateReceived() const OVERRIDE;
|
||||||
@@ -55,7 +54,6 @@ class StreamStatisticianImpl : public StreamStatistician {
|
|||||||
|
|
||||||
// Stats on received RTP packets.
|
// Stats on received RTP packets.
|
||||||
uint32_t jitter_q4_;
|
uint32_t jitter_q4_;
|
||||||
uint32_t jitter_max_q4_;
|
|
||||||
uint32_t cumulative_loss_;
|
uint32_t cumulative_loss_;
|
||||||
uint32_t jitter_q4_transmission_time_offset_;
|
uint32_t jitter_q4_transmission_time_offset_;
|
||||||
|
|
||||||
@@ -79,10 +77,13 @@ class StreamStatisticianImpl : public StreamStatistician {
|
|||||||
uint32_t last_report_inorder_packets_;
|
uint32_t last_report_inorder_packets_;
|
||||||
uint32_t last_report_old_packets_;
|
uint32_t last_report_old_packets_;
|
||||||
uint16_t last_report_seq_max_;
|
uint16_t last_report_seq_max_;
|
||||||
Statistics last_reported_statistics_;
|
RtcpStatistics last_reported_statistics_;
|
||||||
|
|
||||||
|
RtcpStatisticsCallback* const rtcp_callback_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ReceiveStatisticsImpl : public ReceiveStatistics {
|
class ReceiveStatisticsImpl : public ReceiveStatistics,
|
||||||
|
public RtcpStatisticsCallback {
|
||||||
public:
|
public:
|
||||||
explicit ReceiveStatisticsImpl(Clock* clock);
|
explicit ReceiveStatisticsImpl(Clock* clock);
|
||||||
|
|
||||||
@@ -101,6 +102,12 @@ class ReceiveStatisticsImpl : public ReceiveStatistics {
|
|||||||
|
|
||||||
void ChangeSsrc(uint32_t from_ssrc, uint32_t to_ssrc);
|
void ChangeSsrc(uint32_t from_ssrc, uint32_t to_ssrc);
|
||||||
|
|
||||||
|
virtual void RegisterRtcpStatisticsCallback(RtcpStatisticsCallback* callback)
|
||||||
|
OVERRIDE;
|
||||||
|
|
||||||
|
virtual void StatisticsUpdated(const RtcpStatistics& statistics,
|
||||||
|
uint32_t ssrc) OVERRIDE;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::map<uint32_t, StreamStatisticianImpl*> StatisticianImplMap;
|
typedef std::map<uint32_t, StreamStatisticianImpl*> StatisticianImplMap;
|
||||||
|
|
||||||
@@ -108,6 +115,8 @@ class ReceiveStatisticsImpl : public ReceiveStatistics {
|
|||||||
scoped_ptr<CriticalSectionWrapper> crit_sect_;
|
scoped_ptr<CriticalSectionWrapper> crit_sect_;
|
||||||
int64_t last_rate_update_ms_;
|
int64_t last_rate_update_ms_;
|
||||||
StatisticianImplMap statisticians_;
|
StatisticianImplMap statisticians_;
|
||||||
|
|
||||||
|
RtcpStatisticsCallback* rtcp_stats_callback_;
|
||||||
};
|
};
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVE_STATISTICS_IMPL_H_
|
#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVE_STATISTICS_IMPL_H_
|
||||||
|
|||||||
@@ -131,4 +131,87 @@ TEST_F(ReceiveStatisticsTest, ActiveStatisticians) {
|
|||||||
EXPECT_EQ(200u, bytes_received);
|
EXPECT_EQ(200u, bytes_received);
|
||||||
EXPECT_EQ(2u, packets_received);
|
EXPECT_EQ(2u, packets_received);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ReceiveStatisticsTest, Callbacks) {
|
||||||
|
class TestCallback : public RtcpStatisticsCallback {
|
||||||
|
public:
|
||||||
|
TestCallback()
|
||||||
|
: RtcpStatisticsCallback(), num_calls_(0), ssrc_(0), stats_() {}
|
||||||
|
virtual ~TestCallback() {}
|
||||||
|
|
||||||
|
virtual void StatisticsUpdated(const RtcpStatistics& statistics,
|
||||||
|
uint32_t ssrc) {
|
||||||
|
ssrc_ = ssrc;
|
||||||
|
stats_ = statistics;
|
||||||
|
++num_calls_;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t num_calls_;
|
||||||
|
uint32_t ssrc_;
|
||||||
|
RtcpStatistics stats_;
|
||||||
|
} callback;
|
||||||
|
|
||||||
|
receive_statistics_->RegisterRtcpStatisticsCallback(&callback);
|
||||||
|
|
||||||
|
// Add some arbitrary data, with loss and jitter.
|
||||||
|
header1_.sequenceNumber = 1;
|
||||||
|
clock_.AdvanceTimeMilliseconds(7);
|
||||||
|
header1_.timestamp += 3;
|
||||||
|
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
|
||||||
|
header1_.sequenceNumber += 2;
|
||||||
|
clock_.AdvanceTimeMilliseconds(9);
|
||||||
|
header1_.timestamp += 9;
|
||||||
|
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
|
||||||
|
--header1_.sequenceNumber;
|
||||||
|
clock_.AdvanceTimeMilliseconds(13);
|
||||||
|
header1_.timestamp += 47;
|
||||||
|
receive_statistics_->IncomingPacket(header1_, kPacketSize1, true);
|
||||||
|
header1_.sequenceNumber += 3;
|
||||||
|
clock_.AdvanceTimeMilliseconds(11);
|
||||||
|
header1_.timestamp += 17;
|
||||||
|
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
|
||||||
|
++header1_.sequenceNumber;
|
||||||
|
|
||||||
|
EXPECT_EQ(0u, callback.num_calls_);
|
||||||
|
|
||||||
|
// Call GetStatistics, simulating a timed rtcp sender thread.
|
||||||
|
RtcpStatistics statistics;
|
||||||
|
receive_statistics_->GetStatistician(kSsrc1)
|
||||||
|
->GetStatistics(&statistics, true);
|
||||||
|
|
||||||
|
EXPECT_EQ(1u, callback.num_calls_);
|
||||||
|
EXPECT_EQ(callback.ssrc_, kSsrc1);
|
||||||
|
EXPECT_EQ(statistics.cumulative_lost, callback.stats_.cumulative_lost);
|
||||||
|
EXPECT_EQ(statistics.extended_max_sequence_number,
|
||||||
|
callback.stats_.extended_max_sequence_number);
|
||||||
|
EXPECT_EQ(statistics.fraction_lost, callback.stats_.fraction_lost);
|
||||||
|
EXPECT_EQ(statistics.jitter, callback.stats_.jitter);
|
||||||
|
|
||||||
|
receive_statistics_->RegisterRtcpStatisticsCallback(NULL);
|
||||||
|
|
||||||
|
// Add some more data.
|
||||||
|
header1_.sequenceNumber = 1;
|
||||||
|
clock_.AdvanceTimeMilliseconds(7);
|
||||||
|
header1_.timestamp += 3;
|
||||||
|
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
|
||||||
|
header1_.sequenceNumber += 2;
|
||||||
|
clock_.AdvanceTimeMilliseconds(9);
|
||||||
|
header1_.timestamp += 9;
|
||||||
|
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
|
||||||
|
--header1_.sequenceNumber;
|
||||||
|
clock_.AdvanceTimeMilliseconds(13);
|
||||||
|
header1_.timestamp += 47;
|
||||||
|
receive_statistics_->IncomingPacket(header1_, kPacketSize1, true);
|
||||||
|
header1_.sequenceNumber += 3;
|
||||||
|
clock_.AdvanceTimeMilliseconds(11);
|
||||||
|
header1_.timestamp += 17;
|
||||||
|
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
|
||||||
|
++header1_.sequenceNumber;
|
||||||
|
|
||||||
|
receive_statistics_->GetStatistician(kSsrc1)
|
||||||
|
->GetStatistics(&statistics, true);
|
||||||
|
|
||||||
|
// Should not have been called after deregister.
|
||||||
|
EXPECT_EQ(1u, callback.num_calls_);
|
||||||
|
}
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@@ -2065,7 +2065,7 @@ bool RTCPSender::PrepareReport(const FeedbackState& feedback_state,
|
|||||||
RTCPReportBlock* report_block,
|
RTCPReportBlock* report_block,
|
||||||
uint32_t* ntp_secs, uint32_t* ntp_frac) {
|
uint32_t* ntp_secs, uint32_t* ntp_frac) {
|
||||||
// Do we have receive statistics to send?
|
// Do we have receive statistics to send?
|
||||||
StreamStatistician::Statistics stats;
|
RtcpStatistics stats;
|
||||||
if (!statistician->GetStatistics(&stats, true))
|
if (!statistician->GetStatistics(&stats, true))
|
||||||
return false;
|
return false;
|
||||||
report_block->fractionLost = stats.fraction_lost;
|
report_block->fractionLost = stats.fraction_lost;
|
||||||
|
|||||||
@@ -336,7 +336,7 @@ TEST_F(RtpRtcpRtcpTest, RTCP) {
|
|||||||
|
|
||||||
StreamStatistician *statistician =
|
StreamStatistician *statistician =
|
||||||
receive_statistics2_->GetStatistician(reportBlockReceived.sourceSSRC);
|
receive_statistics2_->GetStatistician(reportBlockReceived.sourceSSRC);
|
||||||
StreamStatistician::Statistics stats;
|
RtcpStatistics stats;
|
||||||
EXPECT_TRUE(statistician->GetStatistics(&stats, true));
|
EXPECT_TRUE(statistician->GetStatistics(&stats, true));
|
||||||
EXPECT_EQ(0, stats.fraction_lost);
|
EXPECT_EQ(0, stats.fraction_lost);
|
||||||
EXPECT_EQ((uint32_t)0, stats.cumulative_lost);
|
EXPECT_EQ((uint32_t)0, stats.cumulative_lost);
|
||||||
|
|||||||
@@ -348,7 +348,8 @@ class FakeReceiveStatistics : public NullReceiveStatistics {
|
|||||||
stats_.cumulative_lost = cumulative_lost;
|
stats_.cumulative_lost = cumulative_lost;
|
||||||
stats_.extended_max_sequence_number = extended_max_sequence_number;
|
stats_.extended_max_sequence_number = extended_max_sequence_number;
|
||||||
}
|
}
|
||||||
virtual bool GetStatistics(Statistics* statistics, bool reset) OVERRIDE {
|
virtual bool GetStatistics(RtcpStatistics* statistics,
|
||||||
|
bool reset) OVERRIDE {
|
||||||
*statistics = stats_;
|
*statistics = stats_;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -367,7 +368,8 @@ class FakeReceiveStatistics : public NullReceiveStatistics {
|
|||||||
virtual bool IsPacketInOrder(uint16_t sequence_number) const OVERRIDE {
|
virtual bool IsPacketInOrder(uint16_t sequence_number) const OVERRIDE {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Statistics stats_;
|
|
||||||
|
RtcpStatistics stats_;
|
||||||
};
|
};
|
||||||
|
|
||||||
scoped_ptr<LossyStatistician> lossy_stats_;
|
scoped_ptr<LossyStatistician> lossy_stats_;
|
||||||
|
|||||||
@@ -345,6 +345,8 @@ int32_t ViEChannel::SetSendCodec(const VideoCodec& video_codec,
|
|||||||
}
|
}
|
||||||
rtp_rtcp->SetSendingStatus(rtp_rtcp_->Sending());
|
rtp_rtcp->SetSendingStatus(rtp_rtcp_->Sending());
|
||||||
rtp_rtcp->SetSendingMediaStatus(rtp_rtcp_->SendingMedia());
|
rtp_rtcp->SetSendingMediaStatus(rtp_rtcp_->SendingMedia());
|
||||||
|
rtp_rtcp->RegisterSendChannelRtcpStatisticsCallback(
|
||||||
|
rtp_rtcp_->GetSendChannelRtcpStatisticsCallback());
|
||||||
simulcast_rtp_rtcp_.push_back(rtp_rtcp);
|
simulcast_rtp_rtcp_.push_back(rtp_rtcp);
|
||||||
}
|
}
|
||||||
// Remove last in list if we have too many.
|
// Remove last in list if we have too many.
|
||||||
@@ -1297,7 +1299,7 @@ int32_t ViEChannel::GetReceivedRtcpStatistics(uint16_t* fraction_lost,
|
|||||||
uint32_t remote_ssrc = vie_receiver_.GetRemoteSsrc();
|
uint32_t remote_ssrc = vie_receiver_.GetRemoteSsrc();
|
||||||
StreamStatistician* statistician =
|
StreamStatistician* statistician =
|
||||||
vie_receiver_.GetReceiveStatistics()->GetStatistician(remote_ssrc);
|
vie_receiver_.GetReceiveStatistics()->GetStatistician(remote_ssrc);
|
||||||
StreamStatistician::Statistics receive_stats;
|
RtcpStatistics receive_stats;
|
||||||
if (!statistician || !statistician->GetStatistics(
|
if (!statistician || !statistician->GetStatistics(
|
||||||
&receive_stats, rtp_rtcp_->RTCP() == kRtcpOff)) {
|
&receive_stats, rtp_rtcp_->RTCP() == kRtcpOff)) {
|
||||||
WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_),
|
WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_),
|
||||||
@@ -1319,6 +1321,17 @@ int32_t ViEChannel::GetReceivedRtcpStatistics(uint16_t* fraction_lost,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ViEChannel::RegisterReceiveChannelRtcpStatisticsCallback(
|
||||||
|
RtcpStatisticsCallback* callback) {
|
||||||
|
WEBRTC_TRACE(kTraceInfo,
|
||||||
|
kTraceVideo,
|
||||||
|
ViEId(engine_id_, channel_id_),
|
||||||
|
"%s",
|
||||||
|
__FUNCTION__);
|
||||||
|
vie_receiver_.GetReceiveStatistics()->RegisterRtcpStatisticsCallback(
|
||||||
|
callback);
|
||||||
|
}
|
||||||
|
|
||||||
int32_t ViEChannel::GetRtpStatistics(uint32_t* bytes_sent,
|
int32_t ViEChannel::GetRtpStatistics(uint32_t* bytes_sent,
|
||||||
uint32_t* packets_sent,
|
uint32_t* packets_sent,
|
||||||
uint32_t* bytes_received,
|
uint32_t* bytes_received,
|
||||||
|
|||||||
@@ -185,6 +185,10 @@ class ViEChannel
|
|||||||
uint32_t* jitter_samples,
|
uint32_t* jitter_samples,
|
||||||
int32_t* rtt_ms);
|
int32_t* rtt_ms);
|
||||||
|
|
||||||
|
// Called on generation of RTCP stats
|
||||||
|
void RegisterReceiveChannelRtcpStatisticsCallback(
|
||||||
|
RtcpStatisticsCallback* callback);
|
||||||
|
|
||||||
// Gets sent/received packets statistics.
|
// Gets sent/received packets statistics.
|
||||||
int32_t GetRtpStatistics(uint32_t* bytes_sent,
|
int32_t GetRtpStatistics(uint32_t* bytes_sent,
|
||||||
uint32_t* packets_sent,
|
uint32_t* packets_sent,
|
||||||
|
|||||||
@@ -1168,15 +1168,35 @@ int ViERTP_RTCPImpl::DeregisterSendChannelRtcpStatisticsCallback(
|
|||||||
}
|
}
|
||||||
|
|
||||||
int ViERTP_RTCPImpl::RegisterReceiveChannelRtcpStatisticsCallback(
|
int ViERTP_RTCPImpl::RegisterReceiveChannelRtcpStatisticsCallback(
|
||||||
int channel, RtcpStatisticsCallback* callback) {
|
const int video_channel,
|
||||||
// TODO(sprang): Implement
|
RtcpStatisticsCallback* callback) {
|
||||||
return -1;
|
WEBRTC_TRACE(kTraceApiCall,
|
||||||
|
kTraceVideo,
|
||||||
|
ViEId(shared_data_->instance_id(), video_channel),
|
||||||
|
"%s(channel: %d)",
|
||||||
|
__FUNCTION__,
|
||||||
|
video_channel);
|
||||||
|
ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
|
||||||
|
ViEChannel* vie_channel = cs.Channel(video_channel);
|
||||||
|
assert(vie_channel != NULL);
|
||||||
|
vie_channel->RegisterReceiveChannelRtcpStatisticsCallback(callback);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ViERTP_RTCPImpl::DeregisterReceiveChannelRtcpStatisticsCallback(
|
int ViERTP_RTCPImpl::DeregisterReceiveChannelRtcpStatisticsCallback(
|
||||||
int channel, RtcpStatisticsCallback* callback) {
|
const int video_channel,
|
||||||
// TODO(sprang): Implement
|
RtcpStatisticsCallback* callback) {
|
||||||
return -1;
|
WEBRTC_TRACE(kTraceApiCall,
|
||||||
|
kTraceVideo,
|
||||||
|
ViEId(shared_data_->instance_id(), video_channel),
|
||||||
|
"%s(channel: %d)",
|
||||||
|
__FUNCTION__,
|
||||||
|
video_channel);
|
||||||
|
ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
|
||||||
|
ViEChannel* vie_channel = cs.Channel(video_channel);
|
||||||
|
assert(vie_channel != NULL);
|
||||||
|
vie_channel->RegisterReceiveChannelRtcpStatisticsCallback(NULL);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ViERTP_RTCPImpl::RegisterSendChannelRtpStatisticsCallback(
|
int ViERTP_RTCPImpl::RegisterSendChannelRtpStatisticsCallback(
|
||||||
|
|||||||
@@ -38,6 +38,54 @@
|
|||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
namespace voe {
|
namespace voe {
|
||||||
|
|
||||||
|
// Extend the default RTCP statistics struct with max_jitter, defined as the
|
||||||
|
// maximum jitter value seen in an RTCP report block.
|
||||||
|
struct ChannelStatistics : public RtcpStatistics {
|
||||||
|
ChannelStatistics() : rtcp(), max_jitter(0) {}
|
||||||
|
|
||||||
|
RtcpStatistics rtcp;
|
||||||
|
uint32_t max_jitter;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Statistics callback, called at each generation of a new RTCP report block.
|
||||||
|
class StatisticsProxy : public RtcpStatisticsCallback {
|
||||||
|
public:
|
||||||
|
StatisticsProxy(uint32_t ssrc)
|
||||||
|
: stats_lock_(CriticalSectionWrapper::CreateCriticalSection()),
|
||||||
|
ssrc_(ssrc) {}
|
||||||
|
virtual ~StatisticsProxy() {}
|
||||||
|
|
||||||
|
virtual void StatisticsUpdated(const RtcpStatistics& statistics,
|
||||||
|
uint32_t ssrc) OVERRIDE {
|
||||||
|
if (ssrc != ssrc_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CriticalSectionScoped cs(stats_lock_.get());
|
||||||
|
stats_.rtcp = statistics;
|
||||||
|
if (statistics.jitter > stats_.max_jitter) {
|
||||||
|
stats_.max_jitter = statistics.jitter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResetStatistics() {
|
||||||
|
CriticalSectionScoped cs(stats_lock_.get());
|
||||||
|
stats_ = ChannelStatistics();
|
||||||
|
}
|
||||||
|
|
||||||
|
ChannelStatistics GetStats() {
|
||||||
|
CriticalSectionScoped cs(stats_lock_.get());
|
||||||
|
return stats_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// StatisticsUpdated calls are triggered from threads in the RTP module,
|
||||||
|
// while GetStats calls can be triggered from the public voice engine API,
|
||||||
|
// hence synchronization is needed.
|
||||||
|
scoped_ptr<CriticalSectionWrapper> stats_lock_;
|
||||||
|
const uint32_t ssrc_;
|
||||||
|
ChannelStatistics stats_;
|
||||||
|
};
|
||||||
|
|
||||||
int32_t
|
int32_t
|
||||||
Channel::SendData(FrameType frameType,
|
Channel::SendData(FrameType frameType,
|
||||||
uint8_t payloadType,
|
uint8_t payloadType,
|
||||||
@@ -361,6 +409,7 @@ void Channel::ResetStatistics(uint32_t ssrc) {
|
|||||||
if (statistician) {
|
if (statistician) {
|
||||||
statistician->ResetStatistics();
|
statistician->ResetStatistics();
|
||||||
}
|
}
|
||||||
|
statistics_proxy_->ResetStatistics();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -883,6 +932,7 @@ Channel::Channel(int32_t channelId,
|
|||||||
_rtpDumpOut(*RtpDump::CreateRtpDump()),
|
_rtpDumpOut(*RtpDump::CreateRtpDump()),
|
||||||
_outputAudioLevel(),
|
_outputAudioLevel(),
|
||||||
_externalTransport(false),
|
_externalTransport(false),
|
||||||
|
_audioLevel_dBov(0),
|
||||||
_inputFilePlayerPtr(NULL),
|
_inputFilePlayerPtr(NULL),
|
||||||
_outputFilePlayerPtr(NULL),
|
_outputFilePlayerPtr(NULL),
|
||||||
_outputFileRecorderPtr(NULL),
|
_outputFileRecorderPtr(NULL),
|
||||||
@@ -909,6 +959,7 @@ Channel::Channel(int32_t channelId,
|
|||||||
jitter_buffer_playout_timestamp_(0),
|
jitter_buffer_playout_timestamp_(0),
|
||||||
playout_timestamp_rtp_(0),
|
playout_timestamp_rtp_(0),
|
||||||
playout_timestamp_rtcp_(0),
|
playout_timestamp_rtcp_(0),
|
||||||
|
playout_delay_ms_(0),
|
||||||
_numberOfDiscardedPackets(0),
|
_numberOfDiscardedPackets(0),
|
||||||
send_sequence_number_(0),
|
send_sequence_number_(0),
|
||||||
_engineStatisticsPtr(NULL),
|
_engineStatisticsPtr(NULL),
|
||||||
@@ -984,10 +1035,15 @@ Channel::Channel(int32_t channelId,
|
|||||||
configuration.receive_statistics = rtp_receive_statistics_.get();
|
configuration.receive_statistics = rtp_receive_statistics_.get();
|
||||||
|
|
||||||
_rtpRtcpModule.reset(RtpRtcp::CreateRtpRtcp(configuration));
|
_rtpRtcpModule.reset(RtpRtcp::CreateRtpRtcp(configuration));
|
||||||
|
|
||||||
|
statistics_proxy_.reset(new StatisticsProxy(_rtpRtcpModule->SSRC()));
|
||||||
|
rtp_receive_statistics_->RegisterRtcpStatisticsCallback(
|
||||||
|
statistics_proxy_.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
Channel::~Channel()
|
Channel::~Channel()
|
||||||
{
|
{
|
||||||
|
rtp_receive_statistics_->RegisterRtcpStatisticsCallback(NULL);
|
||||||
WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,_channelId),
|
WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,_channelId),
|
||||||
"Channel::~Channel() - dtor");
|
"Channel::~Channel() - dtor");
|
||||||
|
|
||||||
@@ -3863,23 +3919,25 @@ Channel::GetRTPStatistics(
|
|||||||
{
|
{
|
||||||
// The jitter statistics is updated for each received RTP packet and is
|
// The jitter statistics is updated for each received RTP packet and is
|
||||||
// based on received packets.
|
// based on received packets.
|
||||||
StreamStatistician::Statistics statistics;
|
if (_rtpRtcpModule->RTCP() == kRtcpOff) {
|
||||||
|
// If RTCP is off, there is no timed thread in the RTCP module regularly
|
||||||
|
// generating new stats, trigger the update manually here instead.
|
||||||
StreamStatistician* statistician =
|
StreamStatistician* statistician =
|
||||||
rtp_receive_statistics_->GetStatistician(rtp_receiver_->SSRC());
|
rtp_receive_statistics_->GetStatistician(rtp_receiver_->SSRC());
|
||||||
if (!statistician || !statistician->GetStatistics(
|
if (statistician) {
|
||||||
&statistics, _rtpRtcpModule->RTCP() == kRtcpOff)) {
|
// Don't use returned statistics, use data from proxy instead so that
|
||||||
_engineStatisticsPtr->SetLastError(
|
// max jitter can be fetched atomically.
|
||||||
VE_CANNOT_RETRIEVE_RTP_STAT, kTraceWarning,
|
RtcpStatistics s;
|
||||||
"GetRTPStatistics() failed to read RTP statistics from the "
|
statistician->GetStatistics(&s, true);
|
||||||
"RTP/RTCP module");
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChannelStatistics stats = statistics_proxy_->GetStats();
|
||||||
const int32_t playoutFrequency = audio_coding_->PlayoutFrequency();
|
const int32_t playoutFrequency = audio_coding_->PlayoutFrequency();
|
||||||
if (playoutFrequency > 0)
|
if (playoutFrequency > 0) {
|
||||||
{
|
|
||||||
// Scale RTP statistics given the current playout frequency
|
// Scale RTP statistics given the current playout frequency
|
||||||
maxJitterMs = statistics.max_jitter / (playoutFrequency / 1000);
|
maxJitterMs = stats.max_jitter / (playoutFrequency / 1000);
|
||||||
averageJitterMs = statistics.jitter / (playoutFrequency / 1000);
|
averageJitterMs = stats.rtcp.jitter / (playoutFrequency / 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
discardedPackets = _numberOfDiscardedPackets;
|
discardedPackets = _numberOfDiscardedPackets;
|
||||||
@@ -3959,7 +4017,7 @@ Channel::GetRTPStatistics(CallStatistics& stats)
|
|||||||
|
|
||||||
// The jitter statistics is updated for each received RTP packet and is
|
// The jitter statistics is updated for each received RTP packet and is
|
||||||
// based on received packets.
|
// based on received packets.
|
||||||
StreamStatistician::Statistics statistics;
|
RtcpStatistics statistics;
|
||||||
StreamStatistician* statistician =
|
StreamStatistician* statistician =
|
||||||
rtp_receive_statistics_->GetStatistician(rtp_receiver_->SSRC());
|
rtp_receive_statistics_->GetStatistician(rtp_receiver_->SSRC());
|
||||||
if (!statistician || !statistician->GetStatistics(
|
if (!statistician || !statistician->GetStatistics(
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ struct SenderInfo;
|
|||||||
namespace voe {
|
namespace voe {
|
||||||
|
|
||||||
class Statistics;
|
class Statistics;
|
||||||
|
class StatisticsProxy;
|
||||||
class TransmitMixer;
|
class TransmitMixer;
|
||||||
class OutputMixer;
|
class OutputMixer;
|
||||||
|
|
||||||
@@ -455,6 +456,7 @@ private:
|
|||||||
scoped_ptr<RtpHeaderParser> rtp_header_parser_;
|
scoped_ptr<RtpHeaderParser> rtp_header_parser_;
|
||||||
scoped_ptr<RTPPayloadRegistry> rtp_payload_registry_;
|
scoped_ptr<RTPPayloadRegistry> rtp_payload_registry_;
|
||||||
scoped_ptr<ReceiveStatistics> rtp_receive_statistics_;
|
scoped_ptr<ReceiveStatistics> rtp_receive_statistics_;
|
||||||
|
scoped_ptr<StatisticsProxy> statistics_proxy_;
|
||||||
scoped_ptr<RtpReceiver> rtp_receiver_;
|
scoped_ptr<RtpReceiver> rtp_receiver_;
|
||||||
TelephoneEventHandler* telephone_event_handler_;
|
TelephoneEventHandler* telephone_event_handler_;
|
||||||
scoped_ptr<RtpRtcp> _rtpRtcpModule;
|
scoped_ptr<RtpRtcp> _rtpRtcpModule;
|
||||||
|
|||||||
Reference in New Issue
Block a user