Add support for padding in pacer.

This improves pacer-based padding by making sure it limits padding according to:
- Never pad more than 800 kbps.
- Padding + media should not go above a given target bitrate.

Also adds appropriate unittests to make sure we reach the given targets.

BUG=1837
R=mflodman@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@4168 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
stefan@webrtc.org 2013-06-04 09:36:56 +00:00
parent c69ae69d0b
commit c3cc375499
6 changed files with 304 additions and 171 deletions

View File

@ -21,6 +21,11 @@
namespace webrtc { namespace webrtc {
class CriticalSectionWrapper; class CriticalSectionWrapper;
namespace paced_sender {
class IntervalBudget;
struct Packet;
class PacketList;
} // namespace paced_sender
class PacedSender : public Module { class PacedSender : public Module {
public: public:
@ -40,7 +45,7 @@ class PacedSender : public Module {
virtual void TimeToSendPacket(uint32_t ssrc, uint16_t sequence_number, virtual void TimeToSendPacket(uint32_t ssrc, uint16_t sequence_number,
int64_t capture_time_ms) = 0; int64_t capture_time_ms) = 0;
// Called when it's a good time to send a padding data. // Called when it's a good time to send a padding data.
virtual void TimeToSendPadding(int bytes) = 0; virtual int TimeToSendPadding(int bytes) = 0;
protected: protected:
virtual ~Callback() {} virtual ~Callback() {}
}; };
@ -52,14 +57,19 @@ class PacedSender : public Module {
// Enable/disable pacing. // Enable/disable pacing.
void SetStatus(bool enable); void SetStatus(bool enable);
bool Enabled() const;
// Temporarily pause all sending. // Temporarily pause all sending.
void Pause(); void Pause();
// Resume sending packets. // Resume sending packets.
void Resume(); void Resume();
// Current total estimated bitrate. // Set the pacing target bitrate and the bitrate up to which we are allowed to
void UpdateBitrate(int target_bitrate_kbps); // pad. We will send padding packets to increase the total bitrate until we
// reach |pad_up_to_bitrate_kbps|. If the media bitrate is above
// |pad_up_to_bitrate_kbps| no padding will be sent.
void UpdateBitrate(int target_bitrate_kbps, int pad_up_to_bitrate_kbps);
// Returns true if we send the packet now, else it will add the packet // Returns true if we send the packet now, else it will add the packet
// information to the queue and call TimeToSendPacket when it's time to send. // information to the queue and call TimeToSendPacket when it's time to send.
@ -80,42 +90,13 @@ class PacedSender : public Module {
virtual int32_t Process(); virtual int32_t Process();
private: private:
struct Packet {
Packet(uint32_t ssrc, uint16_t seq_number, int64_t capture_time_ms,
int length_in_bytes)
: ssrc_(ssrc),
sequence_number_(seq_number),
capture_time_ms_(capture_time_ms),
bytes_(length_in_bytes) {
}
uint32_t ssrc_;
uint16_t sequence_number_;
int64_t capture_time_ms_;
int bytes_;
};
// STL list style class which prevents duplicates in the list.
class PacketList {
public:
PacketList() {};
bool empty() const;
Packet front() const;
void pop_front();
void push_back(const Packet& packet);
private:
std::list<Packet> packet_list_;
std::set<uint16_t> sequence_number_set_;
};
// Checks if next packet in line can be transmitted. Returns true on success. // Checks if next packet in line can be transmitted. Returns true on success.
bool GetNextPacket(uint32_t* ssrc, uint16_t* sequence_number, bool GetNextPacket(uint32_t* ssrc, uint16_t* sequence_number,
int64_t* capture_time_ms, Priority* priority, int64_t* capture_time_ms, Priority* priority,
bool* last_packet); bool* last_packet);
// Local helper function to GetNextPacket. // Local helper function to GetNextPacket.
void GetNextPacketFromList(PacketList* list, void GetNextPacketFromList(paced_sender::PacketList* packets,
uint32_t* ssrc, uint16_t* sequence_number, int64_t* capture_time_ms, uint32_t* ssrc, uint16_t* sequence_number, int64_t* capture_time_ms,
bool* last_packet); bool* last_packet);
@ -123,24 +104,32 @@ class PacedSender : public Module {
void UpdateBytesPerInterval(uint32_t delta_time_in_ms); void UpdateBytesPerInterval(uint32_t delta_time_in_ms);
// Updates the buffers with the number of bytes that we sent. // Updates the buffers with the number of bytes that we sent.
void UpdateState(int num_bytes); void UpdateMediaBytesSent(int num_bytes);
Callback* callback_; Callback* callback_;
const float pace_multiplier_; const float pace_multiplier_;
bool enable_; bool enabled_;
bool paused_; bool paused_;
scoped_ptr<CriticalSectionWrapper> critsect_; scoped_ptr<CriticalSectionWrapper> critsect_;
int target_bitrate_kbytes_per_s_; // This is the media budget, keeping track of how many bits of media
int bytes_remaining_interval_; // we can pace out during the current interval.
int padding_bytes_remaining_interval_; scoped_ptr<paced_sender::IntervalBudget> media_budget_;
// This is the padding budget, keeping track of how many bits of padding we're
// allowed to send out during the current interval.
scoped_ptr<paced_sender::IntervalBudget> padding_budget_;
// Media and padding share this budget, therefore no padding will be sent if
// media uses all of this budget. This is used to avoid padding above a given
// bitrate.
scoped_ptr<paced_sender::IntervalBudget> pad_up_to_bitrate_budget_;
TickTime time_last_update_; TickTime time_last_update_;
TickTime time_last_send_; TickTime time_last_send_;
int64_t capture_time_ms_last_queued_; int64_t capture_time_ms_last_queued_;
int64_t capture_time_ms_last_sent_; int64_t capture_time_ms_last_sent_;
PacketList high_priority_packets_; scoped_ptr<paced_sender::PacketList> high_priority_packets_;
PacketList normal_priority_packets_; scoped_ptr<paced_sender::PacketList> normal_priority_packets_;
PacketList low_priority_packets_; scoped_ptr<paced_sender::PacketList> low_priority_packets_;
}; };
} // namespace webrtc } // namespace webrtc
#endif // WEBRTC_MODULES_PACED_SENDER_H_ #endif // WEBRTC_MODULES_PACED_SENDER_H_

View File

@ -28,47 +28,115 @@ const int kMaxIntervalTimeMs = 30;
// packets are sent, regardless of buffer state. In practice only in effect at // packets are sent, regardless of buffer state. In practice only in effect at
// low bitrates (less than 320 kbits/s). // low bitrates (less than 320 kbits/s).
const int kMaxQueueTimeWithoutSendingMs = 30; const int kMaxQueueTimeWithoutSendingMs = 30;
// Max padding bytes per second.
const int kMaxPaddingKbps = 800;
} // namespace } // namespace
namespace webrtc { namespace webrtc {
bool PacedSender::PacketList::empty() const { namespace paced_sender {
return packet_list_.empty(); struct Packet {
} Packet(uint32_t ssrc, uint16_t seq_number, int64_t capture_time_ms,
int length_in_bytes)
PacedSender::Packet PacedSender::PacketList::front() const { : ssrc_(ssrc),
return packet_list_.front(); sequence_number_(seq_number),
} capture_time_ms_(capture_time_ms),
bytes_(length_in_bytes) {
void PacedSender::PacketList::pop_front() {
PacedSender::Packet& packet = packet_list_.front();
uint16_t sequence_number = packet.sequence_number_;
packet_list_.pop_front();
sequence_number_set_.erase(sequence_number);
}
void PacedSender::PacketList::push_back(const PacedSender::Packet& packet) {
if (sequence_number_set_.find(packet.sequence_number_) ==
sequence_number_set_.end()) {
// Don't insert duplicates.
packet_list_.push_back(packet);
sequence_number_set_.insert(packet.sequence_number_);
} }
} uint32_t ssrc_;
uint16_t sequence_number_;
int64_t capture_time_ms_;
int bytes_;
};
// STL list style class which prevents duplicates in the list.
class PacketList {
public:
PacketList() {};
bool empty() const {
return packet_list_.empty();
}
Packet front() const {
return packet_list_.front();
}
void pop_front() {
Packet& packet = packet_list_.front();
uint16_t sequence_number = packet.sequence_number_;
packet_list_.pop_front();
sequence_number_set_.erase(sequence_number);
}
void push_back(const Packet& packet) {
if (sequence_number_set_.find(packet.sequence_number_) ==
sequence_number_set_.end()) {
// Don't insert duplicates.
packet_list_.push_back(packet);
sequence_number_set_.insert(packet.sequence_number_);
}
}
private:
std::list<Packet> packet_list_;
std::set<uint16_t> sequence_number_set_;
};
class IntervalBudget {
public:
explicit IntervalBudget(int initial_target_rate_kbps)
: target_rate_kbps_(initial_target_rate_kbps),
bytes_remaining_(0) {}
void set_target_rate_kbps(int target_rate_kbps) {
target_rate_kbps_ = target_rate_kbps;
}
void IncreaseBudget(int delta_time_ms) {
int bytes = target_rate_kbps_ * delta_time_ms / 8;
if (bytes_remaining_ < 0) {
// We overused last interval, compensate this interval.
bytes_remaining_ = bytes_remaining_ + bytes;
} else {
// If we underused last interval we can't use it this interval.
bytes_remaining_ = bytes;
}
}
void UseBudget(int bytes) {
bytes_remaining_ = std::max(bytes_remaining_ - bytes,
-100 * target_rate_kbps_ / 8);
}
int bytes_remaining() const { return bytes_remaining_; }
private:
int target_rate_kbps_;
int bytes_remaining_;
};
} // namespace paced_sender
PacedSender::PacedSender(Callback* callback, int target_bitrate_kbps, PacedSender::PacedSender(Callback* callback, int target_bitrate_kbps,
float pace_multiplier) float pace_multiplier)
: callback_(callback), : callback_(callback),
pace_multiplier_(pace_multiplier), pace_multiplier_(pace_multiplier),
enable_(false), enabled_(false),
paused_(false), paused_(false),
critsect_(CriticalSectionWrapper::CreateCriticalSection()), critsect_(CriticalSectionWrapper::CreateCriticalSection()),
target_bitrate_kbytes_per_s_(target_bitrate_kbps >> 3), // Divide by 8. media_budget_(new paced_sender::IntervalBudget(
bytes_remaining_interval_(0), pace_multiplier_ * target_bitrate_kbps)),
padding_bytes_remaining_interval_(0), padding_budget_(new paced_sender::IntervalBudget(kMaxPaddingKbps)),
// No padding until UpdateBitrate is called.
pad_up_to_bitrate_budget_(new paced_sender::IntervalBudget(0)),
time_last_update_(TickTime::Now()), time_last_update_(TickTime::Now()),
capture_time_ms_last_queued_(0), capture_time_ms_last_queued_(0),
capture_time_ms_last_sent_(0) { capture_time_ms_last_sent_(0),
high_priority_packets_(new paced_sender::PacketList),
normal_priority_packets_(new paced_sender::PacketList),
low_priority_packets_(new paced_sender::PacketList) {
UpdateBytesPerInterval(kMinPacketLimitMs); UpdateBytesPerInterval(kMinPacketLimitMs);
} }
@ -87,20 +155,27 @@ void PacedSender::Resume() {
void PacedSender::SetStatus(bool enable) { void PacedSender::SetStatus(bool enable) {
CriticalSectionScoped cs(critsect_.get()); CriticalSectionScoped cs(critsect_.get());
enable_ = enable; enabled_ = enable;
} }
void PacedSender::UpdateBitrate(int target_bitrate_kbps) { bool PacedSender::Enabled() const {
CriticalSectionScoped cs(critsect_.get()); CriticalSectionScoped cs(critsect_.get());
target_bitrate_kbytes_per_s_ = target_bitrate_kbps >> 3; // Divide by 8. return enabled_;
}
void PacedSender::UpdateBitrate(int target_bitrate_kbps,
int pad_up_to_bitrate_kbps) {
CriticalSectionScoped cs(critsect_.get());
media_budget_->set_target_rate_kbps(pace_multiplier_ * target_bitrate_kbps);
pad_up_to_bitrate_budget_->set_target_rate_kbps(pad_up_to_bitrate_kbps);
} }
bool PacedSender::SendPacket(Priority priority, uint32_t ssrc, bool PacedSender::SendPacket(Priority priority, uint32_t ssrc,
uint16_t sequence_number, int64_t capture_time_ms, int bytes) { uint16_t sequence_number, int64_t capture_time_ms, int bytes) {
CriticalSectionScoped cs(critsect_.get()); CriticalSectionScoped cs(critsect_.get());
if (!enable_) { if (!enabled_) {
UpdateState(bytes); UpdateMediaBytesSent(bytes);
return true; // We can send now. return true; // We can send now.
} }
if (capture_time_ms < 0) { if (capture_time_ms < 0) {
@ -110,8 +185,10 @@ bool PacedSender::SendPacket(Priority priority, uint32_t ssrc,
// Queue all packets when we are paused. // Queue all packets when we are paused.
switch (priority) { switch (priority) {
case kHighPriority: case kHighPriority:
high_priority_packets_.push_back( high_priority_packets_->push_back(paced_sender::Packet(ssrc,
Packet(ssrc, sequence_number, capture_time_ms, bytes)); sequence_number,
capture_time_ms,
bytes));
break; break;
case kNormalPriority: case kNormalPriority:
if (capture_time_ms > capture_time_ms_last_queued_) { if (capture_time_ms > capture_time_ms_last_queued_) {
@ -122,50 +199,31 @@ bool PacedSender::SendPacket(Priority priority, uint32_t ssrc,
case kLowPriority: case kLowPriority:
// Queue the low priority packets in the normal priority queue when we // Queue the low priority packets in the normal priority queue when we
// are paused to avoid starvation. // are paused to avoid starvation.
normal_priority_packets_.push_back( normal_priority_packets_->push_back(paced_sender::Packet(
Packet(ssrc, sequence_number, capture_time_ms, bytes)); ssrc, sequence_number, capture_time_ms, bytes));
break; break;
} }
return false; return false;
} }
paced_sender::PacketList* packet_list;
switch (priority) { switch (priority) {
case kHighPriority: case kHighPriority:
if (high_priority_packets_.empty() && packet_list = high_priority_packets_.get();
bytes_remaining_interval_ > 0) { break;
UpdateState(bytes);
return true; // We can send now.
}
high_priority_packets_.push_back(
Packet(ssrc, sequence_number, capture_time_ms, bytes));
return false;
case kNormalPriority: case kNormalPriority:
if (high_priority_packets_.empty() && packet_list = normal_priority_packets_.get();
normal_priority_packets_.empty() && break;
bytes_remaining_interval_ > 0) {
UpdateState(bytes);
return true; // We can send now.
}
if (capture_time_ms > capture_time_ms_last_queued_) {
capture_time_ms_last_queued_ = capture_time_ms;
TRACE_EVENT_ASYNC_BEGIN1("webrtc_rtp", "PacedSend", capture_time_ms,
"capture_time_ms", capture_time_ms);
}
normal_priority_packets_.push_back(
Packet(ssrc, sequence_number, capture_time_ms, bytes));
return false;
case kLowPriority: case kLowPriority:
if (high_priority_packets_.empty() && packet_list = low_priority_packets_.get();
normal_priority_packets_.empty() && break;
low_priority_packets_.empty() &&
bytes_remaining_interval_ > 0) {
UpdateState(bytes);
return true; // We can send now.
}
low_priority_packets_.push_back(
Packet(ssrc, sequence_number, capture_time_ms, bytes));
return false;
} }
if (packet_list->empty() &&
media_budget_->bytes_remaining() > 0) {
UpdateMediaBytesSent(bytes);
return true; // We can send now.
}
packet_list->push_back(paced_sender::Packet(ssrc, sequence_number,
capture_time_ms, bytes));
return false; return false;
} }
@ -173,20 +231,20 @@ int PacedSender::QueueInMs() const {
CriticalSectionScoped cs(critsect_.get()); CriticalSectionScoped cs(critsect_.get());
int64_t now_ms = TickTime::MillisecondTimestamp(); int64_t now_ms = TickTime::MillisecondTimestamp();
int64_t oldest_packet_capture_time = now_ms; int64_t oldest_packet_capture_time = now_ms;
if (!high_priority_packets_.empty()) { if (!high_priority_packets_->empty()) {
oldest_packet_capture_time = std::min( oldest_packet_capture_time = std::min(
oldest_packet_capture_time, oldest_packet_capture_time,
high_priority_packets_.front().capture_time_ms_); high_priority_packets_->front().capture_time_ms_);
} }
if (!normal_priority_packets_.empty()) { if (!normal_priority_packets_->empty()) {
oldest_packet_capture_time = std::min( oldest_packet_capture_time = std::min(
oldest_packet_capture_time, oldest_packet_capture_time,
normal_priority_packets_.front().capture_time_ms_); normal_priority_packets_->front().capture_time_ms_);
} }
if (!low_priority_packets_.empty()) { if (!low_priority_packets_->empty()) {
oldest_packet_capture_time = std::min( oldest_packet_capture_time = std::min(
oldest_packet_capture_time, oldest_packet_capture_time,
low_priority_packets_.front().capture_time_ms_); low_priority_packets_->front().capture_time_ms_);
} }
return now_ms - oldest_packet_capture_time; return now_ms - oldest_packet_capture_time;
} }
@ -231,15 +289,20 @@ int32_t PacedSender::Process() {
callback_->TimeToSendPacket(ssrc, sequence_number, capture_time_ms); callback_->TimeToSendPacket(ssrc, sequence_number, capture_time_ms);
critsect_->Enter(); critsect_->Enter();
} }
if (high_priority_packets_.empty() && if (high_priority_packets_->empty() &&
normal_priority_packets_.empty() && normal_priority_packets_->empty() &&
low_priority_packets_.empty() && low_priority_packets_->empty() &&
padding_bytes_remaining_interval_ > 0) { padding_budget_->bytes_remaining() > 0 &&
pad_up_to_bitrate_budget_->bytes_remaining() > 0) {
int padding_needed = std::min(
padding_budget_->bytes_remaining(),
pad_up_to_bitrate_budget_->bytes_remaining());
critsect_->Leave(); critsect_->Leave();
callback_->TimeToSendPadding(padding_bytes_remaining_interval_); int bytes_sent = callback_->TimeToSendPadding(padding_needed);
critsect_->Enter(); critsect_->Enter();
padding_bytes_remaining_interval_ = 0; media_budget_->UseBudget(bytes_sent);
bytes_remaining_interval_ -= padding_bytes_remaining_interval_; padding_budget_->UseBudget(bytes_sent);
pad_up_to_bitrate_budget_->UseBudget(bytes_sent);
} }
} }
return 0; return 0;
@ -247,87 +310,74 @@ int32_t PacedSender::Process() {
// MUST have critsect_ when calling. // MUST have critsect_ when calling.
void PacedSender::UpdateBytesPerInterval(uint32_t delta_time_ms) { void PacedSender::UpdateBytesPerInterval(uint32_t delta_time_ms) {
uint32_t bytes_per_interval = target_bitrate_kbytes_per_s_ * delta_time_ms; media_budget_->IncreaseBudget(delta_time_ms);
padding_budget_->IncreaseBudget(delta_time_ms);
if (bytes_remaining_interval_ < 0) { pad_up_to_bitrate_budget_->IncreaseBudget(delta_time_ms);
// We overused last interval, compensate this interval.
bytes_remaining_interval_ += pace_multiplier_ * bytes_per_interval;
} else {
// If we underused last interval we can't use it this interval.
bytes_remaining_interval_ = pace_multiplier_ * bytes_per_interval;
}
if (padding_bytes_remaining_interval_ < 0) {
// We overused last interval, compensate this interval.
padding_bytes_remaining_interval_ += bytes_per_interval;
} else {
// If we underused last interval we can't use it this interval.
padding_bytes_remaining_interval_ = bytes_per_interval;
}
} }
// MUST have critsect_ when calling. // MUST have critsect_ when calling.
bool PacedSender::GetNextPacket(uint32_t* ssrc, uint16_t* sequence_number, bool PacedSender::GetNextPacket(uint32_t* ssrc, uint16_t* sequence_number,
int64_t* capture_time_ms, Priority* priority, int64_t* capture_time_ms, Priority* priority,
bool* last_packet) { bool* last_packet) {
if (bytes_remaining_interval_ <= 0) { if (media_budget_->bytes_remaining() <= 0) {
// All bytes consumed for this interval. // All bytes consumed for this interval.
// Check if we have not sent in a too long time. // Check if we have not sent in a too long time.
if ((TickTime::Now() - time_last_send_).Milliseconds() > if ((TickTime::Now() - time_last_send_).Milliseconds() >
kMaxQueueTimeWithoutSendingMs) { kMaxQueueTimeWithoutSendingMs) {
if (!high_priority_packets_.empty()) { if (!high_priority_packets_->empty()) {
*priority = kHighPriority; *priority = kHighPriority;
GetNextPacketFromList(&high_priority_packets_, ssrc, sequence_number, GetNextPacketFromList(high_priority_packets_.get(), ssrc,
capture_time_ms, last_packet); sequence_number, capture_time_ms, last_packet);
return true; return true;
} }
if (!normal_priority_packets_.empty()) { if (!normal_priority_packets_->empty()) {
*priority = kNormalPriority; *priority = kNormalPriority;
GetNextPacketFromList(&normal_priority_packets_, ssrc, sequence_number, GetNextPacketFromList(normal_priority_packets_.get(), ssrc,
capture_time_ms, last_packet); sequence_number, capture_time_ms, last_packet);
return true; return true;
} }
} }
return false; return false;
} }
if (!high_priority_packets_.empty()) { if (!high_priority_packets_->empty()) {
*priority = kHighPriority; *priority = kHighPriority;
GetNextPacketFromList(&high_priority_packets_, ssrc, sequence_number, GetNextPacketFromList(high_priority_packets_.get(), ssrc, sequence_number,
capture_time_ms, last_packet); capture_time_ms, last_packet);
return true; return true;
} }
if (!normal_priority_packets_.empty()) { if (!normal_priority_packets_->empty()) {
*priority = kNormalPriority; *priority = kNormalPriority;
GetNextPacketFromList(&normal_priority_packets_, ssrc, sequence_number, GetNextPacketFromList(normal_priority_packets_.get(), ssrc,
capture_time_ms, last_packet); sequence_number, capture_time_ms, last_packet);
return true; return true;
} }
if (!low_priority_packets_.empty()) { if (!low_priority_packets_->empty()) {
*priority = kLowPriority; *priority = kLowPriority;
GetNextPacketFromList(&low_priority_packets_, ssrc, sequence_number, GetNextPacketFromList(low_priority_packets_.get(), ssrc, sequence_number,
capture_time_ms, last_packet); capture_time_ms, last_packet);
return true; return true;
} }
return false; return false;
} }
void PacedSender::GetNextPacketFromList(PacketList* list, void PacedSender::GetNextPacketFromList(paced_sender::PacketList* packets,
uint32_t* ssrc, uint16_t* sequence_number, int64_t* capture_time_ms, uint32_t* ssrc, uint16_t* sequence_number, int64_t* capture_time_ms,
bool* last_packet) { bool* last_packet) {
Packet packet = list->front(); paced_sender::Packet packet = packets->front();
UpdateState(packet.bytes_); UpdateMediaBytesSent(packet.bytes_);
*sequence_number = packet.sequence_number_; *sequence_number = packet.sequence_number_;
*ssrc = packet.ssrc_; *ssrc = packet.ssrc_;
*capture_time_ms = packet.capture_time_ms_; *capture_time_ms = packet.capture_time_ms_;
list->pop_front(); packets->pop_front();
*last_packet = list->empty() || *last_packet = packets->empty() ||
list->front().capture_time_ms_ > *capture_time_ms; packets->front().capture_time_ms_ > *capture_time_ms;
} }
// MUST have critsect_ when calling. // MUST have critsect_ when calling.
void PacedSender::UpdateState(int num_bytes) { void PacedSender::UpdateMediaBytesSent(int num_bytes) {
time_last_send_ = TickTime::Now(); time_last_send_ = TickTime::Now();
bytes_remaining_interval_ -= num_bytes; media_budget_->UseBudget(num_bytes);
padding_bytes_remaining_interval_ -= num_bytes; pad_up_to_bitrate_budget_->UseBudget(num_bytes);
} }
} // namespace webrtc } // namespace webrtc

View File

@ -14,6 +14,7 @@
#include "webrtc/modules/pacing/include/paced_sender.h" #include "webrtc/modules/pacing/include/paced_sender.h"
using testing::_; using testing::_;
using testing::Return;
namespace webrtc { namespace webrtc {
namespace test { namespace test {
@ -26,18 +27,41 @@ class MockPacedSenderCallback : public PacedSender::Callback {
MOCK_METHOD3(TimeToSendPacket, MOCK_METHOD3(TimeToSendPacket,
void(uint32_t ssrc, uint16_t sequence_number, int64_t capture_time_ms)); void(uint32_t ssrc, uint16_t sequence_number, int64_t capture_time_ms));
MOCK_METHOD1(TimeToSendPadding, MOCK_METHOD1(TimeToSendPadding,
void(int bytes)); int(int bytes));
};
class PacedSenderPadding : public PacedSender::Callback {
public:
PacedSenderPadding() : padding_sent_(0) {}
void TimeToSendPacket(uint32_t ssrc, uint16_t sequence_number,
int64_t capture_time_ms) {
}
int TimeToSendPadding(int bytes) {
const int kPaddingPacketSize = 224;
int num_packets = (bytes + kPaddingPacketSize - 1) / kPaddingPacketSize;
padding_sent_ += kPaddingPacketSize * num_packets;
return kPaddingPacketSize * num_packets;
}
int padding_sent() { return padding_sent_; }
private:
int padding_sent_;
}; };
class PacedSenderTest : public ::testing::Test { class PacedSenderTest : public ::testing::Test {
protected: protected:
PacedSenderTest() { PacedSenderTest() {
srand(0);
TickTime::UseFakeClock(123456); TickTime::UseFakeClock(123456);
// Need to initialize PacedSender after we initialize clock. // Need to initialize PacedSender after we initialize clock.
send_bucket_.reset(new PacedSender(&callback_, kTargetBitrate, send_bucket_.reset(new PacedSender(&callback_, kTargetBitrate,
kPaceMultiplier)); kPaceMultiplier));
send_bucket_->SetStatus(true); send_bucket_->SetStatus(true);
} }
MockPacedSenderCallback callback_; MockPacedSenderCallback callback_;
scoped_ptr<PacedSender> send_bucket_; scoped_ptr<PacedSender> send_bucket_;
}; };
@ -164,6 +188,7 @@ TEST_F(PacedSenderTest, Padding) {
uint16_t sequence_number = 1234; uint16_t sequence_number = 1234;
int64_t capture_time_ms = 56789; int64_t capture_time_ms = 56789;
send_bucket_->UpdateBitrate(kTargetBitrate, kTargetBitrate);
// Due to the multiplicative factor we can send 3 packets not 2 packets. // Due to the multiplicative factor we can send 3 packets not 2 packets.
EXPECT_TRUE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc, EXPECT_TRUE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
sequence_number++, capture_time_ms, 250)); sequence_number++, capture_time_ms, 250));
@ -171,7 +196,8 @@ TEST_F(PacedSenderTest, Padding) {
sequence_number++, capture_time_ms, 250)); sequence_number++, capture_time_ms, 250));
EXPECT_TRUE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc, EXPECT_TRUE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
sequence_number++, capture_time_ms, 250)); sequence_number++, capture_time_ms, 250));
EXPECT_CALL(callback_, TimeToSendPadding(250)).Times(1); // No padding is expected since we have sent too much already.
EXPECT_CALL(callback_, TimeToSendPadding(_)).Times(0);
EXPECT_CALL(callback_, EXPECT_CALL(callback_,
TimeToSendPacket(ssrc, sequence_number, capture_time_ms)).Times(0); TimeToSendPacket(ssrc, sequence_number, capture_time_ms)).Times(0);
EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess()); EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess());
@ -179,13 +205,79 @@ TEST_F(PacedSenderTest, Padding) {
EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess()); EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess());
EXPECT_EQ(0, send_bucket_->Process()); EXPECT_EQ(0, send_bucket_->Process());
EXPECT_CALL(callback_, TimeToSendPadding(500)).Times(1); // 5 milliseconds later we have enough budget to send some padding.
EXPECT_CALL(callback_, TimeToSendPadding(250)).Times(1).
WillOnce(Return(250));
EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess()); EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess());
TickTime::AdvanceFakeClock(5); TickTime::AdvanceFakeClock(5);
EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess()); EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess());
EXPECT_EQ(0, send_bucket_->Process()); EXPECT_EQ(0, send_bucket_->Process());
} }
TEST_F(PacedSenderTest, VerifyPaddingUpToBitrate) {
uint32_t ssrc = 12345;
uint16_t sequence_number = 1234;
int64_t capture_time_ms = 56789;
const int kTimeStep = 5;
const int64_t kBitrateWindow = 100;
send_bucket_->UpdateBitrate(kTargetBitrate, kTargetBitrate);
int64_t start_time = TickTime::MillisecondTimestamp();
while (TickTime::MillisecondTimestamp() - start_time < kBitrateWindow) {
EXPECT_TRUE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
sequence_number++, capture_time_ms,
250));
TickTime::AdvanceFakeClock(kTimeStep);
EXPECT_CALL(callback_, TimeToSendPadding(250)).Times(1).
WillOnce(Return(250));
send_bucket_->Process();
}
}
TEST_F(PacedSenderTest, VerifyMaxPaddingBitrate) {
uint32_t ssrc = 12345;
uint16_t sequence_number = 1234;
int64_t capture_time_ms = 56789;
const int kTimeStep = 5;
const int64_t kBitrateWindow = 100;
const int kTargetBitrate = 1500;
send_bucket_->UpdateBitrate(kTargetBitrate, kTargetBitrate);
int64_t start_time = TickTime::MillisecondTimestamp();
while (TickTime::MillisecondTimestamp() - start_time < kBitrateWindow) {
EXPECT_TRUE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
sequence_number++, capture_time_ms,
250));
TickTime::AdvanceFakeClock(kTimeStep);
EXPECT_CALL(callback_, TimeToSendPadding(500)).Times(1).
WillOnce(Return(250));
send_bucket_->Process();
}
}
TEST_F(PacedSenderTest, VerifyAverageBitrateVaryingMediaPayload) {
uint32_t ssrc = 12345;
uint16_t sequence_number = 1234;
int64_t capture_time_ms = 56789;
const int kTimeStep = 5;
const int64_t kBitrateWindow = 10000;
PacedSenderPadding callback;
send_bucket_.reset(new PacedSender(&callback, kTargetBitrate,
kPaceMultiplier));
send_bucket_->UpdateBitrate(kTargetBitrate, kTargetBitrate);
int64_t start_time = TickTime::MillisecondTimestamp();
int media_bytes = 0;
while (TickTime::MillisecondTimestamp() - start_time < kBitrateWindow) {
int media_payload = rand() % 100 + 200; // [200, 300] bytes.
EXPECT_TRUE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
sequence_number++, capture_time_ms,
media_payload));
media_bytes += media_payload;
TickTime::AdvanceFakeClock(kTimeStep);
send_bucket_->Process();
}
EXPECT_NEAR(kTargetBitrate, 8 * (media_bytes + callback.padding_sent()) /
kBitrateWindow, 1);
}
TEST_F(PacedSenderTest, Priority) { TEST_F(PacedSenderTest, Priority) {
uint32_t ssrc_low_priority = 12345; uint32_t ssrc_low_priority = 12345;
uint32_t ssrc = 12346; uint32_t ssrc = 12346;

View File

@ -165,7 +165,7 @@ public:
// < 0, on error. // < 0, on error.
virtual int32_t CodecConfigParameters(uint8_t* buffer, int32_t size) = 0; virtual int32_t CodecConfigParameters(uint8_t* buffer, int32_t size) = 0;
// API to get currently configured encoder target bitrate in kbit/s. // API to get currently configured encoder target bitrate in bits/s.
// //
// Return value : 0, on success. // Return value : 0, on success.
// < 0, on error. // < 0, on error.

View File

@ -942,8 +942,8 @@ int32_t ViEChannel::EnableKeyFrameRequestCallback(const bool enable) {
} }
int32_t ViEChannel::SetSSRC(const uint32_t SSRC, int32_t ViEChannel::SetSSRC(const uint32_t SSRC,
const StreamType usage, const StreamType usage,
const uint8_t simulcast_idx) { const uint8_t simulcast_idx) {
WEBRTC_TRACE(webrtc::kTraceInfo, WEBRTC_TRACE(webrtc::kTraceInfo,
webrtc::kTraceVideo, webrtc::kTraceVideo,
ViEId(engine_id_, channel_id_), ViEId(engine_id_, channel_id_),
@ -973,7 +973,7 @@ int32_t ViEChannel::SetSSRC(const uint32_t SSRC,
} }
int32_t ViEChannel::SetRemoteSSRCType(const StreamType usage, int32_t ViEChannel::SetRemoteSSRCType(const StreamType usage,
const uint32_t SSRC) const { const uint32_t SSRC) const {
WEBRTC_TRACE(webrtc::kTraceInfo, WEBRTC_TRACE(webrtc::kTraceInfo,
webrtc::kTraceVideo, webrtc::kTraceVideo,
ViEId(engine_id_, channel_id_), ViEId(engine_id_, channel_id_),

View File

@ -10,6 +10,7 @@
#include "webrtc/video_engine/vie_encoder.h" #include "webrtc/video_engine/vie_encoder.h"
#include <algorithm>
#include <cassert> #include <cassert>
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
@ -91,8 +92,9 @@ class ViEPacedSenderCallback : public PacedSender::Callback {
int64_t capture_time_ms) { int64_t capture_time_ms) {
owner_->TimeToSendPacket(ssrc, sequence_number, capture_time_ms); owner_->TimeToSendPacket(ssrc, sequence_number, capture_time_ms);
} }
virtual void TimeToSendPadding(int /*bytes*/) { virtual int TimeToSendPadding(int bytes) {
// TODO(pwestin): Hook up this. // TODO(pwestin): Hook up this.
return 0;
} }
private: private:
ViEEncoder* owner_; ViEEncoder* owner_;
@ -1000,7 +1002,7 @@ void ViEEncoder::OnNetworkChanged(const uint32_t bitrate_bps,
vcm_.SetChannelParameters(bitrate_bps, fraction_lost, round_trip_time_ms); vcm_.SetChannelParameters(bitrate_bps, fraction_lost, round_trip_time_ms);
int bitrate_kbps = bitrate_bps / 1000; int bitrate_kbps = bitrate_bps / 1000;
paced_sender_->UpdateBitrate(bitrate_kbps); paced_sender_->UpdateBitrate(bitrate_kbps, 0);
default_rtp_rtcp_->SetTargetSendBitrate(bitrate_bps); default_rtp_rtcp_->SetTargetSendBitrate(bitrate_bps);
} }