blob: 56922b73a4582b8e1a03e8814af4213bda90c42c [file] [log] [blame]
Stefan Holmere5904162015-03-26 11:11:06 +01001/*
2 * Copyright (c) 2015 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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/pacing/packet_router.h"
Stefan Holmere5904162015-03-26 11:11:06 +010012
danilchap47085372017-08-10 06:03:57 -070013#include <algorithm>
Yves Gerey988cc082018-10-23 12:03:01 +020014#include <cstdint>
danilchap47085372017-08-10 06:03:57 -070015#include <limits>
Per Kjellanderee153c92019-10-10 16:43:46 +020016#include <memory>
Erik Språng58ee1872019-06-18 16:20:11 +020017#include <utility>
danilchap47085372017-08-10 06:03:57 -070018
Yves Gerey988cc082018-10-23 12:03:01 +020019#include "absl/types/optional.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "modules/rtp_rtcp/include/rtp_rtcp.h"
21#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
Per Kjellanderee153c92019-10-10 16:43:46 +020022#include "modules/rtp_rtcp/source/rtcp_packet.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020023#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020024#include "rtc_base/checks.h"
Erik Språng58ee1872019-06-18 16:20:11 +020025#include "rtc_base/logging.h"
Steve Anton10542f22019-01-11 09:11:00 -080026#include "rtc_base/time_utils.h"
Stefan Holmere5904162015-03-26 11:11:06 +010027
28namespace webrtc {
danilchap47085372017-08-10 06:03:57 -070029namespace {
30
31constexpr int kRembSendIntervalMs = 200;
32
33} // namespace
Stefan Holmere5904162015-03-26 11:11:06 +010034
Erik Språng5f01bf62019-10-16 17:06:34 +020035PacketRouter::PacketRouter() : PacketRouter(0) {}
36
37PacketRouter::PacketRouter(uint16_t start_transport_seq)
Erik Språng8b7ca4a2018-05-17 13:43:35 +020038 : last_send_module_(nullptr),
39 last_remb_time_ms_(rtc::TimeMillis()),
nisse05843312017-04-18 23:38:35 -070040 last_send_bitrate_bps_(0),
danilchap47085372017-08-10 06:03:57 -070041 bitrate_bps_(0),
42 max_bitrate_bps_(std::numeric_limits<decltype(max_bitrate_bps_)>::max()),
eladalon822ff2b2017-08-01 06:30:28 -070043 active_remb_module_(nullptr),
Erik Språng5f01bf62019-10-16 17:06:34 +020044 transport_seq_(start_transport_seq) {}
Stefan Holmere5904162015-03-26 11:11:06 +010045
46PacketRouter::~PacketRouter() {
Erik Språngfbe84ef2019-10-17 11:17:06 +000047 RTC_DCHECK(rtp_send_modules_.empty());
Danil Chapovaloveb0edd82017-12-14 16:02:31 +010048 RTC_DCHECK(rtcp_feedback_senders_.empty());
eladalon822ff2b2017-08-01 06:30:28 -070049 RTC_DCHECK(sender_remb_candidates_.empty());
50 RTC_DCHECK(receiver_remb_candidates_.empty());
51 RTC_DCHECK(active_remb_module_ == nullptr);
Stefan Holmere5904162015-03-26 11:11:06 +010052}
53
eladalon822ff2b2017-08-01 06:30:28 -070054void PacketRouter::AddSendRtpModule(RtpRtcp* rtp_module, bool remb_candidate) {
stefanbba9dec2016-02-01 04:39:55 -080055 rtc::CritScope cs(&modules_crit_);
Erik Språngfbe84ef2019-10-17 11:17:06 +000056 RTC_DCHECK(std::find(rtp_send_modules_.begin(), rtp_send_modules_.end(),
57 rtp_module) == rtp_send_modules_.end());
58 // Put modules which can use regular payload packets (over rtx) instead of
59 // padding first as it's less of a waste
Erik Språng4208a132019-08-26 08:58:45 +020060 if (rtp_module->SupportsRtxPayloadPadding()) {
Erik Språngfbe84ef2019-10-17 11:17:06 +000061 rtp_send_modules_.push_front(rtp_module);
62 } else {
63 rtp_send_modules_.push_back(rtp_module);
stefan16b02212017-01-27 07:12:16 -080064 }
eladalon822ff2b2017-08-01 06:30:28 -070065
66 if (remb_candidate) {
Danil Chapovaloveb0edd82017-12-14 16:02:31 +010067 AddRembModuleCandidate(rtp_module, /* media_sender = */ true);
eladalon822ff2b2017-08-01 06:30:28 -070068 }
Stefan Holmere5904162015-03-26 11:11:06 +010069}
70
nissefdbfdc92017-03-31 05:44:52 -070071void PacketRouter::RemoveSendRtpModule(RtpRtcp* rtp_module) {
stefanbba9dec2016-02-01 04:39:55 -080072 rtc::CritScope cs(&modules_crit_);
Erik Språngfbe84ef2019-10-17 11:17:06 +000073 rtp_module_cache_map_.clear();
Danil Chapovaloveb0edd82017-12-14 16:02:31 +010074 MaybeRemoveRembModuleCandidate(rtp_module, /* media_sender = */ true);
Erik Språngfbe84ef2019-10-17 11:17:06 +000075 auto it =
76 std::find(rtp_send_modules_.begin(), rtp_send_modules_.end(), rtp_module);
77 RTC_DCHECK(it != rtp_send_modules_.end());
78 rtp_send_modules_.erase(it);
Erik Språng8b7ca4a2018-05-17 13:43:35 +020079 if (last_send_module_ == rtp_module) {
80 last_send_module_ = nullptr;
81 }
nissefdbfdc92017-03-31 05:44:52 -070082}
83
Danil Chapovaloveb0edd82017-12-14 16:02:31 +010084void PacketRouter::AddReceiveRtpModule(RtcpFeedbackSenderInterface* rtcp_sender,
eladalon822ff2b2017-08-01 06:30:28 -070085 bool remb_candidate) {
nissefdbfdc92017-03-31 05:44:52 -070086 rtc::CritScope cs(&modules_crit_);
Danil Chapovaloveb0edd82017-12-14 16:02:31 +010087 RTC_DCHECK(std::find(rtcp_feedback_senders_.begin(),
88 rtcp_feedback_senders_.end(),
89 rtcp_sender) == rtcp_feedback_senders_.end());
eladalon822ff2b2017-08-01 06:30:28 -070090
Danil Chapovaloveb0edd82017-12-14 16:02:31 +010091 rtcp_feedback_senders_.push_back(rtcp_sender);
eladalon822ff2b2017-08-01 06:30:28 -070092
93 if (remb_candidate) {
Danil Chapovaloveb0edd82017-12-14 16:02:31 +010094 AddRembModuleCandidate(rtcp_sender, /* media_sender = */ false);
eladalon822ff2b2017-08-01 06:30:28 -070095 }
nissefdbfdc92017-03-31 05:44:52 -070096}
97
Danil Chapovaloveb0edd82017-12-14 16:02:31 +010098void PacketRouter::RemoveReceiveRtpModule(
99 RtcpFeedbackSenderInterface* rtcp_sender) {
nissefdbfdc92017-03-31 05:44:52 -0700100 rtc::CritScope cs(&modules_crit_);
Danil Chapovaloveb0edd82017-12-14 16:02:31 +0100101 MaybeRemoveRembModuleCandidate(rtcp_sender, /* media_sender = */ false);
102 auto it = std::find(rtcp_feedback_senders_.begin(),
103 rtcp_feedback_senders_.end(), rtcp_sender);
104 RTC_DCHECK(it != rtcp_feedback_senders_.end());
105 rtcp_feedback_senders_.erase(it);
Stefan Holmere5904162015-03-26 11:11:06 +0100106}
107
Erik Språngfbe84ef2019-10-17 11:17:06 +0000108RtpRtcp* PacketRouter::FindRtpModule(uint32_t ssrc) {
109 auto it = rtp_module_cache_map_.find(ssrc);
110 if (it != rtp_module_cache_map_.end()) {
111 if (ssrc == it->second->SSRC() || ssrc == it->second->FlexfecSsrc()) {
112 return it->second;
113 }
114 // This entry is stale due to a changed ssrc - remove it.
115 rtp_module_cache_map_.erase(it);
116 }
117 // Slow path - find and cache matching module
118 for (RtpRtcp* rtp_module : rtp_send_modules_) {
119 if (ssrc == rtp_module->SSRC() || ssrc == rtp_module->FlexfecSsrc()) {
120 rtp_module_cache_map_[ssrc] = rtp_module;
121 return rtp_module;
122 }
123 }
124 return nullptr;
125}
126
Erik Språng58ee1872019-06-18 16:20:11 +0200127void PacketRouter::SendPacket(std::unique_ptr<RtpPacketToSend> packet,
128 const PacedPacketInfo& cluster_info) {
129 rtc::CritScope cs(&modules_crit_);
Erik Språngf6468d22019-07-05 16:53:43 +0200130 // With the new pacer code path, transport sequence numbers are only set here,
131 // on the pacer thread. Therefore we don't need atomics/synchronization.
Erik Språng6cdab462019-07-15 19:40:13 +0200132 if (packet->IsExtensionReserved<TransportSequenceNumber>()) {
Erik Språng4208a132019-08-26 08:58:45 +0200133 packet->SetExtension<TransportSequenceNumber>(AllocateSequenceNumber());
Erik Språngf6468d22019-07-05 16:53:43 +0200134 }
Mirko Bonadei999a72a2019-07-12 17:33:46 +0000135
Erik Språngfbe84ef2019-10-17 11:17:06 +0000136 auto it = rtp_module_cache_map_.find(packet->Ssrc());
137 if (it != rtp_module_cache_map_.end()) {
138 if (TrySendPacket(packet.get(), cluster_info, it->second)) {
139 return;
140 }
141 // Entry is stale, remove it.
142 rtp_module_cache_map_.erase(it);
Mirko Bonadei999a72a2019-07-12 17:33:46 +0000143 }
144
Erik Språngfbe84ef2019-10-17 11:17:06 +0000145 // Slow path, find the correct send module.
146 for (auto* rtp_module : rtp_send_modules_) {
147 if (TrySendPacket(packet.get(), cluster_info, rtp_module)) {
148 return;
149 }
Erik Språng58ee1872019-06-18 16:20:11 +0200150 }
151
Erik Språngfbe84ef2019-10-17 11:17:06 +0000152 RTC_LOG(LS_WARNING) << "Failed to send packet, matching RTP module not found "
153 "or transport error. SSRC = "
154 << packet->Ssrc() << ", sequence number "
155 << packet->SequenceNumber();
Erik Språng58ee1872019-06-18 16:20:11 +0200156}
157
Erik Språngf6468d22019-07-05 16:53:43 +0200158std::vector<std::unique_ptr<RtpPacketToSend>> PacketRouter::GeneratePadding(
159 size_t target_size_bytes) {
Erik Språng478cb462019-06-26 15:49:27 +0200160 rtc::CritScope cs(&modules_crit_);
161 // First try on the last rtp module to have sent media. This increases the
162 // the chance that any payload based padding will be useful as it will be
163 // somewhat distributed over modules according the packet rate, even if it
164 // will be more skewed towards the highest bitrate stream. At the very least
165 // this prevents sending payload padding on a disabled stream where it's
166 // guaranteed not to be useful.
Mirko Bonadei999a72a2019-07-12 17:33:46 +0000167 if (last_send_module_ != nullptr &&
168 last_send_module_->SupportsRtxPayloadPadding()) {
Erik Språngfbe84ef2019-10-17 11:17:06 +0000169 RTC_DCHECK(std::find(rtp_send_modules_.begin(), rtp_send_modules_.end(),
170 last_send_module_) != rtp_send_modules_.end());
171 return last_send_module_->GeneratePadding(target_size_bytes);
172 }
173
174 // Rtp modules are ordered by which stream can most benefit from padding.
175 for (RtpRtcp* rtp_module : rtp_send_modules_) {
176 if (rtp_module->SupportsPadding()) {
177 auto padding_packets = rtp_module->GeneratePadding(target_size_bytes);
178 if (!padding_packets.empty()) {
179 last_send_module_ = rtp_module;
180 }
Mirko Bonadei999a72a2019-07-12 17:33:46 +0000181 return padding_packets;
Erik Språng478cb462019-06-26 15:49:27 +0200182 }
183 }
Erik Språngf6468d22019-07-05 16:53:43 +0200184
Erik Språngfbe84ef2019-10-17 11:17:06 +0000185 return {};
Erik Språng478cb462019-06-26 15:49:27 +0200186}
187
sprang867fb522015-08-03 04:38:41 -0700188void PacketRouter::SetTransportWideSequenceNumber(uint16_t sequence_number) {
Erik Språng5f01bf62019-10-16 17:06:34 +0200189 rtc::CritScope lock(&modules_crit_);
190 transport_seq_ = sequence_number;
sprang867fb522015-08-03 04:38:41 -0700191}
192
193uint16_t PacketRouter::AllocateSequenceNumber() {
Erik Språng5f01bf62019-10-16 17:06:34 +0200194 rtc::CritScope lock(&modules_crit_);
195 transport_seq_ = (transport_seq_ + 1) & 0xFFFF;
196 return transport_seq_;
197}
sprang867fb522015-08-03 04:38:41 -0700198
Erik Språng5f01bf62019-10-16 17:06:34 +0200199uint16_t PacketRouter::CurrentTransportSequenceNumber() const {
200 rtc::CritScope lock(&modules_crit_);
201 return transport_seq_;
sprang867fb522015-08-03 04:38:41 -0700202}
203
nisse05843312017-04-18 23:38:35 -0700204void PacketRouter::OnReceiveBitrateChanged(const std::vector<uint32_t>& ssrcs,
205 uint32_t bitrate_bps) {
nisse05843312017-04-18 23:38:35 -0700206 // % threshold for if we should send a new REMB asap.
Danil Chapovalov1de4b622017-12-13 13:35:10 +0100207 const int64_t kSendThresholdPercent = 97;
208 // TODO(danilchap): Remove receive_bitrate_bps variable and the cast
209 // when OnReceiveBitrateChanged takes bitrate as int64_t.
210 int64_t receive_bitrate_bps = static_cast<int64_t>(bitrate_bps);
nisse05843312017-04-18 23:38:35 -0700211
212 int64_t now_ms = rtc::TimeMillis();
213 {
214 rtc::CritScope lock(&remb_crit_);
215
216 // If we already have an estimate, check if the new total estimate is below
217 // kSendThresholdPercent of the previous estimate.
218 if (last_send_bitrate_bps_ > 0) {
Danil Chapovalov1de4b622017-12-13 13:35:10 +0100219 int64_t new_remb_bitrate_bps =
220 last_send_bitrate_bps_ - bitrate_bps_ + receive_bitrate_bps;
nisse05843312017-04-18 23:38:35 -0700221
222 if (new_remb_bitrate_bps <
223 kSendThresholdPercent * last_send_bitrate_bps_ / 100) {
224 // The new bitrate estimate is less than kSendThresholdPercent % of the
225 // last report. Send a REMB asap.
226 last_remb_time_ms_ = now_ms - kRembSendIntervalMs;
227 }
228 }
Danil Chapovalov1de4b622017-12-13 13:35:10 +0100229 bitrate_bps_ = receive_bitrate_bps;
nisse05843312017-04-18 23:38:35 -0700230
231 if (now_ms - last_remb_time_ms_ < kRembSendIntervalMs) {
232 return;
233 }
234 // NOTE: Updated if we intend to send the data; we might not have
235 // a module to actually send it.
236 last_remb_time_ms_ = now_ms;
Danil Chapovalov1de4b622017-12-13 13:35:10 +0100237 last_send_bitrate_bps_ = receive_bitrate_bps;
danilchap47085372017-08-10 06:03:57 -0700238 // Cap the value to send in remb with configured value.
Danil Chapovalov1de4b622017-12-13 13:35:10 +0100239 receive_bitrate_bps = std::min(receive_bitrate_bps, max_bitrate_bps_);
nisse05843312017-04-18 23:38:35 -0700240 }
Danil Chapovalov1de4b622017-12-13 13:35:10 +0100241 SendRemb(receive_bitrate_bps, ssrcs);
nisse05843312017-04-18 23:38:35 -0700242}
243
Danil Chapovalov1de4b622017-12-13 13:35:10 +0100244void PacketRouter::SetMaxDesiredReceiveBitrate(int64_t bitrate_bps) {
245 RTC_DCHECK_GE(bitrate_bps, 0);
danilchap47085372017-08-10 06:03:57 -0700246 {
247 rtc::CritScope lock(&remb_crit_);
248 max_bitrate_bps_ = bitrate_bps;
249 if (rtc::TimeMillis() - last_remb_time_ms_ < kRembSendIntervalMs &&
250 last_send_bitrate_bps_ > 0 &&
251 last_send_bitrate_bps_ <= max_bitrate_bps_) {
252 // Recent measured bitrate is already below the cap.
253 return;
254 }
255 }
256 SendRemb(bitrate_bps, /*ssrcs=*/{});
257}
258
Danil Chapovalov1de4b622017-12-13 13:35:10 +0100259bool PacketRouter::SendRemb(int64_t bitrate_bps,
nisse05843312017-04-18 23:38:35 -0700260 const std::vector<uint32_t>& ssrcs) {
261 rtc::CritScope lock(&modules_crit_);
eladalon822ff2b2017-08-01 06:30:28 -0700262
263 if (!active_remb_module_) {
nisse05843312017-04-18 23:38:35 -0700264 return false;
eladalon822ff2b2017-08-01 06:30:28 -0700265 }
266
Danil Chapovalov51e21aa2017-10-10 17:46:26 +0200267 // The Add* and Remove* methods above ensure that REMB is disabled on all
268 // other modules, because otherwise, they will send REMB with stale info.
269 active_remb_module_->SetRemb(bitrate_bps, ssrcs);
eladalon822ff2b2017-08-01 06:30:28 -0700270
nisse05843312017-04-18 23:38:35 -0700271 return true;
272}
273
Per Kjellanderee153c92019-10-10 16:43:46 +0200274bool PacketRouter::SendCombinedRtcpPacket(
275 std::vector<std::unique_ptr<rtcp::RtcpPacket>> packets) {
stefanbba9dec2016-02-01 04:39:55 -0800276 rtc::CritScope cs(&modules_crit_);
Per Kjellanderee153c92019-10-10 16:43:46 +0200277
nissefdbfdc92017-03-31 05:44:52 -0700278 // Prefer send modules.
Erik Språngfbe84ef2019-10-17 11:17:06 +0000279 for (auto* rtp_module : rtp_send_modules_) {
Per Kjellanderee153c92019-10-10 16:43:46 +0200280 if (rtp_module->RTCP() == RtcpMode::kOff) {
281 continue;
Erik Språng8b7ca4a2018-05-17 13:43:35 +0200282 }
Per Kjellanderee153c92019-10-10 16:43:46 +0200283 rtp_module->SendCombinedRtcpPacket(std::move(packets));
284 return true;
nissefdbfdc92017-03-31 05:44:52 -0700285 }
sprang233bd872015-09-08 13:25:16 -0700286
Per Kjellanderee153c92019-10-10 16:43:46 +0200287 if (rtcp_feedback_senders_.empty()) {
288 return false;
Per Kjellander52f7ae72019-09-10 19:28:06 +0200289 }
Per Kjellanderee153c92019-10-10 16:43:46 +0200290 auto* rtcp_sender = rtcp_feedback_senders_[0];
291 rtcp_sender->SendCombinedRtcpPacket(std::move(packets));
292 return true;
Per Kjellander52f7ae72019-09-10 19:28:06 +0200293}
294
Danil Chapovaloveb0edd82017-12-14 16:02:31 +0100295void PacketRouter::AddRembModuleCandidate(
296 RtcpFeedbackSenderInterface* candidate_module,
297 bool media_sender) {
eladalon822ff2b2017-08-01 06:30:28 -0700298 RTC_DCHECK(candidate_module);
Danil Chapovaloveb0edd82017-12-14 16:02:31 +0100299 std::vector<RtcpFeedbackSenderInterface*>& candidates =
300 media_sender ? sender_remb_candidates_ : receiver_remb_candidates_;
eladalon822ff2b2017-08-01 06:30:28 -0700301 RTC_DCHECK(std::find(candidates.cbegin(), candidates.cend(),
302 candidate_module) == candidates.cend());
303 candidates.push_back(candidate_module);
304 DetermineActiveRembModule();
305}
306
Danil Chapovaloveb0edd82017-12-14 16:02:31 +0100307void PacketRouter::MaybeRemoveRembModuleCandidate(
308 RtcpFeedbackSenderInterface* candidate_module,
309 bool media_sender) {
eladalon822ff2b2017-08-01 06:30:28 -0700310 RTC_DCHECK(candidate_module);
Danil Chapovaloveb0edd82017-12-14 16:02:31 +0100311 std::vector<RtcpFeedbackSenderInterface*>& candidates =
312 media_sender ? sender_remb_candidates_ : receiver_remb_candidates_;
eladalon822ff2b2017-08-01 06:30:28 -0700313 auto it = std::find(candidates.begin(), candidates.end(), candidate_module);
314
315 if (it == candidates.end()) {
316 return; // Function called due to removal of non-REMB-candidate module.
317 }
318
319 if (*it == active_remb_module_) {
320 UnsetActiveRembModule();
321 }
322 candidates.erase(it);
323 DetermineActiveRembModule();
324}
325
326void PacketRouter::UnsetActiveRembModule() {
327 RTC_CHECK(active_remb_module_);
Danil Chapovalov51e21aa2017-10-10 17:46:26 +0200328 active_remb_module_->UnsetRemb();
eladalon822ff2b2017-08-01 06:30:28 -0700329 active_remb_module_ = nullptr;
330}
331
332void PacketRouter::DetermineActiveRembModule() {
333 // Sender modules take precedence over receiver modules, because SRs (sender
334 // reports) are sent more frequently than RR (receiver reports).
335 // When adding the first sender module, we should change the active REMB
336 // module to be that. Otherwise, we remain with the current active module.
337
Danil Chapovaloveb0edd82017-12-14 16:02:31 +0100338 RtcpFeedbackSenderInterface* new_active_remb_module;
eladalon822ff2b2017-08-01 06:30:28 -0700339
340 if (!sender_remb_candidates_.empty()) {
Danil Chapovalov51e21aa2017-10-10 17:46:26 +0200341 new_active_remb_module = sender_remb_candidates_.front();
eladalon822ff2b2017-08-01 06:30:28 -0700342 } else if (!receiver_remb_candidates_.empty()) {
Danil Chapovalov51e21aa2017-10-10 17:46:26 +0200343 new_active_remb_module = receiver_remb_candidates_.front();
eladalon822ff2b2017-08-01 06:30:28 -0700344 } else {
Danil Chapovalov51e21aa2017-10-10 17:46:26 +0200345 new_active_remb_module = nullptr;
eladalon822ff2b2017-08-01 06:30:28 -0700346 }
347
Danil Chapovalov51e21aa2017-10-10 17:46:26 +0200348 if (new_active_remb_module != active_remb_module_ && active_remb_module_) {
349 UnsetActiveRembModule();
eladalon822ff2b2017-08-01 06:30:28 -0700350 }
351
Danil Chapovalov51e21aa2017-10-10 17:46:26 +0200352 active_remb_module_ = new_active_remb_module;
eladalon822ff2b2017-08-01 06:30:28 -0700353}
354
Erik Språngfbe84ef2019-10-17 11:17:06 +0000355bool PacketRouter::TrySendPacket(RtpPacketToSend* packet,
356 const PacedPacketInfo& cluster_info,
357 RtpRtcp* rtp_module) {
358 uint32_t ssrc = packet->Ssrc();
359 if (rtp_module->TrySendPacket(packet, cluster_info)) {
360 // Sending succeeded, make sure this SSRC mapping for future use.
361 rtp_module_cache_map_[ssrc] = rtp_module;
362
363 if (rtp_module->SupportsRtxPayloadPadding()) {
364 // This is now the last module to send media, and has the desired
365 // properties needed for payload based padding. Cache it for later use.
366 last_send_module_ = rtp_module;
367 }
368
369 return true;
370 }
371 return false;
372}
373
Stefan Holmere5904162015-03-26 11:11:06 +0100374} // namespace webrtc