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