1 /*
2 * Copyright (c) 2019 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 "test/pc/e2e/sdp/sdp_changer.h"
12
13 #include <utility>
14
15 #include "absl/memory/memory.h"
16 #include "api/jsep_session_description.h"
17 #include "media/base/media_constants.h"
18 #include "p2p/base/p2p_constants.h"
19 #include "pc/sdp_utils.h"
20 #include "rtc_base/strings/string_builder.h"
21
22 namespace webrtc {
23 namespace webrtc_pc_e2e {
24 namespace {
25
26 using VideoCodecConfig = PeerConnectionE2EQualityTestFixture::VideoCodecConfig;
27
CodecRequiredParamsToString(const std::map<std::string,std::string> & codec_required_params)28 std::string CodecRequiredParamsToString(
29 const std::map<std::string, std::string>& codec_required_params) {
30 rtc::StringBuilder out;
31 for (const auto& entry : codec_required_params) {
32 out << entry.first << "=" << entry.second << ";";
33 }
34 return out.str();
35 }
36
37 } // namespace
38
FilterVideoCodecCapabilities(rtc::ArrayView<const VideoCodecConfig> video_codecs,bool use_rtx,bool use_ulpfec,bool use_flexfec,rtc::ArrayView<const RtpCodecCapability> supported_codecs)39 std::vector<RtpCodecCapability> FilterVideoCodecCapabilities(
40 rtc::ArrayView<const VideoCodecConfig> video_codecs,
41 bool use_rtx,
42 bool use_ulpfec,
43 bool use_flexfec,
44 rtc::ArrayView<const RtpCodecCapability> supported_codecs) {
45 RTC_LOG(INFO) << "Peer connection support these codecs:";
46 for (const auto& codec : supported_codecs) {
47 RTC_LOG(INFO) << "Codec: " << codec.name;
48 if (!codec.parameters.empty()) {
49 RTC_LOG(INFO) << "Params:";
50 for (const auto& param : codec.parameters) {
51 RTC_LOG(INFO) << " " << param.first << "=" << param.second;
52 }
53 }
54 }
55 std::vector<RtpCodecCapability> output_codecs;
56 // Find requested codecs among supported and add them to output in the order
57 // they were requested.
58 for (auto& codec_request : video_codecs) {
59 size_t size_before = output_codecs.size();
60 for (auto& codec : supported_codecs) {
61 if (codec.name != codec_request.name) {
62 continue;
63 }
64 bool parameters_matched = true;
65 for (const auto& item : codec_request.required_params) {
66 auto it = codec.parameters.find(item.first);
67 if (it == codec.parameters.end()) {
68 parameters_matched = false;
69 break;
70 }
71 if (item.second != it->second) {
72 parameters_matched = false;
73 break;
74 }
75 }
76 if (parameters_matched) {
77 output_codecs.push_back(codec);
78 }
79 }
80 RTC_CHECK_GT(output_codecs.size(), size_before)
81 << "Codec with name=" << codec_request.name << " and params {"
82 << CodecRequiredParamsToString(codec_request.required_params)
83 << "} is unsupported for this peer connection";
84 }
85
86 // Add required FEC and RTX codecs to output.
87 for (auto& codec : supported_codecs) {
88 if (codec.name == cricket::kRtxCodecName && use_rtx) {
89 output_codecs.push_back(codec);
90 } else if (codec.name == cricket::kFlexfecCodecName && use_flexfec) {
91 output_codecs.push_back(codec);
92 } else if ((codec.name == cricket::kRedCodecName ||
93 codec.name == cricket::kUlpfecCodecName) &&
94 use_ulpfec) {
95 // Red and ulpfec should be enabled or disabled together.
96 output_codecs.push_back(codec);
97 }
98 }
99 return output_codecs;
100 }
101
102 // If offer has no simulcast video sections - do nothing.
103 //
104 // If offer has simulcast video sections - for each section creates
105 // SimulcastSectionInfo and put it into |context_|.
FillSimulcastContext(SessionDescriptionInterface * offer)106 void SignalingInterceptor::FillSimulcastContext(
107 SessionDescriptionInterface* offer) {
108 for (auto& content : offer->description()->contents()) {
109 cricket::MediaContentDescription* media_desc = content.media_description();
110 if (media_desc->type() != cricket::MediaType::MEDIA_TYPE_VIDEO) {
111 continue;
112 }
113 if (media_desc->HasSimulcast()) {
114 // We support only single stream simulcast sections with rids.
115 RTC_CHECK_EQ(media_desc->mutable_streams().size(), 1);
116 RTC_CHECK(media_desc->mutable_streams()[0].has_rids());
117
118 // Create SimulcastSectionInfo for this video section.
119 SimulcastSectionInfo info(content.mid(), content.type,
120 media_desc->mutable_streams()[0].rids());
121
122 // Set new rids basing on created SimulcastSectionInfo.
123 std::vector<cricket::RidDescription> rids;
124 cricket::SimulcastDescription simulcast_description;
125 for (std::string& rid : info.rids) {
126 rids.emplace_back(rid, cricket::RidDirection::kSend);
127 simulcast_description.send_layers().AddLayer(
128 cricket::SimulcastLayer(rid, false));
129 }
130 media_desc->mutable_streams()[0].set_rids(rids);
131 media_desc->set_simulcast_description(simulcast_description);
132
133 info.simulcast_description = media_desc->simulcast_description();
134 for (const auto& extension : media_desc->rtp_header_extensions()) {
135 if (extension.uri == RtpExtension::kMidUri) {
136 info.mid_extension = extension;
137 } else if (extension.uri == RtpExtension::kRidUri) {
138 info.rid_extension = extension;
139 } else if (extension.uri == RtpExtension::kRepairedRidUri) {
140 info.rrid_extension = extension;
141 }
142 }
143 RTC_CHECK_NE(info.rid_extension.id, 0);
144 RTC_CHECK_NE(info.mid_extension.id, 0);
145 bool transport_description_found = false;
146 for (auto& transport_info : offer->description()->transport_infos()) {
147 if (transport_info.content_name == info.mid) {
148 info.transport_description = transport_info.description;
149 transport_description_found = true;
150 break;
151 }
152 }
153 RTC_CHECK(transport_description_found);
154
155 context_.AddSimulcastInfo(info);
156 }
157 }
158 }
159
PatchOffer(std::unique_ptr<SessionDescriptionInterface> offer)160 LocalAndRemoteSdp SignalingInterceptor::PatchOffer(
161 std::unique_ptr<SessionDescriptionInterface> offer) {
162 for (auto& content : offer->description()->contents()) {
163 context_.mids_order.push_back(content.mid());
164 cricket::MediaContentDescription* media_desc = content.media_description();
165 if (media_desc->type() != cricket::MediaType::MEDIA_TYPE_VIDEO) {
166 continue;
167 }
168 if (content.media_description()->streams().size() == 0) {
169 // It means that this media section describes receive only media section
170 // in SDP.
171 RTC_CHECK_EQ(content.media_description()->direction(),
172 RtpTransceiverDirection::kRecvOnly);
173 continue;
174 }
175 media_desc->set_conference_mode(params_.use_conference_mode);
176 }
177
178 if (params_.stream_label_to_simulcast_streams_count.size() > 0) {
179 // Because simulcast enabled |params_.video_codecs| has only 1 element.
180 if (params_.video_codecs[0].name == cricket::kVp8CodecName) {
181 return PatchVp8Offer(std::move(offer));
182 }
183
184 if (params_.video_codecs[0].name == cricket::kVp9CodecName) {
185 return PatchVp9Offer(std::move(offer));
186 }
187 }
188
189 auto offer_for_remote = CloneSessionDescription(offer.get());
190 return LocalAndRemoteSdp(std::move(offer), std::move(offer_for_remote));
191 }
192
PatchVp8Offer(std::unique_ptr<SessionDescriptionInterface> offer)193 LocalAndRemoteSdp SignalingInterceptor::PatchVp8Offer(
194 std::unique_ptr<SessionDescriptionInterface> offer) {
195 FillSimulcastContext(offer.get());
196 if (!context_.HasSimulcast()) {
197 auto offer_for_remote = CloneSessionDescription(offer.get());
198 return LocalAndRemoteSdp(std::move(offer), std::move(offer_for_remote));
199 }
200
201 // Clone original offer description. We mustn't access original offer after
202 // this point.
203 std::unique_ptr<cricket::SessionDescription> desc =
204 offer->description()->Clone();
205
206 for (auto& info : context_.simulcast_infos) {
207 // For each simulcast section we have to perform:
208 // 1. Swap MID and RID header extensions
209 // 2. Remove RIDs from streams and remove SimulcastDescription
210 // 3. For each RID duplicate media section
211 cricket::ContentInfo* simulcast_content = desc->GetContentByName(info.mid);
212
213 // Now we need to prepare common prototype for "m=video" sections, in which
214 // single simulcast section will be converted. Do it before removing content
215 // because otherwise description will be deleted.
216 std::unique_ptr<cricket::MediaContentDescription> prototype_media_desc =
217 simulcast_content->media_description()->Clone();
218
219 // Remove simulcast video section from offer.
220 RTC_CHECK(desc->RemoveContentByName(simulcast_content->mid()));
221 // Clear |simulcast_content|, because now it is pointing to removed object.
222 simulcast_content = nullptr;
223
224 // Swap mid and rid extensions, so remote peer will understand rid as mid.
225 // Also remove rid extension.
226 std::vector<webrtc::RtpExtension> extensions =
227 prototype_media_desc->rtp_header_extensions();
228 for (auto ext_it = extensions.begin(); ext_it != extensions.end();) {
229 if (ext_it->uri == RtpExtension::kRidUri) {
230 // We don't need rid extension for remote peer.
231 ext_it = extensions.erase(ext_it);
232 continue;
233 }
234 if (ext_it->uri == RtpExtension::kRepairedRidUri) {
235 // We don't support RTX in simulcast.
236 ext_it = extensions.erase(ext_it);
237 continue;
238 }
239 if (ext_it->uri == RtpExtension::kMidUri) {
240 ext_it->id = info.rid_extension.id;
241 }
242 ++ext_it;
243 }
244
245 prototype_media_desc->ClearRtpHeaderExtensions();
246 prototype_media_desc->set_rtp_header_extensions(extensions);
247
248 // We support only single stream inside video section with simulcast
249 RTC_CHECK_EQ(prototype_media_desc->mutable_streams().size(), 1);
250 // This stream must have rids.
251 RTC_CHECK(prototype_media_desc->mutable_streams()[0].has_rids());
252
253 // Remove rids and simulcast description from media description.
254 prototype_media_desc->mutable_streams()[0].set_rids({});
255 prototype_media_desc->set_simulcast_description(
256 cricket::SimulcastDescription());
257
258 // For each rid add separate video section.
259 for (std::string& rid : info.rids) {
260 desc->AddContent(rid, info.media_protocol_type,
261 prototype_media_desc->Clone());
262 }
263 }
264
265 // Now we need to add bundle line to have all media bundled together.
266 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
267 for (auto& content : desc->contents()) {
268 bundle_group.AddContentName(content.mid());
269 }
270 if (desc->HasGroup(cricket::GROUP_TYPE_BUNDLE)) {
271 desc->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
272 }
273 desc->AddGroup(bundle_group);
274
275 // Update transport_infos to add TransportInfo for each new media section.
276 std::vector<cricket::TransportInfo> transport_infos = desc->transport_infos();
277 transport_infos.erase(std::remove_if(
278 transport_infos.begin(), transport_infos.end(),
279 [this](const cricket::TransportInfo& ti) {
280 // Remove transport infos that correspond to simulcast video sections.
281 return context_.simulcast_infos_by_mid.find(ti.content_name) !=
282 context_.simulcast_infos_by_mid.end();
283 }));
284 for (auto& info : context_.simulcast_infos) {
285 for (auto& rid : info.rids) {
286 transport_infos.emplace_back(rid, info.transport_description);
287 }
288 }
289 desc->set_transport_infos(transport_infos);
290
291 // Create patched offer.
292 auto patched_offer =
293 std::make_unique<JsepSessionDescription>(SdpType::kOffer);
294 patched_offer->Initialize(std::move(desc), offer->session_id(),
295 offer->session_version());
296 return LocalAndRemoteSdp(std::move(offer), std::move(patched_offer));
297 }
298
PatchVp9Offer(std::unique_ptr<SessionDescriptionInterface> offer)299 LocalAndRemoteSdp SignalingInterceptor::PatchVp9Offer(
300 std::unique_ptr<SessionDescriptionInterface> offer) {
301 rtc::UniqueRandomIdGenerator ssrcs_generator;
302 for (auto& content : offer->description()->contents()) {
303 for (auto& stream : content.media_description()->streams()) {
304 for (auto& ssrc : stream.ssrcs) {
305 ssrcs_generator.AddKnownId(ssrc);
306 }
307 }
308 }
309
310 for (auto& content : offer->description()->contents()) {
311 if (content.media_description()->type() !=
312 cricket::MediaType::MEDIA_TYPE_VIDEO) {
313 // We are interested in only video tracks
314 continue;
315 }
316 if (content.media_description()->direction() ==
317 RtpTransceiverDirection::kRecvOnly) {
318 // If direction is receive only, then there is no media in this track from
319 // sender side, so we needn't to do anything with this track.
320 continue;
321 }
322 RTC_CHECK_EQ(content.media_description()->streams().size(), 1);
323 cricket::StreamParams& stream =
324 content.media_description()->mutable_streams()[0];
325 RTC_CHECK_EQ(stream.stream_ids().size(), 2)
326 << "Expected 2 stream ids in video stream: 1st - sync_group, 2nd - "
327 "unique label";
328 std::string stream_label = stream.stream_ids()[1];
329
330 auto it =
331 params_.stream_label_to_simulcast_streams_count.find(stream_label);
332 if (it == params_.stream_label_to_simulcast_streams_count.end()) {
333 continue;
334 }
335 int svc_layers_count = it->second;
336
337 RTC_CHECK(stream.has_ssrc_groups()) << "Only SVC with RTX is supported";
338 RTC_CHECK_EQ(stream.ssrc_groups.size(), 1)
339 << "Too many ssrc groups in the track";
340 std::vector<uint32_t> primary_ssrcs;
341 stream.GetPrimarySsrcs(&primary_ssrcs);
342 RTC_CHECK(primary_ssrcs.size() == 1);
343 for (int i = 1; i < svc_layers_count; ++i) {
344 uint32_t ssrc = ssrcs_generator.GenerateId();
345 primary_ssrcs.push_back(ssrc);
346 stream.add_ssrc(ssrc);
347 stream.AddFidSsrc(ssrc, ssrcs_generator.GenerateId());
348 }
349 stream.ssrc_groups.push_back(
350 cricket::SsrcGroup(cricket::kSimSsrcGroupSemantics, primary_ssrcs));
351 }
352 auto offer_for_remote = CloneSessionDescription(offer.get());
353 return LocalAndRemoteSdp(std::move(offer), std::move(offer_for_remote));
354 }
355
PatchAnswer(std::unique_ptr<SessionDescriptionInterface> answer)356 LocalAndRemoteSdp SignalingInterceptor::PatchAnswer(
357 std::unique_ptr<SessionDescriptionInterface> answer) {
358 for (auto& content : answer->description()->contents()) {
359 cricket::MediaContentDescription* media_desc = content.media_description();
360 if (media_desc->type() != cricket::MediaType::MEDIA_TYPE_VIDEO) {
361 continue;
362 }
363 if (content.media_description()->direction() !=
364 RtpTransceiverDirection::kRecvOnly) {
365 continue;
366 }
367 media_desc->set_conference_mode(params_.use_conference_mode);
368 }
369
370 if (params_.stream_label_to_simulcast_streams_count.size() > 0) {
371 // Because simulcast enabled |params_.video_codecs| has only 1 element.
372 if (params_.video_codecs[0].name == cricket::kVp8CodecName) {
373 return PatchVp8Answer(std::move(answer));
374 }
375
376 if (params_.video_codecs[0].name == cricket::kVp9CodecName) {
377 return PatchVp9Answer(std::move(answer));
378 }
379 }
380
381 auto answer_for_remote = CloneSessionDescription(answer.get());
382 return LocalAndRemoteSdp(std::move(answer), std::move(answer_for_remote));
383 }
384
PatchVp8Answer(std::unique_ptr<SessionDescriptionInterface> answer)385 LocalAndRemoteSdp SignalingInterceptor::PatchVp8Answer(
386 std::unique_ptr<SessionDescriptionInterface> answer) {
387 if (!context_.HasSimulcast()) {
388 auto answer_for_remote = CloneSessionDescription(answer.get());
389 return LocalAndRemoteSdp(std::move(answer), std::move(answer_for_remote));
390 }
391
392 std::unique_ptr<cricket::SessionDescription> desc =
393 answer->description()->Clone();
394
395 for (auto& info : context_.simulcast_infos) {
396 cricket::ContentInfo* simulcast_content =
397 desc->GetContentByName(info.rids[0]);
398 RTC_CHECK(simulcast_content);
399
400 // Get media description, which will be converted to simulcast answer.
401 std::unique_ptr<cricket::MediaContentDescription> media_desc =
402 simulcast_content->media_description()->Clone();
403 // Set |simulcast_content| to nullptr, because then it will be removed, so
404 // it will point to deleted object.
405 simulcast_content = nullptr;
406
407 // Remove separate media sections for simulcast streams.
408 for (auto& rid : info.rids) {
409 RTC_CHECK(desc->RemoveContentByName(rid));
410 }
411
412 // Patch |media_desc| to make it simulcast answer description.
413 // Restore mid/rid rtp header extensions
414 std::vector<webrtc::RtpExtension> extensions =
415 media_desc->rtp_header_extensions();
416 // First remove existing rid/mid header extensions.
417 extensions.erase(std::remove_if(extensions.begin(), extensions.end(),
418 [](const webrtc::RtpExtension& e) {
419 return e.uri == RtpExtension::kMidUri ||
420 e.uri == RtpExtension::kRidUri ||
421 e.uri ==
422 RtpExtension::kRepairedRidUri;
423 }));
424
425 // Then add right ones.
426 extensions.push_back(info.mid_extension);
427 extensions.push_back(info.rid_extension);
428 // extensions.push_back(info.rrid_extension);
429 media_desc->ClearRtpHeaderExtensions();
430 media_desc->set_rtp_header_extensions(extensions);
431
432 // Add StreamParams with rids for receive.
433 RTC_CHECK_EQ(media_desc->mutable_streams().size(), 0);
434 std::vector<cricket::RidDescription> rids;
435 for (auto& rid : info.rids) {
436 rids.emplace_back(rid, cricket::RidDirection::kReceive);
437 }
438 cricket::StreamParams stream_params;
439 stream_params.set_rids(rids);
440 media_desc->mutable_streams().push_back(stream_params);
441
442 // Restore SimulcastDescription. It should correspond to one from offer,
443 // but it have to have receive layers instead of send. So we need to put
444 // send layers from offer to receive layers in answer.
445 cricket::SimulcastDescription simulcast_description;
446 for (const auto& layer : info.simulcast_description.send_layers()) {
447 simulcast_description.receive_layers().AddLayerWithAlternatives(layer);
448 }
449 media_desc->set_simulcast_description(simulcast_description);
450
451 // Add simulcast media section.
452 desc->AddContent(info.mid, info.media_protocol_type, std::move(media_desc));
453 }
454
455 desc = RestoreMediaSectionsOrder(std::move(desc));
456
457 // Now we need to add bundle line to have all media bundled together.
458 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
459 for (auto& content : desc->contents()) {
460 bundle_group.AddContentName(content.mid());
461 }
462 if (desc->HasGroup(cricket::GROUP_TYPE_BUNDLE)) {
463 desc->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
464 }
465 desc->AddGroup(bundle_group);
466
467 // Fix transport_infos: it have to have single info for simulcast section.
468 std::vector<cricket::TransportInfo> transport_infos = desc->transport_infos();
469 std::map<std::string, cricket::TransportDescription>
470 mid_to_transport_description;
471 for (auto info_it = transport_infos.begin();
472 info_it != transport_infos.end();) {
473 auto it = context_.simulcast_infos_by_rid.find(info_it->content_name);
474 if (it != context_.simulcast_infos_by_rid.end()) {
475 // This transport info correspond to some extra added media section.
476 mid_to_transport_description.insert(
477 {it->second->mid, info_it->description});
478 info_it = transport_infos.erase(info_it);
479 } else {
480 ++info_it;
481 }
482 }
483 for (auto& info : context_.simulcast_infos) {
484 transport_infos.emplace_back(info.mid,
485 mid_to_transport_description.at(info.mid));
486 }
487 desc->set_transport_infos(transport_infos);
488
489 auto patched_answer =
490 std::make_unique<JsepSessionDescription>(SdpType::kAnswer);
491 patched_answer->Initialize(std::move(desc), answer->session_id(),
492 answer->session_version());
493 return LocalAndRemoteSdp(std::move(answer), std::move(patched_answer));
494 }
495
496 std::unique_ptr<cricket::SessionDescription>
RestoreMediaSectionsOrder(std::unique_ptr<cricket::SessionDescription> source)497 SignalingInterceptor::RestoreMediaSectionsOrder(
498 std::unique_ptr<cricket::SessionDescription> source) {
499 std::unique_ptr<cricket::SessionDescription> out = source->Clone();
500 for (auto& mid : context_.mids_order) {
501 RTC_CHECK(out->RemoveContentByName(mid));
502 }
503 RTC_CHECK_EQ(out->contents().size(), 0);
504 for (auto& mid : context_.mids_order) {
505 cricket::ContentInfo* content = source->GetContentByName(mid);
506 RTC_CHECK(content);
507 out->AddContent(mid, content->type, content->media_description()->Clone());
508 }
509 return out;
510 }
511
PatchVp9Answer(std::unique_ptr<SessionDescriptionInterface> answer)512 LocalAndRemoteSdp SignalingInterceptor::PatchVp9Answer(
513 std::unique_ptr<SessionDescriptionInterface> answer) {
514 auto answer_for_remote = CloneSessionDescription(answer.get());
515 return LocalAndRemoteSdp(std::move(answer), std::move(answer_for_remote));
516 }
517
518 std::vector<std::unique_ptr<IceCandidateInterface>>
PatchOffererIceCandidates(rtc::ArrayView<const IceCandidateInterface * const> candidates)519 SignalingInterceptor::PatchOffererIceCandidates(
520 rtc::ArrayView<const IceCandidateInterface* const> candidates) {
521 std::vector<std::unique_ptr<IceCandidateInterface>> out;
522 for (auto* candidate : candidates) {
523 auto simulcast_info_it =
524 context_.simulcast_infos_by_mid.find(candidate->sdp_mid());
525 if (simulcast_info_it != context_.simulcast_infos_by_mid.end()) {
526 // This is candidate for simulcast section, so it should be transformed
527 // into candidates for replicated sections
528 out.push_back(CreateIceCandidate(simulcast_info_it->second->rids[0], 0,
529 candidate->candidate()));
530 } else {
531 out.push_back(CreateIceCandidate(candidate->sdp_mid(),
532 candidate->sdp_mline_index(),
533 candidate->candidate()));
534 }
535 }
536 RTC_CHECK_GT(out.size(), 0);
537 return out;
538 }
539
540 std::vector<std::unique_ptr<IceCandidateInterface>>
PatchAnswererIceCandidates(rtc::ArrayView<const IceCandidateInterface * const> candidates)541 SignalingInterceptor::PatchAnswererIceCandidates(
542 rtc::ArrayView<const IceCandidateInterface* const> candidates) {
543 std::vector<std::unique_ptr<IceCandidateInterface>> out;
544 for (auto* candidate : candidates) {
545 auto simulcast_info_it =
546 context_.simulcast_infos_by_rid.find(candidate->sdp_mid());
547 if (simulcast_info_it != context_.simulcast_infos_by_rid.end()) {
548 // This is candidate for replicated section, created from single simulcast
549 // section, so it should be transformed into candidates for simulcast
550 // section.
551 out.push_back(CreateIceCandidate(simulcast_info_it->second->mid, 0,
552 candidate->candidate()));
553 } else {
554 out.push_back(CreateIceCandidate(candidate->sdp_mid(),
555 candidate->sdp_mline_index(),
556 candidate->candidate()));
557 }
558 }
559 RTC_CHECK_GT(out.size(), 0);
560 return out;
561 }
562
SimulcastSectionInfo(const std::string & mid,cricket::MediaProtocolType media_protocol_type,const std::vector<cricket::RidDescription> & rids_desc)563 SignalingInterceptor::SimulcastSectionInfo::SimulcastSectionInfo(
564 const std::string& mid,
565 cricket::MediaProtocolType media_protocol_type,
566 const std::vector<cricket::RidDescription>& rids_desc)
567 : mid(mid), media_protocol_type(media_protocol_type) {
568 for (auto& rid : rids_desc) {
569 rids.push_back(rid.rid);
570 }
571 }
572
AddSimulcastInfo(const SimulcastSectionInfo & info)573 void SignalingInterceptor::SignalingContext::AddSimulcastInfo(
574 const SimulcastSectionInfo& info) {
575 simulcast_infos.push_back(info);
576 bool inserted =
577 simulcast_infos_by_mid.insert({info.mid, &simulcast_infos.back()}).second;
578 RTC_CHECK(inserted);
579 for (auto& rid : info.rids) {
580 inserted =
581 simulcast_infos_by_rid.insert({rid, &simulcast_infos.back()}).second;
582 RTC_CHECK(inserted);
583 }
584 }
585
586 } // namespace webrtc_pc_e2e
587 } // namespace webrtc
588