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