Fix for sent/received RTCP packet counters returned by GetRtcpPacketTypeCounters. The returned counters are incorrect: sent_packets returns stats from a sent stream (and received_packets returns stats from a receive stream).

Add separate functions for returning stats from send/receive stream and updated how functions are used.

Add test implementation for histogram methods in system_wrappers/interface/metrics.h.

BUG=4519
R=pbos@webrtc.org, stefan@webrtc.org

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

Cr-Commit-Position: refs/heads/master@{#9009}
This commit is contained in:
Åsa Persson 2015-04-15 18:00:40 +02:00
parent 4b76c02362
commit 352b2d7a19
13 changed files with 304 additions and 64 deletions

View File

@ -1176,8 +1176,10 @@ class FakeWebRtcVideoEngine
reserved_transmit_bitrate_bps;
return 0;
}
WEBRTC_STUB_CONST(GetRtcpPacketTypeCounters, (int,
webrtc::RtcpPacketTypeCounter*, webrtc::RtcpPacketTypeCounter*));
WEBRTC_STUB_CONST(GetSendRtcpPacketTypeCounter, (int,
webrtc::RtcpPacketTypeCounter*));
WEBRTC_STUB_CONST(GetReceiveRtcpPacketTypeCounter, (int,
webrtc::RtcpPacketTypeCounter*));
WEBRTC_STUB_CONST(GetReceivedRTCPStatistics, (const int, unsigned short&,
unsigned int&, unsigned int&, unsigned int&, int64_t&));
WEBRTC_STUB_CONST(GetSentRTCPStatistics, (const int, unsigned short&,

View File

@ -2646,13 +2646,12 @@ bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) {
sinfo.avg_encode_ms = metrics.avg_encode_time_ms;
sinfo.encode_usage_percent = metrics.encode_usage_percent;
webrtc::RtcpPacketTypeCounter rtcp_sent;
webrtc::RtcpPacketTypeCounter rtcp_received;
if (engine()->vie()->rtp()->GetRtcpPacketTypeCounters(
channel_id, &rtcp_sent, &rtcp_received) == 0) {
sinfo.firs_rcvd = rtcp_received.fir_packets;
sinfo.plis_rcvd = rtcp_received.pli_packets;
sinfo.nacks_rcvd = rtcp_received.nack_packets;
webrtc::RtcpPacketTypeCounter rtcp_counter;
if (engine()->vie()->rtp()->GetSendRtcpPacketTypeCounter(
channel_id, &rtcp_counter) == 0) {
sinfo.firs_rcvd = rtcp_counter.fir_packets;
sinfo.plis_rcvd = rtcp_counter.pli_packets;
sinfo.nacks_rcvd = rtcp_counter.nack_packets;
} else {
sinfo.firs_rcvd = -1;
sinfo.plis_rcvd = -1;
@ -2753,13 +2752,12 @@ bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) {
channel->render_adapter()->capture_start_ntp_time_ms();
channel->decoder_observer()->ExportTo(&rinfo);
webrtc::RtcpPacketTypeCounter rtcp_sent;
webrtc::RtcpPacketTypeCounter rtcp_received;
if (engine()->vie()->rtp()->GetRtcpPacketTypeCounters(
channel->channel_id(), &rtcp_sent, &rtcp_received) == 0) {
rinfo.firs_sent = rtcp_sent.fir_packets;
rinfo.plis_sent = rtcp_sent.pli_packets;
rinfo.nacks_sent = rtcp_sent.nack_packets;
webrtc::RtcpPacketTypeCounter rtcp_counter;
if (engine()->vie()->rtp()->GetReceiveRtcpPacketTypeCounter(
channel->channel_id(), &rtcp_counter) == 0) {
rinfo.firs_sent = rtcp_counter.fir_packets;
rinfo.plis_sent = rtcp_counter.pli_packets;
rinfo.nacks_sent = rtcp_counter.nack_packets;
} else {
rinfo.firs_sent = -1;
rinfo.plis_sent = -1;

View File

@ -178,8 +178,6 @@ class MockRtpRtcp : public RtpRtcp {
int32_t(const uint32_t SSRC, const RTCPReportBlock* receiveBlock));
MOCK_METHOD1(RemoveRTCPReportBlock,
int32_t(const uint32_t SSRC));
MOCK_CONST_METHOD2(GetRtcpPacketTypeCounters,
void(RtcpPacketTypeCounter*, RtcpPacketTypeCounter*));
MOCK_METHOD4(SetRTCPApplicationSpecificData,
int32_t(const uint8_t subType, const uint32_t name, const uint8_t* data, const uint16_t length));
MOCK_METHOD1(SetRTCPVoIPMetrics,

View File

@ -33,6 +33,21 @@ source_set("field_trial") {
public_configs = [ "..:common_inherited_config"]
}
source_set("histogram") {
sources = [
"histogram.cc",
"histogram.h",
]
deps = [
"..:webrtc_common",
"../system_wrappers",
]
configs += [ "..:common_config" ]
public_configs = [ "..:common_inherited_config"]
}
source_set("test_support") {
testonly = true
@ -80,8 +95,8 @@ source_set("test_support_main") {
deps = [
":field_trial",
":histogram",
":test_support",
"../system_wrappers:metrics_default",
"//testing/gmock",
"//testing/gtest",
"//third_party/gflags",

49
webrtc/test/histogram.cc Normal file
View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2015 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/test/histogram.h"
#include <map>
#include "webrtc/system_wrappers/interface/metrics.h"
// Test implementation of histogram methods in
// webrtc/system_wrappers/interface/metrics.h.
namespace webrtc {
namespace {
// Map holding the last added sample to a histogram (mapped by histogram name).
std::map<std::string, int> histograms_;
} // namespace
namespace metrics {
Histogram* HistogramFactoryGetCounts(const std::string& name, int min, int max,
int bucket_count) { return NULL; }
Histogram* HistogramFactoryGetEnumeration(const std::string& name,
int boundary) { return NULL; }
void HistogramAdd(
Histogram* histogram_pointer, const std::string& name, int sample) {
histograms_[name] = sample;
}
} // namespace metrics
namespace test {
int LastHistogramSample(const std::string& name) {
std::map<std::string, int>::const_iterator it = histograms_.find(name);
if (it == histograms_.end()) {
return -1;
}
return it->second;
}
} // namespace test
} // namespace webrtc

27
webrtc/test/histogram.h Normal file
View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2015 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_TEST_HISTOGRAM_H_
#define WEBRTC_TEST_HISTOGRAM_H_
#include <string>
namespace webrtc {
namespace test {
// Returns the last added sample to a histogram (or -1 if the histogram is not
// found).
int LastHistogramSample(const std::string& name);
} // namespace test
} // namespace webrtc
#endif // WEBRTC_TEST_HISTOGRAM_H_

View File

@ -82,6 +82,18 @@
'<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers',
],
},
{
'target_name': 'histogram',
'type': 'static_library',
'sources': [
'histogram.cc',
'histogram.h',
],
'dependencies': [
'<(webrtc_root)/common.gyp:webrtc_common',
'<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers',
],
},
{
'target_name': 'test_main',
'type': 'static_library',
@ -90,9 +102,9 @@
],
'dependencies': [
'field_trial',
'histogram',
'<(DEPTH)/testing/gtest.gyp:gtest',
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
'<(webrtc_root)/system_wrappers/system_wrappers.gyp:metrics_default',
],
},
{
@ -129,11 +141,11 @@
'type': 'static_library',
'dependencies': [
'field_trial',
'histogram',
'test_support',
'<(DEPTH)/testing/gmock.gyp:gmock',
'<(DEPTH)/testing/gtest.gyp:gtest',
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
'<(webrtc_root)/system_wrappers/system_wrappers.gyp:metrics_default',
],
'sources': [
'run_all_unittests.cc',

View File

@ -24,6 +24,7 @@
#include "webrtc/modules/video_coding/main/interface/video_coding_defines.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/event_wrapper.h"
#include "webrtc/system_wrappers/interface/metrics.h"
#include "webrtc/system_wrappers/interface/sleep.h"
#include "webrtc/test/call_test.h"
#include "webrtc/test/direct_transport.h"
@ -33,6 +34,7 @@
#include "webrtc/test/fake_encoder.h"
#include "webrtc/test/frame_generator.h"
#include "webrtc/test/frame_generator_capturer.h"
#include "webrtc/test/histogram.h"
#include "webrtc/test/null_transport.h"
#include "webrtc/test/rtcp_packet_parser.h"
#include "webrtc/test/rtp_rtcp_observer.h"
@ -1376,6 +1378,115 @@ TEST_F(EndToEndTest, VerifyBandwidthStats) {
RunBaseTest(&test);
}
TEST_F(EndToEndTest, VerifyNackStats) {
static const int kPacketNumberToDrop = 200;
class NackObserver : public test::EndToEndTest {
public:
NackObserver()
: EndToEndTest(kLongTimeoutMs),
sent_rtp_packets_(0),
dropped_rtp_packet_(0),
dropped_rtp_packet_requested_(false),
send_stream_(nullptr),
start_runtime_ms_(-1) {}
private:
Action OnSendRtp(const uint8_t* packet, size_t length) override {
if (++sent_rtp_packets_ == kPacketNumberToDrop) {
rtc::scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
RTPHeader header;
EXPECT_TRUE(parser->Parse(packet, length, &header));
dropped_rtp_packet_ = header.sequenceNumber;
return DROP_PACKET;
}
VerifyStats();
return SEND_PACKET;
}
Action OnReceiveRtcp(const uint8_t* packet, size_t length) override {
test::RtcpPacketParser rtcp_parser;
rtcp_parser.Parse(packet, length);
std::vector<uint16_t> nacks = rtcp_parser.nack_item()->last_nack_list();
if (!nacks.empty() && std::find(
nacks.begin(), nacks.end(), dropped_rtp_packet_) != nacks.end()) {
dropped_rtp_packet_requested_ = true;
}
return SEND_PACKET;
}
void VerifyStats() {
if (!dropped_rtp_packet_requested_)
return;
int send_stream_nack_packets = 0;
int receive_stream_nack_packets = 0;
VideoSendStream::Stats stats = send_stream_->GetStats();
for (std::map<uint32_t, VideoSendStream::StreamStats>::const_iterator it =
stats.substreams.begin(); it != stats.substreams.end(); ++it) {
const VideoSendStream::StreamStats& stream_stats = it->second;
send_stream_nack_packets +=
stream_stats.rtcp_packet_type_counts.nack_packets;
}
for (size_t i = 0; i < receive_streams_.size(); ++i) {
VideoReceiveStream::Stats stats = receive_streams_[i]->GetStats();
receive_stream_nack_packets +=
stats.rtcp_packet_type_counts.nack_packets;
}
if (send_stream_nack_packets >= 1 && receive_stream_nack_packets >= 1) {
// NACK packet sent on receive stream and received on sent stream.
if (MinMetricRunTimePassed())
observation_complete_->Set();
}
}
bool MinMetricRunTimePassed() {
int64_t now = Clock::GetRealTimeClock()->TimeInMilliseconds();
if (start_runtime_ms_ == -1) {
start_runtime_ms_ = now;
return false;
}
int64_t elapsed_sec = (now - start_runtime_ms_) / 1000;
return elapsed_sec > metrics::kMinRunTimeInSeconds;
}
void ModifyConfigs(VideoSendStream::Config* send_config,
std::vector<VideoReceiveStream::Config>* receive_configs,
VideoEncoderConfig* encoder_config) override {
send_config->rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
(*receive_configs)[0].rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
}
void OnStreamsCreated(
VideoSendStream* send_stream,
const std::vector<VideoReceiveStream*>& receive_streams) override {
send_stream_ = send_stream;
receive_streams_ = receive_streams;
}
void PerformTest() override {
EXPECT_EQ(kEventSignaled, Wait())
<< "Timed out waiting for packet to be NACKed.";
}
uint64_t sent_rtp_packets_;
uint16_t dropped_rtp_packet_;
bool dropped_rtp_packet_requested_;
std::vector<VideoReceiveStream*> receive_streams_;
VideoSendStream* send_stream_;
int64_t start_runtime_ms_;
} test;
RunBaseTest(&test);
EXPECT_NE(-1, test::LastHistogramSample(
"WebRTC.Video.UniqueNackRequestsSentInPercent"));
EXPECT_NE(-1, test::LastHistogramSample(
"WebRTC.Video.UniqueNackRequestsReceivedInPercent"));
EXPECT_GT(test::LastHistogramSample(
"WebRTC.Video.NackPacketsSentPerMinute"), 0);
EXPECT_GT(test::LastHistogramSample(
"WebRTC.Video.NackPacketsReceivedPerMinute"), 0);
}
void EndToEndTest::TestXrReceiverReferenceTimeReport(bool enable_rrtr) {
static const int kNumRtcpReportPacketsToObserve = 5;
class RtcpXrObserver : public test::EndToEndTest {

View File

@ -377,12 +377,14 @@ class WEBRTC_DLLEXPORT ViERTP_RTCP {
int video_channel, StreamDataCountersCallback* callback) = 0;
// Gets sent and received RTCP packet types.
// TODO(asapersson): Remove default implementation.
virtual int GetRtcpPacketTypeCounters(
// Gets RTCP packet type statistics from a sent/received stream.
virtual int GetSendRtcpPacketTypeCounter(
int video_channel,
RtcpPacketTypeCounter* packets_sent,
RtcpPacketTypeCounter* packets_received) const { return -1; }
RtcpPacketTypeCounter* packet_counter) const = 0;
virtual int GetReceiveRtcpPacketTypeCounter(
int video_channel,
RtcpPacketTypeCounter* packet_counter) const = 0;
// The function gets bandwidth usage statistics from the sent RTP streams in
// bits/s.

View File

@ -232,23 +232,22 @@ ViEChannel::~ViEChannel() {
void ViEChannel::UpdateHistograms() {
int64_t now = Clock::GetRealTimeClock()->TimeInMilliseconds();
RtcpPacketTypeCounter rtcp_sent;
RtcpPacketTypeCounter rtcp_received;
GetRtcpPacketTypeCounters(&rtcp_sent, &rtcp_received);
if (sender_) {
int64_t elapsed_sec = rtcp_received.TimeSinceFirstPacketInMs(now) / 1000;
RtcpPacketTypeCounter rtcp_counter;
GetSendRtcpPacketTypeCounter(&rtcp_counter);
int64_t elapsed_sec = rtcp_counter.TimeSinceFirstPacketInMs(now) / 1000;
if (elapsed_sec > metrics::kMinRunTimeInSeconds) {
RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.NackPacketsReceivedPerMinute",
rtcp_received.nack_packets * 60 / elapsed_sec);
rtcp_counter.nack_packets * 60 / elapsed_sec);
RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.FirPacketsReceivedPerMinute",
rtcp_received.fir_packets * 60 / elapsed_sec);
rtcp_counter.fir_packets * 60 / elapsed_sec);
RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.PliPacketsReceivedPerMinute",
rtcp_received.pli_packets * 60 / elapsed_sec);
if (rtcp_received.nack_requests > 0) {
rtcp_counter.pli_packets * 60 / elapsed_sec);
if (rtcp_counter.nack_requests > 0) {
RTC_HISTOGRAM_PERCENTAGE(
"WebRTC.Video.UniqueNackRequestsReceivedInPercent",
rtcp_received.UniqueNackRequestsInPercent());
rtcp_counter.UniqueNackRequestsInPercent());
}
int fraction_lost = report_block_stats_sender_->FractionLostInPercent();
if (fraction_lost != -1) {
@ -259,17 +258,19 @@ void ViEChannel::UpdateHistograms() {
} else if (vie_receiver_.GetRemoteSsrc() > 0) {
// Get receive stats if we are receiving packets, i.e. there is a remote
// ssrc.
int64_t elapsed_sec = rtcp_sent.TimeSinceFirstPacketInMs(now) / 1000;
RtcpPacketTypeCounter rtcp_counter;
GetReceiveRtcpPacketTypeCounter(&rtcp_counter);
int64_t elapsed_sec = rtcp_counter.TimeSinceFirstPacketInMs(now) / 1000;
if (elapsed_sec > metrics::kMinRunTimeInSeconds) {
RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.NackPacketsSentPerMinute",
rtcp_sent.nack_packets * 60 / elapsed_sec);
rtcp_counter.nack_packets * 60 / elapsed_sec);
RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.FirPacketsSentPerMinute",
rtcp_sent.fir_packets * 60 / elapsed_sec);
rtcp_counter.fir_packets * 60 / elapsed_sec);
RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.PliPacketsSentPerMinute",
rtcp_sent.pli_packets * 60 / elapsed_sec);
if (rtcp_sent.nack_requests > 0) {
rtcp_counter.pli_packets * 60 / elapsed_sec);
if (rtcp_counter.nack_requests > 0) {
RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.UniqueNackRequestsSentInPercent",
rtcp_sent.UniqueNackRequestsInPercent());
rtcp_counter.UniqueNackRequestsInPercent());
}
int fraction_lost = report_block_stats_receiver_->FractionLostInPercent();
if (fraction_lost != -1) {
@ -1357,27 +1358,35 @@ void ViEChannel::RegisterReceiveChannelRtpStatisticsCallback(
vie_receiver_.GetReceiveStatistics()->RegisterRtpStatisticsCallback(callback);
}
void ViEChannel::GetRtcpPacketTypeCounters(
RtcpPacketTypeCounter* packets_sent,
RtcpPacketTypeCounter* packets_received) const {
void ViEChannel::GetSendRtcpPacketTypeCounter(
RtcpPacketTypeCounter* packet_counter) const {
std::map<uint32_t, RtcpPacketTypeCounter> counter_map =
rtcp_packet_type_counter_observer_.GetPacketTypeCounterMap();
RtcpPacketTypeCounter sent_counter;
sent_counter.Add(counter_map[rtp_rtcp_->SSRC()]);
RtcpPacketTypeCounter received_counter;
received_counter.Add(counter_map[vie_receiver_.GetRemoteSsrc()]);
RtcpPacketTypeCounter counter;
counter.Add(counter_map[rtp_rtcp_->SSRC()]);
CriticalSectionScoped cs(rtp_rtcp_cs_.get());
for (std::list<RtpRtcp*>::const_iterator it = simulcast_rtp_rtcp_.begin();
it != simulcast_rtp_rtcp_.end(); ++it) {
sent_counter.Add(counter_map[(*it)->SSRC()]);
counter.Add(counter_map[(*it)->SSRC()]);
}
for (std::list<RtpRtcp*>::const_iterator it = removed_rtp_rtcp_.begin();
it != removed_rtp_rtcp_.end(); ++it) {
sent_counter.Add(counter_map[(*it)->SSRC()]);
counter.Add(counter_map[(*it)->SSRC()]);
}
*packets_sent = sent_counter;
*packets_received = received_counter;
*packet_counter = counter;
}
void ViEChannel::GetReceiveRtcpPacketTypeCounter(
RtcpPacketTypeCounter* packet_counter) const {
std::map<uint32_t, RtcpPacketTypeCounter> counter_map =
rtcp_packet_type_counter_observer_.GetPacketTypeCounterMap();
RtcpPacketTypeCounter counter;
counter.Add(counter_map[vie_receiver_.GetRemoteSsrc()]);
*packet_counter = counter;
}
void ViEChannel::GetBandwidthUsage(uint32_t* total_bitrate_sent,

View File

@ -219,8 +219,11 @@ class ViEChannel
void RegisterReceiveChannelRtpStatisticsCallback(
StreamDataCountersCallback* callback);
void GetRtcpPacketTypeCounters(RtcpPacketTypeCounter* packets_sent,
RtcpPacketTypeCounter* packets_received) const;
void GetSendRtcpPacketTypeCounter(
RtcpPacketTypeCounter* packet_counter) const;
void GetReceiveRtcpPacketTypeCounter(
RtcpPacketTypeCounter* packet_counter) const;
void GetBandwidthUsage(uint32_t* total_bitrate_sent,
uint32_t* video_bitrate_sent,

View File

@ -774,17 +774,29 @@ int ViERTP_RTCPImpl::GetRtpStatistics(const int video_channel,
return 0;
}
int ViERTP_RTCPImpl::GetRtcpPacketTypeCounters(
int ViERTP_RTCPImpl::GetSendRtcpPacketTypeCounter(
int video_channel,
RtcpPacketTypeCounter* packets_sent,
RtcpPacketTypeCounter* packets_received) const {
RtcpPacketTypeCounter* packet_counter) const {
ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
ViEChannel* vie_channel = cs.Channel(video_channel);
if (!vie_channel) {
shared_data_->SetLastError(kViERtpRtcpInvalidChannelId);
return -1;
}
vie_channel->GetRtcpPacketTypeCounters(packets_sent, packets_received);
vie_channel->GetSendRtcpPacketTypeCounter(packet_counter);
return 0;
}
int ViERTP_RTCPImpl::GetReceiveRtcpPacketTypeCounter(
int video_channel,
RtcpPacketTypeCounter* packet_counter) const {
ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
ViEChannel* vie_channel = cs.Channel(video_channel);
if (!vie_channel) {
shared_data_->SetLastError(kViERtpRtcpInvalidChannelId);
return -1;
}
vie_channel->GetReceiveRtcpPacketTypeCounter(packet_counter);
return 0;
}

View File

@ -111,10 +111,12 @@ class ViERTP_RTCPImpl
virtual int GetRtpStatistics(const int video_channel,
StreamDataCounters& sent,
StreamDataCounters& received) const;
virtual int GetRtcpPacketTypeCounters(
virtual int GetSendRtcpPacketTypeCounter(
int video_channel,
RtcpPacketTypeCounter* packets_sent,
RtcpPacketTypeCounter* packets_received) const;
RtcpPacketTypeCounter* packet_counter) const;
virtual int GetReceiveRtcpPacketTypeCounter(
int video_channel,
RtcpPacketTypeCounter* packet_counter) const;
virtual int GetBandwidthUsage(const int video_channel,
unsigned int& total_bitrate_sent,
unsigned int& video_bitrate_sent,
@ -141,9 +143,9 @@ class ViERTP_RTCPImpl
virtual int DeregisterSendChannelRtcpStatisticsCallback(
int channel, RtcpStatisticsCallback* callback);
virtual int RegisterReceiveChannelRtcpStatisticsCallback(
int channel, RtcpStatisticsCallback* callback);
virtual int DeregisterReceiveChannelRtcpStatisticsCallback(
int channel, RtcpStatisticsCallback* callback);
int channel, RtcpStatisticsCallback* callback);
virtual int DeregisterReceiveChannelRtcpStatisticsCallback(
int channel, RtcpStatisticsCallback* callback);
virtual int RegisterSendChannelRtpStatisticsCallback(
int channel, StreamDataCountersCallback* callback);
virtual int DeregisterSendChannelRtpStatisticsCallback(