blob: 98f508ccc5eb9535a68434a036ddbc8abb6a063f [file] [log] [blame]
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +00001/*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/modules/pacing/include/paced_sender.h"
12
13#include <assert.h>
14
pbos@webrtc.org03c817e2014-07-07 10:20:35 +000015#include <map>
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +000016#include <queue>
pbos@webrtc.org03c817e2014-07-07 10:20:35 +000017#include <set>
18
pwestin@webrtc.org52b4e882013-05-02 19:02:17 +000019#include "webrtc/modules/interface/module_common_types.h"
stefan@webrtc.org82462aa2014-10-23 11:57:05 +000020#include "webrtc/modules/pacing/bitrate_prober.h"
stefan@webrtc.org88e0dda2014-07-04 09:20:42 +000021#include "webrtc/system_wrappers/interface/clock.h"
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +000022#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
stefan@webrtc.org82462aa2014-10-23 11:57:05 +000023#include "webrtc/system_wrappers/interface/field_trial.h"
24#include "webrtc/system_wrappers/interface/logging.h"
pwestin@webrtc.org52b4e882013-05-02 19:02:17 +000025#include "webrtc/system_wrappers/interface/trace_event.h"
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +000026
27namespace {
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +000028// Time limit in milliseconds between packet bursts.
29const int kMinPacketLimitMs = 5;
30
31// Upper cap on process interval, in case process has not been called in a long
32// time.
33const int kMaxIntervalTimeMs = 30;
34
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +000035} // namespace
36
37namespace webrtc {
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +000038namespace paced_sender {
39struct Packet {
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +000040 Packet(PacedSender::Priority priority,
41 uint32_t ssrc,
pbos@webrtc.org03c817e2014-07-07 10:20:35 +000042 uint16_t seq_number,
43 int64_t capture_time_ms,
44 int64_t enqueue_time_ms,
45 int length_in_bytes,
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +000046 bool retransmission,
47 uint64_t enqueue_order)
48 : priority(priority),
49 ssrc(ssrc),
pbos@webrtc.org03c817e2014-07-07 10:20:35 +000050 sequence_number(seq_number),
51 capture_time_ms(capture_time_ms),
52 enqueue_time_ms(enqueue_time_ms),
53 bytes(length_in_bytes),
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +000054 retransmission(retransmission),
55 enqueue_order(enqueue_order) {}
56
57 PacedSender::Priority priority;
pbos@webrtc.org03c817e2014-07-07 10:20:35 +000058 uint32_t ssrc;
59 uint16_t sequence_number;
60 int64_t capture_time_ms;
61 int64_t enqueue_time_ms;
62 int bytes;
63 bool retransmission;
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +000064 uint64_t enqueue_order;
65 std::list<Packet>::iterator this_it;
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +000066};
67
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +000068// Used by priority queue to sort packets.
69struct Comparator {
70 bool operator()(const Packet* first, const Packet* second) {
71 // Highest prio = 0.
72 if (first->priority != second->priority)
73 return first->priority > second->priority;
74
75 // Retransmissions go first.
76 if (second->retransmission && !first->retransmission)
77 return true;
78
79 // Older frames have higher prio.
80 if (first->capture_time_ms != second->capture_time_ms)
81 return first->capture_time_ms > second->capture_time_ms;
82
83 return first->enqueue_order > second->enqueue_order;
84 }
85};
86
87// Class encapsulating a priority queue with some extensions.
88class PacketQueue {
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +000089 public:
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +000090 PacketQueue() : bytes_(0) {}
91 virtual ~PacketQueue() {}
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +000092
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +000093 void Push(const Packet& packet) {
94 if (!AddToDupeSet(packet))
95 return;
96
97 // Store packet in list, use pointers in priority queue for cheaper moves.
98 // Packets have a handle to its own iterator in the list, for easy removal
99 // when popping from queue.
100 packet_list_.push_front(packet);
101 std::list<Packet>::iterator it = packet_list_.begin();
102 it->this_it = it; // Handle for direct removal from list.
103 prio_queue_.push(&(*it)); // Pointer into list.
104 bytes_ += packet.bytes;
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +0000105 }
106
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000107 const Packet& BeginPop() {
108 const Packet& packet = *prio_queue_.top();
109 prio_queue_.pop();
110 return packet;
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +0000111 }
112
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000113 void CancelPop(const Packet& packet) { prio_queue_.push(&(*packet.this_it)); }
114
115 void FinalizePop(const Packet& packet) {
116 RemoveFromDupeSet(packet);
117 bytes_ -= packet.bytes;
118 packet_list_.erase(packet.this_it);
stefan@webrtc.org82462aa2014-10-23 11:57:05 +0000119 }
120
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000121 bool Empty() const { return prio_queue_.empty(); }
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +0000122
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000123 size_t SizeInPackets() const { return prio_queue_.size(); }
124
pkasting@chromium.org2656bf82014-11-17 22:21:14 +0000125 uint64_t SizeInBytes() const { return bytes_; }
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000126
127 int64_t OldestEnqueueTime() const {
128 std::list<Packet>::const_reverse_iterator it = packet_list_.rbegin();
129 if (it == packet_list_.rend())
130 return 0;
131 return it->enqueue_time_ms;
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +0000132 }
133
134 private:
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000135 // Try to add a packet to the set of ssrc/seqno identifiers currently in the
136 // queue. Return true if inserted, false if this is a duplicate.
137 bool AddToDupeSet(const Packet& packet) {
138 SsrcSeqNoMap::iterator it = dupe_map_.find(packet.ssrc);
139 if (it == dupe_map_.end()) {
140 // First for this ssrc, just insert.
141 dupe_map_[packet.ssrc].insert(packet.sequence_number);
142 return true;
143 }
144
145 // Insert returns a pair, where second is a bool set to true if new element.
146 return it->second.insert(packet.sequence_number).second;
147 }
148
149 void RemoveFromDupeSet(const Packet& packet) {
150 SsrcSeqNoMap::iterator it = dupe_map_.find(packet.ssrc);
151 assert(it != dupe_map_.end());
152 it->second.erase(packet.sequence_number);
153 if (it->second.empty()) {
154 dupe_map_.erase(it);
155 }
156 }
157
158 // List of packets, in the order the were enqueued. Since dequeueing may
159 // occur out of order, use list instead of vector.
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +0000160 std::list<Packet> packet_list_;
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000161 // Priority queue of the packets, sorted according to Comparator.
162 // Use pointers into list, to avoid moving whole struct within heap.
163 std::priority_queue<Packet*, std::vector<Packet*>, Comparator> prio_queue_;
164 // Total number of bytes in the queue.
165 uint64_t bytes_;
166 // Map<ssrc, set<seq_no> >, for checking duplicates.
167 typedef std::map<uint32_t, std::set<uint16_t> > SsrcSeqNoMap;
168 SsrcSeqNoMap dupe_map_;
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +0000169};
170
171class IntervalBudget {
172 public:
173 explicit IntervalBudget(int initial_target_rate_kbps)
174 : target_rate_kbps_(initial_target_rate_kbps),
175 bytes_remaining_(0) {}
176
177 void set_target_rate_kbps(int target_rate_kbps) {
178 target_rate_kbps_ = target_rate_kbps;
179 }
180
181 void IncreaseBudget(int delta_time_ms) {
182 int bytes = target_rate_kbps_ * delta_time_ms / 8;
183 if (bytes_remaining_ < 0) {
184 // We overused last interval, compensate this interval.
185 bytes_remaining_ = bytes_remaining_ + bytes;
186 } else {
187 // If we underused last interval we can't use it this interval.
188 bytes_remaining_ = bytes;
189 }
190 }
191
192 void UseBudget(int bytes) {
193 bytes_remaining_ = std::max(bytes_remaining_ - bytes,
stefan@webrtc.orgef2d5542013-11-21 14:37:11 +0000194 -500 * target_rate_kbps_ / 8);
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +0000195 }
196
197 int bytes_remaining() const { return bytes_remaining_; }
198
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000199 int target_rate_kbps() const { return target_rate_kbps_; }
200
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +0000201 private:
202 int target_rate_kbps_;
203 int bytes_remaining_;
204};
205} // namespace paced_sender
pwestin@webrtc.org52aa0192013-04-25 17:35:56 +0000206
stefan@webrtc.org88e0dda2014-07-04 09:20:42 +0000207const float PacedSender::kDefaultPaceMultiplier = 2.5f;
208
209PacedSender::PacedSender(Clock* clock,
210 Callback* callback,
stefan@webrtc.org82462aa2014-10-23 11:57:05 +0000211 int bitrate_kbps,
pbos@webrtc.org709e2972014-03-19 10:59:52 +0000212 int max_bitrate_kbps,
213 int min_bitrate_kbps)
stefan@webrtc.org88e0dda2014-07-04 09:20:42 +0000214 : clock_(clock),
215 callback_(callback),
pbos@webrtc.org03c817e2014-07-07 10:20:35 +0000216 critsect_(CriticalSectionWrapper::CreateCriticalSection()),
stefan@webrtc.orgcb254aa2014-06-12 15:12:25 +0000217 enabled_(true),
pwestin@webrtc.orgdb418562013-03-22 23:39:29 +0000218 paused_(false),
pbos@webrtc.org709e2972014-03-19 10:59:52 +0000219 media_budget_(new paced_sender::IntervalBudget(max_bitrate_kbps)),
220 padding_budget_(new paced_sender::IntervalBudget(min_bitrate_kbps)),
stefan@webrtc.org82462aa2014-10-23 11:57:05 +0000221 prober_(new BitrateProber()),
222 bitrate_bps_(1000 * bitrate_kbps),
stefan@webrtc.org89fd1e82014-07-15 16:40:38 +0000223 time_last_update_us_(clock->TimeInMicroseconds()),
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000224 packets_(new paced_sender::PacketQueue()),
225 packet_counter_(0) {
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +0000226 UpdateBytesPerInterval(kMinPacketLimitMs);
227}
228
stefan@webrtc.org89fd1e82014-07-15 16:40:38 +0000229PacedSender::~PacedSender() {}
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +0000230
pwestin@webrtc.orgdb418562013-03-22 23:39:29 +0000231void PacedSender::Pause() {
232 CriticalSectionScoped cs(critsect_.get());
233 paused_ = true;
234}
235
236void PacedSender::Resume() {
237 CriticalSectionScoped cs(critsect_.get());
238 paused_ = false;
239}
240
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +0000241void PacedSender::SetStatus(bool enable) {
242 CriticalSectionScoped cs(critsect_.get());
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +0000243 enabled_ = enable;
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +0000244}
245
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +0000246bool PacedSender::Enabled() const {
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +0000247 CriticalSectionScoped cs(critsect_.get());
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +0000248 return enabled_;
249}
250
stefan@webrtc.org82462aa2014-10-23 11:57:05 +0000251void PacedSender::UpdateBitrate(int bitrate_kbps,
252 int max_bitrate_kbps,
pbos@webrtc.org709e2972014-03-19 10:59:52 +0000253 int min_bitrate_kbps) {
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +0000254 CriticalSectionScoped cs(critsect_.get());
pbos@webrtc.org709e2972014-03-19 10:59:52 +0000255 media_budget_->set_target_rate_kbps(max_bitrate_kbps);
256 padding_budget_->set_target_rate_kbps(min_bitrate_kbps);
stefan@webrtc.org82462aa2014-10-23 11:57:05 +0000257 bitrate_bps_ = 1000 * bitrate_kbps;
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +0000258}
259
260bool PacedSender::SendPacket(Priority priority, uint32_t ssrc,
stefan@webrtc.org9b82f5a2013-11-13 15:29:21 +0000261 uint16_t sequence_number, int64_t capture_time_ms, int bytes,
262 bool retransmission) {
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +0000263 CriticalSectionScoped cs(critsect_.get());
264
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +0000265 if (!enabled_) {
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +0000266 return true; // We can send now.
267 }
stefan@webrtc.org82462aa2014-10-23 11:57:05 +0000268 // Enable probing if the probing experiment is enabled.
269 if (!prober_->IsProbing() && ProbingExperimentIsEnabled()) {
270 prober_->SetEnabled(true);
271 }
272 prober_->MaybeInitializeProbe(bitrate_bps_);
273
stefan@webrtc.orgbfacda62013-03-27 16:36:01 +0000274 if (capture_time_ms < 0) {
stefan@webrtc.org88e0dda2014-07-04 09:20:42 +0000275 capture_time_ms = clock_->TimeInMilliseconds();
stefan@webrtc.orgbfacda62013-03-27 16:36:01 +0000276 }
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000277
278 packets_->Push(paced_sender::Packet(
279 priority, ssrc, sequence_number, capture_time_ms,
280 clock_->TimeInMilliseconds(), bytes, retransmission, packet_counter_++));
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +0000281 return false;
282}
283
pkasting@chromium.org2656bf82014-11-17 22:21:14 +0000284int64_t PacedSender::ExpectedQueueTimeMs() const {
stefan@webrtc.org19a40ff2013-11-27 14:16:20 +0000285 CriticalSectionScoped cs(critsect_.get());
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000286 int target_rate = media_budget_->target_rate_kbps();
287 assert(target_rate > 0);
pkasting@chromium.org2656bf82014-11-17 22:21:14 +0000288 return static_cast<int64_t>(packets_->SizeInBytes() * 8 / target_rate);
stefan@webrtc.orgbfacda62013-03-27 16:36:01 +0000289}
290
stefan@webrtc.org82462aa2014-10-23 11:57:05 +0000291size_t PacedSender::QueueSizePackets() const {
292 CriticalSectionScoped cs(critsect_.get());
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000293 return packets_->SizeInPackets();
294}
295
296int PacedSender::QueueInMs() const {
297 CriticalSectionScoped cs(critsect_.get());
298
299 int64_t oldest_packet = packets_->OldestEnqueueTime();
300 if (oldest_packet == 0)
301 return 0;
302
303 return clock_->TimeInMilliseconds() - oldest_packet;
stefan@webrtc.org82462aa2014-10-23 11:57:05 +0000304}
305
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +0000306int32_t PacedSender::TimeUntilNextProcess() {
307 CriticalSectionScoped cs(critsect_.get());
stefan@webrtc.org82462aa2014-10-23 11:57:05 +0000308 int64_t elapsed_time_us = clock_->TimeInMicroseconds() - time_last_update_us_;
309 int elapsed_time_ms = static_cast<int>((elapsed_time_us + 500) / 1000);
310 if (prober_->IsProbing()) {
311 int next_probe = prober_->TimeUntilNextProbe(clock_->TimeInMilliseconds());
312 return next_probe;
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +0000313 }
314 if (elapsed_time_ms >= kMinPacketLimitMs) {
315 return 0;
316 }
317 return kMinPacketLimitMs - elapsed_time_ms;
318}
319
320int32_t PacedSender::Process() {
stefan@webrtc.org89fd1e82014-07-15 16:40:38 +0000321 int64_t now_us = clock_->TimeInMicroseconds();
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +0000322 CriticalSectionScoped cs(critsect_.get());
stefan@webrtc.org89fd1e82014-07-15 16:40:38 +0000323 int elapsed_time_ms = (now_us - time_last_update_us_ + 500) / 1000;
324 time_last_update_us_ = now_us;
stefan@webrtc.org80865fd2013-08-09 11:31:23 +0000325 if (!enabled_) {
326 return 0;
327 }
stefan@webrtc.org8ccb9f92013-06-19 14:13:42 +0000328 if (!paused_) {
329 if (elapsed_time_ms > 0) {
330 uint32_t delta_time_ms = std::min(kMaxIntervalTimeMs, elapsed_time_ms);
331 UpdateBytesPerInterval(delta_time_ms);
332 }
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000333
334 while (!packets_->Empty()) {
335 if (media_budget_->bytes_remaining() <= 0 && !prober_->IsProbing())
hclam@chromium.org2e402ce2013-06-20 20:18:31 +0000336 return 0;
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000337
338 // Since we need to release the lock in order to send, we first pop the
339 // element from the priority queue but keep it in storage, so that we can
340 // reinsert it if send fails.
341 const paced_sender::Packet& packet = packets_->BeginPop();
342 if (SendPacket(packet)) {
343 // Send succeeded, remove it from the queue.
344 packets_->FinalizePop(packet);
345 if (prober_->IsProbing())
346 return 0;
347 } else {
348 // Send failed, put it back into the queue.
349 packets_->CancelPop(packet);
stefan@webrtc.org82462aa2014-10-23 11:57:05 +0000350 return 0;
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000351 }
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +0000352 }
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000353
354 int padding_needed = padding_budget_->bytes_remaining();
355 if (padding_needed > 0) {
356 SendPadding(padding_needed);
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +0000357 }
358 }
359 return 0;
360}
361
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000362bool PacedSender::SendPacket(const paced_sender::Packet& packet) {
stefan@webrtc.org19a40ff2013-11-27 14:16:20 +0000363 critsect_->Leave();
pbos@webrtc.org03c817e2014-07-07 10:20:35 +0000364 const bool success = callback_->TimeToSendPacket(packet.ssrc,
365 packet.sequence_number,
366 packet.capture_time_ms,
367 packet.retransmission);
stefan@webrtc.org19a40ff2013-11-27 14:16:20 +0000368 critsect_->Enter();
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000369
370 if (success) {
371 // Update media bytes sent.
372 prober_->PacketSent(clock_->TimeInMilliseconds(), packet.bytes);
373 media_budget_->UseBudget(packet.bytes);
374 padding_budget_->UseBudget(packet.bytes);
stefan@webrtc.org19a40ff2013-11-27 14:16:20 +0000375 }
sprang@webrtc.orgdcebf2d2014-11-04 16:27:16 +0000376
377 return success;
378}
379
380void PacedSender::SendPadding(int padding_needed) {
381 critsect_->Leave();
382 int bytes_sent = callback_->TimeToSendPadding(padding_needed);
383 critsect_->Enter();
384
385 // Update padding bytes sent.
386 media_budget_->UseBudget(bytes_sent);
387 padding_budget_->UseBudget(bytes_sent);
stefan@webrtc.org19a40ff2013-11-27 14:16:20 +0000388}
389
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +0000390void PacedSender::UpdateBytesPerInterval(uint32_t delta_time_ms) {
stefan@webrtc.orgc3cc3752013-06-04 09:36:56 +0000391 media_budget_->IncreaseBudget(delta_time_ms);
392 padding_budget_->IncreaseBudget(delta_time_ms);
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +0000393}
394
stefan@webrtc.org82462aa2014-10-23 11:57:05 +0000395bool PacedSender::ProbingExperimentIsEnabled() const {
396 return webrtc::field_trial::FindFullName("WebRTC-BitrateProbing") ==
397 "Enabled";
398}
pwestin@webrtc.orgb5180172012-11-09 20:56:23 +0000399} // namespace webrtc