Add histogram stats for average send delay of sent packets for a sent video stream. The delay is measured from a packet is sent to the transport until leaving the socket.

- "WebRTC.Video.SendDelayInMs"

Change so that PacketOption packet id is always set in RtpSender (if having a TransportSequenceNumberAllocator).
Add SendDelayStats class for computing delays.
Add SendPacketObserver to RtpRtcp config and register SendDelayStats as observer.
Wire up OnSentPacket to SendDelayStats.

BUG=webrtc:5215

Review-Url: https://codereview.webrtc.org/1478253002
Cr-Commit-Position: refs/heads/master@{#12600}
This commit is contained in:
asapersson 2016-05-02 23:44:01 -07:00 committed by Commit bot
parent 5a2463796e
commit 35151f35ec
19 changed files with 558 additions and 121 deletions

View File

@ -40,6 +40,7 @@
#include "webrtc/system_wrappers/include/rw_lock_wrapper.h"
#include "webrtc/system_wrappers/include/trace.h"
#include "webrtc/video/call_stats.h"
#include "webrtc/video/send_delay_stats.h"
#include "webrtc/video/video_receive_stream.h"
#include "webrtc/video/video_send_stream.h"
#include "webrtc/video/vie_remb.h"
@ -177,6 +178,7 @@ class Call : public webrtc::Call, public PacketReceiver,
VieRemb remb_;
const std::unique_ptr<CongestionController> congestion_controller_;
const std::unique_ptr<SendDelayStats> video_send_delay_stats_;
RTC_DISALLOW_COPY_AND_ASSIGN(Call);
};
@ -210,7 +212,8 @@ Call::Call(const Call::Config& config)
pacer_bitrate_sum_kbits_(0),
num_bitrate_updates_(0),
remb_(clock_),
congestion_controller_(new CongestionController(clock_, this, &remb_)) {
congestion_controller_(new CongestionController(clock_, this, &remb_)),
video_send_delay_stats_(new SendDelayStats(clock_)) {
RTC_DCHECK(configuration_thread_checker_.CalledOnValidThread());
RTC_DCHECK_GE(config.bitrate_config.min_bitrate_bps, 0);
RTC_DCHECK_GE(config.bitrate_config.start_bitrate_bps,
@ -403,12 +406,14 @@ webrtc::VideoSendStream* Call::CreateVideoSendStream(
TRACE_EVENT0("webrtc", "Call::CreateVideoSendStream");
RTC_DCHECK(configuration_thread_checker_.CalledOnValidThread());
video_send_delay_stats_->AddSsrcs(config);
// TODO(mflodman): Base the start bitrate on a current bandwidth estimate, if
// the call has already started.
VideoSendStream* send_stream = new VideoSendStream(
num_cpu_cores_, module_process_thread_.get(), call_stats_.get(),
congestion_controller_.get(), bitrate_allocator_.get(), &remb_, config,
encoder_config, suspended_video_send_ssrcs_);
congestion_controller_.get(), bitrate_allocator_.get(),
video_send_delay_stats_.get(), &remb_, config, encoder_config,
suspended_video_send_ssrcs_);
{
WriteLockScoped write_lock(*send_crit_);
for (uint32_t ssrc : config.rtp.ssrcs) {
@ -661,6 +666,8 @@ void Call::UpdateAggregateNetworkState() {
void Call::OnSentPacket(const rtc::SentPacket& sent_packet) {
if (first_packet_sent_ms_ == -1)
first_packet_sent_ms_ = clock_->TimeInMilliseconds();
video_send_delay_stats_->OnSentPacket(sent_packet.packet_id,
clock_->TimeInMilliseconds());
congestion_controller_->OnSentPacket(sent_packet);
}

View File

@ -319,7 +319,8 @@ size_t GenerateRtpPacket(uint32_t extensions_bitvector,
nullptr, // BitrateStatisticsObserver*
nullptr, // FrameCountObserver*
nullptr, // SendSideDelayObserver*
nullptr); // RtcEventLog*
nullptr, // RtcEventLog*
nullptr); // SendPacketObserver*
std::vector<uint32_t> csrcs;
for (unsigned i = 0; i < csrcs_count; i++) {

View File

@ -296,6 +296,18 @@ class SendSideDelayObserver {
uint32_t ssrc) = 0;
};
// Callback, used to notify an observer whenever a packet is sent to the
// transport.
// TODO(asapersson): This class will remove the need for SendSideDelayObserver.
// Remove SendSideDelayObserver once possible.
class SendPacketObserver {
public:
virtual ~SendPacketObserver() {}
virtual void OnSendPacket(uint16_t packet_id,
int64_t capture_time_ms,
uint32_t ssrc) = 0;
};
// ==================================================================
// Voice specific types
// ==================================================================

View File

@ -78,7 +78,7 @@ class RtpRtcp : public Module {
FrameCountObserver* send_frame_count_observer;
SendSideDelayObserver* send_side_delay_observer;
RtcEventLog* event_log;
SendPacketObserver* send_packet_observer;
RTC_DISALLOW_COPY_AND_ASSIGN(Configuration);
};

View File

@ -60,7 +60,8 @@ RtpRtcp::Configuration::Configuration()
send_bitrate_observer(nullptr),
send_frame_count_observer(nullptr),
send_side_delay_observer(nullptr),
event_log(nullptr) {}
event_log(nullptr),
send_packet_observer(nullptr) {}
RtpRtcp* RtpRtcp::CreateRtpRtcp(const RtpRtcp::Configuration& configuration) {
if (configuration.clock) {
@ -85,7 +86,8 @@ ModuleRtpRtcpImpl::ModuleRtpRtcpImpl(const Configuration& configuration)
configuration.send_bitrate_observer,
configuration.send_frame_count_observer,
configuration.send_side_delay_observer,
configuration.event_log),
configuration.event_log,
configuration.send_packet_observer),
rtcp_sender_(configuration.audio,
configuration.clock,
configuration.receive_statistics,
@ -105,7 +107,7 @@ ModuleRtpRtcpImpl::ModuleRtpRtcpImpl(const Configuration& configuration)
last_process_time_(configuration.clock->TimeInMilliseconds()),
last_bitrate_process_time_(configuration.clock->TimeInMilliseconds()),
last_rtt_process_time_(configuration.clock->TimeInMilliseconds()),
packet_overhead_(28), // IPV4 UDP.
packet_overhead_(28), // IPV4 UDP.
nack_last_time_sent_full_(0),
nack_last_time_sent_full_prev_(0),
nack_last_seq_number_sent_(0),

View File

@ -113,7 +113,8 @@ RTPSender::RTPSender(
BitrateStatisticsObserver* bitrate_callback,
FrameCountObserver* frame_count_observer,
SendSideDelayObserver* send_side_delay_observer,
RtcEventLog* event_log)
RtcEventLog* event_log,
SendPacketObserver* send_packet_observer)
: clock_(clock),
// TODO(holmer): Remove this conversion when we remove the use of
// TickTime.
@ -150,6 +151,7 @@ RTPSender::RTPSender(
frame_count_observer_(frame_count_observer),
send_side_delay_observer_(send_side_delay_observer),
event_log_(event_log),
send_packet_observer_(send_packet_observer),
// RTP variables
start_timestamp_forced_(false),
start_timestamp_(0),
@ -672,13 +674,13 @@ size_t RTPSender::SendPadData(size_t bytes,
UpdateAbsoluteSendTime(padding_packet, length, rtp_header, now_ms);
PacketOptions options;
if (using_transport_seq) {
options.packet_id =
UpdateTransportSequenceNumber(padding_packet, length, rtp_header);
}
if (using_transport_seq && transport_feedback_observer_) {
transport_feedback_observer_->AddPacket(options.packet_id, length, true);
if (AllocateTransportSequenceNumber(&options.packet_id)) {
if (UpdateTransportSequenceNumber(options.packet_id, padding_packet,
length, rtp_header)) {
if (transport_feedback_observer_)
transport_feedback_observer_->AddPacket(options.packet_id, length,
true);
}
}
if (!SendPacketToNetwork(padding_packet, length, options))
@ -883,9 +885,7 @@ bool RTPSender::TimeToSendPacket(uint16_t sequence_number,
// Packet cannot be found. Allow sending to continue.
return true;
}
if (!retransmission && capture_time_ms > 0) {
UpdateDelayStatistics(capture_time_ms, clock_->TimeInMilliseconds());
}
int rtx;
{
rtc::CritScope lock(&send_critsect_);
@ -929,19 +929,19 @@ bool RTPSender::PrepareAndSendPacket(uint8_t* buffer,
diff_ms);
UpdateAbsoluteSendTime(buffer_to_send_ptr, length, rtp_header, now_ms);
// TODO(sprang): Potentially too much overhead in IsRegistered()?
bool using_transport_seq = rtp_header_extension_map_.IsRegistered(
kRtpExtensionTransportSequenceNumber) &&
transport_sequence_number_allocator_;
PacketOptions options;
if (using_transport_seq) {
options.packet_id =
UpdateTransportSequenceNumber(buffer_to_send_ptr, length, rtp_header);
if (AllocateTransportSequenceNumber(&options.packet_id)) {
if (UpdateTransportSequenceNumber(options.packet_id, buffer_to_send_ptr,
length, rtp_header)) {
if (transport_feedback_observer_)
transport_feedback_observer_->AddPacket(options.packet_id, length,
true);
}
}
if (using_transport_seq && transport_feedback_observer_) {
transport_feedback_observer_->AddPacket(options.packet_id, length, true);
if (!is_retransmit && !send_over_rtx) {
UpdateDelayStatistics(capture_time_ms, now_ms);
UpdateOnSendPacket(options.packet_id, capture_time_ms, rtp_header.ssrc);
}
bool ret = SendPacketToNetwork(buffer_to_send_ptr, length, options);
@ -1058,23 +1058,18 @@ int32_t RTPSender::SendToNetwork(uint8_t* buffer,
}
return 0;
}
if (capture_time_ms > 0) {
UpdateDelayStatistics(capture_time_ms, now_ms);
}
// TODO(sprang): Potentially too much overhead in IsRegistered()?
bool using_transport_seq = rtp_header_extension_map_.IsRegistered(
kRtpExtensionTransportSequenceNumber) &&
transport_sequence_number_allocator_;
PacketOptions options;
if (using_transport_seq) {
options.packet_id =
UpdateTransportSequenceNumber(buffer, length, rtp_header);
if (transport_feedback_observer_) {
transport_feedback_observer_->AddPacket(options.packet_id, length, true);
if (AllocateTransportSequenceNumber(&options.packet_id)) {
if (UpdateTransportSequenceNumber(options.packet_id, buffer, length,
rtp_header)) {
if (transport_feedback_observer_)
transport_feedback_observer_->AddPacket(options.packet_id, length,
true);
}
}
UpdateDelayStatistics(capture_time_ms, now_ms);
UpdateOnSendPacket(options.packet_id, capture_time_ms, rtp_header.ssrc);
bool sent = SendPacketToNetwork(buffer, length, options);
@ -1095,7 +1090,7 @@ int32_t RTPSender::SendToNetwork(uint8_t* buffer,
}
void RTPSender::UpdateDelayStatistics(int64_t capture_time_ms, int64_t now_ms) {
if (!send_side_delay_observer_)
if (!send_side_delay_observer_ || capture_time_ms <= 0)
return;
uint32_t ssrc;
@ -1127,6 +1122,15 @@ void RTPSender::UpdateDelayStatistics(int64_t capture_time_ms, int64_t now_ms) {
ssrc);
}
void RTPSender::UpdateOnSendPacket(int packet_id,
int64_t capture_time_ms,
uint32_t ssrc) {
if (!send_packet_observer_ || capture_time_ms <= 0 || packet_id == -1)
return;
send_packet_observer_->OnSendPacket(packet_id, capture_time_ms, ssrc);
}
void RTPSender::ProcessBitrate() {
rtc::CritScope lock(&send_critsect_);
total_bitrate_sent_.Process();
@ -1610,7 +1614,8 @@ void RTPSender::UpdateAbsoluteSendTime(uint8_t* rtp_packet,
ConvertMsTo24Bits(now_ms));
}
uint16_t RTPSender::UpdateTransportSequenceNumber(
bool RTPSender::UpdateTransportSequenceNumber(
uint16_t sequence_number,
uint8_t* rtp_packet,
size_t rtp_packet_length,
const RTPHeader& rtp_header) const {
@ -1621,19 +1626,26 @@ uint16_t RTPSender::UpdateTransportSequenceNumber(
rtp_packet_length, rtp_header,
kTransportSequenceNumberLength, &offset)) {
case ExtensionStatus::kNotRegistered:
return 0;
return false;
case ExtensionStatus::kError:
LOG(LS_WARNING) << "Failed to update transport sequence number";
return 0;
return false;
case ExtensionStatus::kOk:
break;
default:
RTC_NOTREACHED();
}
uint16_t seq = transport_sequence_number_allocator_->AllocateSequenceNumber();
BuildTransportSequenceNumberExtension(rtp_packet + offset, seq);
return seq;
BuildTransportSequenceNumberExtension(rtp_packet + offset, sequence_number);
return true;
}
bool RTPSender::AllocateTransportSequenceNumber(int* packet_id) const {
if (!transport_sequence_number_allocator_)
return false;
*packet_id = transport_sequence_number_allocator_->AllocateSequenceNumber();
return true;
}
void RTPSender::SetSendingStatus(bool enabled) {

View File

@ -97,7 +97,9 @@ class RTPSender : public RTPSenderInterface {
BitrateStatisticsObserver* bitrate_callback,
FrameCountObserver* frame_count_observer,
SendSideDelayObserver* send_side_delay_observer,
RtcEventLog* event_log);
RtcEventLog* event_log,
SendPacketObserver* send_packet_observer);
virtual ~RTPSender();
void ProcessBitrate();
@ -353,6 +355,9 @@ class RTPSender : public RTPSenderInterface {
const PacketOptions& options);
void UpdateDelayStatistics(int64_t capture_time_ms, int64_t now_ms);
void UpdateOnSendPacket(int packet_id,
int64_t capture_time_ms,
uint32_t ssrc);
// Find the byte position of the RTP extension as indicated by |type| in
// |rtp_packet|. Return false if such extension doesn't exist.
@ -370,12 +375,13 @@ class RTPSender : public RTPSenderInterface {
size_t rtp_packet_length,
const RTPHeader& rtp_header,
int64_t now_ms) const;
// Update the transport sequence number of the packet using a new sequence
// number allocated by SequenceNumberAllocator. Returns the assigned sequence
// number, or 0 if extension could not be updated.
uint16_t UpdateTransportSequenceNumber(uint8_t* rtp_packet,
size_t rtp_packet_length,
const RTPHeader& rtp_header) const;
bool UpdateTransportSequenceNumber(uint16_t sequence_number,
uint8_t* rtp_packet,
size_t rtp_packet_length,
const RTPHeader& rtp_header) const;
bool AllocateTransportSequenceNumber(int* packet_id) const;
void UpdateRtpStats(const uint8_t* buffer,
size_t packet_length,
@ -465,6 +471,7 @@ class RTPSender : public RTPSenderInterface {
FrameCountObserver* const frame_count_observer_;
SendSideDelayObserver* const send_side_delay_observer_;
RtcEventLog* const event_log_;
SendPacketObserver* const send_packet_observer_;
// RTP variables
bool start_timestamp_forced_ GUARDED_BY(send_critsect_);

View File

@ -122,6 +122,11 @@ class MockTransportSequenceNumberAllocator
MOCK_METHOD0(AllocateSequenceNumber, uint16_t());
};
class MockSendPacketObserver : public SendPacketObserver {
public:
MOCK_METHOD3(OnSendPacket, void(uint16_t, int64_t, uint32_t));
};
class RtpSenderTest : public ::testing::Test {
protected:
RtpSenderTest()
@ -137,10 +142,10 @@ class RtpSenderTest : public ::testing::Test {
void SetUp() override { SetUpRtpSender(true); }
void SetUpRtpSender(bool pacer) {
rtp_sender_.reset(new RTPSender(false, &fake_clock_, &transport_,
pacer ? &mock_paced_sender_ : nullptr,
&seq_num_allocator_, nullptr, nullptr,
nullptr, nullptr, &mock_rtc_event_log_));
rtp_sender_.reset(new RTPSender(
false, &fake_clock_, &transport_, pacer ? &mock_paced_sender_ : nullptr,
&seq_num_allocator_, nullptr, nullptr, nullptr, nullptr,
&mock_rtc_event_log_, &send_packet_observer_));
rtp_sender_->SetSequenceNumber(kSeqNum);
}
@ -148,6 +153,7 @@ class RtpSenderTest : public ::testing::Test {
MockRtcEventLog mock_rtc_event_log_;
MockRtpPacketSender mock_paced_sender_;
MockTransportSequenceNumberAllocator seq_num_allocator_;
MockSendPacketObserver send_packet_observer_;
std::unique_ptr<RTPSender> rtp_sender_;
int payload_;
LoopbackTransportTest transport_;
@ -185,6 +191,20 @@ class RtpSenderTest : public ::testing::Test {
packet_, payload_length, rtp_length, capture_time_ms,
kAllowRetransmission, RtpPacketSender::kNormalPriority));
}
void SendGenericPayload() {
const uint8_t kPayload[] = {47, 11, 32, 93, 89};
const uint32_t kTimestamp = 1234;
const uint8_t kPayloadType = 127;
const int64_t kCaptureTimeMs = fake_clock_.TimeInMilliseconds();
char payload_name[RTP_PAYLOAD_NAME_SIZE] = "GENERIC";
EXPECT_EQ(0, rtp_sender_->RegisterPayload(payload_name, kPayloadType, 90000,
0, 1500));
EXPECT_EQ(0, rtp_sender_->SendOutgoingData(
kVideoFrameKey, kPayloadType, kTimestamp, kCaptureTimeMs,
kPayload, sizeof(kPayload), nullptr));
}
};
// TODO(pbos): Move tests over from WithoutPacer to RtpSenderTest as this is our
@ -479,21 +499,13 @@ TEST_F(RtpSenderTestWithoutPacer, SendsPacketsWithTransportSequenceNumber) {
kRtpExtensionTransportSequenceNumber,
kTransportSequenceNumberExtensionId));
char payload_name[RTP_PAYLOAD_NAME_SIZE] = "GENERIC";
const uint8_t payload_type = 127;
ASSERT_EQ(0, rtp_sender_->RegisterPayload(payload_name, payload_type, 90000,
0, 1500));
// Create a dummy payload of 5 bytes.
uint8_t payload[] = {47, 11, 32, 93, 89};
const uint16_t kTransportSequenceNumber = 17;
EXPECT_CALL(seq_num_allocator_, AllocateSequenceNumber())
.WillOnce(testing::Return(kTransportSequenceNumber));
const uint32_t kTimestamp = 1234;
const int64_t kCaptureTimeMs = 4321;
ASSERT_EQ(0, rtp_sender_->SendOutgoingData(
kVideoFrameKey, payload_type, kTimestamp, kCaptureTimeMs,
payload, sizeof(payload), nullptr));
EXPECT_CALL(send_packet_observer_,
OnSendPacket(kTransportSequenceNumber, _, _))
.Times(1);
SendGenericPayload();
RtpUtility::RtpHeaderParser rtp_parser(transport_.last_sent_packet_,
transport_.last_sent_packet_len_);
@ -509,6 +521,19 @@ TEST_F(RtpSenderTestWithoutPacer, SendsPacketsWithTransportSequenceNumber) {
rtp_header.extension.transportSequenceNumber);
}
TEST_F(RtpSenderTestWithoutPacer, OnSendPacketUpdated) {
EXPECT_CALL(mock_rtc_event_log_, // Ignore rtc event calls.
LogRtpHeader(PacketDirection::kOutgoingPacket, _, _, _));
EXPECT_CALL(seq_num_allocator_, AllocateSequenceNumber())
.WillOnce(testing::Return(kTransportSequenceNumber));
EXPECT_CALL(send_packet_observer_,
OnSendPacket(kTransportSequenceNumber, _, _))
.Times(1);
SendGenericPayload();
}
// Test CVO header extension is only set when marker bit is true.
TEST_F(RtpSenderTestWithoutPacer, BuildRTPPacketWithVideoRotation_MarkerBit) {
rtp_sender_->SetVideoRotation(kRotation);
@ -947,11 +972,66 @@ TEST_F(RtpSenderTest, SendPadding) {
EXPECT_EQ(expected_send_time, rtp_header.extension.absoluteSendTime);
}
TEST_F(RtpSenderTest, OnSendPacketUpdated) {
EXPECT_CALL(mock_rtc_event_log_, // Ignore rtc event calls.
LogRtpHeader(PacketDirection::kOutgoingPacket, _, _, _));
rtp_sender_->SetStorePacketsStatus(true, 10);
EXPECT_CALL(send_packet_observer_,
OnSendPacket(kTransportSequenceNumber, _, _))
.Times(1);
EXPECT_CALL(seq_num_allocator_, AllocateSequenceNumber())
.WillOnce(testing::Return(kTransportSequenceNumber));
EXPECT_CALL(mock_paced_sender_, InsertPacket(_, _, _, _, _, _)).Times(1);
SendGenericPayload(); // Packet passed to pacer.
const bool kIsRetransmit = false;
rtp_sender_->TimeToSendPacket(kSeqNum, fake_clock_.TimeInMilliseconds(),
kIsRetransmit);
EXPECT_EQ(1, transport_.packets_sent_);
}
TEST_F(RtpSenderTest, OnSendPacketNotUpdatedForRetransmits) {
EXPECT_CALL(mock_rtc_event_log_, // Ignore rtc event calls.
LogRtpHeader(PacketDirection::kOutgoingPacket, _, _, _));
rtp_sender_->SetStorePacketsStatus(true, 10);
EXPECT_CALL(send_packet_observer_, OnSendPacket(_, _, _)).Times(0);
EXPECT_CALL(seq_num_allocator_, AllocateSequenceNumber())
.WillOnce(testing::Return(kTransportSequenceNumber));
EXPECT_CALL(mock_paced_sender_, InsertPacket(_, _, _, _, _, _)).Times(1);
SendGenericPayload(); // Packet passed to pacer.
const bool kIsRetransmit = true;
rtp_sender_->TimeToSendPacket(kSeqNum, fake_clock_.TimeInMilliseconds(),
kIsRetransmit);
EXPECT_EQ(1, transport_.packets_sent_);
}
TEST_F(RtpSenderTest, OnSendPacketNotUpdatedWithoutSeqNumAllocator) {
rtp_sender_.reset(new RTPSender(
false, &fake_clock_, &transport_, &mock_paced_sender_,
nullptr /* TransportSequenceNumberAllocator */, nullptr, nullptr, nullptr,
nullptr, nullptr, &send_packet_observer_));
rtp_sender_->SetSequenceNumber(kSeqNum);
rtp_sender_->SetStorePacketsStatus(true, 10);
EXPECT_CALL(send_packet_observer_, OnSendPacket(_, _, _)).Times(0);
EXPECT_CALL(mock_paced_sender_, InsertPacket(_, _, _, _, _, _)).Times(1);
SendGenericPayload(); // Packet passed to pacer.
const bool kIsRetransmit = false;
rtp_sender_->TimeToSendPacket(kSeqNum, fake_clock_.TimeInMilliseconds(),
kIsRetransmit);
EXPECT_EQ(1, transport_.packets_sent_);
}
TEST_F(RtpSenderTest, SendRedundantPayloads) {
MockTransport transport;
rtp_sender_.reset(new RTPSender(
false, &fake_clock_, &transport, &mock_paced_sender_, nullptr,
nullptr, nullptr, nullptr, nullptr, &mock_rtc_event_log_));
false, &fake_clock_, &transport, &mock_paced_sender_, nullptr, nullptr,
nullptr, nullptr, nullptr, &mock_rtc_event_log_, nullptr));
rtp_sender_->SetSequenceNumber(kSeqNum);
rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload);
@ -1092,9 +1172,9 @@ TEST_F(RtpSenderTest, FrameCountCallbacks) {
FrameCounts frame_counts_;
} callback;
rtp_sender_.reset(new RTPSender(false, &fake_clock_, &transport_,
&mock_paced_sender_, nullptr, nullptr,
nullptr, &callback, nullptr, nullptr));
rtp_sender_.reset(new RTPSender(
false, &fake_clock_, &transport_, &mock_paced_sender_, nullptr, nullptr,
nullptr, &callback, nullptr, nullptr, nullptr));
char payload_name[RTP_PAYLOAD_NAME_SIZE] = "GENERIC";
const uint8_t payload_type = 127;
@ -1148,8 +1228,8 @@ TEST_F(RtpSenderTest, BitrateCallbacks) {
BitrateStatistics total_stats_;
BitrateStatistics retransmit_stats_;
} callback;
rtp_sender_.reset(new RTPSender(false, &fake_clock_, &transport_,
nullptr, nullptr, nullptr, &callback, nullptr,
rtp_sender_.reset(new RTPSender(false, &fake_clock_, &transport_, nullptr,
nullptr, nullptr, &callback, nullptr, nullptr,
nullptr, nullptr));
// Simulate kNumPackets sent with kPacketInterval ms intervals.
@ -1201,7 +1281,7 @@ class RtpSenderAudioTest : public RtpSenderTest {
void SetUp() override {
payload_ = kAudioPayload;
rtp_sender_.reset(new RTPSender(true, &fake_clock_, &transport_,
rtp_sender_.reset(new RTPSender(true, &fake_clock_, &transport_, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr));
rtp_sender_->SetSequenceNumber(kSeqNum);

View File

@ -26,6 +26,8 @@ source_set("video") {
"report_block_stats.h",
"rtp_stream_receiver.cc",
"rtp_stream_receiver.h",
"send_delay_stats.cc",
"send_delay_stats.h",
"send_statistics_proxy.cc",
"send_statistics_proxy.h",
"stream_synchronization.cc",

View File

@ -2176,6 +2176,7 @@ void EndToEndTest::VerifyHistogramStats(bool use_rtx,
EXPECT_EQ(1, test::NumHistogramSamples(video_prefix + "SendSideDelayInMs"));
EXPECT_EQ(1,
test::NumHistogramSamples(video_prefix + "SendSideDelayMaxInMs"));
EXPECT_EQ(1, test::NumHistogramSamples("WebRTC.Video.SendDelayInMs"));
int num_rtx_samples = use_rtx ? 1 : 0;
EXPECT_EQ(num_rtx_samples, test::NumHistogramSamples(

View File

@ -53,6 +53,7 @@ std::unique_ptr<RtpRtcp> CreateRtpRtcpModule(
configuration.send_bitrate_observer = nullptr;
configuration.send_frame_count_observer = nullptr;
configuration.send_side_delay_observer = nullptr;
configuration.send_packet_observer = nullptr;
configuration.bandwidth_callback = nullptr;
configuration.transport_feedback_callback = nullptr;

View File

@ -0,0 +1,118 @@
/*
* Copyright (c) 2016 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_delay_stats.h"
#include "webrtc/base/logging.h"
#include "webrtc/system_wrappers/include/metrics.h"
namespace webrtc {
namespace {
// Packet with a larger delay are removed and excluded from the delay stats.
// Set to larger than max histogram delay which is 10000.
const int64_t kMaxSentPacketDelayMs = 11000;
const size_t kMaxPacketMapSize = 2000;
// Limit for the maximum number of streams to calculate stats for.
const size_t kMaxSsrcMapSize = 50;
const int kMinRequiredSamples = 200;
} // namespace
SendDelayStats::SendDelayStats(Clock* clock)
: clock_(clock), num_old_packets_(0), num_skipped_packets_(0) {}
SendDelayStats::~SendDelayStats() {
if (num_old_packets_ > 0 || num_skipped_packets_ > 0) {
LOG(LS_WARNING) << "Delay stats: number of old packets " << num_old_packets_
<< ", skipped packets " << num_skipped_packets_
<< ". Number of streams " << send_delay_counters_.size();
}
UpdateHistograms();
}
void SendDelayStats::UpdateHistograms() {
rtc::CritScope lock(&crit_);
for (const auto& it : send_delay_counters_) {
int send_delay_ms = it.second.Avg(kMinRequiredSamples);
if (send_delay_ms != -1) {
RTC_LOGGED_HISTOGRAM_COUNTS_10000("WebRTC.Video.SendDelayInMs",
send_delay_ms);
}
}
}
void SendDelayStats::AddSsrcs(const VideoSendStream::Config& config) {
rtc::CritScope lock(&crit_);
if (ssrcs_.size() > kMaxSsrcMapSize)
return;
for (const auto& ssrc : config.rtp.ssrcs)
ssrcs_.insert(ssrc);
}
void SendDelayStats::OnSendPacket(uint16_t packet_id,
int64_t capture_time_ms,
uint32_t ssrc) {
// Packet sent to transport.
rtc::CritScope lock(&crit_);
if (ssrcs_.find(ssrc) == ssrcs_.end())
return;
int64_t now = clock_->TimeInMilliseconds();
RemoveOld(now, &packets_);
if (packets_.size() > kMaxPacketMapSize) {
++num_skipped_packets_;
return;
}
packets_.insert(
std::make_pair(packet_id, Packet(ssrc, capture_time_ms, now)));
}
bool SendDelayStats::OnSentPacket(int packet_id, int64_t time_ms) {
// Packet leaving socket.
if (packet_id == -1)
return false;
rtc::CritScope lock(&crit_);
auto it = packets_.find(packet_id);
if (it == packets_.end())
return false;
// TODO(asapersson): Remove SendSideDelayUpdated(), use capture -> sent.
// Elapsed time from send (to transport) -> sent (leaving socket).
int diff_ms = time_ms - it->second.send_time_ms;
send_delay_counters_[it->second.ssrc].Add(diff_ms);
packets_.erase(it);
return true;
}
void SendDelayStats::RemoveOld(int64_t now, PacketMap* packets) {
while (!packets->empty()) {
auto it = packets->begin();
if (now - it->second.capture_time_ms < kMaxSentPacketDelayMs)
break;
packets->erase(it);
++num_old_packets_;
}
}
void SendDelayStats::SampleCounter::Add(int sample) {
sum += sample;
++num_samples;
}
int SendDelayStats::SampleCounter::Avg(int min_required_samples) const {
if (num_samples < min_required_samples || num_samples == 0)
return -1;
return (sum + (num_samples / 2)) / num_samples;
}
} // namespace webrtc

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2016 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_DELAY_STATS_H_
#define WEBRTC_VIDEO_SEND_DELAY_STATS_H_
#include <map>
#include <memory>
#include <set>
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/thread_annotations.h"
#include "webrtc/common_types.h"
#include "webrtc/modules/include/module_common_types.h"
#include "webrtc/system_wrappers/include/clock.h"
#include "webrtc/video_send_stream.h"
namespace webrtc {
class SendDelayStats : public SendPacketObserver {
public:
explicit SendDelayStats(Clock* clock);
virtual ~SendDelayStats();
// Adds the configured ssrcs for the rtp streams.
// Stats will be calculated for these streams.
void AddSsrcs(const VideoSendStream::Config& config);
// Called when a packet is sent (leaving socket).
bool OnSentPacket(int packet_id, int64_t time_ms);
protected:
// From SendPacketObserver.
// Called when a packet is sent to the transport.
void OnSendPacket(uint16_t packet_id,
int64_t capture_time_ms,
uint32_t ssrc) override;
private:
// Map holding sent packets (mapped by sequence number).
struct SequenceNumberOlderThan {
bool operator()(uint16_t seq1, uint16_t seq2) const {
return IsNewerSequenceNumber(seq2, seq1);
}
};
struct Packet {
Packet(uint32_t ssrc, int64_t capture_time_ms, int64_t send_time_ms)
: ssrc(ssrc),
capture_time_ms(capture_time_ms),
send_time_ms(send_time_ms) {}
uint32_t ssrc;
int64_t capture_time_ms;
int64_t send_time_ms;
};
typedef std::map<uint16_t, Packet, SequenceNumberOlderThan> PacketMap;
class SampleCounter {
public:
SampleCounter() : sum(0), num_samples(0) {}
~SampleCounter() {}
void Add(int sample);
int Avg(int min_required_samples) const;
private:
int sum;
int num_samples;
};
void UpdateHistograms();
void RemoveOld(int64_t now, PacketMap* packets)
EXCLUSIVE_LOCKS_REQUIRED(crit_);
Clock* const clock_;
rtc::CriticalSection crit_;
PacketMap packets_ GUARDED_BY(crit_);
size_t num_old_packets_ GUARDED_BY(crit_);
size_t num_skipped_packets_ GUARDED_BY(crit_);
std::set<uint32_t> ssrcs_ GUARDED_BY(crit_);
std::map<uint32_t, SampleCounter> send_delay_counters_
GUARDED_BY(crit_); // Mapped by SSRC.
};
} // namespace webrtc
#endif // WEBRTC_VIDEO_SEND_DELAY_STATS_H_

View File

@ -0,0 +1,122 @@
/*
* Copyright (c) 2016 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_delay_stats.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/system_wrappers/include/metrics.h"
#include "webrtc/test/histogram.h"
namespace webrtc {
namespace {
const uint32_t kSsrc1 = 17;
const uint32_t kSsrc2 = 42;
const uint32_t kRtxSsrc1 = 18;
const uint32_t kRtxSsrc2 = 43;
const uint16_t kPacketId = 2345;
const int64_t kMaxPacketDelayMs = 11000;
const int kMinRequiredSamples = 200;
} // namespace
class SendDelayStatsTest : public ::testing::Test {
public:
SendDelayStatsTest() : clock_(1234), config_(CreateConfig()) {}
virtual ~SendDelayStatsTest() {}
protected:
virtual void SetUp() {
stats_.reset(new SendDelayStats(&clock_));
stats_->AddSsrcs(config_);
}
VideoSendStream::Config CreateConfig() {
VideoSendStream::Config config(nullptr);
config.rtp.ssrcs.push_back(kSsrc1);
config.rtp.ssrcs.push_back(kSsrc2);
config.rtp.rtx.ssrcs.push_back(kRtxSsrc1);
config.rtp.rtx.ssrcs.push_back(kRtxSsrc2);
return config;
}
void OnSendPacket(uint16_t id, uint32_t ssrc) {
OnSendPacket(id, ssrc, clock_.TimeInMilliseconds());
}
void OnSendPacket(uint16_t id, uint32_t ssrc, int64_t capture_ms) {
SendPacketObserver* observer = stats_.get();
observer->OnSendPacket(id, capture_ms, ssrc);
}
bool OnSentPacket(uint16_t id) {
return stats_->OnSentPacket(id, clock_.TimeInMilliseconds());
}
SimulatedClock clock_;
VideoSendStream::Config config_;
std::unique_ptr<SendDelayStats> stats_;
};
TEST_F(SendDelayStatsTest, SentPacketFound) {
EXPECT_FALSE(OnSentPacket(kPacketId));
OnSendPacket(kPacketId, kSsrc1);
EXPECT_TRUE(OnSentPacket(kPacketId)); // Packet found.
EXPECT_FALSE(OnSentPacket(kPacketId)); // Packet removed when found.
}
TEST_F(SendDelayStatsTest, SentPacketNotFoundForNonRegisteredSsrc) {
OnSendPacket(kPacketId, kSsrc1);
EXPECT_TRUE(OnSentPacket(kPacketId));
OnSendPacket(kPacketId + 1, kSsrc2);
EXPECT_TRUE(OnSentPacket(kPacketId + 1));
OnSendPacket(kPacketId + 2, kRtxSsrc1); // RTX SSRC not registered.
EXPECT_FALSE(OnSentPacket(kPacketId + 2));
}
TEST_F(SendDelayStatsTest, SentPacketFoundWithMaxSendDelay) {
OnSendPacket(kPacketId, kSsrc1);
clock_.AdvanceTimeMilliseconds(kMaxPacketDelayMs - 1);
OnSendPacket(kPacketId + 1, kSsrc1); // kPacketId -> not old/removed.
EXPECT_TRUE(OnSentPacket(kPacketId)); // Packet found.
EXPECT_TRUE(OnSentPacket(kPacketId + 1)); // Packet found.
}
TEST_F(SendDelayStatsTest, OldPacketsRemoved) {
const int64_t kCaptureTimeMs = clock_.TimeInMilliseconds();
OnSendPacket(0xffffu, kSsrc1, kCaptureTimeMs);
OnSendPacket(0u, kSsrc1, kCaptureTimeMs);
OnSendPacket(1u, kSsrc1, kCaptureTimeMs + 1);
clock_.AdvanceTimeMilliseconds(kMaxPacketDelayMs); // 0xffff, 0 -> old.
OnSendPacket(2u, kSsrc1, kCaptureTimeMs + 2);
EXPECT_FALSE(OnSentPacket(0xffffu)); // Old removed.
EXPECT_FALSE(OnSentPacket(0u)); // Old removed.
EXPECT_TRUE(OnSentPacket(1u));
EXPECT_TRUE(OnSentPacket(2u));
}
TEST_F(SendDelayStatsTest, HistogramsAreUpdated) {
test::ClearHistograms();
const int64_t kDelayMs1 = 5;
const int64_t kDelayMs2 = 10;
uint16_t id = 0;
for (int i = 0; i < kMinRequiredSamples; ++i) {
OnSendPacket(++id, kSsrc1);
clock_.AdvanceTimeMilliseconds(kDelayMs1);
EXPECT_TRUE(OnSentPacket(id));
OnSendPacket(++id, kSsrc2);
clock_.AdvanceTimeMilliseconds(kDelayMs2);
EXPECT_TRUE(OnSentPacket(id));
}
stats_.reset();
EXPECT_EQ(2, test::NumHistogramSamples("WebRTC.Video.SendDelayInMs"));
EXPECT_EQ(kDelayMs2, test::LastHistogramSample("WebRTC.Video.SendDelayInMs"));
}
} // namespace webrtc

View File

@ -110,10 +110,7 @@ class SendStatisticsProxyTest : public ::testing::Test {
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;
for (const auto& ssrc : config_.rtp.ssrcs) {
VideoSendStream::StreamStats& ssrc_stats = expected_.substreams[ssrc];
// Add statistics with some arbitrary, but unique, numbers.
@ -124,10 +121,7 @@ TEST_F(SendStatisticsProxyTest, RtcpStatistics) {
ssrc_stats.rtcp_stats.jitter = offset + 3;
callback->StatisticsUpdated(ssrc_stats.rtcp_stats, ssrc);
}
for (std::vector<uint32_t>::const_iterator it = config_.rtp.rtx.ssrcs.begin();
it != config_.rtp.rtx.ssrcs.end();
++it) {
const uint32_t ssrc = *it;
for (const auto& ssrc : config_.rtp.rtx.ssrcs) {
VideoSendStream::StreamStats& ssrc_stats = expected_.substreams[ssrc];
// Add statistics with some arbitrary, but unique, numbers.
@ -170,10 +164,7 @@ TEST_F(SendStatisticsProxyTest, Suspended) {
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;
for (const auto& ssrc : config_.rtp.ssrcs) {
// Add statistics with some arbitrary, but unique, numbers.
VideoSendStream::StreamStats& stats = expected_.substreams[ssrc];
uint32_t offset = ssrc * sizeof(VideoSendStream::StreamStats);
@ -183,10 +174,7 @@ TEST_F(SendStatisticsProxyTest, FrameCounts) {
stats.frame_counts = frame_counts;
observer->FrameCountUpdated(frame_counts, ssrc);
}
for (std::vector<uint32_t>::const_iterator it = config_.rtp.rtx.ssrcs.begin();
it != config_.rtp.rtx.ssrcs.end();
++it) {
const uint32_t ssrc = *it;
for (const auto& ssrc : config_.rtp.rtx.ssrcs) {
// Add statistics with some arbitrary, but unique, numbers.
VideoSendStream::StreamStats& stats = expected_.substreams[ssrc];
uint32_t offset = ssrc * sizeof(VideoSendStream::StreamStats);
@ -203,10 +191,7 @@ TEST_F(SendStatisticsProxyTest, FrameCounts) {
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;
for (const auto& ssrc : config_.rtp.ssrcs) {
StreamDataCounters& counters = expected_.substreams[ssrc].rtp_stats;
// Add statistics with some arbitrary, but unique, numbers.
size_t offset = ssrc * sizeof(StreamDataCounters);
@ -219,10 +204,7 @@ TEST_F(SendStatisticsProxyTest, DataCounters) {
counters.transmitted.packets = offset_uint32 + 5;
callback->DataCountersUpdated(counters, ssrc);
}
for (std::vector<uint32_t>::const_iterator it = config_.rtp.rtx.ssrcs.begin();
it != config_.rtp.rtx.ssrcs.end();
++it) {
const uint32_t ssrc = *it;
for (const auto& ssrc : config_.rtp.rtx.ssrcs) {
StreamDataCounters& counters = expected_.substreams[ssrc].rtp_stats;
// Add statistics with some arbitrary, but unique, numbers.
size_t offset = ssrc * sizeof(StreamDataCounters);
@ -242,10 +224,7 @@ TEST_F(SendStatisticsProxyTest, DataCounters) {
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;
for (const auto& ssrc : config_.rtp.ssrcs) {
BitrateStatistics total;
BitrateStatistics retransmit;
// Use ssrc as bitrate_bps to get a unique value for each stream.
@ -255,10 +234,7 @@ TEST_F(SendStatisticsProxyTest, Bitrate) {
expected_.substreams[ssrc].total_bitrate_bps = total.bitrate_bps;
expected_.substreams[ssrc].retransmit_bitrate_bps = retransmit.bitrate_bps;
}
for (std::vector<uint32_t>::const_iterator it = config_.rtp.rtx.ssrcs.begin();
it != config_.rtp.rtx.ssrcs.end();
++it) {
const uint32_t ssrc = *it;
for (const auto& ssrc : config_.rtp.rtx.ssrcs) {
BitrateStatistics total;
BitrateStatistics retransmit;
// Use ssrc as bitrate_bps to get a unique value for each stream.
@ -275,10 +251,7 @@ TEST_F(SendStatisticsProxyTest, Bitrate) {
TEST_F(SendStatisticsProxyTest, SendSideDelay) {
SendSideDelayObserver* 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;
for (const auto& ssrc : config_.rtp.ssrcs) {
// Use ssrc as avg_delay_ms and max_delay_ms to get a unique value for each
// stream.
int avg_delay_ms = ssrc;
@ -287,10 +260,7 @@ TEST_F(SendStatisticsProxyTest, SendSideDelay) {
expected_.substreams[ssrc].avg_delay_ms = avg_delay_ms;
expected_.substreams[ssrc].max_delay_ms = max_delay_ms;
}
for (std::vector<uint32_t>::const_iterator it = config_.rtp.rtx.ssrcs.begin();
it != config_.rtp.rtx.ssrcs.end();
++it) {
const uint32_t ssrc = *it;
for (const auto& ssrc : config_.rtp.rtx.ssrcs) {
// Use ssrc as avg_delay_ms and max_delay_ms to get a unique value for each
// stream.
int avg_delay_ms = ssrc;

View File

@ -47,6 +47,7 @@ std::vector<RtpRtcp*> CreateRtpRtcpModules(
RtpPacketSender* paced_sender,
TransportSequenceNumberAllocator* transport_sequence_number_allocator,
SendStatisticsProxy* stats_proxy,
SendDelayStats* send_delay_stats,
size_t num_modules) {
RTC_DCHECK_GT(num_modules, 0u);
RtpRtcp::Configuration configuration;
@ -64,6 +65,7 @@ std::vector<RtpRtcp*> CreateRtpRtcpModules(
configuration.send_bitrate_observer = stats_proxy;
configuration.send_frame_count_observer = stats_proxy;
configuration.send_side_delay_observer = stats_proxy;
configuration.send_packet_observer = send_delay_stats;
configuration.bandwidth_callback = bandwidth_callback;
configuration.transport_feedback_callback = transport_feedback_callback;
@ -348,6 +350,7 @@ VideoSendStream::VideoSendStream(
CallStats* call_stats,
CongestionController* congestion_controller,
BitrateAllocator* bitrate_allocator,
SendDelayStats* send_delay_stats,
VieRemb* remb,
const VideoSendStream::Config& config,
const VideoEncoderConfig& encoder_config,
@ -391,6 +394,7 @@ VideoSendStream::VideoSendStream(
congestion_controller_->pacer(),
congestion_controller_->packet_router(),
&stats_proxy_,
send_delay_stats,
config_.rtp.ssrcs.size())),
payload_router_(rtp_rtcp_modules_, config.encoder_settings.payload_type),
input_(&encoder_wakeup_event_,

View File

@ -22,6 +22,7 @@
#include "webrtc/video/encoded_frame_callback_adapter.h"
#include "webrtc/video/encoder_state_feedback.h"
#include "webrtc/video/payload_router.h"
#include "webrtc/video/send_delay_stats.h"
#include "webrtc/video/send_statistics_proxy.h"
#include "webrtc/video/video_capture_input.h"
#include "webrtc/video/vie_channel.h"
@ -57,6 +58,7 @@ class VideoSendStream : public webrtc::VideoSendStream,
CallStats* call_stats,
CongestionController* congestion_controller,
BitrateAllocator* bitrate_allocator,
SendDelayStats* send_delay_stats,
VieRemb* remb,
const VideoSendStream::Config& config,
const VideoEncoderConfig& encoder_config,

View File

@ -40,6 +40,8 @@
'video/report_block_stats.h',
'video/rtp_stream_receiver.cc',
'video/rtp_stream_receiver.h',
'video/send_delay_stats.cc',
'video/send_delay_stats.h',
'video/send_statistics_proxy.cc',
'video/send_statistics_proxy.h',
'video/stream_synchronization.cc',

View File

@ -168,6 +168,7 @@
'video/overuse_frame_detector_unittest.cc',
'video/payload_router_unittest.cc',
'video/report_block_stats_unittest.cc',
'video/send_delay_stats_unittest.cc',
'video/send_statistics_proxy_unittest.cc',
'video/stream_synchronization_unittest.cc',
'video/video_capture_input_unittest.cc',