1 /*
2 * Copyright (c) 2017 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 "call/rtp_demuxer.h"
12
13 #include "call/rtp_packet_sink_interface.h"
14 #include "modules/rtp_rtcp/source/rtp_header_extensions.h"
15 #include "modules/rtp_rtcp/source/rtp_packet_received.h"
16 #include "rtc_base/checks.h"
17 #include "rtc_base/logging.h"
18 #include "rtc_base/strings/string_builder.h"
19
20 namespace webrtc {
21 namespace {
22
23 template <typename Container, typename Value>
RemoveFromMultimapByValue(Container * multimap,const Value & value)24 size_t RemoveFromMultimapByValue(Container* multimap, const Value& value) {
25 size_t count = 0;
26 for (auto it = multimap->begin(); it != multimap->end();) {
27 if (it->second == value) {
28 it = multimap->erase(it);
29 ++count;
30 } else {
31 ++it;
32 }
33 }
34 return count;
35 }
36
37 template <typename Map, typename Value>
RemoveFromMapByValue(Map * map,const Value & value)38 size_t RemoveFromMapByValue(Map* map, const Value& value) {
39 size_t count = 0;
40 for (auto it = map->begin(); it != map->end();) {
41 if (it->second == value) {
42 it = map->erase(it);
43 ++count;
44 } else {
45 ++it;
46 }
47 }
48 return count;
49 }
50
51 } // namespace
52
53 RtpDemuxerCriteria::RtpDemuxerCriteria() = default;
54 RtpDemuxerCriteria::~RtpDemuxerCriteria() = default;
55
ToString() const56 std::string RtpDemuxerCriteria::ToString() const {
57 rtc::StringBuilder sb;
58 sb << "{mid: " << (mid.empty() ? "<empty>" : mid)
59 << ", rsid: " << (rsid.empty() ? "<empty>" : rsid) << ", ssrcs: [";
60
61 for (auto ssrc : ssrcs) {
62 sb << ssrc << ", ";
63 }
64
65 sb << "], payload_types = [";
66
67 for (auto pt : payload_types) {
68 sb << pt << ", ";
69 }
70
71 sb << "]}";
72 return sb.Release();
73 }
74
75 // static
DescribePacket(const RtpPacketReceived & packet)76 std::string RtpDemuxer::DescribePacket(const RtpPacketReceived& packet) {
77 rtc::StringBuilder sb;
78 sb << "PT=" << packet.PayloadType() << " SSRC=" << packet.Ssrc();
79 std::string mid;
80 if (packet.GetExtension<RtpMid>(&mid)) {
81 sb << " MID=" << mid;
82 }
83 std::string rsid;
84 if (packet.GetExtension<RtpStreamId>(&rsid)) {
85 sb << " RSID=" << rsid;
86 }
87 std::string rrsid;
88 if (packet.GetExtension<RepairedRtpStreamId>(&rrsid)) {
89 sb << " RRSID=" << rrsid;
90 }
91 return sb.Release();
92 }
93
94 RtpDemuxer::RtpDemuxer() = default;
95
~RtpDemuxer()96 RtpDemuxer::~RtpDemuxer() {
97 RTC_DCHECK(sink_by_mid_.empty());
98 RTC_DCHECK(sink_by_ssrc_.empty());
99 RTC_DCHECK(sinks_by_pt_.empty());
100 RTC_DCHECK(sink_by_mid_and_rsid_.empty());
101 RTC_DCHECK(sink_by_rsid_.empty());
102 }
103
AddSink(const RtpDemuxerCriteria & criteria,RtpPacketSinkInterface * sink)104 bool RtpDemuxer::AddSink(const RtpDemuxerCriteria& criteria,
105 RtpPacketSinkInterface* sink) {
106 RTC_DCHECK(!criteria.payload_types.empty() || !criteria.ssrcs.empty() ||
107 !criteria.mid.empty() || !criteria.rsid.empty());
108 RTC_DCHECK(criteria.mid.empty() || IsLegalMidName(criteria.mid));
109 RTC_DCHECK(criteria.rsid.empty() || IsLegalRsidName(criteria.rsid));
110 RTC_DCHECK(sink);
111
112 // We return false instead of DCHECKing for logical conflicts with the new
113 // criteria because new sinks are created according to user-specified SDP and
114 // we do not want to crash due to a data validation error.
115 if (CriteriaWouldConflict(criteria)) {
116 RTC_LOG(LS_ERROR) << "Unable to add sink = " << sink
117 << " due conflicting criteria " << criteria.ToString();
118 return false;
119 }
120
121 if (!criteria.mid.empty()) {
122 if (criteria.rsid.empty()) {
123 sink_by_mid_.emplace(criteria.mid, sink);
124 } else {
125 sink_by_mid_and_rsid_.emplace(std::make_pair(criteria.mid, criteria.rsid),
126 sink);
127 }
128 } else {
129 if (!criteria.rsid.empty()) {
130 sink_by_rsid_.emplace(criteria.rsid, sink);
131 }
132 }
133
134 for (uint32_t ssrc : criteria.ssrcs) {
135 sink_by_ssrc_.emplace(ssrc, sink);
136 }
137
138 for (uint8_t payload_type : criteria.payload_types) {
139 sinks_by_pt_.emplace(payload_type, sink);
140 }
141
142 RefreshKnownMids();
143
144 RTC_LOG(LS_INFO) << "Added sink = " << sink << " for criteria "
145 << criteria.ToString();
146
147 return true;
148 }
149
CriteriaWouldConflict(const RtpDemuxerCriteria & criteria) const150 bool RtpDemuxer::CriteriaWouldConflict(
151 const RtpDemuxerCriteria& criteria) const {
152 if (!criteria.mid.empty()) {
153 if (criteria.rsid.empty()) {
154 // If the MID is in the known_mids_ set, then there is already a sink
155 // added for this MID directly, or there is a sink already added with a
156 // MID, RSID pair for our MID and some RSID.
157 // Adding this criteria would cause one of these rules to be shadowed, so
158 // reject this new criteria.
159 if (known_mids_.find(criteria.mid) != known_mids_.end()) {
160 RTC_LOG(LS_INFO) << criteria.ToString()
161 << " would conflict with known mid";
162 return true;
163 }
164 } else {
165 // If the exact rule already exists, then reject this duplicate.
166 const auto sink_by_mid_and_rsid = sink_by_mid_and_rsid_.find(
167 std::make_pair(criteria.mid, criteria.rsid));
168 if (sink_by_mid_and_rsid != sink_by_mid_and_rsid_.end()) {
169 RTC_LOG(LS_INFO) << criteria.ToString()
170 << " would conflict with existing sink = "
171 << sink_by_mid_and_rsid->second
172 << " by mid+rsid binding";
173 return true;
174 }
175 // If there is already a sink registered for the bare MID, then this
176 // criteria will never receive any packets because they will just be
177 // directed to that MID sink, so reject this new criteria.
178 const auto sink_by_mid = sink_by_mid_.find(criteria.mid);
179 if (sink_by_mid != sink_by_mid_.end()) {
180 RTC_LOG(LS_INFO) << criteria.ToString()
181 << " would conflict with existing sink = "
182 << sink_by_mid->second << " by mid binding";
183 return true;
184 }
185 }
186 }
187
188 for (uint32_t ssrc : criteria.ssrcs) {
189 const auto sink_by_ssrc = sink_by_ssrc_.find(ssrc);
190 if (sink_by_ssrc != sink_by_ssrc_.end()) {
191 RTC_LOG(LS_INFO) << criteria.ToString()
192 << " would conflict with existing sink = "
193 << sink_by_ssrc->second << " binding by SSRC=" << ssrc;
194 return true;
195 }
196 }
197
198 // TODO(steveanton): May also sanity check payload types.
199
200 return false;
201 }
202
RefreshKnownMids()203 void RtpDemuxer::RefreshKnownMids() {
204 known_mids_.clear();
205
206 for (auto const& item : sink_by_mid_) {
207 const std::string& mid = item.first;
208 known_mids_.insert(mid);
209 }
210
211 for (auto const& item : sink_by_mid_and_rsid_) {
212 const std::string& mid = item.first.first;
213 known_mids_.insert(mid);
214 }
215 }
216
AddSink(uint32_t ssrc,RtpPacketSinkInterface * sink)217 bool RtpDemuxer::AddSink(uint32_t ssrc, RtpPacketSinkInterface* sink) {
218 RtpDemuxerCriteria criteria;
219 criteria.ssrcs.insert(ssrc);
220 return AddSink(criteria, sink);
221 }
222
AddSink(const std::string & rsid,RtpPacketSinkInterface * sink)223 void RtpDemuxer::AddSink(const std::string& rsid,
224 RtpPacketSinkInterface* sink) {
225 RtpDemuxerCriteria criteria;
226 criteria.rsid = rsid;
227 AddSink(criteria, sink);
228 }
229
RemoveSink(const RtpPacketSinkInterface * sink)230 bool RtpDemuxer::RemoveSink(const RtpPacketSinkInterface* sink) {
231 RTC_DCHECK(sink);
232 size_t num_removed = RemoveFromMapByValue(&sink_by_mid_, sink) +
233 RemoveFromMapByValue(&sink_by_ssrc_, sink) +
234 RemoveFromMultimapByValue(&sinks_by_pt_, sink) +
235 RemoveFromMapByValue(&sink_by_mid_and_rsid_, sink) +
236 RemoveFromMapByValue(&sink_by_rsid_, sink);
237 RefreshKnownMids();
238 bool removed = num_removed > 0;
239 if (removed) {
240 RTC_LOG(LS_INFO) << "Removed sink = " << sink << " bindings";
241 }
242 return removed;
243 }
244
OnRtpPacket(const RtpPacketReceived & packet)245 bool RtpDemuxer::OnRtpPacket(const RtpPacketReceived& packet) {
246 RtpPacketSinkInterface* sink = ResolveSink(packet);
247 if (sink != nullptr) {
248 sink->OnRtpPacket(packet);
249 return true;
250 }
251 return false;
252 }
253
ResolveSink(const RtpPacketReceived & packet)254 RtpPacketSinkInterface* RtpDemuxer::ResolveSink(
255 const RtpPacketReceived& packet) {
256 // See the BUNDLE spec for high level reference to this algorithm:
257 // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-38#section-10.2
258
259 // RSID and RRID are routed to the same sinks. If an RSID is specified on a
260 // repair packet, it should be ignored and the RRID should be used.
261 std::string packet_mid, packet_rsid;
262 bool has_mid = use_mid_ && packet.GetExtension<RtpMid>(&packet_mid);
263 bool has_rsid = packet.GetExtension<RepairedRtpStreamId>(&packet_rsid);
264 if (!has_rsid) {
265 has_rsid = packet.GetExtension<RtpStreamId>(&packet_rsid);
266 }
267 uint32_t ssrc = packet.Ssrc();
268
269 // The BUNDLE spec says to drop any packets with unknown MIDs, even if the
270 // SSRC is known/latched.
271 if (has_mid && known_mids_.find(packet_mid) == known_mids_.end()) {
272 return nullptr;
273 }
274
275 // Cache information we learn about SSRCs and IDs. We need to do this even if
276 // there isn't a rule/sink yet because we might add an MID/RSID rule after
277 // learning an MID/RSID<->SSRC association.
278
279 std::string* mid = nullptr;
280 if (has_mid) {
281 mid_by_ssrc_[ssrc] = packet_mid;
282 mid = &packet_mid;
283 } else {
284 // If the packet does not include a MID header extension, check if there is
285 // a latched MID for the SSRC.
286 const auto it = mid_by_ssrc_.find(ssrc);
287 if (it != mid_by_ssrc_.end()) {
288 mid = &it->second;
289 }
290 }
291
292 std::string* rsid = nullptr;
293 if (has_rsid) {
294 rsid_by_ssrc_[ssrc] = packet_rsid;
295 rsid = &packet_rsid;
296 } else {
297 // If the packet does not include an RRID/RSID header extension, check if
298 // there is a latched RSID for the SSRC.
299 const auto it = rsid_by_ssrc_.find(ssrc);
300 if (it != rsid_by_ssrc_.end()) {
301 rsid = &it->second;
302 }
303 }
304
305 // If MID and/or RSID is specified, prioritize that for demuxing the packet.
306 // The motivation behind the BUNDLE algorithm is that we trust these are used
307 // deliberately by senders and are more likely to be correct than SSRC/payload
308 // type which are included with every packet.
309 // TODO(steveanton): According to the BUNDLE spec, new SSRC mappings are only
310 // accepted if the packet's extended sequence number is
311 // greater than that of the last SSRC mapping update.
312 // https://tools.ietf.org/html/rfc7941#section-4.2.6
313 if (mid != nullptr) {
314 RtpPacketSinkInterface* sink_by_mid = ResolveSinkByMid(*mid, ssrc);
315 if (sink_by_mid != nullptr) {
316 return sink_by_mid;
317 }
318
319 // RSID is scoped to a given MID if both are included.
320 if (rsid != nullptr) {
321 RtpPacketSinkInterface* sink_by_mid_rsid =
322 ResolveSinkByMidRsid(*mid, *rsid, ssrc);
323 if (sink_by_mid_rsid != nullptr) {
324 return sink_by_mid_rsid;
325 }
326 }
327
328 // At this point, there is at least one sink added for this MID and an RSID
329 // but either the packet does not have an RSID or it is for a different
330 // RSID. This falls outside the BUNDLE spec so drop the packet.
331 return nullptr;
332 }
333
334 // RSID can be used without MID as long as they are unique.
335 if (rsid != nullptr) {
336 RtpPacketSinkInterface* sink_by_rsid = ResolveSinkByRsid(*rsid, ssrc);
337 if (sink_by_rsid != nullptr) {
338 return sink_by_rsid;
339 }
340 }
341
342 // We trust signaled SSRC more than payload type which is likely to conflict
343 // between streams.
344 const auto ssrc_sink_it = sink_by_ssrc_.find(ssrc);
345 if (ssrc_sink_it != sink_by_ssrc_.end()) {
346 return ssrc_sink_it->second;
347 }
348
349 // Legacy senders will only signal payload type, support that as last resort.
350 return ResolveSinkByPayloadType(packet.PayloadType(), ssrc);
351 }
352
ResolveSinkByMid(const std::string & mid,uint32_t ssrc)353 RtpPacketSinkInterface* RtpDemuxer::ResolveSinkByMid(const std::string& mid,
354 uint32_t ssrc) {
355 const auto it = sink_by_mid_.find(mid);
356 if (it != sink_by_mid_.end()) {
357 RtpPacketSinkInterface* sink = it->second;
358 AddSsrcSinkBinding(ssrc, sink);
359 return sink;
360 }
361 return nullptr;
362 }
363
ResolveSinkByMidRsid(const std::string & mid,const std::string & rsid,uint32_t ssrc)364 RtpPacketSinkInterface* RtpDemuxer::ResolveSinkByMidRsid(
365 const std::string& mid,
366 const std::string& rsid,
367 uint32_t ssrc) {
368 const auto it = sink_by_mid_and_rsid_.find(std::make_pair(mid, rsid));
369 if (it != sink_by_mid_and_rsid_.end()) {
370 RtpPacketSinkInterface* sink = it->second;
371 AddSsrcSinkBinding(ssrc, sink);
372 return sink;
373 }
374 return nullptr;
375 }
376
ResolveSinkByRsid(const std::string & rsid,uint32_t ssrc)377 RtpPacketSinkInterface* RtpDemuxer::ResolveSinkByRsid(const std::string& rsid,
378 uint32_t ssrc) {
379 const auto it = sink_by_rsid_.find(rsid);
380 if (it != sink_by_rsid_.end()) {
381 RtpPacketSinkInterface* sink = it->second;
382 AddSsrcSinkBinding(ssrc, sink);
383 return sink;
384 }
385 return nullptr;
386 }
387
ResolveSinkByPayloadType(uint8_t payload_type,uint32_t ssrc)388 RtpPacketSinkInterface* RtpDemuxer::ResolveSinkByPayloadType(
389 uint8_t payload_type,
390 uint32_t ssrc) {
391 const auto range = sinks_by_pt_.equal_range(payload_type);
392 if (range.first != range.second) {
393 auto it = range.first;
394 const auto end = range.second;
395 if (std::next(it) == end) {
396 RtpPacketSinkInterface* sink = it->second;
397 AddSsrcSinkBinding(ssrc, sink);
398 return sink;
399 }
400 }
401 return nullptr;
402 }
403
AddSsrcSinkBinding(uint32_t ssrc,RtpPacketSinkInterface * sink)404 void RtpDemuxer::AddSsrcSinkBinding(uint32_t ssrc,
405 RtpPacketSinkInterface* sink) {
406 if (sink_by_ssrc_.size() >= kMaxSsrcBindings) {
407 RTC_LOG(LS_WARNING) << "New SSRC=" << ssrc
408 << " sink binding ignored; limit of" << kMaxSsrcBindings
409 << " bindings has been reached.";
410 return;
411 }
412
413 auto result = sink_by_ssrc_.emplace(ssrc, sink);
414 auto it = result.first;
415 bool inserted = result.second;
416 if (inserted) {
417 RTC_LOG(LS_INFO) << "Added sink = " << sink
418 << " binding with SSRC=" << ssrc;
419 } else if (it->second != sink) {
420 RTC_LOG(LS_INFO) << "Updated sink = " << sink
421 << " binding with SSRC=" << ssrc;
422 it->second = sink;
423 }
424 }
425
426 } // namespace webrtc
427