Merge mozilla-central to autoland. a=merge CLOSED TREE
[gecko.git] / third_party / libwebrtc / pc / peer_connection_encodings_integrationtest.cc
blob4a93e915df8b271d1b25e3b7856542a556cca874
1 /*
2 * Copyright 2023 The WebRTC project authors. All Rights Reserved.
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 */
11 #include <string>
12 #include <vector>
14 #include "absl/strings/match.h"
15 #include "absl/types/optional.h"
16 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
17 #include "api/audio_codecs/builtin_audio_encoder_factory.h"
18 #include "api/audio_codecs/opus_audio_decoder_factory.h"
19 #include "api/audio_codecs/opus_audio_encoder_factory.h"
20 #include "api/media_types.h"
21 #include "api/rtc_error.h"
22 #include "api/rtp_parameters.h"
23 #include "api/rtp_transceiver_direction.h"
24 #include "api/rtp_transceiver_interface.h"
25 #include "api/stats/rtcstats_objects.h"
26 #include "api/units/data_rate.h"
27 #include "api/video_codecs/video_decoder_factory_template.h"
28 #include "api/video_codecs/video_decoder_factory_template_dav1d_adapter.h"
29 #include "api/video_codecs/video_decoder_factory_template_libvpx_vp8_adapter.h"
30 #include "api/video_codecs/video_decoder_factory_template_libvpx_vp9_adapter.h"
31 #include "api/video_codecs/video_decoder_factory_template_open_h264_adapter.h"
32 #include "api/video_codecs/video_encoder_factory_template.h"
33 #include "api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h"
34 #include "api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h"
35 #include "api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h"
36 #include "api/video_codecs/video_encoder_factory_template_open_h264_adapter.h"
37 #include "pc/sdp_utils.h"
38 #include "pc/simulcast_description.h"
39 #include "pc/test/mock_peer_connection_observers.h"
40 #include "pc/test/peer_connection_test_wrapper.h"
41 #include "pc/test/simulcast_layer_util.h"
42 #include "rtc_base/gunit.h"
43 #include "rtc_base/physical_socket_server.h"
44 #include "test/gmock.h"
45 #include "test/gtest.h"
47 using ::testing::Eq;
48 using ::testing::Optional;
49 using ::testing::SizeIs;
50 using ::testing::StrCaseEq;
51 using ::testing::StrEq;
53 namespace webrtc {
55 namespace {
57 constexpr TimeDelta kDefaultTimeout = TimeDelta::Seconds(5);
58 // Most tests pass in 20-30 seconds, but some tests take longer such as AV1
59 // requiring additional ramp-up time (https://crbug.com/webrtc/15006) or SVC
60 // (LxTx_KEY) being slower than simulcast to send top spatial layer.
61 // TODO(https://crbug.com/webrtc/15076): Remove need for long rampup timeouts by
62 // using simulated time.
63 constexpr TimeDelta kLongTimeoutForRampingUp = TimeDelta::Minutes(1);
65 // The max bitrate 1500 kbps may be subject to change in the future. What we're
66 // interested in here is that all code paths that result in L1T3 result in the
67 // same target bitrate which does not exceed this limit.
68 constexpr DataRate kVp9ExpectedMaxBitrateForL1T3 =
69 DataRate::KilobitsPerSec(1500);
71 struct StringParamToString {
72 std::string operator()(const ::testing::TestParamInfo<std::string>& info) {
73 return info.param;
77 std::string GetCurrentCodecMimeType(
78 rtc::scoped_refptr<const RTCStatsReport> report,
79 const RTCOutboundRtpStreamStats& outbound_rtp) {
80 return outbound_rtp.codec_id.has_value()
81 ? *report->GetAs<RTCCodecStats>(*outbound_rtp.codec_id)->mime_type
82 : "";
85 struct RidAndResolution {
86 std::string rid;
87 uint32_t width;
88 uint32_t height;
91 const RTCOutboundRtpStreamStats* FindOutboundRtpByRid(
92 const std::vector<const RTCOutboundRtpStreamStats*>& outbound_rtps,
93 const absl::string_view& rid) {
94 for (const auto* outbound_rtp : outbound_rtps) {
95 if (outbound_rtp->rid.has_value() && *outbound_rtp->rid == rid) {
96 return outbound_rtp;
99 return nullptr;
102 } // namespace
104 class PeerConnectionEncodingsIntegrationTest : public ::testing::Test {
105 public:
106 PeerConnectionEncodingsIntegrationTest()
107 : background_thread_(std::make_unique<rtc::Thread>(&pss_)) {
108 RTC_CHECK(background_thread_->Start());
111 rtc::scoped_refptr<PeerConnectionTestWrapper> CreatePc() {
112 auto pc_wrapper = rtc::make_ref_counted<PeerConnectionTestWrapper>(
113 "pc", &pss_, background_thread_.get(), background_thread_.get());
114 pc_wrapper->CreatePc({}, CreateBuiltinAudioEncoderFactory(),
115 CreateBuiltinAudioDecoderFactory());
116 return pc_wrapper;
119 rtc::scoped_refptr<RtpTransceiverInterface> AddTransceiverWithSimulcastLayers(
120 rtc::scoped_refptr<PeerConnectionTestWrapper> local,
121 rtc::scoped_refptr<PeerConnectionTestWrapper> remote,
122 std::vector<cricket::SimulcastLayer> init_layers) {
123 rtc::scoped_refptr<MediaStreamInterface> stream = local->GetUserMedia(
124 /*audio=*/false, cricket::AudioOptions(), /*video=*/true,
125 {.width = 1280, .height = 720});
126 rtc::scoped_refptr<VideoTrackInterface> track = stream->GetVideoTracks()[0];
128 RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>>
129 transceiver_or_error = local->pc()->AddTransceiver(
130 track, CreateTransceiverInit(init_layers));
131 EXPECT_TRUE(transceiver_or_error.ok());
132 return transceiver_or_error.value();
135 bool HasSenderVideoCodecCapability(
136 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
137 absl::string_view codec_name) {
138 std::vector<RtpCodecCapability> codecs =
139 pc_wrapper->pc_factory()
140 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
141 .codecs;
142 return std::find_if(codecs.begin(), codecs.end(),
143 [&codec_name](const RtpCodecCapability& codec) {
144 return absl::EqualsIgnoreCase(codec.name, codec_name);
145 }) != codecs.end();
148 std::vector<RtpCodecCapability> GetCapabilitiesAndRestrictToCodec(
149 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
150 absl::string_view codec_name) {
151 std::vector<RtpCodecCapability> codecs =
152 pc_wrapper->pc_factory()
153 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
154 .codecs;
155 codecs.erase(std::remove_if(codecs.begin(), codecs.end(),
156 [&codec_name](const RtpCodecCapability& codec) {
157 return !codec.IsResiliencyCodec() &&
158 !absl::EqualsIgnoreCase(codec.name,
159 codec_name);
161 codecs.end());
162 RTC_DCHECK(std::find_if(codecs.begin(), codecs.end(),
163 [&codec_name](const RtpCodecCapability& codec) {
164 return absl::EqualsIgnoreCase(codec.name,
165 codec_name);
166 }) != codecs.end());
167 return codecs;
170 void ExchangeIceCandidates(
171 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper,
172 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper) {
173 local_pc_wrapper->SignalOnIceCandidateReady.connect(
174 remote_pc_wrapper.get(), &PeerConnectionTestWrapper::AddIceCandidate);
175 remote_pc_wrapper->SignalOnIceCandidateReady.connect(
176 local_pc_wrapper.get(), &PeerConnectionTestWrapper::AddIceCandidate);
179 void NegotiateWithSimulcastTweaks(
180 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper,
181 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper) {
182 // Create and set offer for `local_pc_wrapper`.
183 std::unique_ptr<SessionDescriptionInterface> offer =
184 CreateOffer(local_pc_wrapper);
185 rtc::scoped_refptr<MockSetSessionDescriptionObserver> p1 =
186 SetLocalDescription(local_pc_wrapper, offer.get());
187 // Modify the offer before handoff because `remote_pc_wrapper` only supports
188 // receiving singlecast.
189 cricket::SimulcastDescription simulcast_description =
190 RemoveSimulcast(offer.get());
191 rtc::scoped_refptr<MockSetSessionDescriptionObserver> p2 =
192 SetRemoteDescription(remote_pc_wrapper, offer.get());
193 EXPECT_TRUE(Await({p1, p2}));
195 // Create and set answer for `remote_pc_wrapper`.
196 std::unique_ptr<SessionDescriptionInterface> answer =
197 CreateAnswer(remote_pc_wrapper);
198 p1 = SetLocalDescription(remote_pc_wrapper, answer.get());
199 // Modify the answer before handoff because `local_pc_wrapper` should still
200 // send simulcast.
201 cricket::MediaContentDescription* mcd_answer =
202 answer->description()->contents()[0].media_description();
203 mcd_answer->mutable_streams().clear();
204 std::vector<cricket::SimulcastLayer> simulcast_layers =
205 simulcast_description.send_layers().GetAllLayers();
206 cricket::SimulcastLayerList& receive_layers =
207 mcd_answer->simulcast_description().receive_layers();
208 for (const auto& layer : simulcast_layers) {
209 receive_layers.AddLayer(layer);
211 p2 = SetRemoteDescription(local_pc_wrapper, answer.get());
212 EXPECT_TRUE(Await({p1, p2}));
215 rtc::scoped_refptr<const RTCStatsReport> GetStats(
216 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper) {
217 auto callback = rtc::make_ref_counted<MockRTCStatsCollectorCallback>();
218 pc_wrapper->pc()->GetStats(callback.get());
219 EXPECT_TRUE_WAIT(callback->called(), kDefaultTimeout.ms());
220 return callback->report();
223 bool IsCodecIdDifferent(
224 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
225 size_t index,
226 const std::string& codec_id) {
227 return IsCodecIdDifferentWithScalabilityMode(pc_wrapper, index, codec_id,
228 absl::nullopt);
231 bool IsCodecIdDifferentWithScalabilityMode(
232 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
233 size_t index,
234 const std::string& codec_id,
235 absl::optional<std::string> wanted_scalability_mode) {
236 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(pc_wrapper);
237 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
238 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
239 return outbound_rtps[index]->codec_id.value() != codec_id &&
240 (!wanted_scalability_mode ||
241 (outbound_rtps[index]->scalability_mode.has_value() &&
242 outbound_rtps[index]->scalability_mode.value() ==
243 wanted_scalability_mode));
246 bool HasOutboundRtpBytesSent(
247 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
248 size_t num_layers) {
249 return HasOutboundRtpBytesSent(pc_wrapper, num_layers, num_layers);
252 bool HasOutboundRtpBytesSent(
253 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
254 size_t num_layers,
255 size_t num_active_layers) {
256 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(pc_wrapper);
257 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
258 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
259 if (outbound_rtps.size() != num_layers) {
260 return false;
262 size_t num_sending_layers = 0;
263 for (const auto* outbound_rtp : outbound_rtps) {
264 if (outbound_rtp->bytes_sent.has_value() &&
265 *outbound_rtp->bytes_sent > 0u) {
266 ++num_sending_layers;
269 return num_sending_layers == num_active_layers;
272 bool HasOutboundRtpWithRidAndScalabilityMode(
273 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
274 absl::string_view rid,
275 absl::string_view expected_scalability_mode,
276 uint32_t frame_height) {
277 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(pc_wrapper);
278 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
279 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
280 auto* outbound_rtp = FindOutboundRtpByRid(outbound_rtps, rid);
281 if (!outbound_rtp || !outbound_rtp->scalability_mode.has_value() ||
282 *outbound_rtp->scalability_mode != expected_scalability_mode) {
283 return false;
285 if (outbound_rtp->frame_height.has_value()) {
286 RTC_LOG(LS_INFO) << "Waiting for target resolution (" << frame_height
287 << "p). Currently at " << *outbound_rtp->frame_height
288 << "p...";
289 } else {
290 RTC_LOG(LS_INFO)
291 << "Waiting for target resolution. No frames encoded yet...";
293 if (!outbound_rtp->frame_height.has_value() ||
294 *outbound_rtp->frame_height != frame_height) {
295 // Sleep to avoid log spam when this is used in ASSERT_TRUE_WAIT().
296 rtc::Thread::Current()->SleepMs(1000);
297 return false;
299 return true;
302 bool OutboundRtpResolutionsAreLessThanOrEqualToExpectations(
303 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
304 std::vector<RidAndResolution> resolutions) {
305 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(pc_wrapper);
306 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
307 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
308 for (const RidAndResolution& resolution : resolutions) {
309 const RTCOutboundRtpStreamStats* outbound_rtp = nullptr;
310 if (!resolution.rid.empty()) {
311 outbound_rtp = FindOutboundRtpByRid(outbound_rtps, resolution.rid);
312 } else if (outbound_rtps.size() == 1u) {
313 outbound_rtp = outbound_rtps[0];
315 if (!outbound_rtp || !outbound_rtp->frame_width.has_value() ||
316 !outbound_rtp->frame_height.has_value()) {
317 // RTP not found by rid or has not encoded a frame yet.
318 RTC_LOG(LS_ERROR) << "rid=" << resolution.rid << " does not have "
319 << "resolution metrics";
320 return false;
322 if (*outbound_rtp->frame_width > resolution.width ||
323 *outbound_rtp->frame_height > resolution.height) {
324 RTC_LOG(LS_ERROR) << "rid=" << resolution.rid << " is "
325 << *outbound_rtp->frame_width << "x"
326 << *outbound_rtp->frame_height
327 << ", this is greater than the "
328 << "expected " << resolution.width << "x"
329 << resolution.height;
330 return false;
333 return true;
336 protected:
337 std::unique_ptr<SessionDescriptionInterface> CreateOffer(
338 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper) {
339 auto observer =
340 rtc::make_ref_counted<MockCreateSessionDescriptionObserver>();
341 pc_wrapper->pc()->CreateOffer(observer.get(), {});
342 EXPECT_EQ_WAIT(true, observer->called(), kDefaultTimeout.ms());
343 return observer->MoveDescription();
346 std::unique_ptr<SessionDescriptionInterface> CreateAnswer(
347 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper) {
348 auto observer =
349 rtc::make_ref_counted<MockCreateSessionDescriptionObserver>();
350 pc_wrapper->pc()->CreateAnswer(observer.get(), {});
351 EXPECT_EQ_WAIT(true, observer->called(), kDefaultTimeout.ms());
352 return observer->MoveDescription();
355 rtc::scoped_refptr<MockSetSessionDescriptionObserver> SetLocalDescription(
356 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
357 SessionDescriptionInterface* sdp) {
358 auto observer = rtc::make_ref_counted<MockSetSessionDescriptionObserver>();
359 pc_wrapper->pc()->SetLocalDescription(
360 observer.get(), CloneSessionDescription(sdp).release());
361 return observer;
364 rtc::scoped_refptr<MockSetSessionDescriptionObserver> SetRemoteDescription(
365 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
366 SessionDescriptionInterface* sdp) {
367 auto observer = rtc::make_ref_counted<MockSetSessionDescriptionObserver>();
368 pc_wrapper->pc()->SetRemoteDescription(
369 observer.get(), CloneSessionDescription(sdp).release());
370 return observer;
373 // To avoid ICE candidates arriving before the remote endpoint has received
374 // the offer it is important to SetLocalDescription() and
375 // SetRemoteDescription() are kicked off without awaiting in-between. This
376 // helper is used to await multiple observers.
377 bool Await(std::vector<rtc::scoped_refptr<MockSetSessionDescriptionObserver>>
378 observers) {
379 for (auto& observer : observers) {
380 EXPECT_EQ_WAIT(true, observer->called(), kDefaultTimeout.ms());
381 if (!observer->result()) {
382 return false;
385 return true;
388 rtc::PhysicalSocketServer pss_;
389 std::unique_ptr<rtc::Thread> background_thread_;
392 TEST_F(PeerConnectionEncodingsIntegrationTest,
393 VP8_SingleEncodingDefaultsToL1T1) {
394 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
395 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
396 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
398 std::vector<cricket::SimulcastLayer> layers =
399 CreateLayers({"f"}, /*active=*/true);
400 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
401 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
402 layers);
403 std::vector<RtpCodecCapability> codecs =
404 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP8");
405 transceiver->SetCodecPreferences(codecs);
407 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
408 local_pc_wrapper->WaitForConnection();
409 remote_pc_wrapper->WaitForConnection();
411 // Wait until media is flowing.
412 ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 1u),
413 kDefaultTimeout.ms());
414 EXPECT_TRUE(OutboundRtpResolutionsAreLessThanOrEqualToExpectations(
415 local_pc_wrapper, {{"", 1280, 720}}));
416 // Verify codec and scalability mode.
417 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
418 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
419 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
420 ASSERT_THAT(outbound_rtps, SizeIs(1u));
421 EXPECT_THAT(GetCurrentCodecMimeType(report, *outbound_rtps[0]),
422 StrCaseEq("video/VP8"));
423 EXPECT_THAT(*outbound_rtps[0]->scalability_mode, StrEq("L1T1"));
426 TEST_F(PeerConnectionEncodingsIntegrationTest,
427 VP8_RejectsSvcAndDefaultsToL1T1) {
428 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
429 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
430 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
432 std::vector<cricket::SimulcastLayer> layers =
433 CreateLayers({"f"}, /*active=*/true);
434 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
435 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
436 layers);
437 // Restricting codecs restricts what SetParameters() will accept or reject.
438 std::vector<RtpCodecCapability> codecs =
439 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP8");
440 transceiver->SetCodecPreferences(codecs);
441 // Attempt SVC (L3T3_KEY). This is not possible because only VP8 is up for
442 // negotiation and VP8 does not support it.
443 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
444 RtpParameters parameters = sender->GetParameters();
445 ASSERT_EQ(parameters.encodings.size(), 1u);
446 parameters.encodings[0].scalability_mode = "L3T3_KEY";
447 parameters.encodings[0].scale_resolution_down_by = 1;
448 EXPECT_FALSE(sender->SetParameters(parameters).ok());
449 // `scalability_mode` remains unset because SetParameters() failed.
450 parameters = sender->GetParameters();
451 ASSERT_EQ(parameters.encodings.size(), 1u);
452 EXPECT_THAT(parameters.encodings[0].scalability_mode, Eq(absl::nullopt));
454 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
455 local_pc_wrapper->WaitForConnection();
456 remote_pc_wrapper->WaitForConnection();
458 // Wait until media is flowing.
459 ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 1u),
460 kDefaultTimeout.ms());
461 // When `scalability_mode` is not set, VP8 defaults to L1T1.
462 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
463 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
464 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
465 ASSERT_THAT(outbound_rtps, SizeIs(1u));
466 EXPECT_THAT(GetCurrentCodecMimeType(report, *outbound_rtps[0]),
467 StrCaseEq("video/VP8"));
468 EXPECT_THAT(*outbound_rtps[0]->scalability_mode, StrEq("L1T1"));
469 // GetParameters() confirms `scalability_mode` is still not set.
470 parameters = sender->GetParameters();
471 ASSERT_EQ(parameters.encodings.size(), 1u);
472 EXPECT_THAT(parameters.encodings[0].scalability_mode, Eq(absl::nullopt));
475 TEST_F(PeerConnectionEncodingsIntegrationTest,
476 VP8_FallbackFromSvcResultsInL1T2) {
477 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
478 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
479 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
481 std::vector<cricket::SimulcastLayer> layers =
482 CreateLayers({"f"}, /*active=*/true);
483 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
484 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
485 layers);
486 // Verify test assumption that VP8 is first in the list, but don't modify the
487 // codec preferences because we want the sender to think SVC is a possibility.
488 std::vector<RtpCodecCapability> codecs =
489 local_pc_wrapper->pc_factory()
490 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
491 .codecs;
492 EXPECT_THAT(codecs[0].name, StrCaseEq("VP8"));
493 // Attempt SVC (L3T3_KEY), which is not possible with VP8, but the sender does
494 // not yet know which codec we'll use so the parameters will be accepted.
495 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
496 RtpParameters parameters = sender->GetParameters();
497 ASSERT_EQ(parameters.encodings.size(), 1u);
498 parameters.encodings[0].scalability_mode = "L3T3_KEY";
499 parameters.encodings[0].scale_resolution_down_by = 1;
500 EXPECT_TRUE(sender->SetParameters(parameters).ok());
501 // Verify fallback has not happened yet.
502 parameters = sender->GetParameters();
503 ASSERT_EQ(parameters.encodings.size(), 1u);
504 EXPECT_THAT(parameters.encodings[0].scalability_mode,
505 Optional(std::string("L3T3_KEY")));
507 // Negotiate, this results in VP8 being picked and fallback happening.
508 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
509 local_pc_wrapper->WaitForConnection();
510 remote_pc_wrapper->WaitForConnection();
511 // `scalaiblity_mode` is assigned the fallback value "L1T2" which is different
512 // than the default of absl::nullopt.
513 parameters = sender->GetParameters();
514 ASSERT_EQ(parameters.encodings.size(), 1u);
515 EXPECT_THAT(parameters.encodings[0].scalability_mode,
516 Optional(std::string("L1T2")));
518 // Wait until media is flowing, no significant time needed because we only
519 // have one layer.
520 ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 1u),
521 kDefaultTimeout.ms());
522 // GetStats() confirms "L1T2" is used which is different than the "L1T1"
523 // default or the "L3T3_KEY" that was attempted.
524 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
525 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
526 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
527 ASSERT_THAT(outbound_rtps, SizeIs(1u));
528 EXPECT_THAT(GetCurrentCodecMimeType(report, *outbound_rtps[0]),
529 StrCaseEq("video/VP8"));
530 EXPECT_THAT(*outbound_rtps[0]->scalability_mode, StrEq("L1T2"));
533 // The legacy SVC path is triggered when VP9 us used, but `scalability_mode` has
534 // not been specified.
535 // TODO(https://crbug.com/webrtc/14889): When legacy VP9 SVC path has been
536 // deprecated and removed, update this test to assert that simulcast is used
537 // (i.e. VP9 is not treated differently than VP8).
538 TEST_F(PeerConnectionEncodingsIntegrationTest,
539 VP9_LegacySvcWhenScalabilityModeNotSpecified) {
540 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
541 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
542 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
544 std::vector<cricket::SimulcastLayer> layers =
545 CreateLayers({"f", "h", "q"}, /*active=*/true);
546 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
547 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
548 layers);
549 std::vector<RtpCodecCapability> codecs =
550 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9");
551 transceiver->SetCodecPreferences(codecs);
553 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
554 local_pc_wrapper->WaitForConnection();
555 remote_pc_wrapper->WaitForConnection();
557 // Wait until media is flowing. We only expect a single RTP stream.
558 // We expect to see bytes flowing almost immediately on the lowest layer.
559 ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 1u),
560 kDefaultTimeout.ms());
561 // Wait until scalability mode is reported and expected resolution reached.
562 // Ramp up time may be significant.
563 ASSERT_TRUE_WAIT(HasOutboundRtpWithRidAndScalabilityMode(
564 local_pc_wrapper, "f", "L3T3_KEY", 720),
565 kLongTimeoutForRampingUp.ms());
567 // Despite SVC being used on a single RTP stream, GetParameters() returns the
568 // three encodings that we configured earlier (this is not spec-compliant but
569 // it is how legacy SVC behaves).
570 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
571 std::vector<RtpEncodingParameters> encodings =
572 sender->GetParameters().encodings;
573 ASSERT_EQ(encodings.size(), 3u);
574 // When legacy SVC is used, `scalability_mode` is not specified.
575 EXPECT_FALSE(encodings[0].scalability_mode.has_value());
576 EXPECT_FALSE(encodings[1].scalability_mode.has_value());
577 EXPECT_FALSE(encodings[2].scalability_mode.has_value());
580 // The spec-compliant way to configure SVC for a single stream. The expected
581 // outcome is the same as for the legacy SVC case except that we only have one
582 // encoding in GetParameters().
583 TEST_F(PeerConnectionEncodingsIntegrationTest,
584 VP9_StandardSvcWithOnlyOneEncoding) {
585 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
586 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
587 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
589 std::vector<cricket::SimulcastLayer> layers =
590 CreateLayers({"f"}, /*active=*/true);
591 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
592 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
593 layers);
594 std::vector<RtpCodecCapability> codecs =
595 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9");
596 transceiver->SetCodecPreferences(codecs);
597 // Configure SVC, a.k.a. "L3T3_KEY".
598 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
599 RtpParameters parameters = sender->GetParameters();
600 ASSERT_EQ(parameters.encodings.size(), 1u);
601 parameters.encodings[0].scalability_mode = "L3T3_KEY";
602 parameters.encodings[0].scale_resolution_down_by = 1;
603 EXPECT_TRUE(sender->SetParameters(parameters).ok());
605 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
606 local_pc_wrapper->WaitForConnection();
607 remote_pc_wrapper->WaitForConnection();
609 // Wait until media is flowing. We only expect a single RTP stream.
610 // We expect to see bytes flowing almost immediately on the lowest layer.
611 ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 1u),
612 kDefaultTimeout.ms());
613 EXPECT_TRUE(OutboundRtpResolutionsAreLessThanOrEqualToExpectations(
614 local_pc_wrapper, {{"", 1280, 720}}));
615 // Verify codec and scalability mode.
616 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
617 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
618 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
619 ASSERT_THAT(outbound_rtps, SizeIs(1u));
620 EXPECT_THAT(GetCurrentCodecMimeType(report, *outbound_rtps[0]),
621 StrCaseEq("video/VP9"));
622 EXPECT_THAT(*outbound_rtps[0]->scalability_mode, StrEq("L3T3_KEY"));
624 // GetParameters() is consistent with what we asked for and got.
625 parameters = sender->GetParameters();
626 ASSERT_EQ(parameters.encodings.size(), 1u);
627 EXPECT_THAT(parameters.encodings[0].scalability_mode,
628 Optional(std::string("L3T3_KEY")));
631 // The {active,inactive,inactive} case is technically simulcast but since we
632 // only have one active stream, we're able to do SVC (multiple spatial layers
633 // is not supported if multiple encodings are active). The expected outcome is
634 // the same as above except we end up with two inactive RTP streams which are
635 // observable in GetStats().
636 TEST_F(PeerConnectionEncodingsIntegrationTest,
637 VP9_StandardSvcWithSingleActiveEncoding) {
638 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
639 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
640 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
642 std::vector<cricket::SimulcastLayer> layers =
643 CreateLayers({"f", "h", "q"}, /*active=*/true);
644 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
645 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
646 layers);
647 std::vector<RtpCodecCapability> codecs =
648 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9");
649 transceiver->SetCodecPreferences(codecs);
650 // Configure SVC, a.k.a. "L3T3_KEY".
651 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
652 RtpParameters parameters = sender->GetParameters();
653 ASSERT_THAT(parameters.encodings, SizeIs(3));
654 parameters.encodings[0].scalability_mode = "L3T3_KEY";
655 parameters.encodings[0].scale_resolution_down_by = 1;
656 parameters.encodings[1].active = false;
657 parameters.encodings[2].active = false;
658 EXPECT_TRUE(sender->SetParameters(parameters).ok());
660 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
661 local_pc_wrapper->WaitForConnection();
662 remote_pc_wrapper->WaitForConnection();
664 // Since the standard API is configuring simulcast we get three outbound-rtps,
665 // but only one is active.
666 ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 3u, 1u),
667 kDefaultTimeout.ms());
668 // Wait until scalability mode is reported and expected resolution reached.
669 // Ramp up time is significant.
670 ASSERT_TRUE_WAIT(HasOutboundRtpWithRidAndScalabilityMode(
671 local_pc_wrapper, "f", "L3T3_KEY", 720),
672 kLongTimeoutForRampingUp.ms());
674 // GetParameters() is consistent with what we asked for and got.
675 parameters = sender->GetParameters();
676 ASSERT_THAT(parameters.encodings, SizeIs(3));
677 EXPECT_THAT(parameters.encodings[0].scalability_mode,
678 Optional(std::string("L3T3_KEY")));
679 EXPECT_FALSE(parameters.encodings[1].scalability_mode.has_value());
680 EXPECT_FALSE(parameters.encodings[2].scalability_mode.has_value());
683 // Exercise common path where `scalability_mode` is not specified until after
684 // negotiation, requring us to recreate the stream when the number of streams
685 // changes from 1 (legacy SVC) to 3 (standard simulcast).
686 TEST_F(PeerConnectionEncodingsIntegrationTest,
687 VP9_SwitchFromLegacySvcToStandardSingleActiveEncodingSvc) {
688 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
689 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
690 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
692 std::vector<cricket::SimulcastLayer> layers =
693 CreateLayers({"f", "h", "q"}, /*active=*/true);
694 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
695 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
696 layers);
697 std::vector<RtpCodecCapability> codecs =
698 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9");
699 transceiver->SetCodecPreferences(codecs);
701 // The original negotiation triggers legacy SVC because we didn't specify
702 // any scalability mode.
703 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
704 local_pc_wrapper->WaitForConnection();
705 remote_pc_wrapper->WaitForConnection();
707 // Switch to the standard mode. Despite only having a single active stream in
708 // both cases, this internally reconfigures from 1 stream to 3 streams.
709 // Test coverage for https://crbug.com/webrtc/15016.
710 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
711 RtpParameters parameters = sender->GetParameters();
712 ASSERT_THAT(parameters.encodings, SizeIs(3));
713 parameters.encodings[0].active = true;
714 parameters.encodings[0].scalability_mode = "L2T2_KEY";
715 parameters.encodings[0].scale_resolution_down_by = 2.0;
716 parameters.encodings[1].active = false;
717 parameters.encodings[1].scalability_mode = absl::nullopt;
718 parameters.encodings[2].active = false;
719 parameters.encodings[2].scalability_mode = absl::nullopt;
720 sender->SetParameters(parameters);
722 // Since the standard API is configuring simulcast we get three outbound-rtps,
723 // but only one is active.
724 ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 3u, 1u),
725 kDefaultTimeout.ms());
726 // Wait until scalability mode is reported and expected resolution reached.
727 // Ramp up time may be significant.
728 ASSERT_TRUE_WAIT(HasOutboundRtpWithRidAndScalabilityMode(
729 local_pc_wrapper, "f", "L2T2_KEY", 720 / 2),
730 kLongTimeoutForRampingUp.ms());
732 // GetParameters() does not report any fallback.
733 parameters = sender->GetParameters();
734 ASSERT_THAT(parameters.encodings, SizeIs(3));
735 EXPECT_THAT(parameters.encodings[0].scalability_mode,
736 Optional(std::string("L2T2_KEY")));
737 EXPECT_FALSE(parameters.encodings[1].scalability_mode.has_value());
738 EXPECT_FALSE(parameters.encodings[2].scalability_mode.has_value());
741 TEST_F(PeerConnectionEncodingsIntegrationTest,
742 VP9_AllLayersInactive_LegacySvc) {
743 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
744 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
745 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
747 std::vector<cricket::SimulcastLayer> layers =
748 CreateLayers({"f", "h", "q"}, /*active=*/true);
749 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
750 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
751 layers);
752 std::vector<RtpCodecCapability> codecs =
753 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9");
754 transceiver->SetCodecPreferences(codecs);
756 // Legacy SVC mode and all layers inactive.
757 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
758 RtpParameters parameters = sender->GetParameters();
759 ASSERT_THAT(parameters.encodings, SizeIs(3));
760 parameters.encodings[0].active = false;
761 parameters.encodings[1].active = false;
762 parameters.encodings[2].active = false;
763 sender->SetParameters(parameters);
765 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
766 local_pc_wrapper->WaitForConnection();
767 remote_pc_wrapper->WaitForConnection();
769 // Ensure no media is flowing (1 second should be enough).
770 rtc::Thread::Current()->SleepMs(1000);
771 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
772 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
773 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
774 ASSERT_THAT(outbound_rtps, SizeIs(1u));
775 EXPECT_EQ(*outbound_rtps[0]->bytes_sent, 0u);
778 TEST_F(PeerConnectionEncodingsIntegrationTest,
779 VP9_AllLayersInactive_StandardSvc) {
780 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
781 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
782 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
784 std::vector<cricket::SimulcastLayer> layers =
785 CreateLayers({"f", "h", "q"}, /*active=*/true);
786 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
787 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
788 layers);
789 std::vector<RtpCodecCapability> codecs =
790 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9");
791 transceiver->SetCodecPreferences(codecs);
793 // Standard mode and all layers inactive.
794 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
795 RtpParameters parameters = sender->GetParameters();
796 ASSERT_THAT(parameters.encodings, SizeIs(3));
797 parameters.encodings[0].scalability_mode = "L3T3_KEY";
798 parameters.encodings[0].scale_resolution_down_by = 1;
799 parameters.encodings[0].active = false;
800 parameters.encodings[1].active = false;
801 parameters.encodings[2].active = false;
802 sender->SetParameters(parameters);
804 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
805 local_pc_wrapper->WaitForConnection();
806 remote_pc_wrapper->WaitForConnection();
808 // Ensure no media is flowing (1 second should be enough).
809 rtc::Thread::Current()->SleepMs(1000);
810 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
811 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
812 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
813 ASSERT_THAT(outbound_rtps, SizeIs(3u));
814 EXPECT_EQ(*outbound_rtps[0]->bytes_sent, 0u);
815 EXPECT_EQ(*outbound_rtps[1]->bytes_sent, 0u);
816 EXPECT_EQ(*outbound_rtps[2]->bytes_sent, 0u);
819 TEST_F(PeerConnectionEncodingsIntegrationTest, VP9_TargetBitrate_LegacyL1T3) {
820 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
821 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
822 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
824 std::vector<cricket::SimulcastLayer> layers =
825 CreateLayers({"f", "h", "q"}, /*active=*/true);
826 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
827 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
828 layers);
829 std::vector<RtpCodecCapability> codecs =
830 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9");
831 transceiver->SetCodecPreferences(codecs);
833 // In legacy SVC, disabling the bottom two layers encodings is interpreted as
834 // disabling the bottom two spatial layers resulting in L1T3.
835 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
836 RtpParameters parameters = sender->GetParameters();
837 parameters.encodings[0].active = false;
838 parameters.encodings[1].active = false;
839 parameters.encodings[2].active = true;
840 sender->SetParameters(parameters);
842 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
843 local_pc_wrapper->WaitForConnection();
844 remote_pc_wrapper->WaitForConnection();
846 // Wait until 720p L1T3 has ramped up to 720p. It may take additional time
847 // for the target bitrate to reach its maximum.
848 ASSERT_TRUE_WAIT(HasOutboundRtpWithRidAndScalabilityMode(local_pc_wrapper,
849 "f", "L1T3", 720),
850 kLongTimeoutForRampingUp.ms());
852 // The target bitrate typically reaches `kVp9ExpectedMaxBitrateForL1T3`
853 // in a short period of time. However to reduce risk of flakiness in bot
854 // environments, this test only fails if we we exceed the expected target.
855 rtc::Thread::Current()->SleepMs(1000);
856 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
857 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
858 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
859 ASSERT_THAT(outbound_rtps, SizeIs(1));
860 DataRate target_bitrate =
861 DataRate::BitsPerSec(*outbound_rtps[0]->target_bitrate);
862 EXPECT_LE(target_bitrate.kbps(), kVp9ExpectedMaxBitrateForL1T3.kbps());
865 // Test coverage for https://crbug.com/1455039.
866 TEST_F(PeerConnectionEncodingsIntegrationTest, VP9_TargetBitrate_StandardL1T3) {
867 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
868 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
869 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
871 std::vector<cricket::SimulcastLayer> layers =
872 CreateLayers({"f", "h", "q"}, /*active=*/true);
873 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
874 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
875 layers);
876 std::vector<RtpCodecCapability> codecs =
877 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9");
878 transceiver->SetCodecPreferences(codecs);
880 // With standard APIs, L1T3 is explicitly specified and the encodings refers
881 // to the RTP streams, not the spatial layers. The end result should be
882 // equivalent to the legacy L1T3 case.
883 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
884 RtpParameters parameters = sender->GetParameters();
885 parameters.encodings[0].active = true;
886 parameters.encodings[0].scale_resolution_down_by = 1.0;
887 parameters.encodings[0].scalability_mode = "L1T3";
888 parameters.encodings[1].active = false;
889 parameters.encodings[2].active = false;
890 sender->SetParameters(parameters);
892 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
893 local_pc_wrapper->WaitForConnection();
894 remote_pc_wrapper->WaitForConnection();
896 // Wait until 720p L1T3 has ramped up to 720p. It may take additional time
897 // for the target bitrate to reach its maximum.
898 ASSERT_TRUE_WAIT(HasOutboundRtpWithRidAndScalabilityMode(local_pc_wrapper,
899 "f", "L1T3", 720),
900 kLongTimeoutForRampingUp.ms());
902 // The target bitrate typically reaches `kVp9ExpectedMaxBitrateForL1T3`
903 // in a short period of time. However to reduce risk of flakiness in bot
904 // environments, this test only fails if we we exceed the expected target.
905 rtc::Thread::Current()->SleepMs(1000);
906 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
907 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
908 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
909 ASSERT_THAT(outbound_rtps, SizeIs(3));
910 auto* outbound_rtp = FindOutboundRtpByRid(outbound_rtps, "f");
911 ASSERT_TRUE(outbound_rtp);
912 DataRate target_bitrate = DataRate::BitsPerSec(*outbound_rtp->target_bitrate);
913 EXPECT_LE(target_bitrate.kbps(), kVp9ExpectedMaxBitrateForL1T3.kbps());
916 TEST_F(PeerConnectionEncodingsIntegrationTest,
917 SimulcastProducesUniqueSsrcAndRtxSsrcs) {
918 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
919 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
920 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
922 std::vector<cricket::SimulcastLayer> layers =
923 CreateLayers({"f", "h", "q"}, /*active=*/true);
924 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
925 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
926 layers);
927 std::vector<RtpCodecCapability> codecs =
928 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP8");
929 transceiver->SetCodecPreferences(codecs);
931 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
932 local_pc_wrapper->WaitForConnection();
933 remote_pc_wrapper->WaitForConnection();
935 // Wait until media is flowing on all three layers.
936 // Ramp up time is needed before all three layers are sending.
937 ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 3u),
938 kLongTimeoutForRampingUp.ms());
939 // Verify SSRCs and RTX SSRCs.
940 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
941 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
942 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
943 ASSERT_THAT(outbound_rtps, SizeIs(3u));
945 std::set<uint32_t> ssrcs;
946 std::set<uint32_t> rtx_ssrcs;
947 for (const auto& outbound_rtp : outbound_rtps) {
948 ASSERT_TRUE(outbound_rtp->ssrc.has_value());
949 ASSERT_TRUE(outbound_rtp->rtx_ssrc.has_value());
950 ssrcs.insert(*outbound_rtp->ssrc);
951 rtx_ssrcs.insert(*outbound_rtp->rtx_ssrc);
953 EXPECT_EQ(ssrcs.size(), 3u);
954 EXPECT_EQ(rtx_ssrcs.size(), 3u);
957 TEST_F(PeerConnectionEncodingsIntegrationTest,
958 EncodingParameterCodecIsEmptyWhenCreatedAudio) {
959 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
961 auto transceiver_or_error =
962 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
963 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
964 transceiver_or_error.MoveValue();
965 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
966 EXPECT_FALSE(parameters.encodings[0].codec.has_value());
969 TEST_F(PeerConnectionEncodingsIntegrationTest,
970 EncodingParameterCodecIsEmptyWhenCreatedVideo) {
971 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
973 auto transceiver_or_error =
974 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
975 rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
976 transceiver_or_error.MoveValue();
977 RtpParameters parameters = video_transceiver->sender()->GetParameters();
978 EXPECT_FALSE(parameters.encodings[0].codec.has_value());
981 TEST_F(PeerConnectionEncodingsIntegrationTest,
982 EncodingParameterCodecIsSetByAddTransceiverAudio) {
983 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
984 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
985 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
987 rtc::scoped_refptr<MediaStreamInterface> stream =
988 local_pc_wrapper->GetUserMedia(
989 /*audio=*/true, {}, /*video=*/false, {});
990 rtc::scoped_refptr<AudioTrackInterface> track = stream->GetAudioTracks()[0];
992 absl::optional<RtpCodecCapability> pcmu =
993 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
994 "pcmu");
995 ASSERT_TRUE(pcmu);
997 RtpTransceiverInit init;
998 init.direction = RtpTransceiverDirection::kSendOnly;
999 RtpEncodingParameters encoding_parameters;
1000 encoding_parameters.codec = pcmu;
1001 init.send_encodings.push_back(encoding_parameters);
1003 auto transceiver_or_error =
1004 local_pc_wrapper->pc()->AddTransceiver(track, init);
1005 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1006 transceiver_or_error.MoveValue();
1007 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
1008 EXPECT_EQ(*parameters.encodings[0].codec, *pcmu);
1010 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1011 local_pc_wrapper->WaitForConnection();
1012 remote_pc_wrapper->WaitForConnection();
1014 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
1015 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
1016 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1017 ASSERT_EQ(outbound_rtps.size(), 1u);
1018 std::string codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
1019 EXPECT_STRCASEEQ(("audio/" + pcmu->name).c_str(), codec_name.c_str());
1022 TEST_F(PeerConnectionEncodingsIntegrationTest,
1023 EncodingParameterCodecIsSetByAddTransceiverVideo) {
1024 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1025 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1026 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1028 rtc::scoped_refptr<MediaStreamInterface> stream =
1029 local_pc_wrapper->GetUserMedia(
1030 /*audio=*/false, {}, /*video=*/true, {.width = 1280, .height = 720});
1031 rtc::scoped_refptr<VideoTrackInterface> track = stream->GetVideoTracks()[0];
1033 absl::optional<RtpCodecCapability> vp9 =
1034 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1035 "vp9");
1036 ASSERT_TRUE(vp9);
1038 RtpTransceiverInit init;
1039 init.direction = RtpTransceiverDirection::kSendOnly;
1040 RtpEncodingParameters encoding_parameters;
1041 encoding_parameters.codec = vp9;
1042 encoding_parameters.scalability_mode = "L3T3";
1043 init.send_encodings.push_back(encoding_parameters);
1045 auto transceiver_or_error =
1046 local_pc_wrapper->pc()->AddTransceiver(track, init);
1047 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1048 transceiver_or_error.MoveValue();
1049 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
1050 EXPECT_EQ(*parameters.encodings[0].codec, *vp9);
1052 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1053 local_pc_wrapper->WaitForConnection();
1054 remote_pc_wrapper->WaitForConnection();
1056 EXPECT_TRUE_WAIT(
1057 IsCodecIdDifferentWithScalabilityMode(local_pc_wrapper, 0, "", "L3T3"),
1058 kDefaultTimeout.ms());
1060 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
1061 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
1062 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1063 ASSERT_EQ(outbound_rtps.size(), 1u);
1064 std::string codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
1065 EXPECT_STRCASEEQ(("video/" + vp9->name).c_str(), codec_name.c_str());
1066 EXPECT_EQ(outbound_rtps[0]->scalability_mode.value(), "L3T3");
1069 TEST_F(PeerConnectionEncodingsIntegrationTest,
1070 EncodingParameterCodecIsSetBySetParametersBeforeNegotiationAudio) {
1071 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1072 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1073 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1075 rtc::scoped_refptr<MediaStreamInterface> stream =
1076 local_pc_wrapper->GetUserMedia(
1077 /*audio=*/true, {}, /*video=*/false, {});
1078 rtc::scoped_refptr<AudioTrackInterface> track = stream->GetAudioTracks()[0];
1080 absl::optional<RtpCodecCapability> pcmu =
1081 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
1082 "pcmu");
1084 auto transceiver_or_error = local_pc_wrapper->pc()->AddTransceiver(track);
1085 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1086 transceiver_or_error.MoveValue();
1087 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
1088 parameters.encodings[0].codec = pcmu;
1089 EXPECT_TRUE(audio_transceiver->sender()->SetParameters(parameters).ok());
1091 parameters = audio_transceiver->sender()->GetParameters();
1092 EXPECT_EQ(parameters.encodings[0].codec, pcmu);
1094 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1095 local_pc_wrapper->WaitForConnection();
1096 remote_pc_wrapper->WaitForConnection();
1098 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
1099 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
1100 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1101 ASSERT_EQ(outbound_rtps.size(), 1u);
1102 std::string codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
1103 EXPECT_STRCASEEQ(("audio/" + pcmu->name).c_str(), codec_name.c_str());
1106 TEST_F(PeerConnectionEncodingsIntegrationTest,
1107 EncodingParameterCodecIsSetBySetParametersAfterNegotiationAudio) {
1108 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1109 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1110 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1112 rtc::scoped_refptr<MediaStreamInterface> stream =
1113 local_pc_wrapper->GetUserMedia(
1114 /*audio=*/true, {}, /*video=*/false, {});
1115 rtc::scoped_refptr<AudioTrackInterface> track = stream->GetAudioTracks()[0];
1117 absl::optional<RtpCodecCapability> pcmu =
1118 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
1119 "pcmu");
1121 auto transceiver_or_error = local_pc_wrapper->pc()->AddTransceiver(track);
1122 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1123 transceiver_or_error.MoveValue();
1125 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1126 local_pc_wrapper->WaitForConnection();
1127 remote_pc_wrapper->WaitForConnection();
1129 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
1130 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
1131 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1132 ASSERT_EQ(outbound_rtps.size(), 1u);
1133 std::string codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
1134 EXPECT_STRCASENE(("audio/" + pcmu->name).c_str(), codec_name.c_str());
1135 std::string last_codec_id = outbound_rtps[0]->codec_id.value();
1137 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
1138 parameters.encodings[0].codec = pcmu;
1139 EXPECT_TRUE(audio_transceiver->sender()->SetParameters(parameters).ok());
1141 parameters = audio_transceiver->sender()->GetParameters();
1142 EXPECT_EQ(parameters.encodings[0].codec, pcmu);
1144 EXPECT_TRUE_WAIT(IsCodecIdDifferent(local_pc_wrapper, 0, last_codec_id),
1145 kDefaultTimeout.ms());
1147 report = GetStats(local_pc_wrapper);
1148 outbound_rtps = report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1149 ASSERT_EQ(outbound_rtps.size(), 1u);
1150 codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
1151 EXPECT_STRCASEEQ(("audio/" + pcmu->name).c_str(), codec_name.c_str());
1154 TEST_F(PeerConnectionEncodingsIntegrationTest,
1155 EncodingParameterCodecIsSetBySetParametersBeforeNegotiationVideo) {
1156 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1157 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1158 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1160 rtc::scoped_refptr<MediaStreamInterface> stream =
1161 local_pc_wrapper->GetUserMedia(
1162 /*audio=*/false, {}, /*video=*/true, {.width = 1280, .height = 720});
1163 rtc::scoped_refptr<VideoTrackInterface> track = stream->GetVideoTracks()[0];
1165 absl::optional<RtpCodecCapability> vp9 =
1166 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1167 "vp9");
1169 auto transceiver_or_error = local_pc_wrapper->pc()->AddTransceiver(track);
1170 rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
1171 transceiver_or_error.MoveValue();
1172 RtpParameters parameters = video_transceiver->sender()->GetParameters();
1173 parameters.encodings[0].codec = vp9;
1174 parameters.encodings[0].scalability_mode = "L3T3";
1175 EXPECT_TRUE(video_transceiver->sender()->SetParameters(parameters).ok());
1177 parameters = video_transceiver->sender()->GetParameters();
1178 EXPECT_EQ(parameters.encodings[0].codec, vp9);
1179 EXPECT_EQ(parameters.encodings[0].scalability_mode, "L3T3");
1181 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1182 local_pc_wrapper->WaitForConnection();
1183 remote_pc_wrapper->WaitForConnection();
1185 EXPECT_TRUE_WAIT(
1186 IsCodecIdDifferentWithScalabilityMode(local_pc_wrapper, 0, "", "L3T3"),
1187 kDefaultTimeout.ms());
1188 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
1189 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
1190 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1191 ASSERT_EQ(outbound_rtps.size(), 1u);
1192 std::string codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
1193 EXPECT_STRCASEEQ(("video/" + vp9->name).c_str(), codec_name.c_str());
1194 EXPECT_EQ(outbound_rtps[0]->scalability_mode.value_or(""), "L3T3");
1197 TEST_F(PeerConnectionEncodingsIntegrationTest,
1198 EncodingParameterCodecIsSetBySetParametersAfterNegotiationVideo) {
1199 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1200 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1201 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1203 rtc::scoped_refptr<MediaStreamInterface> stream =
1204 local_pc_wrapper->GetUserMedia(
1205 /*audio=*/false, {}, /*video=*/true, {.width = 1280, .height = 720});
1206 rtc::scoped_refptr<VideoTrackInterface> track = stream->GetVideoTracks()[0];
1208 absl::optional<RtpCodecCapability> vp9 =
1209 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1210 "vp9");
1212 auto transceiver_or_error = local_pc_wrapper->pc()->AddTransceiver(track);
1213 rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
1214 transceiver_or_error.MoveValue();
1216 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1217 local_pc_wrapper->WaitForConnection();
1218 remote_pc_wrapper->WaitForConnection();
1220 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
1221 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
1222 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1223 ASSERT_EQ(outbound_rtps.size(), 1u);
1224 std::string codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
1225 EXPECT_STRCASENE(("audio/" + vp9->name).c_str(), codec_name.c_str());
1226 std::string last_codec_id = outbound_rtps[0]->codec_id.value();
1228 RtpParameters parameters = video_transceiver->sender()->GetParameters();
1229 parameters.encodings[0].codec = vp9;
1230 parameters.encodings[0].scalability_mode = "L3T3";
1231 EXPECT_TRUE(video_transceiver->sender()->SetParameters(parameters).ok());
1233 parameters = video_transceiver->sender()->GetParameters();
1234 EXPECT_EQ(parameters.encodings[0].codec, vp9);
1235 EXPECT_EQ(parameters.encodings[0].scalability_mode, "L3T3");
1237 EXPECT_TRUE_WAIT(IsCodecIdDifferentWithScalabilityMode(local_pc_wrapper, 0,
1238 last_codec_id, "L3T3"),
1239 kDefaultTimeout.ms());
1241 report = GetStats(local_pc_wrapper);
1242 outbound_rtps = report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1243 ASSERT_EQ(outbound_rtps.size(), 1u);
1244 codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
1245 EXPECT_STRCASEEQ(("video/" + vp9->name).c_str(), codec_name.c_str());
1246 EXPECT_EQ(outbound_rtps[0]->scalability_mode.value(), "L3T3");
1249 TEST_F(PeerConnectionEncodingsIntegrationTest,
1250 AddTransceiverRejectsUnknownCodecParameterAudio) {
1251 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1253 RtpCodec dummy_codec;
1254 dummy_codec.kind = cricket::MEDIA_TYPE_AUDIO;
1255 dummy_codec.name = "FOOBAR";
1256 dummy_codec.clock_rate = 90000;
1257 dummy_codec.num_channels = 2;
1259 RtpTransceiverInit init;
1260 init.direction = RtpTransceiverDirection::kSendOnly;
1261 RtpEncodingParameters encoding_parameters;
1262 encoding_parameters.codec = dummy_codec;
1263 init.send_encodings.push_back(encoding_parameters);
1265 auto transceiver_or_error =
1266 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init);
1267 EXPECT_FALSE(transceiver_or_error.ok());
1268 EXPECT_EQ(transceiver_or_error.error().type(),
1269 RTCErrorType::UNSUPPORTED_OPERATION);
1272 TEST_F(PeerConnectionEncodingsIntegrationTest,
1273 AddTransceiverRejectsUnknownCodecParameterVideo) {
1274 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1276 RtpCodec dummy_codec;
1277 dummy_codec.kind = cricket::MEDIA_TYPE_VIDEO;
1278 dummy_codec.name = "FOOBAR";
1279 dummy_codec.clock_rate = 90000;
1281 RtpTransceiverInit init;
1282 init.direction = RtpTransceiverDirection::kSendOnly;
1283 RtpEncodingParameters encoding_parameters;
1284 encoding_parameters.codec = dummy_codec;
1285 init.send_encodings.push_back(encoding_parameters);
1287 auto transceiver_or_error =
1288 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
1289 EXPECT_FALSE(transceiver_or_error.ok());
1290 EXPECT_EQ(transceiver_or_error.error().type(),
1291 RTCErrorType::UNSUPPORTED_OPERATION);
1294 TEST_F(PeerConnectionEncodingsIntegrationTest,
1295 SetParametersRejectsUnknownCodecParameterAudio) {
1296 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1298 RtpCodec dummy_codec;
1299 dummy_codec.kind = cricket::MEDIA_TYPE_AUDIO;
1300 dummy_codec.name = "FOOBAR";
1301 dummy_codec.clock_rate = 90000;
1302 dummy_codec.num_channels = 2;
1304 auto transceiver_or_error =
1305 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
1306 ASSERT_TRUE(transceiver_or_error.ok());
1307 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1308 transceiver_or_error.MoveValue();
1310 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
1311 parameters.encodings[0].codec = dummy_codec;
1312 RTCError error = audio_transceiver->sender()->SetParameters(parameters);
1313 EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
1316 TEST_F(PeerConnectionEncodingsIntegrationTest,
1317 SetParametersRejectsUnknownCodecParameterVideo) {
1318 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1320 RtpCodec dummy_codec;
1321 dummy_codec.kind = cricket::MEDIA_TYPE_VIDEO;
1322 dummy_codec.name = "FOOBAR";
1323 dummy_codec.clock_rate = 90000;
1325 auto transceiver_or_error =
1326 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
1327 ASSERT_TRUE(transceiver_or_error.ok());
1328 rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
1329 transceiver_or_error.MoveValue();
1331 RtpParameters parameters = video_transceiver->sender()->GetParameters();
1332 parameters.encodings[0].codec = dummy_codec;
1333 RTCError error = video_transceiver->sender()->SetParameters(parameters);
1334 EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
1337 TEST_F(PeerConnectionEncodingsIntegrationTest,
1338 SetParametersRejectsNonPreferredCodecParameterAudio) {
1339 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1341 absl::optional<RtpCodecCapability> opus =
1342 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
1343 "opus");
1344 ASSERT_TRUE(opus);
1346 std::vector<RtpCodecCapability> not_opus_codecs =
1347 local_pc_wrapper->pc_factory()
1348 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
1349 .codecs;
1350 not_opus_codecs.erase(
1351 std::remove_if(not_opus_codecs.begin(), not_opus_codecs.end(),
1352 [&](const auto& codec) {
1353 return absl::EqualsIgnoreCase(codec.name, opus->name);
1355 not_opus_codecs.end());
1357 auto transceiver_or_error =
1358 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
1359 ASSERT_TRUE(transceiver_or_error.ok());
1360 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1361 transceiver_or_error.MoveValue();
1362 ASSERT_TRUE(audio_transceiver->SetCodecPreferences(not_opus_codecs).ok());
1364 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
1365 parameters.encodings[0].codec = opus;
1366 RTCError error = audio_transceiver->sender()->SetParameters(parameters);
1367 EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
1370 TEST_F(PeerConnectionEncodingsIntegrationTest,
1371 SetParametersRejectsNonPreferredCodecParameterVideo) {
1372 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1374 absl::optional<RtpCodecCapability> vp8 =
1375 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1376 "vp8");
1377 ASSERT_TRUE(vp8);
1379 std::vector<RtpCodecCapability> not_vp8_codecs =
1380 local_pc_wrapper->pc_factory()
1381 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1382 .codecs;
1383 not_vp8_codecs.erase(
1384 std::remove_if(not_vp8_codecs.begin(), not_vp8_codecs.end(),
1385 [&](const auto& codec) {
1386 return absl::EqualsIgnoreCase(codec.name, vp8->name);
1388 not_vp8_codecs.end());
1390 auto transceiver_or_error =
1391 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
1392 ASSERT_TRUE(transceiver_or_error.ok());
1393 rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
1394 transceiver_or_error.MoveValue();
1395 ASSERT_TRUE(video_transceiver->SetCodecPreferences(not_vp8_codecs).ok());
1397 RtpParameters parameters = video_transceiver->sender()->GetParameters();
1398 parameters.encodings[0].codec = vp8;
1399 RTCError error = video_transceiver->sender()->SetParameters(parameters);
1400 EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
1403 TEST_F(PeerConnectionEncodingsIntegrationTest,
1404 SetParametersRejectsNonNegotiatedCodecParameterAudio) {
1405 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1406 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1407 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1409 absl::optional<RtpCodecCapability> opus =
1410 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
1411 "opus");
1412 ASSERT_TRUE(opus);
1414 std::vector<RtpCodecCapability> not_opus_codecs =
1415 local_pc_wrapper->pc_factory()
1416 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
1417 .codecs;
1418 not_opus_codecs.erase(
1419 std::remove_if(not_opus_codecs.begin(), not_opus_codecs.end(),
1420 [&](const auto& codec) {
1421 return absl::EqualsIgnoreCase(codec.name, opus->name);
1423 not_opus_codecs.end());
1425 auto transceiver_or_error =
1426 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
1427 ASSERT_TRUE(transceiver_or_error.ok());
1428 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1429 transceiver_or_error.MoveValue();
1430 ASSERT_TRUE(audio_transceiver->SetCodecPreferences(not_opus_codecs).ok());
1432 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1433 local_pc_wrapper->WaitForConnection();
1434 remote_pc_wrapper->WaitForConnection();
1436 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
1437 parameters.encodings[0].codec = opus;
1438 RTCError error = audio_transceiver->sender()->SetParameters(parameters);
1439 EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
1442 TEST_F(PeerConnectionEncodingsIntegrationTest,
1443 SetParametersRejectsNonRemotelyNegotiatedCodecParameterAudio) {
1444 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1445 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1446 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1448 absl::optional<RtpCodecCapability> opus =
1449 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
1450 "opus");
1451 ASSERT_TRUE(opus);
1453 std::vector<RtpCodecCapability> not_opus_codecs =
1454 local_pc_wrapper->pc_factory()
1455 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
1456 .codecs;
1457 not_opus_codecs.erase(
1458 std::remove_if(not_opus_codecs.begin(), not_opus_codecs.end(),
1459 [&](const auto& codec) {
1460 return absl::EqualsIgnoreCase(codec.name, opus->name);
1462 not_opus_codecs.end());
1464 auto transceiver_or_error =
1465 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
1466 ASSERT_TRUE(transceiver_or_error.ok());
1467 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1468 transceiver_or_error.MoveValue();
1470 // Negotiation, create offer and apply it
1471 std::unique_ptr<SessionDescriptionInterface> offer =
1472 CreateOffer(local_pc_wrapper);
1473 rtc::scoped_refptr<MockSetSessionDescriptionObserver> p1 =
1474 SetLocalDescription(local_pc_wrapper, offer.get());
1475 rtc::scoped_refptr<MockSetSessionDescriptionObserver> p2 =
1476 SetRemoteDescription(remote_pc_wrapper, offer.get());
1477 EXPECT_TRUE(Await({p1, p2}));
1479 // Update the remote transceiver to reject Opus
1480 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>> remote_transceivers =
1481 remote_pc_wrapper->pc()->GetTransceivers();
1482 ASSERT_TRUE(!remote_transceivers.empty());
1483 rtc::scoped_refptr<RtpTransceiverInterface> remote_audio_transceiver =
1484 remote_transceivers[0];
1485 ASSERT_TRUE(
1486 remote_audio_transceiver->SetCodecPreferences(not_opus_codecs).ok());
1488 // Create answer and apply it
1489 std::unique_ptr<SessionDescriptionInterface> answer =
1490 CreateAnswer(remote_pc_wrapper);
1491 p1 = SetLocalDescription(remote_pc_wrapper, answer.get());
1492 p2 = SetRemoteDescription(local_pc_wrapper, answer.get());
1493 EXPECT_TRUE(Await({p1, p2}));
1495 local_pc_wrapper->WaitForConnection();
1496 remote_pc_wrapper->WaitForConnection();
1498 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
1499 parameters.encodings[0].codec = opus;
1500 RTCError error = audio_transceiver->sender()->SetParameters(parameters);
1501 EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
1504 TEST_F(PeerConnectionEncodingsIntegrationTest,
1505 SetParametersRejectsNonNegotiatedCodecParameterVideo) {
1506 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1507 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1508 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1510 absl::optional<RtpCodecCapability> vp8 =
1511 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1512 "vp8");
1513 ASSERT_TRUE(vp8);
1515 std::vector<RtpCodecCapability> not_vp8_codecs =
1516 local_pc_wrapper->pc_factory()
1517 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1518 .codecs;
1519 not_vp8_codecs.erase(
1520 std::remove_if(not_vp8_codecs.begin(), not_vp8_codecs.end(),
1521 [&](const auto& codec) {
1522 return absl::EqualsIgnoreCase(codec.name, vp8->name);
1524 not_vp8_codecs.end());
1526 auto transceiver_or_error =
1527 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
1528 ASSERT_TRUE(transceiver_or_error.ok());
1529 rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
1530 transceiver_or_error.MoveValue();
1531 ASSERT_TRUE(video_transceiver->SetCodecPreferences(not_vp8_codecs).ok());
1533 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1534 local_pc_wrapper->WaitForConnection();
1535 remote_pc_wrapper->WaitForConnection();
1537 RtpParameters parameters = video_transceiver->sender()->GetParameters();
1538 parameters.encodings[0].codec = vp8;
1539 RTCError error = video_transceiver->sender()->SetParameters(parameters);
1540 EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
1543 TEST_F(PeerConnectionEncodingsIntegrationTest,
1544 SetParametersRejectsNonRemotelyNegotiatedCodecParameterVideo) {
1545 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1546 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1547 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1549 absl::optional<RtpCodecCapability> vp8 =
1550 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1551 "vp8");
1552 ASSERT_TRUE(vp8);
1554 std::vector<RtpCodecCapability> not_vp8_codecs =
1555 local_pc_wrapper->pc_factory()
1556 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1557 .codecs;
1558 not_vp8_codecs.erase(
1559 std::remove_if(not_vp8_codecs.begin(), not_vp8_codecs.end(),
1560 [&](const auto& codec) {
1561 return absl::EqualsIgnoreCase(codec.name, vp8->name);
1563 not_vp8_codecs.end());
1565 auto transceiver_or_error =
1566 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
1567 ASSERT_TRUE(transceiver_or_error.ok());
1568 rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
1569 transceiver_or_error.MoveValue();
1571 // Negotiation, create offer and apply it
1572 std::unique_ptr<SessionDescriptionInterface> offer =
1573 CreateOffer(local_pc_wrapper);
1574 rtc::scoped_refptr<MockSetSessionDescriptionObserver> p1 =
1575 SetLocalDescription(local_pc_wrapper, offer.get());
1576 rtc::scoped_refptr<MockSetSessionDescriptionObserver> p2 =
1577 SetRemoteDescription(remote_pc_wrapper, offer.get());
1578 EXPECT_TRUE(Await({p1, p2}));
1580 // Update the remote transceiver to reject VP8
1581 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>> remote_transceivers =
1582 remote_pc_wrapper->pc()->GetTransceivers();
1583 ASSERT_TRUE(!remote_transceivers.empty());
1584 rtc::scoped_refptr<RtpTransceiverInterface> remote_video_transceiver =
1585 remote_transceivers[0];
1586 ASSERT_TRUE(
1587 remote_video_transceiver->SetCodecPreferences(not_vp8_codecs).ok());
1589 // Create answer and apply it
1590 std::unique_ptr<SessionDescriptionInterface> answer =
1591 CreateAnswer(remote_pc_wrapper);
1592 p1 = SetLocalDescription(remote_pc_wrapper, answer.get());
1593 p2 = SetRemoteDescription(local_pc_wrapper, answer.get());
1594 EXPECT_TRUE(Await({p1, p2}));
1596 local_pc_wrapper->WaitForConnection();
1597 remote_pc_wrapper->WaitForConnection();
1599 RtpParameters parameters = video_transceiver->sender()->GetParameters();
1600 parameters.encodings[0].codec = vp8;
1601 RTCError error = video_transceiver->sender()->SetParameters(parameters);
1602 EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
1605 TEST_F(PeerConnectionEncodingsIntegrationTest,
1606 EncodingParametersCodecRemovedAfterNegotiationAudio) {
1607 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1608 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1609 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1611 absl::optional<RtpCodecCapability> opus =
1612 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
1613 "opus");
1614 ASSERT_TRUE(opus);
1616 std::vector<RtpCodecCapability> not_opus_codecs =
1617 local_pc_wrapper->pc_factory()
1618 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
1619 .codecs;
1620 not_opus_codecs.erase(
1621 std::remove_if(not_opus_codecs.begin(), not_opus_codecs.end(),
1622 [&](const auto& codec) {
1623 return absl::EqualsIgnoreCase(codec.name, opus->name);
1625 not_opus_codecs.end());
1627 RtpTransceiverInit init;
1628 init.direction = RtpTransceiverDirection::kSendOnly;
1629 RtpEncodingParameters encoding_parameters;
1630 encoding_parameters.codec = opus;
1631 init.send_encodings.push_back(encoding_parameters);
1633 auto transceiver_or_error =
1634 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init);
1635 ASSERT_TRUE(transceiver_or_error.ok());
1636 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1637 transceiver_or_error.MoveValue();
1639 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1640 local_pc_wrapper->WaitForConnection();
1641 remote_pc_wrapper->WaitForConnection();
1643 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
1644 EXPECT_EQ(parameters.encodings[0].codec, opus);
1646 ASSERT_TRUE(audio_transceiver->SetCodecPreferences(not_opus_codecs).ok());
1647 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1649 parameters = audio_transceiver->sender()->GetParameters();
1650 EXPECT_FALSE(parameters.encodings[0].codec);
1653 TEST_F(PeerConnectionEncodingsIntegrationTest,
1654 EncodingParametersRedEnabledBeforeNegotiationAudio) {
1655 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1656 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1657 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1659 std::vector<RtpCodecCapability> send_codecs =
1660 local_pc_wrapper->pc_factory()
1661 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
1662 .codecs;
1664 absl::optional<RtpCodecCapability> opus =
1665 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
1666 "opus");
1667 ASSERT_TRUE(opus);
1669 absl::optional<RtpCodecCapability> red =
1670 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
1671 "red");
1672 ASSERT_TRUE(red);
1674 RtpTransceiverInit init;
1675 init.direction = RtpTransceiverDirection::kSendOnly;
1676 RtpEncodingParameters encoding_parameters;
1677 encoding_parameters.codec = opus;
1678 init.send_encodings.push_back(encoding_parameters);
1680 auto transceiver_or_error =
1681 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init);
1682 ASSERT_TRUE(transceiver_or_error.ok());
1683 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1684 transceiver_or_error.MoveValue();
1686 // Preferring RED over Opus should enable RED with Opus encoding.
1687 send_codecs[0] = red.value();
1688 send_codecs[1] = opus.value();
1690 ASSERT_TRUE(audio_transceiver->SetCodecPreferences(send_codecs).ok());
1691 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1692 local_pc_wrapper->WaitForConnection();
1693 remote_pc_wrapper->WaitForConnection();
1695 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
1696 EXPECT_EQ(parameters.encodings[0].codec, opus);
1697 EXPECT_EQ(parameters.codecs[0].payload_type, red->preferred_payload_type);
1698 EXPECT_EQ(parameters.codecs[0].name, red->name);
1700 // Check that it's possible to switch back to Opus without RED.
1701 send_codecs[0] = opus.value();
1702 send_codecs[1] = red.value();
1704 ASSERT_TRUE(audio_transceiver->SetCodecPreferences(send_codecs).ok());
1705 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1707 parameters = audio_transceiver->sender()->GetParameters();
1708 EXPECT_EQ(parameters.encodings[0].codec, opus);
1709 EXPECT_EQ(parameters.codecs[0].payload_type, opus->preferred_payload_type);
1710 EXPECT_EQ(parameters.codecs[0].name, opus->name);
1713 TEST_F(PeerConnectionEncodingsIntegrationTest,
1714 SetParametersRejectsScalabilityModeForSelectedCodec) {
1715 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1717 absl::optional<RtpCodecCapability> vp8 =
1718 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1719 "vp8");
1720 ASSERT_TRUE(vp8);
1722 RtpTransceiverInit init;
1723 init.direction = RtpTransceiverDirection::kSendOnly;
1724 RtpEncodingParameters encoding_parameters;
1725 encoding_parameters.codec = vp8;
1726 encoding_parameters.scalability_mode = "L1T3";
1727 init.send_encodings.push_back(encoding_parameters);
1729 auto transceiver_or_error =
1730 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
1731 ASSERT_TRUE(transceiver_or_error.ok());
1732 rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
1733 transceiver_or_error.MoveValue();
1735 RtpParameters parameters = video_transceiver->sender()->GetParameters();
1736 parameters.encodings[0].scalability_mode = "L3T3";
1737 RTCError error = video_transceiver->sender()->SetParameters(parameters);
1738 EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
1741 TEST_F(PeerConnectionEncodingsIntegrationTest,
1742 EncodingParametersCodecRemovedByNegotiationVideo) {
1743 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1744 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1745 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1747 absl::optional<RtpCodecCapability> vp8 =
1748 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1749 "vp8");
1750 ASSERT_TRUE(vp8);
1752 std::vector<RtpCodecCapability> not_vp8_codecs =
1753 local_pc_wrapper->pc_factory()
1754 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1755 .codecs;
1756 not_vp8_codecs.erase(
1757 std::remove_if(not_vp8_codecs.begin(), not_vp8_codecs.end(),
1758 [&](const auto& codec) {
1759 return absl::EqualsIgnoreCase(codec.name, vp8->name);
1761 not_vp8_codecs.end());
1763 RtpTransceiverInit init;
1764 init.direction = RtpTransceiverDirection::kSendOnly;
1765 RtpEncodingParameters encoding_parameters;
1766 encoding_parameters.rid = "h";
1767 encoding_parameters.codec = vp8;
1768 encoding_parameters.scale_resolution_down_by = 2;
1769 init.send_encodings.push_back(encoding_parameters);
1770 encoding_parameters.rid = "f";
1771 encoding_parameters.scale_resolution_down_by = 1;
1772 init.send_encodings.push_back(encoding_parameters);
1774 auto transceiver_or_error =
1775 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
1776 ASSERT_TRUE(transceiver_or_error.ok());
1777 rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
1778 transceiver_or_error.MoveValue();
1780 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1781 local_pc_wrapper->WaitForConnection();
1782 remote_pc_wrapper->WaitForConnection();
1784 RtpParameters parameters = video_transceiver->sender()->GetParameters();
1785 ASSERT_EQ(parameters.encodings.size(), 2u);
1786 EXPECT_EQ(parameters.encodings[0].codec, vp8);
1787 EXPECT_EQ(parameters.encodings[1].codec, vp8);
1789 ASSERT_TRUE(video_transceiver->SetCodecPreferences(not_vp8_codecs).ok());
1790 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1792 parameters = video_transceiver->sender()->GetParameters();
1793 EXPECT_FALSE(parameters.encodings[0].codec);
1794 EXPECT_FALSE(parameters.encodings[1].codec);
1797 TEST_F(PeerConnectionEncodingsIntegrationTest,
1798 AddTransceiverRejectsMixedCodecSimulcast) {
1799 // Mixed Codec Simulcast is not yet supported, so we ensure that we reject
1800 // such parameters.
1801 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1802 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1803 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1805 absl::optional<RtpCodecCapability> vp8 =
1806 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1807 "vp8");
1808 ASSERT_TRUE(vp8);
1809 absl::optional<RtpCodecCapability> vp9 =
1810 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1811 "vp9");
1813 RtpTransceiverInit init;
1814 init.direction = RtpTransceiverDirection::kSendOnly;
1815 RtpEncodingParameters encoding_parameters;
1816 encoding_parameters.rid = "h";
1817 encoding_parameters.codec = vp8;
1818 encoding_parameters.scale_resolution_down_by = 2;
1819 init.send_encodings.push_back(encoding_parameters);
1820 encoding_parameters.rid = "f";
1821 encoding_parameters.codec = vp9;
1822 encoding_parameters.scale_resolution_down_by = 1;
1823 init.send_encodings.push_back(encoding_parameters);
1825 auto transceiver_or_error =
1826 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
1827 ASSERT_FALSE(transceiver_or_error.ok());
1828 EXPECT_EQ(transceiver_or_error.error().type(),
1829 RTCErrorType::UNSUPPORTED_OPERATION);
1832 // Tests that use the standard path (specifying both `scalability_mode` and
1833 // `scale_resolution_down_by`) should pass for all codecs.
1834 class PeerConnectionEncodingsIntegrationParameterizedTest
1835 : public PeerConnectionEncodingsIntegrationTest,
1836 public ::testing::WithParamInterface<std::string> {
1837 public:
1838 PeerConnectionEncodingsIntegrationParameterizedTest()
1839 : codec_name_(GetParam()), mime_type_("video/" + codec_name_) {}
1841 // Work-around for the fact that whether or not AV1 is supported is not known
1842 // at compile-time so we have to skip tests early if missing.
1843 // TODO(https://crbug.com/webrtc/15011): Increase availability of AV1 or make
1844 // it possible to check support at compile-time.
1845 bool SkipTestDueToAv1Missing(
1846 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper) {
1847 if (codec_name_ == "AV1" &&
1848 !HasSenderVideoCodecCapability(local_pc_wrapper, "AV1")) {
1849 RTC_LOG(LS_WARNING) << "\n***\nAV1 is not available, skipping test.\n***";
1850 return true;
1852 return false;
1855 protected:
1856 const std::string codec_name_; // E.g. "VP9"
1857 const std::string mime_type_; // E.g. "video/VP9"
1860 TEST_P(PeerConnectionEncodingsIntegrationParameterizedTest, AllLayersInactive) {
1861 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1862 if (SkipTestDueToAv1Missing(local_pc_wrapper)) {
1863 return;
1865 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1866 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1868 std::vector<cricket::SimulcastLayer> layers =
1869 CreateLayers({"f", "h", "q"}, /*active=*/true);
1870 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
1871 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
1872 layers);
1873 std::vector<RtpCodecCapability> codecs =
1874 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, codec_name_);
1875 transceiver->SetCodecPreferences(codecs);
1877 // Standard mode and all layers inactive.
1878 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
1879 RtpParameters parameters = sender->GetParameters();
1880 ASSERT_THAT(parameters.encodings, SizeIs(3));
1881 parameters.encodings[0].scalability_mode = "L1T3";
1882 parameters.encodings[0].scale_resolution_down_by = 1;
1883 parameters.encodings[0].active = false;
1884 parameters.encodings[1].active = false;
1885 parameters.encodings[2].active = false;
1886 sender->SetParameters(parameters);
1888 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1889 local_pc_wrapper->WaitForConnection();
1890 remote_pc_wrapper->WaitForConnection();
1892 // Ensure no media is flowing (1 second should be enough).
1893 rtc::Thread::Current()->SleepMs(1000);
1894 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
1895 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
1896 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1897 ASSERT_THAT(outbound_rtps, SizeIs(3u));
1898 EXPECT_EQ(*outbound_rtps[0]->bytes_sent, 0u);
1899 EXPECT_EQ(*outbound_rtps[1]->bytes_sent, 0u);
1900 EXPECT_EQ(*outbound_rtps[2]->bytes_sent, 0u);
1903 TEST_P(PeerConnectionEncodingsIntegrationParameterizedTest, Simulcast) {
1904 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1905 if (SkipTestDueToAv1Missing(local_pc_wrapper)) {
1906 return;
1908 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1909 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1911 std::vector<cricket::SimulcastLayer> layers =
1912 CreateLayers({"f", "h", "q"}, /*active=*/true);
1913 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
1914 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
1915 layers);
1916 std::vector<RtpCodecCapability> codecs =
1917 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, codec_name_);
1918 transceiver->SetCodecPreferences(codecs);
1920 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
1921 RtpParameters parameters = sender->GetParameters();
1922 ASSERT_THAT(parameters.encodings, SizeIs(3));
1923 parameters.encodings[0].scalability_mode = "L1T3";
1924 parameters.encodings[0].scale_resolution_down_by = 4;
1925 parameters.encodings[1].scalability_mode = "L1T3";
1926 parameters.encodings[1].scale_resolution_down_by = 2;
1927 parameters.encodings[2].scalability_mode = "L1T3";
1928 parameters.encodings[2].scale_resolution_down_by = 1;
1929 sender->SetParameters(parameters);
1931 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1932 local_pc_wrapper->WaitForConnection();
1933 remote_pc_wrapper->WaitForConnection();
1935 // GetParameters() does not report any fallback.
1936 parameters = sender->GetParameters();
1937 ASSERT_THAT(parameters.encodings, SizeIs(3));
1938 EXPECT_THAT(parameters.encodings[0].scalability_mode,
1939 Optional(std::string("L1T3")));
1940 EXPECT_THAT(parameters.encodings[1].scalability_mode,
1941 Optional(std::string("L1T3")));
1942 EXPECT_THAT(parameters.encodings[2].scalability_mode,
1943 Optional(std::string("L1T3")));
1945 // Wait until media is flowing on all three layers.
1946 // Ramp up time is needed before all three layers are sending.
1947 ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 3u),
1948 kLongTimeoutForRampingUp.ms());
1949 EXPECT_TRUE(OutboundRtpResolutionsAreLessThanOrEqualToExpectations(
1950 local_pc_wrapper, {{"f", 320, 180}, {"h", 640, 360}, {"q", 1280, 720}}));
1951 // Verify codec and scalability mode.
1952 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
1953 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
1954 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1955 ASSERT_THAT(outbound_rtps, SizeIs(3u));
1956 EXPECT_THAT(GetCurrentCodecMimeType(report, *outbound_rtps[0]),
1957 StrCaseEq(mime_type_));
1958 EXPECT_THAT(GetCurrentCodecMimeType(report, *outbound_rtps[1]),
1959 StrCaseEq(mime_type_));
1960 EXPECT_THAT(GetCurrentCodecMimeType(report, *outbound_rtps[2]),
1961 StrCaseEq(mime_type_));
1962 EXPECT_THAT(*outbound_rtps[0]->scalability_mode, StrEq("L1T3"));
1963 EXPECT_THAT(*outbound_rtps[1]->scalability_mode, StrEq("L1T3"));
1964 EXPECT_THAT(*outbound_rtps[2]->scalability_mode, StrEq("L1T3"));
1967 INSTANTIATE_TEST_SUITE_P(StandardPath,
1968 PeerConnectionEncodingsIntegrationParameterizedTest,
1969 ::testing::Values("VP8",
1970 "VP9",
1971 #if defined(WEBRTC_USE_H264)
1972 "H264",
1973 #endif // defined(WEBRTC_USE_H264)
1974 "AV1"),
1975 StringParamToString());
1977 } // namespace webrtc