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