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:
parent
c69ae69d0b
commit
c3cc375499
@ -21,6 +21,11 @@
|
||||
|
||||
namespace webrtc {
|
||||
class CriticalSectionWrapper;
|
||||
namespace paced_sender {
|
||||
class IntervalBudget;
|
||||
struct Packet;
|
||||
class PacketList;
|
||||
} // namespace paced_sender
|
||||
|
||||
class PacedSender : public Module {
|
||||
public:
|
||||
@ -40,7 +45,7 @@ class PacedSender : public Module {
|
||||
virtual void TimeToSendPacket(uint32_t ssrc, uint16_t sequence_number,
|
||||
int64_t capture_time_ms) = 0;
|
||||
// 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:
|
||||
virtual ~Callback() {}
|
||||
};
|
||||
@ -52,14 +57,19 @@ class PacedSender : public Module {
|
||||
// Enable/disable pacing.
|
||||
void SetStatus(bool enable);
|
||||
|
||||
bool Enabled() const;
|
||||
|
||||
// Temporarily pause all sending.
|
||||
void Pause();
|
||||
|
||||
// Resume sending packets.
|
||||
void Resume();
|
||||
|
||||
// Current total estimated bitrate.
|
||||
void UpdateBitrate(int target_bitrate_kbps);
|
||||
// Set the pacing target bitrate and the bitrate up to which we are allowed to
|
||||
// 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
|
||||
// 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();
|
||||
|
||||
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.
|
||||
bool GetNextPacket(uint32_t* ssrc, uint16_t* sequence_number,
|
||||
int64_t* capture_time_ms, Priority* priority,
|
||||
bool* last_packet);
|
||||
|
||||
// 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,
|
||||
bool* last_packet);
|
||||
|
||||
@ -123,24 +104,32 @@ class PacedSender : public Module {
|
||||
void UpdateBytesPerInterval(uint32_t delta_time_in_ms);
|
||||
|
||||
// Updates the buffers with the number of bytes that we sent.
|
||||
void UpdateState(int num_bytes);
|
||||
void UpdateMediaBytesSent(int num_bytes);
|
||||
|
||||
Callback* callback_;
|
||||
const float pace_multiplier_;
|
||||
bool enable_;
|
||||
bool enabled_;
|
||||
bool paused_;
|
||||
scoped_ptr<CriticalSectionWrapper> critsect_;
|
||||
int target_bitrate_kbytes_per_s_;
|
||||
int bytes_remaining_interval_;
|
||||
int padding_bytes_remaining_interval_;
|
||||
// This is the media budget, keeping track of how many bits of media
|
||||
// we can pace out during the current 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_send_;
|
||||
int64_t capture_time_ms_last_queued_;
|
||||
int64_t capture_time_ms_last_sent_;
|
||||
|
||||
PacketList high_priority_packets_;
|
||||
PacketList normal_priority_packets_;
|
||||
PacketList low_priority_packets_;
|
||||
scoped_ptr<paced_sender::PacketList> high_priority_packets_;
|
||||
scoped_ptr<paced_sender::PacketList> normal_priority_packets_;
|
||||
scoped_ptr<paced_sender::PacketList> low_priority_packets_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
#endif // WEBRTC_MODULES_PACED_SENDER_H_
|
||||
|
@ -28,47 +28,115 @@ const int kMaxIntervalTimeMs = 30;
|
||||
// packets are sent, regardless of buffer state. In practice only in effect at
|
||||
// low bitrates (less than 320 kbits/s).
|
||||
const int kMaxQueueTimeWithoutSendingMs = 30;
|
||||
|
||||
// Max padding bytes per second.
|
||||
const int kMaxPaddingKbps = 800;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
bool PacedSender::PacketList::empty() const {
|
||||
return packet_list_.empty();
|
||||
}
|
||||
|
||||
PacedSender::Packet PacedSender::PacketList::front() const {
|
||||
return packet_list_.front();
|
||||
}
|
||||
|
||||
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_);
|
||||
namespace paced_sender {
|
||||
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 {
|
||||
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,
|
||||
float pace_multiplier)
|
||||
: callback_(callback),
|
||||
pace_multiplier_(pace_multiplier),
|
||||
enable_(false),
|
||||
enabled_(false),
|
||||
paused_(false),
|
||||
critsect_(CriticalSectionWrapper::CreateCriticalSection()),
|
||||
target_bitrate_kbytes_per_s_(target_bitrate_kbps >> 3), // Divide by 8.
|
||||
bytes_remaining_interval_(0),
|
||||
padding_bytes_remaining_interval_(0),
|
||||
media_budget_(new paced_sender::IntervalBudget(
|
||||
pace_multiplier_ * target_bitrate_kbps)),
|
||||
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()),
|
||||
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);
|
||||
}
|
||||
|
||||
@ -87,20 +155,27 @@ void PacedSender::Resume() {
|
||||
|
||||
void PacedSender::SetStatus(bool enable) {
|
||||
CriticalSectionScoped cs(critsect_.get());
|
||||
enable_ = enable;
|
||||
enabled_ = enable;
|
||||
}
|
||||
|
||||
void PacedSender::UpdateBitrate(int target_bitrate_kbps) {
|
||||
bool PacedSender::Enabled() const {
|
||||
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,
|
||||
uint16_t sequence_number, int64_t capture_time_ms, int bytes) {
|
||||
CriticalSectionScoped cs(critsect_.get());
|
||||
|
||||
if (!enable_) {
|
||||
UpdateState(bytes);
|
||||
if (!enabled_) {
|
||||
UpdateMediaBytesSent(bytes);
|
||||
return true; // We can send now.
|
||||
}
|
||||
if (capture_time_ms < 0) {
|
||||
@ -110,8 +185,10 @@ bool PacedSender::SendPacket(Priority priority, uint32_t ssrc,
|
||||
// Queue all packets when we are paused.
|
||||
switch (priority) {
|
||||
case kHighPriority:
|
||||
high_priority_packets_.push_back(
|
||||
Packet(ssrc, sequence_number, capture_time_ms, bytes));
|
||||
high_priority_packets_->push_back(paced_sender::Packet(ssrc,
|
||||
sequence_number,
|
||||
capture_time_ms,
|
||||
bytes));
|
||||
break;
|
||||
case kNormalPriority:
|
||||
if (capture_time_ms > capture_time_ms_last_queued_) {
|
||||
@ -122,50 +199,31 @@ bool PacedSender::SendPacket(Priority priority, uint32_t ssrc,
|
||||
case kLowPriority:
|
||||
// Queue the low priority packets in the normal priority queue when we
|
||||
// are paused to avoid starvation.
|
||||
normal_priority_packets_.push_back(
|
||||
Packet(ssrc, sequence_number, capture_time_ms, bytes));
|
||||
normal_priority_packets_->push_back(paced_sender::Packet(
|
||||
ssrc, sequence_number, capture_time_ms, bytes));
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
paced_sender::PacketList* packet_list;
|
||||
switch (priority) {
|
||||
case kHighPriority:
|
||||
if (high_priority_packets_.empty() &&
|
||||
bytes_remaining_interval_ > 0) {
|
||||
UpdateState(bytes);
|
||||
return true; // We can send now.
|
||||
}
|
||||
high_priority_packets_.push_back(
|
||||
Packet(ssrc, sequence_number, capture_time_ms, bytes));
|
||||
return false;
|
||||
packet_list = high_priority_packets_.get();
|
||||
break;
|
||||
case kNormalPriority:
|
||||
if (high_priority_packets_.empty() &&
|
||||
normal_priority_packets_.empty() &&
|
||||
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;
|
||||
packet_list = normal_priority_packets_.get();
|
||||
break;
|
||||
case kLowPriority:
|
||||
if (high_priority_packets_.empty() &&
|
||||
normal_priority_packets_.empty() &&
|
||||
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;
|
||||
packet_list = low_priority_packets_.get();
|
||||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@ -173,20 +231,20 @@ int PacedSender::QueueInMs() const {
|
||||
CriticalSectionScoped cs(critsect_.get());
|
||||
int64_t now_ms = TickTime::MillisecondTimestamp();
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
low_priority_packets_.front().capture_time_ms_);
|
||||
low_priority_packets_->front().capture_time_ms_);
|
||||
}
|
||||
return now_ms - oldest_packet_capture_time;
|
||||
}
|
||||
@ -231,15 +289,20 @@ int32_t PacedSender::Process() {
|
||||
callback_->TimeToSendPacket(ssrc, sequence_number, capture_time_ms);
|
||||
critsect_->Enter();
|
||||
}
|
||||
if (high_priority_packets_.empty() &&
|
||||
normal_priority_packets_.empty() &&
|
||||
low_priority_packets_.empty() &&
|
||||
padding_bytes_remaining_interval_ > 0) {
|
||||
if (high_priority_packets_->empty() &&
|
||||
normal_priority_packets_->empty() &&
|
||||
low_priority_packets_->empty() &&
|
||||
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();
|
||||
callback_->TimeToSendPadding(padding_bytes_remaining_interval_);
|
||||
int bytes_sent = callback_->TimeToSendPadding(padding_needed);
|
||||
critsect_->Enter();
|
||||
padding_bytes_remaining_interval_ = 0;
|
||||
bytes_remaining_interval_ -= padding_bytes_remaining_interval_;
|
||||
media_budget_->UseBudget(bytes_sent);
|
||||
padding_budget_->UseBudget(bytes_sent);
|
||||
pad_up_to_bitrate_budget_->UseBudget(bytes_sent);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@ -247,87 +310,74 @@ int32_t PacedSender::Process() {
|
||||
|
||||
// MUST have critsect_ when calling.
|
||||
void PacedSender::UpdateBytesPerInterval(uint32_t delta_time_ms) {
|
||||
uint32_t bytes_per_interval = target_bitrate_kbytes_per_s_ * delta_time_ms;
|
||||
|
||||
if (bytes_remaining_interval_ < 0) {
|
||||
// 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;
|
||||
}
|
||||
media_budget_->IncreaseBudget(delta_time_ms);
|
||||
padding_budget_->IncreaseBudget(delta_time_ms);
|
||||
pad_up_to_bitrate_budget_->IncreaseBudget(delta_time_ms);
|
||||
}
|
||||
|
||||
// MUST have critsect_ when calling.
|
||||
bool PacedSender::GetNextPacket(uint32_t* ssrc, uint16_t* sequence_number,
|
||||
int64_t* capture_time_ms, Priority* priority,
|
||||
bool* last_packet) {
|
||||
if (bytes_remaining_interval_ <= 0) {
|
||||
if (media_budget_->bytes_remaining() <= 0) {
|
||||
// All bytes consumed for this interval.
|
||||
// Check if we have not sent in a too long time.
|
||||
if ((TickTime::Now() - time_last_send_).Milliseconds() >
|
||||
kMaxQueueTimeWithoutSendingMs) {
|
||||
if (!high_priority_packets_.empty()) {
|
||||
if (!high_priority_packets_->empty()) {
|
||||
*priority = kHighPriority;
|
||||
GetNextPacketFromList(&high_priority_packets_, ssrc, sequence_number,
|
||||
capture_time_ms, last_packet);
|
||||
GetNextPacketFromList(high_priority_packets_.get(), ssrc,
|
||||
sequence_number, capture_time_ms, last_packet);
|
||||
return true;
|
||||
}
|
||||
if (!normal_priority_packets_.empty()) {
|
||||
if (!normal_priority_packets_->empty()) {
|
||||
*priority = kNormalPriority;
|
||||
GetNextPacketFromList(&normal_priority_packets_, ssrc, sequence_number,
|
||||
capture_time_ms, last_packet);
|
||||
GetNextPacketFromList(normal_priority_packets_.get(), ssrc,
|
||||
sequence_number, capture_time_ms, last_packet);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!high_priority_packets_.empty()) {
|
||||
if (!high_priority_packets_->empty()) {
|
||||
*priority = kHighPriority;
|
||||
GetNextPacketFromList(&high_priority_packets_, ssrc, sequence_number,
|
||||
GetNextPacketFromList(high_priority_packets_.get(), ssrc, sequence_number,
|
||||
capture_time_ms, last_packet);
|
||||
return true;
|
||||
}
|
||||
if (!normal_priority_packets_.empty()) {
|
||||
if (!normal_priority_packets_->empty()) {
|
||||
*priority = kNormalPriority;
|
||||
GetNextPacketFromList(&normal_priority_packets_, ssrc, sequence_number,
|
||||
capture_time_ms, last_packet);
|
||||
GetNextPacketFromList(normal_priority_packets_.get(), ssrc,
|
||||
sequence_number, capture_time_ms, last_packet);
|
||||
return true;
|
||||
}
|
||||
if (!low_priority_packets_.empty()) {
|
||||
if (!low_priority_packets_->empty()) {
|
||||
*priority = kLowPriority;
|
||||
GetNextPacketFromList(&low_priority_packets_, ssrc, sequence_number,
|
||||
GetNextPacketFromList(low_priority_packets_.get(), ssrc, sequence_number,
|
||||
capture_time_ms, last_packet);
|
||||
return true;
|
||||
}
|
||||
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,
|
||||
bool* last_packet) {
|
||||
Packet packet = list->front();
|
||||
UpdateState(packet.bytes_);
|
||||
paced_sender::Packet packet = packets->front();
|
||||
UpdateMediaBytesSent(packet.bytes_);
|
||||
*sequence_number = packet.sequence_number_;
|
||||
*ssrc = packet.ssrc_;
|
||||
*capture_time_ms = packet.capture_time_ms_;
|
||||
list->pop_front();
|
||||
*last_packet = list->empty() ||
|
||||
list->front().capture_time_ms_ > *capture_time_ms;
|
||||
packets->pop_front();
|
||||
*last_packet = packets->empty() ||
|
||||
packets->front().capture_time_ms_ > *capture_time_ms;
|
||||
}
|
||||
|
||||
// MUST have critsect_ when calling.
|
||||
void PacedSender::UpdateState(int num_bytes) {
|
||||
void PacedSender::UpdateMediaBytesSent(int num_bytes) {
|
||||
time_last_send_ = TickTime::Now();
|
||||
bytes_remaining_interval_ -= num_bytes;
|
||||
padding_bytes_remaining_interval_ -= num_bytes;
|
||||
media_budget_->UseBudget(num_bytes);
|
||||
pad_up_to_bitrate_budget_->UseBudget(num_bytes);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "webrtc/modules/pacing/include/paced_sender.h"
|
||||
|
||||
using testing::_;
|
||||
using testing::Return;
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
@ -26,18 +27,41 @@ class MockPacedSenderCallback : public PacedSender::Callback {
|
||||
MOCK_METHOD3(TimeToSendPacket,
|
||||
void(uint32_t ssrc, uint16_t sequence_number, int64_t capture_time_ms));
|
||||
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 {
|
||||
protected:
|
||||
PacedSenderTest() {
|
||||
srand(0);
|
||||
TickTime::UseFakeClock(123456);
|
||||
// Need to initialize PacedSender after we initialize clock.
|
||||
send_bucket_.reset(new PacedSender(&callback_, kTargetBitrate,
|
||||
kPaceMultiplier));
|
||||
send_bucket_->SetStatus(true);
|
||||
}
|
||||
|
||||
MockPacedSenderCallback callback_;
|
||||
scoped_ptr<PacedSender> send_bucket_;
|
||||
};
|
||||
@ -164,6 +188,7 @@ TEST_F(PacedSenderTest, Padding) {
|
||||
uint16_t sequence_number = 1234;
|
||||
int64_t capture_time_ms = 56789;
|
||||
|
||||
send_bucket_->UpdateBitrate(kTargetBitrate, kTargetBitrate);
|
||||
// Due to the multiplicative factor we can send 3 packets not 2 packets.
|
||||
EXPECT_TRUE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
|
||||
sequence_number++, capture_time_ms, 250));
|
||||
@ -171,7 +196,8 @@ TEST_F(PacedSenderTest, Padding) {
|
||||
sequence_number++, capture_time_ms, 250));
|
||||
EXPECT_TRUE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
|
||||
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_,
|
||||
TimeToSendPacket(ssrc, sequence_number, capture_time_ms)).Times(0);
|
||||
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_->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());
|
||||
TickTime::AdvanceFakeClock(5);
|
||||
EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess());
|
||||
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) {
|
||||
uint32_t ssrc_low_priority = 12345;
|
||||
uint32_t ssrc = 12346;
|
||||
|
@ -165,7 +165,7 @@ public:
|
||||
// < 0, on error.
|
||||
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.
|
||||
// < 0, on error.
|
||||
|
@ -942,8 +942,8 @@ int32_t ViEChannel::EnableKeyFrameRequestCallback(const bool enable) {
|
||||
}
|
||||
|
||||
int32_t ViEChannel::SetSSRC(const uint32_t SSRC,
|
||||
const StreamType usage,
|
||||
const uint8_t simulcast_idx) {
|
||||
const StreamType usage,
|
||||
const uint8_t simulcast_idx) {
|
||||
WEBRTC_TRACE(webrtc::kTraceInfo,
|
||||
webrtc::kTraceVideo,
|
||||
ViEId(engine_id_, channel_id_),
|
||||
@ -973,7 +973,7 @@ int32_t ViEChannel::SetSSRC(const uint32_t SSRC,
|
||||
}
|
||||
|
||||
int32_t ViEChannel::SetRemoteSSRCType(const StreamType usage,
|
||||
const uint32_t SSRC) const {
|
||||
const uint32_t SSRC) const {
|
||||
WEBRTC_TRACE(webrtc::kTraceInfo,
|
||||
webrtc::kTraceVideo,
|
||||
ViEId(engine_id_, channel_id_),
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "webrtc/video_engine/vie_encoder.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
|
||||
@ -91,8 +92,9 @@ class ViEPacedSenderCallback : public PacedSender::Callback {
|
||||
int64_t 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.
|
||||
return 0;
|
||||
}
|
||||
private:
|
||||
ViEEncoder* owner_;
|
||||
@ -1000,7 +1002,7 @@ void ViEEncoder::OnNetworkChanged(const uint32_t bitrate_bps,
|
||||
|
||||
vcm_.SetChannelParameters(bitrate_bps, fraction_lost, round_trip_time_ms);
|
||||
int bitrate_kbps = bitrate_bps / 1000;
|
||||
paced_sender_->UpdateBitrate(bitrate_kbps);
|
||||
paced_sender_->UpdateBitrate(bitrate_kbps, 0);
|
||||
default_rtp_rtcp_->SetTargetSendBitrate(bitrate_bps);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user