blob: 10c4f3970303569dfc74ef22081bd78da05de0bd [file] [log] [blame]
Amit Hilbuchaa584152019-02-06 17:09:52 -08001/*
2 * Copyright 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
Harald Alvestrandc24a2182022-02-23 13:44:59 +000011#include <algorithm>
12#include <iterator>
13#include <map>
Mirko Bonadei317a1f02019-09-17 17:06:18 +020014#include <memory>
Amit Hilbuchaa584152019-02-06 17:09:52 -080015#include <ostream> // no-presubmit-check TODO(webrtc:8982)
Harald Alvestrandc24a2182022-02-23 13:44:59 +000016#include <string>
17#include <utility>
18#include <vector>
Amit Hilbuchaa584152019-02-06 17:09:52 -080019
20#include "absl/algorithm/container.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000021#include "absl/strings/string_view.h"
22#include "api/audio/audio_mixer.h"
Amit Hilbuchaa584152019-02-06 17:09:52 -080023#include "api/audio_codecs/builtin_audio_decoder_factory.h"
24#include "api/audio_codecs/builtin_audio_encoder_factory.h"
25#include "api/create_peerconnection_factory.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000026#include "api/jsep.h"
Amit Hilbuchf4770402019-04-08 14:11:57 -070027#include "api/media_types.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000028#include "api/peer_connection_interface.h"
Amit Hilbuchf4770402019-04-08 14:11:57 -070029#include "api/rtc_error.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000030#include "api/rtp_parameters.h"
31#include "api/rtp_sender_interface.h"
32#include "api/rtp_transceiver_direction.h"
Amit Hilbuchaa584152019-02-06 17:09:52 -080033#include "api/rtp_transceiver_interface.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000034#include "api/scoped_refptr.h"
Amit Hilbuche2a284d2019-03-05 12:36:31 -080035#include "api/uma_metrics.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000036#include "api/video/video_codec_constants.h"
Amit Hilbuchaa584152019-02-06 17:09:52 -080037#include "api/video_codecs/builtin_video_decoder_factory.h"
38#include "api/video_codecs/builtin_video_encoder_factory.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000039#include "media/base/rid_description.h"
40#include "media/base/stream_params.h"
41#include "modules/audio_device/include/audio_device.h"
42#include "modules/audio_processing/include/audio_processing.h"
43#include "pc/channel_interface.h"
Amit Hilbuchaa584152019-02-06 17:09:52 -080044#include "pc/peer_connection_wrapper.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000045#include "pc/session_description.h"
46#include "pc/simulcast_description.h"
Amit Hilbuchaa584152019-02-06 17:09:52 -080047#include "pc/test/fake_audio_capture_module.h"
48#include "pc/test/mock_peer_connection_observers.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000049#include "rtc_base/checks.h"
50#include "rtc_base/strings/string_builder.h"
51#include "rtc_base/thread.h"
52#include "rtc_base/unique_id_generator.h"
Amit Hilbuche2a284d2019-03-05 12:36:31 -080053#include "system_wrappers/include/metrics.h"
Amit Hilbuchaa584152019-02-06 17:09:52 -080054#include "test/gmock.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000055#include "test/gtest.h"
Amit Hilbuchaa584152019-02-06 17:09:52 -080056
57using ::testing::Contains;
58using ::testing::Each;
59using ::testing::ElementsAre;
60using ::testing::ElementsAreArray;
61using ::testing::Eq;
62using ::testing::Field;
63using ::testing::IsEmpty;
64using ::testing::Ne;
Amit Hilbuche2a284d2019-03-05 12:36:31 -080065using ::testing::Pair;
Amit Hilbuchaa584152019-02-06 17:09:52 -080066using ::testing::Property;
67using ::testing::SizeIs;
68
69using cricket::MediaContentDescription;
70using cricket::RidDescription;
71using cricket::SimulcastDescription;
72using cricket::SimulcastLayer;
73using cricket::StreamParams;
74
75namespace cricket {
76
77std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982)
78 std::ostream& os, // no-presubmit-check TODO(webrtc:8982)
79 const SimulcastLayer& layer) {
80 if (layer.is_paused) {
81 os << "~";
82 }
83 return os << layer.rid;
84}
85
86} // namespace cricket
87
88namespace {
89
90std::vector<SimulcastLayer> CreateLayers(const std::vector<std::string>& rids,
91 const std::vector<bool>& active) {
92 RTC_DCHECK_EQ(rids.size(), active.size());
93 std::vector<SimulcastLayer> result;
94 absl::c_transform(rids, active, std::back_inserter(result),
95 [](const std::string& rid, bool is_active) {
96 return SimulcastLayer(rid, !is_active);
97 });
98 return result;
99}
100std::vector<SimulcastLayer> CreateLayers(const std::vector<std::string>& rids,
101 bool active) {
102 return CreateLayers(rids, std::vector<bool>(rids.size(), active));
103}
104
Ying Wangef3998f2019-12-09 13:06:53 +0100105#if RTC_METRICS_ENABLED
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800106std::vector<SimulcastLayer> CreateLayers(int num_layers, bool active) {
107 rtc::UniqueStringGenerator rid_generator;
108 std::vector<std::string> rids;
109 for (int i = 0; i < num_layers; ++i) {
110 rids.push_back(rid_generator());
111 }
112 return CreateLayers(rids, active);
113}
Ying Wangef3998f2019-12-09 13:06:53 +0100114#endif
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800115
Amit Hilbuchaa584152019-02-06 17:09:52 -0800116} // namespace
Ying Wangef3998f2019-12-09 13:06:53 +0100117
Amit Hilbuchaa584152019-02-06 17:09:52 -0800118namespace webrtc {
119
Mirko Bonadei6a489f22019-04-09 15:11:12 +0200120class PeerConnectionSimulcastTests : public ::testing::Test {
Amit Hilbuchaa584152019-02-06 17:09:52 -0800121 public:
122 PeerConnectionSimulcastTests()
123 : pc_factory_(
124 CreatePeerConnectionFactory(rtc::Thread::Current(),
125 rtc::Thread::Current(),
126 rtc::Thread::Current(),
127 FakeAudioCaptureModule::Create(),
128 CreateBuiltinAudioEncoderFactory(),
129 CreateBuiltinAudioDecoderFactory(),
130 CreateBuiltinVideoEncoderFactory(),
131 CreateBuiltinVideoDecoderFactory(),
132 nullptr,
133 nullptr)) {}
134
135 rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection(
136 MockPeerConnectionObserver* observer) {
137 PeerConnectionInterface::RTCConfiguration config;
138 config.sdp_semantics = SdpSemantics::kUnifiedPlan;
139 PeerConnectionDependencies pcd(observer);
140 auto pc = pc_factory_->CreatePeerConnection(config, std::move(pcd));
141 EXPECT_TRUE(pc);
142 observer->SetPeerConnectionInterface(pc);
143 return pc;
144 }
145
146 std::unique_ptr<PeerConnectionWrapper> CreatePeerConnectionWrapper() {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200147 auto observer = std::make_unique<MockPeerConnectionObserver>();
Amit Hilbuchaa584152019-02-06 17:09:52 -0800148 auto pc = CreatePeerConnection(observer.get());
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200149 return std::make_unique<PeerConnectionWrapper>(pc_factory_, pc,
150 std::move(observer));
Amit Hilbuchaa584152019-02-06 17:09:52 -0800151 }
152
Amit Hilbuch619b2942019-02-26 15:55:19 -0800153 void ExchangeOfferAnswer(PeerConnectionWrapper* local,
154 PeerConnectionWrapper* remote,
155 const std::vector<SimulcastLayer>& answer_layers) {
156 auto offer = local->CreateOfferAndSetAsLocal();
157 // Remove simulcast as the second peer connection won't support it.
Amit Hilbuchaabd0362019-03-01 13:14:46 -0800158 RemoveSimulcast(offer.get());
Amit Hilbuch619b2942019-02-26 15:55:19 -0800159 std::string err;
160 EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &err)) << err;
161 auto answer = remote->CreateAnswerAndSetAsLocal();
162 // Setup the answer to look like a server response.
163 auto mcd_answer = answer->description()->contents()[0].media_description();
164 auto& receive_layers = mcd_answer->simulcast_description().receive_layers();
165 for (const SimulcastLayer& layer : answer_layers) {
166 receive_layers.AddLayer(layer);
167 }
168 EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &err)) << err;
169 }
170
Amit Hilbuchaa584152019-02-06 17:09:52 -0800171 RtpTransceiverInit CreateTransceiverInit(
172 const std::vector<SimulcastLayer>& layers) {
173 RtpTransceiverInit init;
174 for (const SimulcastLayer& layer : layers) {
175 RtpEncodingParameters encoding;
176 encoding.rid = layer.rid;
177 encoding.active = !layer.is_paused;
178 init.send_encodings.push_back(encoding);
179 }
180 return init;
181 }
182
183 rtc::scoped_refptr<RtpTransceiverInterface> AddTransceiver(
184 PeerConnectionWrapper* pc,
Florent Castellie1b685a2021-04-30 19:11:37 +0200185 const std::vector<SimulcastLayer>& layers,
186 cricket::MediaType media_type = cricket::MEDIA_TYPE_VIDEO) {
Amit Hilbuchaa584152019-02-06 17:09:52 -0800187 auto init = CreateTransceiverInit(layers);
Florent Castellie1b685a2021-04-30 19:11:37 +0200188 return pc->AddTransceiver(media_type, init);
Amit Hilbuchaa584152019-02-06 17:09:52 -0800189 }
190
191 SimulcastDescription RemoveSimulcast(SessionDescriptionInterface* sd) {
192 auto mcd = sd->description()->contents()[0].media_description();
193 auto result = mcd->simulcast_description();
194 mcd->set_simulcast_description(SimulcastDescription());
195 return result;
196 }
197
198 void AddRequestToReceiveSimulcast(const std::vector<SimulcastLayer>& layers,
199 SessionDescriptionInterface* sd) {
200 auto mcd = sd->description()->contents()[0].media_description();
201 SimulcastDescription simulcast;
202 auto& receive_layers = simulcast.receive_layers();
203 for (const SimulcastLayer& layer : layers) {
204 receive_layers.AddLayer(layer);
205 }
206 mcd->set_simulcast_description(simulcast);
207 }
208
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800209 void ValidateTransceiverParameters(
Amit Hilbuchaa584152019-02-06 17:09:52 -0800210 rtc::scoped_refptr<RtpTransceiverInterface> transceiver,
211 const std::vector<SimulcastLayer>& layers) {
Amit Hilbuchaa584152019-02-06 17:09:52 -0800212 auto parameters = transceiver->sender()->GetParameters();
213 std::vector<SimulcastLayer> result_layers;
214 absl::c_transform(parameters.encodings, std::back_inserter(result_layers),
215 [](const RtpEncodingParameters& encoding) {
216 return SimulcastLayer(encoding.rid, !encoding.active);
217 });
218 EXPECT_THAT(result_layers, ElementsAreArray(layers));
Amit Hilbuchaa584152019-02-06 17:09:52 -0800219 }
220
221 private:
222 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
223};
224
Ying Wangef3998f2019-12-09 13:06:53 +0100225#if RTC_METRICS_ENABLED
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800226// This class is used to test the metrics emitted for simulcast.
227class PeerConnectionSimulcastMetricsTests
228 : public PeerConnectionSimulcastTests,
229 public ::testing::WithParamInterface<int> {
230 protected:
231 PeerConnectionSimulcastMetricsTests() { webrtc::metrics::Reset(); }
232
233 std::map<int, int> LocalDescriptionSamples() {
234 return metrics::Samples(
235 "WebRTC.PeerConnection.Simulcast.ApplyLocalDescription");
236 }
237 std::map<int, int> RemoteDescriptionSamples() {
238 return metrics::Samples(
239 "WebRTC.PeerConnection.Simulcast.ApplyRemoteDescription");
240 }
241};
Ying Wangef3998f2019-12-09 13:06:53 +0100242#endif
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800243
Amit Hilbuchaa584152019-02-06 17:09:52 -0800244// Validates that RIDs are supported arguments when adding a transceiver.
245TEST_F(PeerConnectionSimulcastTests, CanCreateTransceiverWithRid) {
246 auto pc = CreatePeerConnectionWrapper();
247 auto layers = CreateLayers({"f"}, true);
248 auto transceiver = AddTransceiver(pc.get(), layers);
249 ASSERT_TRUE(transceiver);
250 auto parameters = transceiver->sender()->GetParameters();
251 // Single RID should be removed.
252 EXPECT_THAT(parameters.encodings,
Amit Hilbuch2297d332019-02-19 12:49:22 -0800253 ElementsAre(Field("rid", &RtpEncodingParameters::rid, Eq(""))));
Amit Hilbuchaa584152019-02-06 17:09:52 -0800254}
255
256TEST_F(PeerConnectionSimulcastTests, CanCreateTransceiverWithSimulcast) {
257 auto pc = CreatePeerConnectionWrapper();
258 auto layers = CreateLayers({"f", "h", "q"}, true);
259 auto transceiver = AddTransceiver(pc.get(), layers);
260 ASSERT_TRUE(transceiver);
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800261 ValidateTransceiverParameters(transceiver, layers);
Amit Hilbuchaa584152019-02-06 17:09:52 -0800262}
263
264TEST_F(PeerConnectionSimulcastTests, RidsAreAutogeneratedIfNotProvided) {
265 auto pc = CreatePeerConnectionWrapper();
266 auto init = CreateTransceiverInit(CreateLayers({"f", "h", "q"}, true));
267 for (RtpEncodingParameters& parameters : init.send_encodings) {
268 parameters.rid = "";
269 }
270 auto transceiver = pc->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
271 auto parameters = transceiver->sender()->GetParameters();
272 ASSERT_EQ(3u, parameters.encodings.size());
273 EXPECT_THAT(parameters.encodings,
Amit Hilbuch2297d332019-02-19 12:49:22 -0800274 Each(Field("rid", &RtpEncodingParameters::rid, Ne(""))));
Amit Hilbuchaa584152019-02-06 17:09:52 -0800275}
276
277// Validates that an error is returned when there is a mix of supplied and not
278// supplied RIDs in a call to AddTransceiver.
279TEST_F(PeerConnectionSimulcastTests, MustSupplyAllOrNoRidsInSimulcast) {
280 auto pc_wrapper = CreatePeerConnectionWrapper();
281 auto pc = pc_wrapper->pc();
282 // Cannot create a layer with empty RID. Remove the RID after init is created.
283 auto layers = CreateLayers({"f", "h", "remove"}, true);
284 auto init = CreateTransceiverInit(layers);
285 init.send_encodings[2].rid = "";
286 auto error = pc->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
287 EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, error.error().type());
288}
289
Amit Hilbuchf4770402019-04-08 14:11:57 -0700290// Validates that an error is returned when illegal RIDs are supplied.
291TEST_F(PeerConnectionSimulcastTests, ChecksForIllegalRidValues) {
292 auto pc_wrapper = CreatePeerConnectionWrapper();
293 auto pc = pc_wrapper->pc();
294 auto layers = CreateLayers({"f", "h", "~q"}, true);
295 auto init = CreateTransceiverInit(layers);
296 auto error = pc->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
297 EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, error.error().type());
298}
299
Amit Hilbuch2297d332019-02-19 12:49:22 -0800300// Validates that a single RID is removed from the encoding layer.
Amit Hilbuchaa584152019-02-06 17:09:52 -0800301TEST_F(PeerConnectionSimulcastTests, SingleRidIsRemovedFromSessionDescription) {
302 auto pc = CreatePeerConnectionWrapper();
303 auto transceiver = AddTransceiver(pc.get(), CreateLayers({"1"}, true));
304 auto offer = pc->CreateOfferAndSetAsLocal();
305 ASSERT_TRUE(offer);
306 auto contents = offer->description()->contents();
307 ASSERT_EQ(1u, contents.size());
308 EXPECT_THAT(contents[0].media_description()->streams(),
309 ElementsAre(Property(&StreamParams::has_rids, false)));
310}
311
312TEST_F(PeerConnectionSimulcastTests, SimulcastLayersRemovedFromTail) {
313 static_assert(
314 kMaxSimulcastStreams < 8,
315 "Test assumes that the platform does not allow 8 simulcast layers");
316 auto pc = CreatePeerConnectionWrapper();
317 auto layers = CreateLayers({"1", "2", "3", "4", "5", "6", "7", "8"}, true);
318 std::vector<SimulcastLayer> expected_layers;
319 std::copy_n(layers.begin(), kMaxSimulcastStreams,
320 std::back_inserter(expected_layers));
321 auto transceiver = AddTransceiver(pc.get(), layers);
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800322 ValidateTransceiverParameters(transceiver, expected_layers);
Amit Hilbuchaa584152019-02-06 17:09:52 -0800323}
324
325// Checks that an offfer to send simulcast contains a SimulcastDescription.
326TEST_F(PeerConnectionSimulcastTests, SimulcastAppearsInSessionDescription) {
327 auto pc = CreatePeerConnectionWrapper();
328 std::vector<std::string> rids({"f", "h", "q"});
329 auto layers = CreateLayers(rids, true);
330 auto transceiver = AddTransceiver(pc.get(), layers);
331 auto offer = pc->CreateOffer();
332 ASSERT_TRUE(offer);
333 auto contents = offer->description()->contents();
334 ASSERT_EQ(1u, contents.size());
335 auto content = contents[0];
336 auto mcd = content.media_description();
337 ASSERT_TRUE(mcd->HasSimulcast());
338 auto simulcast = mcd->simulcast_description();
339 EXPECT_THAT(simulcast.receive_layers(), IsEmpty());
340 // The size is validated separately because GetAllLayers() flattens the list.
341 EXPECT_THAT(simulcast.send_layers(), SizeIs(3));
342 std::vector<SimulcastLayer> result = simulcast.send_layers().GetAllLayers();
343 EXPECT_THAT(result, ElementsAreArray(layers));
344 auto streams = mcd->streams();
345 ASSERT_EQ(1u, streams.size());
346 auto stream = streams[0];
347 EXPECT_FALSE(stream.has_ssrcs());
348 EXPECT_TRUE(stream.has_rids());
349 std::vector<std::string> result_rids;
350 absl::c_transform(stream.rids(), std::back_inserter(result_rids),
351 [](const RidDescription& rid) { return rid.rid; });
352 EXPECT_THAT(result_rids, ElementsAreArray(rids));
353}
354
355// Checks that Simulcast layers propagate to the sender parameters.
356TEST_F(PeerConnectionSimulcastTests, SimulcastLayersAreSetInSender) {
357 auto local = CreatePeerConnectionWrapper();
358 auto remote = CreatePeerConnectionWrapper();
359 auto layers = CreateLayers({"f", "h", "q"}, true);
360 auto transceiver = AddTransceiver(local.get(), layers);
361 auto offer = local->CreateOfferAndSetAsLocal();
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800362 {
363 SCOPED_TRACE("after create offer");
364 ValidateTransceiverParameters(transceiver, layers);
365 }
Amit Hilbuchaa584152019-02-06 17:09:52 -0800366 // Remove simulcast as the second peer connection won't support it.
367 auto simulcast = RemoveSimulcast(offer.get());
368 std::string error;
369 EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
370 auto answer = remote->CreateAnswerAndSetAsLocal();
371
372 // Setup an answer that mimics a server accepting simulcast.
373 auto mcd_answer = answer->description()->contents()[0].media_description();
374 mcd_answer->mutable_streams().clear();
375 auto simulcast_layers = simulcast.send_layers().GetAllLayers();
376 auto& receive_layers = mcd_answer->simulcast_description().receive_layers();
377 for (const auto& layer : simulcast_layers) {
378 receive_layers.AddLayer(layer);
379 }
380 EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &error)) << error;
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800381 {
382 SCOPED_TRACE("after set remote");
383 ValidateTransceiverParameters(transceiver, layers);
384 }
Amit Hilbuchaa584152019-02-06 17:09:52 -0800385}
386
387// Checks that paused Simulcast layers propagate to the sender parameters.
388TEST_F(PeerConnectionSimulcastTests, PausedSimulcastLayersAreDisabledInSender) {
389 auto local = CreatePeerConnectionWrapper();
390 auto remote = CreatePeerConnectionWrapper();
391 auto layers = CreateLayers({"f", "h", "q"}, {true, false, true});
392 auto server_layers = CreateLayers({"f", "h", "q"}, {true, false, false});
393 RTC_DCHECK_EQ(layers.size(), server_layers.size());
394 auto transceiver = AddTransceiver(local.get(), layers);
395 auto offer = local->CreateOfferAndSetAsLocal();
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800396 {
397 SCOPED_TRACE("after create offer");
398 ValidateTransceiverParameters(transceiver, layers);
399 }
Amit Hilbuchaa584152019-02-06 17:09:52 -0800400
401 // Remove simulcast as the second peer connection won't support it.
402 RemoveSimulcast(offer.get());
403 std::string error;
404 EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
405 auto answer = remote->CreateAnswerAndSetAsLocal();
406
407 // Setup an answer that mimics a server accepting simulcast.
408 auto mcd_answer = answer->description()->contents()[0].media_description();
409 mcd_answer->mutable_streams().clear();
410 auto& receive_layers = mcd_answer->simulcast_description().receive_layers();
411 for (const SimulcastLayer& layer : server_layers) {
412 receive_layers.AddLayer(layer);
413 }
414 EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &error)) << error;
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800415 {
416 SCOPED_TRACE("after set remote");
417 ValidateTransceiverParameters(transceiver, server_layers);
418 }
Amit Hilbuchaa584152019-02-06 17:09:52 -0800419}
420
421// Checks that when Simulcast is not supported by the remote party, then all
Amit Hilbuch2297d332019-02-19 12:49:22 -0800422// the layers (except the first) are removed.
423TEST_F(PeerConnectionSimulcastTests, SimulcastRejectedRemovesExtraLayers) {
Amit Hilbuchaa584152019-02-06 17:09:52 -0800424 auto local = CreatePeerConnectionWrapper();
425 auto remote = CreatePeerConnectionWrapper();
426 auto layers = CreateLayers({"1", "2", "3", "4"}, true);
Amit Hilbuchaa584152019-02-06 17:09:52 -0800427 auto transceiver = AddTransceiver(local.get(), layers);
Amit Hilbuch619b2942019-02-26 15:55:19 -0800428 ExchangeOfferAnswer(local.get(), remote.get(), {});
Amit Hilbuch2297d332019-02-19 12:49:22 -0800429 auto parameters = transceiver->sender()->GetParameters();
430 // Should only have the first layer.
431 EXPECT_THAT(parameters.encodings,
432 ElementsAre(Field("rid", &RtpEncodingParameters::rid, Eq("1"))));
Amit Hilbuchaa584152019-02-06 17:09:52 -0800433}
434
Amit Hilbuch2297d332019-02-19 12:49:22 -0800435// Checks that if Simulcast is supported by remote party, but some layers are
436// rejected, then only rejected layers are removed from the sender.
Amit Hilbuchaa584152019-02-06 17:09:52 -0800437TEST_F(PeerConnectionSimulcastTests, RejectedSimulcastLayersAreDeactivated) {
438 auto local = CreatePeerConnectionWrapper();
439 auto remote = CreatePeerConnectionWrapper();
Florent Castelli3fbf1e22019-07-04 17:09:05 +0200440 auto layers = CreateLayers({"1", "2", "3"}, true);
441 auto expected_layers = CreateLayers({"2", "3"}, true);
Amit Hilbuchaa584152019-02-06 17:09:52 -0800442 auto transceiver = AddTransceiver(local.get(), layers);
443 auto offer = local->CreateOfferAndSetAsLocal();
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800444 {
445 SCOPED_TRACE("after create offer");
446 ValidateTransceiverParameters(transceiver, layers);
447 }
Amit Hilbuchaa584152019-02-06 17:09:52 -0800448 // Remove simulcast as the second peer connection won't support it.
449 auto removed_simulcast = RemoveSimulcast(offer.get());
450 std::string error;
451 EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
452 auto answer = remote->CreateAnswerAndSetAsLocal();
453 auto mcd_answer = answer->description()->contents()[0].media_description();
454 // Setup the answer to look like a server response.
455 // Remove one of the layers to reject it in the answer.
456 auto simulcast_layers = removed_simulcast.send_layers().GetAllLayers();
457 simulcast_layers.erase(simulcast_layers.begin());
458 auto& receive_layers = mcd_answer->simulcast_description().receive_layers();
459 for (const auto& layer : simulcast_layers) {
460 receive_layers.AddLayer(layer);
461 }
462 ASSERT_TRUE(mcd_answer->HasSimulcast());
463 EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &error)) << error;
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800464 {
465 SCOPED_TRACE("after set remote");
466 ValidateTransceiverParameters(transceiver, expected_layers);
467 }
Amit Hilbuchaa584152019-02-06 17:09:52 -0800468}
469
470// Checks that simulcast is set up correctly when the server sends an offer
471// requesting to receive simulcast.
472TEST_F(PeerConnectionSimulcastTests, ServerSendsOfferToReceiveSimulcast) {
473 auto local = CreatePeerConnectionWrapper();
474 auto remote = CreatePeerConnectionWrapper();
475 auto layers = CreateLayers({"f", "h", "q"}, true);
476 AddTransceiver(local.get(), layers);
477 auto offer = local->CreateOfferAndSetAsLocal();
478 // Remove simulcast as a sender and set it up as a receiver.
479 RemoveSimulcast(offer.get());
480 AddRequestToReceiveSimulcast(layers, offer.get());
481 std::string error;
482 EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
483 auto transceiver = remote->pc()->GetTransceivers()[0];
Harald Alvestrand6060df52020-08-11 09:54:02 +0200484 transceiver->SetDirectionWithError(RtpTransceiverDirection::kSendRecv);
Amit Hilbuchaa584152019-02-06 17:09:52 -0800485 EXPECT_TRUE(remote->CreateAnswerAndSetAsLocal());
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800486 ValidateTransceiverParameters(transceiver, layers);
Amit Hilbuchaa584152019-02-06 17:09:52 -0800487}
488
489// Checks that SetRemoteDescription doesn't attempt to associate a transceiver
490// when simulcast is requested by the server.
491TEST_F(PeerConnectionSimulcastTests, TransceiverIsNotRecycledWithSimulcast) {
492 auto local = CreatePeerConnectionWrapper();
493 auto remote = CreatePeerConnectionWrapper();
494 auto layers = CreateLayers({"f", "h", "q"}, true);
495 AddTransceiver(local.get(), layers);
496 auto offer = local->CreateOfferAndSetAsLocal();
497 // Remove simulcast as a sender and set it up as a receiver.
498 RemoveSimulcast(offer.get());
499 AddRequestToReceiveSimulcast(layers, offer.get());
500 // Call AddTrack so that a transceiver is created.
501 remote->AddVideoTrack("fake_track");
502 std::string error;
503 EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
504 auto transceivers = remote->pc()->GetTransceivers();
505 ASSERT_EQ(2u, transceivers.size());
506 auto transceiver = transceivers[1];
Harald Alvestrand6060df52020-08-11 09:54:02 +0200507 transceiver->SetDirectionWithError(RtpTransceiverDirection::kSendRecv);
Amit Hilbuchaa584152019-02-06 17:09:52 -0800508 EXPECT_TRUE(remote->CreateAnswerAndSetAsLocal());
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800509 ValidateTransceiverParameters(transceiver, layers);
Amit Hilbuchaa584152019-02-06 17:09:52 -0800510}
511
Amit Hilbuch619b2942019-02-26 15:55:19 -0800512// Checks that if the number of layers changes during negotiation, then any
513// outstanding get/set parameters transaction is invalidated.
514TEST_F(PeerConnectionSimulcastTests, ParametersAreInvalidatedWhenLayersChange) {
515 auto local = CreatePeerConnectionWrapper();
516 auto remote = CreatePeerConnectionWrapper();
517 auto layers = CreateLayers({"1", "2", "3"}, true);
518 auto transceiver = AddTransceiver(local.get(), layers);
519 auto parameters = transceiver->sender()->GetParameters();
520 ASSERT_EQ(3u, parameters.encodings.size());
521 // Response will reject simulcast altogether.
522 ExchangeOfferAnswer(local.get(), remote.get(), {});
523 auto result = transceiver->sender()->SetParameters(parameters);
524 EXPECT_EQ(RTCErrorType::INVALID_STATE, result.type());
525}
526
527// Checks that even though negotiation modifies the sender's parameters, an
528// outstanding get/set parameters transaction is not invalidated.
529// This test negotiates twice because initial parameters before negotiation
530// is missing critical information and cannot be set on the sender.
531TEST_F(PeerConnectionSimulcastTests,
532 NegotiationDoesNotInvalidateParameterTransactions) {
533 auto local = CreatePeerConnectionWrapper();
534 auto remote = CreatePeerConnectionWrapper();
535 auto layers = CreateLayers({"1", "2", "3"}, true);
536 auto expected_layers = CreateLayers({"1", "2", "3"}, false);
537 auto transceiver = AddTransceiver(local.get(), layers);
538 ExchangeOfferAnswer(local.get(), remote.get(), expected_layers);
539
540 // Verify that negotiation does not invalidate the parameters.
541 auto parameters = transceiver->sender()->GetParameters();
542 ExchangeOfferAnswer(local.get(), remote.get(), expected_layers);
543
544 auto result = transceiver->sender()->SetParameters(parameters);
545 EXPECT_TRUE(result.ok());
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800546 ValidateTransceiverParameters(transceiver, expected_layers);
Amit Hilbuch619b2942019-02-26 15:55:19 -0800547}
548
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800549// Tests that simulcast is disabled if the RID extension is not negotiated
550// regardless of if the RIDs and simulcast attribute were negotiated properly.
Amit Hilbuchaabd0362019-03-01 13:14:46 -0800551TEST_F(PeerConnectionSimulcastTests, NegotiationDoesNotHaveRidExtension) {
552 auto local = CreatePeerConnectionWrapper();
553 auto remote = CreatePeerConnectionWrapper();
554 auto layers = CreateLayers({"1", "2", "3"}, true);
555 auto expected_layers = CreateLayers({"1"}, true);
556 auto transceiver = AddTransceiver(local.get(), layers);
557 auto offer = local->CreateOfferAndSetAsLocal();
558 // Remove simulcast as the second peer connection won't support it.
559 RemoveSimulcast(offer.get());
560 std::string err;
561 EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &err)) << err;
562 auto answer = remote->CreateAnswerAndSetAsLocal();
563 // Setup the answer to look like a server response.
564 // Drop the RID header extension.
565 auto mcd_answer = answer->description()->contents()[0].media_description();
566 auto& receive_layers = mcd_answer->simulcast_description().receive_layers();
567 for (const SimulcastLayer& layer : layers) {
568 receive_layers.AddLayer(layer);
569 }
570 cricket::RtpHeaderExtensions extensions;
571 for (auto extension : mcd_answer->rtp_header_extensions()) {
572 if (extension.uri != RtpExtension::kRidUri) {
573 extensions.push_back(extension);
574 }
575 }
576 mcd_answer->set_rtp_header_extensions(extensions);
577 EXPECT_EQ(layers.size(), mcd_answer->simulcast_description()
578 .receive_layers()
579 .GetAllLayers()
580 .size());
581 EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &err)) << err;
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800582 ValidateTransceiverParameters(transceiver, expected_layers);
Amit Hilbuchaabd0362019-03-01 13:14:46 -0800583}
Ying Wangef3998f2019-12-09 13:06:53 +0100584
Florent Castellie1b685a2021-04-30 19:11:37 +0200585TEST_F(PeerConnectionSimulcastTests, SimulcastAudioRejected) {
586 auto local = CreatePeerConnectionWrapper();
587 auto remote = CreatePeerConnectionWrapper();
588 auto layers = CreateLayers({"1", "2", "3", "4"}, true);
589 auto transceiver =
590 AddTransceiver(local.get(), layers, cricket::MEDIA_TYPE_AUDIO);
591 // Should only have the first layer.
592 auto parameters = transceiver->sender()->GetParameters();
593 EXPECT_EQ(1u, parameters.encodings.size());
594 EXPECT_THAT(parameters.encodings,
595 ElementsAre(Field("rid", &RtpEncodingParameters::rid, Eq(""))));
596 ExchangeOfferAnswer(local.get(), remote.get(), {});
597 // Still have a single layer after negotiation
598 parameters = transceiver->sender()->GetParameters();
599 EXPECT_EQ(1u, parameters.encodings.size());
600 EXPECT_THAT(parameters.encodings,
601 ElementsAre(Field("rid", &RtpEncodingParameters::rid, Eq(""))));
602}
603
Ying Wangef3998f2019-12-09 13:06:53 +0100604#if RTC_METRICS_ENABLED
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800605//
606// Checks the logged metrics when simulcast is not used.
607TEST_F(PeerConnectionSimulcastMetricsTests, NoSimulcastUsageIsLogged) {
608 auto local = CreatePeerConnectionWrapper();
609 auto remote = CreatePeerConnectionWrapper();
610 auto layers = CreateLayers(0, true);
611 AddTransceiver(local.get(), layers);
612 ExchangeOfferAnswer(local.get(), remote.get(), layers);
613
614 EXPECT_THAT(LocalDescriptionSamples(),
615 ElementsAre(Pair(kSimulcastApiVersionNone, 2)));
616 EXPECT_THAT(RemoteDescriptionSamples(),
617 ElementsAre(Pair(kSimulcastApiVersionNone, 2)));
618}
619
620// Checks the logged metrics when spec-compliant simulcast is used.
621TEST_F(PeerConnectionSimulcastMetricsTests, SpecComplianceIsLogged) {
622 auto local = CreatePeerConnectionWrapper();
623 auto remote = CreatePeerConnectionWrapper();
624 auto layers = CreateLayers(3, true);
625 AddTransceiver(local.get(), layers);
626 ExchangeOfferAnswer(local.get(), remote.get(), layers);
627
628 // Expecting 2 invocations of each, because we have 2 peer connections.
629 // Only the local peer connection will be aware of simulcast.
630 // The remote peer connection will think that there is no simulcast.
631 EXPECT_THAT(LocalDescriptionSamples(),
632 ElementsAre(Pair(kSimulcastApiVersionNone, 1),
633 Pair(kSimulcastApiVersionSpecCompliant, 1)));
634 EXPECT_THAT(RemoteDescriptionSamples(),
635 ElementsAre(Pair(kSimulcastApiVersionNone, 1),
636 Pair(kSimulcastApiVersionSpecCompliant, 1)));
637}
638
639// Checks the logged metrics when and incoming request to send spec-compliant
640// simulcast is received from the remote party.
641TEST_F(PeerConnectionSimulcastMetricsTests, IncomingSimulcastIsLogged) {
642 auto local = CreatePeerConnectionWrapper();
643 auto remote = CreatePeerConnectionWrapper();
644 auto layers = CreateLayers(3, true);
645 AddTransceiver(local.get(), layers);
646 auto offer = local->CreateOfferAndSetAsLocal();
647 EXPECT_THAT(LocalDescriptionSamples(),
648 ElementsAre(Pair(kSimulcastApiVersionSpecCompliant, 1)));
649
650 // Remove simulcast as a sender and set it up as a receiver.
651 RemoveSimulcast(offer.get());
652 AddRequestToReceiveSimulcast(layers, offer.get());
653 std::string error;
654 EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
655 EXPECT_THAT(RemoteDescriptionSamples(),
656 ElementsAre(Pair(kSimulcastApiVersionSpecCompliant, 1)));
657
658 auto transceiver = remote->pc()->GetTransceivers()[0];
Harald Alvestrand6060df52020-08-11 09:54:02 +0200659 transceiver->SetDirectionWithError(RtpTransceiverDirection::kSendRecv);
Amit Hilbuche2a284d2019-03-05 12:36:31 -0800660 EXPECT_TRUE(remote->CreateAnswerAndSetAsLocal());
661 EXPECT_THAT(LocalDescriptionSamples(),
662 ElementsAre(Pair(kSimulcastApiVersionSpecCompliant, 2)));
663}
664
665// Checks that a spec-compliant simulcast offer that is rejected is logged.
666TEST_F(PeerConnectionSimulcastMetricsTests, RejectedSimulcastIsLogged) {
667 auto local = CreatePeerConnectionWrapper();
668 auto remote = CreatePeerConnectionWrapper();
669 auto layers = CreateLayers({"1", "2", "3"}, true);
670 AddTransceiver(local.get(), layers);
671 auto offer = local->CreateOfferAndSetAsLocal();
672 EXPECT_THAT(LocalDescriptionSamples(),
673 ElementsAre(Pair(kSimulcastApiVersionSpecCompliant, 1)));
674 RemoveSimulcast(offer.get());
675 std::string error;
676 EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
677 EXPECT_THAT(RemoteDescriptionSamples(),
678 ElementsAre(Pair(kSimulcastApiVersionNone, 1)));
679
680 auto answer = remote->CreateAnswerAndSetAsLocal();
681 EXPECT_THAT(LocalDescriptionSamples(),
682 ElementsAre(Pair(kSimulcastApiVersionNone, 1),
683 Pair(kSimulcastApiVersionSpecCompliant, 1)));
684 EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &error)) << error;
685 EXPECT_THAT(RemoteDescriptionSamples(),
686 ElementsAre(Pair(kSimulcastApiVersionNone, 2)));
687}
688
689// Checks the logged metrics when legacy munging simulcast technique is used.
690TEST_F(PeerConnectionSimulcastMetricsTests, LegacySimulcastIsLogged) {
691 auto local = CreatePeerConnectionWrapper();
692 auto remote = CreatePeerConnectionWrapper();
693 auto layers = CreateLayers(0, true);
694 AddTransceiver(local.get(), layers);
695 auto offer = local->CreateOffer();
696 // Munge the SDP to set up legacy simulcast.
697 const std::string end_line = "\r\n";
698 std::string sdp;
699 offer->ToString(&sdp);
700 rtc::StringBuilder builder(sdp);
701 builder << "a=ssrc:1111 cname:slimshady" << end_line;
702 builder << "a=ssrc:2222 cname:slimshady" << end_line;
703 builder << "a=ssrc:3333 cname:slimshady" << end_line;
704 builder << "a=ssrc-group:SIM 1111 2222 3333" << end_line;
705
706 SdpParseError parse_error;
707 auto sd =
708 CreateSessionDescription(SdpType::kOffer, builder.str(), &parse_error);
709 ASSERT_TRUE(sd) << parse_error.line << parse_error.description;
710 std::string error;
711 EXPECT_TRUE(local->SetLocalDescription(std::move(sd), &error)) << error;
712 EXPECT_THAT(LocalDescriptionSamples(),
713 ElementsAre(Pair(kSimulcastApiVersionLegacy, 1)));
714 EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
715 EXPECT_THAT(RemoteDescriptionSamples(),
716 ElementsAre(Pair(kSimulcastApiVersionNone, 1)));
717 auto answer = remote->CreateAnswerAndSetAsLocal();
718 EXPECT_THAT(LocalDescriptionSamples(),
719 ElementsAre(Pair(kSimulcastApiVersionNone, 1),
720 Pair(kSimulcastApiVersionLegacy, 1)));
721 // Legacy simulcast is not signaled in remote description.
722 EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &error)) << error;
723 EXPECT_THAT(RemoteDescriptionSamples(),
724 ElementsAre(Pair(kSimulcastApiVersionNone, 2)));
725}
726
727// Checks that disabling simulcast is logged in the metrics.
728TEST_F(PeerConnectionSimulcastMetricsTests, SimulcastDisabledIsLogged) {
729 auto local = CreatePeerConnectionWrapper();
730 auto remote = CreatePeerConnectionWrapper();
731 auto layers = CreateLayers({"1", "2", "3"}, true);
732 AddTransceiver(local.get(), layers);
733 auto offer = local->CreateOfferAndSetAsLocal();
734 RemoveSimulcast(offer.get());
735 std::string error;
736 EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
737 auto answer = remote->CreateAnswerAndSetAsLocal();
738 EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &error)) << error;
739
740 EXPECT_EQ(1, metrics::NumSamples("WebRTC.PeerConnection.Simulcast.Disabled"));
741 EXPECT_EQ(1,
742 metrics::NumEvents("WebRTC.PeerConnection.Simulcast.Disabled", 1));
743}
744
745// Checks that the disabled metric is not logged if simulcast is not disabled.
746TEST_F(PeerConnectionSimulcastMetricsTests, SimulcastDisabledIsNotLogged) {
747 auto local = CreatePeerConnectionWrapper();
748 auto remote = CreatePeerConnectionWrapper();
749 auto layers = CreateLayers({"1", "2", "3"}, true);
750 AddTransceiver(local.get(), layers);
751 ExchangeOfferAnswer(local.get(), remote.get(), layers);
752
753 EXPECT_EQ(0, metrics::NumSamples("WebRTC.PeerConnection.Simulcast.Disabled"));
754}
755
756const int kMaxLayersInMetricsTest = 8;
757
758// Checks that the number of send encodings is logged in a metric.
759TEST_P(PeerConnectionSimulcastMetricsTests, NumberOfSendEncodingsIsLogged) {
760 auto local = CreatePeerConnectionWrapper();
761 auto num_layers = GetParam();
762 auto layers = CreateLayers(num_layers, true);
763 AddTransceiver(local.get(), layers);
764 EXPECT_EQ(1, metrics::NumSamples(
765 "WebRTC.PeerConnection.Simulcast.NumberOfSendEncodings"));
766 EXPECT_EQ(1, metrics::NumEvents(
767 "WebRTC.PeerConnection.Simulcast.NumberOfSendEncodings",
768 num_layers));
769}
770
771INSTANTIATE_TEST_SUITE_P(NumberOfSendEncodings,
772 PeerConnectionSimulcastMetricsTests,
773 ::testing::Range(0, kMaxLayersInMetricsTest));
Ying Wangef3998f2019-12-09 13:06:53 +0100774#endif
Amit Hilbuchaa584152019-02-06 17:09:52 -0800775} // namespace webrtc