blob: ae238671c24041f1befcad65c73993767c72aa15 [file] [log] [blame]
Henrik Boströmda9e2842023-04-06 13:27:331/*
2 * Copyright 2023 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include <string>
12#include <vector>
13
Florent Castelli43a5dd82023-04-12 10:45:0714#include "absl/strings/match.h"
15#include "absl/types/optional.h"
Henrik Boströmda9e2842023-04-06 13:27:3316#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"
Florent Castelli43a5dd82023-04-12 10:45:0720#include "api/media_types.h"
21#include "api/rtc_error.h"
Henrik Boströmda9e2842023-04-06 13:27:3322#include "api/rtp_parameters.h"
Florent Castelli43a5dd82023-04-12 10:45:0723#include "api/rtp_transceiver_direction.h"
24#include "api/rtp_transceiver_interface.h"
Henrik Boströmda9e2842023-04-06 13:27:3325#include "api/stats/rtcstats_objects.h"
Henrik Boström2fec6442023-06-15 10:49:2626#include "api/units/data_rate.h"
Henrik Boströmda9e2842023-04-06 13:27:3327#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"
46
47using ::testing::Eq;
48using ::testing::Optional;
49using ::testing::SizeIs;
50using ::testing::StrCaseEq;
51using ::testing::StrEq;
52
53namespace webrtc {
54
55namespace {
56
57constexpr TimeDelta kDefaultTimeout = TimeDelta::Seconds(5);
Henrik Boströmfb65d232023-04-11 13:32:4058// 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.
63constexpr TimeDelta kLongTimeoutForRampingUp = TimeDelta::Minutes(1);
64
Henrik Boström2fec6442023-06-15 10:49:2665// 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.
68constexpr DataRate kVp9ExpectedMaxBitrateForL1T3 =
69 DataRate::KilobitsPerSec(1500);
70
Henrik Boströmfb65d232023-04-11 13:32:4071struct StringParamToString {
72 std::string operator()(const ::testing::TestParamInfo<std::string>& info) {
73 return info.param;
74 }
75};
Henrik Boströmda9e2842023-04-06 13:27:3376
77// RTX, RED and FEC are reliability mechanisms used in combinations with other
78// codecs, but are not themselves a specific codec. Typically you don't want to
79// filter these out of the list of codec preferences.
Harald Alvestranda6544372023-11-13 09:33:5680bool IsReliabilityMechanism(const RtpCodecCapability& codec) {
Henrik Boströmda9e2842023-04-06 13:27:3381 return absl::EqualsIgnoreCase(codec.name, cricket::kRtxCodecName) ||
82 absl::EqualsIgnoreCase(codec.name, cricket::kRedCodecName) ||
83 absl::EqualsIgnoreCase(codec.name, cricket::kUlpfecCodecName);
84}
85
86std::string GetCurrentCodecMimeType(
Harald Alvestranda6544372023-11-13 09:33:5687 rtc::scoped_refptr<const RTCStatsReport> report,
88 const RTCOutboundRtpStreamStats& outbound_rtp) {
Henrik Boströmda9e2842023-04-06 13:27:3389 return outbound_rtp.codec_id.is_defined()
Harald Alvestranda6544372023-11-13 09:33:5690 ? *report->GetAs<RTCCodecStats>(*outbound_rtp.codec_id)->mime_type
Henrik Boströmda9e2842023-04-06 13:27:3391 : "";
92}
93
94struct RidAndResolution {
95 std::string rid;
96 uint32_t width;
97 uint32_t height;
98};
99
Harald Alvestranda6544372023-11-13 09:33:56100const RTCOutboundRtpStreamStats* FindOutboundRtpByRid(
101 const std::vector<const RTCOutboundRtpStreamStats*>& outbound_rtps,
Henrik Boströmda9e2842023-04-06 13:27:33102 const absl::string_view& rid) {
103 for (const auto* outbound_rtp : outbound_rtps) {
104 if (outbound_rtp->rid.is_defined() && *outbound_rtp->rid == rid) {
105 return outbound_rtp;
106 }
107 }
108 return nullptr;
109}
110
111} // namespace
112
113class PeerConnectionEncodingsIntegrationTest : public ::testing::Test {
114 public:
115 PeerConnectionEncodingsIntegrationTest()
116 : background_thread_(std::make_unique<rtc::Thread>(&pss_)) {
117 RTC_CHECK(background_thread_->Start());
118 }
119
120 rtc::scoped_refptr<PeerConnectionTestWrapper> CreatePc() {
121 auto pc_wrapper = rtc::make_ref_counted<PeerConnectionTestWrapper>(
122 "pc", &pss_, background_thread_.get(), background_thread_.get());
Harald Alvestranda6544372023-11-13 09:33:56123 pc_wrapper->CreatePc({}, CreateBuiltinAudioEncoderFactory(),
124 CreateBuiltinAudioDecoderFactory());
Henrik Boströmda9e2842023-04-06 13:27:33125 return pc_wrapper;
126 }
127
128 rtc::scoped_refptr<RtpTransceiverInterface> AddTransceiverWithSimulcastLayers(
129 rtc::scoped_refptr<PeerConnectionTestWrapper> local,
130 rtc::scoped_refptr<PeerConnectionTestWrapper> remote,
131 std::vector<cricket::SimulcastLayer> init_layers) {
Harald Alvestranda6544372023-11-13 09:33:56132 rtc::scoped_refptr<MediaStreamInterface> stream = local->GetUserMedia(
133 /*audio=*/false, cricket::AudioOptions(), /*video=*/true,
134 {.width = 1280, .height = 720});
Henrik Boströmda9e2842023-04-06 13:27:33135 rtc::scoped_refptr<VideoTrackInterface> track = stream->GetVideoTracks()[0];
136
137 RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>>
138 transceiver_or_error = local->pc()->AddTransceiver(
139 track, CreateTransceiverInit(init_layers));
140 EXPECT_TRUE(transceiver_or_error.ok());
141 return transceiver_or_error.value();
142 }
143
144 bool HasSenderVideoCodecCapability(
145 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
146 absl::string_view codec_name) {
147 std::vector<RtpCodecCapability> codecs =
148 pc_wrapper->pc_factory()
149 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
150 .codecs;
151 return std::find_if(codecs.begin(), codecs.end(),
152 [&codec_name](const RtpCodecCapability& codec) {
153 return absl::EqualsIgnoreCase(codec.name, codec_name);
154 }) != codecs.end();
155 }
156
157 std::vector<RtpCodecCapability> GetCapabilitiesAndRestrictToCodec(
158 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
159 absl::string_view codec_name) {
160 std::vector<RtpCodecCapability> codecs =
161 pc_wrapper->pc_factory()
162 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
163 .codecs;
164 codecs.erase(std::remove_if(codecs.begin(), codecs.end(),
165 [&codec_name](const RtpCodecCapability& codec) {
166 return !IsReliabilityMechanism(codec) &&
167 !absl::EqualsIgnoreCase(codec.name,
168 codec_name);
169 }),
170 codecs.end());
171 RTC_DCHECK(std::find_if(codecs.begin(), codecs.end(),
172 [&codec_name](const RtpCodecCapability& codec) {
173 return absl::EqualsIgnoreCase(codec.name,
174 codec_name);
175 }) != codecs.end());
176 return codecs;
177 }
178
179 void ExchangeIceCandidates(
180 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper,
181 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper) {
182 local_pc_wrapper->SignalOnIceCandidateReady.connect(
183 remote_pc_wrapper.get(), &PeerConnectionTestWrapper::AddIceCandidate);
184 remote_pc_wrapper->SignalOnIceCandidateReady.connect(
185 local_pc_wrapper.get(), &PeerConnectionTestWrapper::AddIceCandidate);
186 }
187
188 void NegotiateWithSimulcastTweaks(
189 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper,
Florent Castelli43a5dd82023-04-12 10:45:07190 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper) {
Henrik Boströmda9e2842023-04-06 13:27:33191 // Create and set offer for `local_pc_wrapper`.
192 std::unique_ptr<SessionDescriptionInterface> offer =
193 CreateOffer(local_pc_wrapper);
194 rtc::scoped_refptr<MockSetSessionDescriptionObserver> p1 =
195 SetLocalDescription(local_pc_wrapper, offer.get());
196 // Modify the offer before handoff because `remote_pc_wrapper` only supports
197 // receiving singlecast.
198 cricket::SimulcastDescription simulcast_description =
199 RemoveSimulcast(offer.get());
200 rtc::scoped_refptr<MockSetSessionDescriptionObserver> p2 =
201 SetRemoteDescription(remote_pc_wrapper, offer.get());
202 EXPECT_TRUE(Await({p1, p2}));
203
204 // Create and set answer for `remote_pc_wrapper`.
205 std::unique_ptr<SessionDescriptionInterface> answer =
206 CreateAnswer(remote_pc_wrapper);
207 p1 = SetLocalDescription(remote_pc_wrapper, answer.get());
208 // Modify the answer before handoff because `local_pc_wrapper` should still
209 // send simulcast.
210 cricket::MediaContentDescription* mcd_answer =
211 answer->description()->contents()[0].media_description();
212 mcd_answer->mutable_streams().clear();
213 std::vector<cricket::SimulcastLayer> simulcast_layers =
214 simulcast_description.send_layers().GetAllLayers();
215 cricket::SimulcastLayerList& receive_layers =
216 mcd_answer->simulcast_description().receive_layers();
217 for (const auto& layer : simulcast_layers) {
218 receive_layers.AddLayer(layer);
219 }
220 p2 = SetRemoteDescription(local_pc_wrapper, answer.get());
221 EXPECT_TRUE(Await({p1, p2}));
222 }
223
224 rtc::scoped_refptr<const RTCStatsReport> GetStats(
225 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper) {
226 auto callback = rtc::make_ref_counted<MockRTCStatsCollectorCallback>();
227 pc_wrapper->pc()->GetStats(callback.get());
228 EXPECT_TRUE_WAIT(callback->called(), kDefaultTimeout.ms());
229 return callback->report();
230 }
231
Florent Castelli43a5dd82023-04-12 10:45:07232 bool IsCodecIdDifferent(
233 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
234 size_t index,
235 const std::string& codec_id) {
236 return IsCodecIdDifferentWithScalabilityMode(pc_wrapper, index, codec_id,
237 absl::nullopt);
238 }
239
240 bool IsCodecIdDifferentWithScalabilityMode(
241 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
242 size_t index,
243 const std::string& codec_id,
244 absl::optional<std::string> wanted_scalability_mode) {
245 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(pc_wrapper);
246 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
247 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
248 return outbound_rtps[index]->codec_id.value() != codec_id &&
249 (!wanted_scalability_mode ||
250 (outbound_rtps[index]->scalability_mode.has_value() &&
251 outbound_rtps[index]->scalability_mode.value() ==
252 wanted_scalability_mode));
253 }
254
Henrik Boströmda9e2842023-04-06 13:27:33255 bool HasOutboundRtpBytesSent(
256 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
257 size_t num_layers) {
258 return HasOutboundRtpBytesSent(pc_wrapper, num_layers, num_layers);
259 }
260
261 bool HasOutboundRtpBytesSent(
262 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
263 size_t num_layers,
264 size_t num_active_layers) {
265 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(pc_wrapper);
266 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
267 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
268 if (outbound_rtps.size() != num_layers) {
269 return false;
270 }
271 size_t num_sending_layers = 0;
272 for (const auto* outbound_rtp : outbound_rtps) {
273 if (outbound_rtp->bytes_sent.is_defined() &&
274 *outbound_rtp->bytes_sent > 0u) {
275 ++num_sending_layers;
276 }
277 }
278 return num_sending_layers == num_active_layers;
279 }
280
281 bool HasOutboundRtpWithRidAndScalabilityMode(
282 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
283 absl::string_view rid,
284 absl::string_view expected_scalability_mode,
285 uint32_t frame_height) {
286 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(pc_wrapper);
287 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
288 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
289 auto* outbound_rtp = FindOutboundRtpByRid(outbound_rtps, rid);
290 if (!outbound_rtp || !outbound_rtp->scalability_mode.is_defined() ||
291 *outbound_rtp->scalability_mode != expected_scalability_mode) {
292 return false;
293 }
294 if (outbound_rtp->frame_height.is_defined()) {
295 RTC_LOG(LS_INFO) << "Waiting for target resolution (" << frame_height
296 << "p). Currently at " << *outbound_rtp->frame_height
297 << "p...";
298 } else {
299 RTC_LOG(LS_INFO)
300 << "Waiting for target resolution. No frames encoded yet...";
301 }
302 if (!outbound_rtp->frame_height.is_defined() ||
303 *outbound_rtp->frame_height != frame_height) {
304 // Sleep to avoid log spam when this is used in ASSERT_TRUE_WAIT().
305 rtc::Thread::Current()->SleepMs(1000);
306 return false;
307 }
308 return true;
309 }
310
311 bool OutboundRtpResolutionsAreLessThanOrEqualToExpectations(
312 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
313 std::vector<RidAndResolution> resolutions) {
314 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(pc_wrapper);
315 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
316 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
317 for (const RidAndResolution& resolution : resolutions) {
318 const RTCOutboundRtpStreamStats* outbound_rtp = nullptr;
319 if (!resolution.rid.empty()) {
320 outbound_rtp = FindOutboundRtpByRid(outbound_rtps, resolution.rid);
321 } else if (outbound_rtps.size() == 1u) {
322 outbound_rtp = outbound_rtps[0];
323 }
324 if (!outbound_rtp || !outbound_rtp->frame_width.is_defined() ||
325 !outbound_rtp->frame_height.is_defined()) {
326 // RTP not found by rid or has not encoded a frame yet.
327 RTC_LOG(LS_ERROR) << "rid=" << resolution.rid << " does not have "
328 << "resolution metrics";
329 return false;
330 }
331 if (*outbound_rtp->frame_width > resolution.width ||
332 *outbound_rtp->frame_height > resolution.height) {
333 RTC_LOG(LS_ERROR) << "rid=" << resolution.rid << " is "
334 << *outbound_rtp->frame_width << "x"
335 << *outbound_rtp->frame_height
336 << ", this is greater than the "
337 << "expected " << resolution.width << "x"
338 << resolution.height;
339 return false;
340 }
341 }
342 return true;
343 }
344
345 protected:
346 std::unique_ptr<SessionDescriptionInterface> CreateOffer(
347 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper) {
348 auto observer =
349 rtc::make_ref_counted<MockCreateSessionDescriptionObserver>();
350 pc_wrapper->pc()->CreateOffer(observer.get(), {});
351 EXPECT_EQ_WAIT(true, observer->called(), kDefaultTimeout.ms());
352 return observer->MoveDescription();
353 }
354
355 std::unique_ptr<SessionDescriptionInterface> CreateAnswer(
356 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper) {
357 auto observer =
358 rtc::make_ref_counted<MockCreateSessionDescriptionObserver>();
359 pc_wrapper->pc()->CreateAnswer(observer.get(), {});
360 EXPECT_EQ_WAIT(true, observer->called(), kDefaultTimeout.ms());
361 return observer->MoveDescription();
362 }
363
364 rtc::scoped_refptr<MockSetSessionDescriptionObserver> SetLocalDescription(
365 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
366 SessionDescriptionInterface* sdp) {
367 auto observer = rtc::make_ref_counted<MockSetSessionDescriptionObserver>();
368 pc_wrapper->pc()->SetLocalDescription(
369 observer.get(), CloneSessionDescription(sdp).release());
370 return observer;
371 }
372
373 rtc::scoped_refptr<MockSetSessionDescriptionObserver> SetRemoteDescription(
374 rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
375 SessionDescriptionInterface* sdp) {
376 auto observer = rtc::make_ref_counted<MockSetSessionDescriptionObserver>();
377 pc_wrapper->pc()->SetRemoteDescription(
378 observer.get(), CloneSessionDescription(sdp).release());
379 return observer;
380 }
381
382 // To avoid ICE candidates arriving before the remote endpoint has received
383 // the offer it is important to SetLocalDescription() and
384 // SetRemoteDescription() are kicked off without awaiting in-between. This
385 // helper is used to await multiple observers.
386 bool Await(std::vector<rtc::scoped_refptr<MockSetSessionDescriptionObserver>>
387 observers) {
388 for (auto& observer : observers) {
389 EXPECT_EQ_WAIT(true, observer->called(), kDefaultTimeout.ms());
390 if (!observer->result()) {
391 return false;
392 }
393 }
394 return true;
395 }
396
397 rtc::PhysicalSocketServer pss_;
398 std::unique_ptr<rtc::Thread> background_thread_;
399};
400
401TEST_F(PeerConnectionEncodingsIntegrationTest,
Henrik Boströmfb65d232023-04-11 13:32:40402 VP8_SingleEncodingDefaultsToL1T1) {
Henrik Boströmda9e2842023-04-06 13:27:33403 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
404 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
405 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
406
407 std::vector<cricket::SimulcastLayer> layers =
408 CreateLayers({"f"}, /*active=*/true);
409 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
410 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
411 layers);
412 std::vector<RtpCodecCapability> codecs =
413 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP8");
414 transceiver->SetCodecPreferences(codecs);
415
Florent Castelli43a5dd82023-04-12 10:45:07416 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
Henrik Boströmda9e2842023-04-06 13:27:33417 local_pc_wrapper->WaitForConnection();
418 remote_pc_wrapper->WaitForConnection();
419
420 // Wait until media is flowing.
421 ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 1u),
422 kDefaultTimeout.ms());
423 EXPECT_TRUE(OutboundRtpResolutionsAreLessThanOrEqualToExpectations(
424 local_pc_wrapper, {{"", 1280, 720}}));
425 // Verify codec and scalability mode.
426 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
427 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
428 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
429 ASSERT_THAT(outbound_rtps, SizeIs(1u));
430 EXPECT_THAT(GetCurrentCodecMimeType(report, *outbound_rtps[0]),
431 StrCaseEq("video/VP8"));
432 EXPECT_THAT(*outbound_rtps[0]->scalability_mode, StrEq("L1T1"));
433}
434
435TEST_F(PeerConnectionEncodingsIntegrationTest,
Henrik Boströmfb65d232023-04-11 13:32:40436 VP8_RejectsSvcAndDefaultsToL1T1) {
Henrik Boströmda9e2842023-04-06 13:27:33437 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
438 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
439 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
440
441 std::vector<cricket::SimulcastLayer> layers =
442 CreateLayers({"f"}, /*active=*/true);
443 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
444 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
445 layers);
446 // Restricting codecs restricts what SetParameters() will accept or reject.
447 std::vector<RtpCodecCapability> codecs =
448 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP8");
449 transceiver->SetCodecPreferences(codecs);
450 // Attempt SVC (L3T3_KEY). This is not possible because only VP8 is up for
451 // negotiation and VP8 does not support it.
452 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
453 RtpParameters parameters = sender->GetParameters();
454 ASSERT_EQ(parameters.encodings.size(), 1u);
455 parameters.encodings[0].scalability_mode = "L3T3_KEY";
456 parameters.encodings[0].scale_resolution_down_by = 1;
457 EXPECT_FALSE(sender->SetParameters(parameters).ok());
458 // `scalability_mode` remains unset because SetParameters() failed.
459 parameters = sender->GetParameters();
460 ASSERT_EQ(parameters.encodings.size(), 1u);
461 EXPECT_THAT(parameters.encodings[0].scalability_mode, Eq(absl::nullopt));
462
Florent Castelli43a5dd82023-04-12 10:45:07463 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
Henrik Boströmda9e2842023-04-06 13:27:33464 local_pc_wrapper->WaitForConnection();
465 remote_pc_wrapper->WaitForConnection();
466
467 // Wait until media is flowing.
468 ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 1u),
469 kDefaultTimeout.ms());
470 // When `scalability_mode` is not set, VP8 defaults to L1T1.
471 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
472 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
473 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
474 ASSERT_THAT(outbound_rtps, SizeIs(1u));
475 EXPECT_THAT(GetCurrentCodecMimeType(report, *outbound_rtps[0]),
476 StrCaseEq("video/VP8"));
477 EXPECT_THAT(*outbound_rtps[0]->scalability_mode, StrEq("L1T1"));
478 // GetParameters() confirms `scalability_mode` is still not set.
479 parameters = sender->GetParameters();
480 ASSERT_EQ(parameters.encodings.size(), 1u);
481 EXPECT_THAT(parameters.encodings[0].scalability_mode, Eq(absl::nullopt));
482}
483
484TEST_F(PeerConnectionEncodingsIntegrationTest,
Henrik Boströmfb65d232023-04-11 13:32:40485 VP8_FallbackFromSvcResultsInL1T2) {
Henrik Boströmda9e2842023-04-06 13:27:33486 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
487 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
488 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
489
490 std::vector<cricket::SimulcastLayer> layers =
491 CreateLayers({"f"}, /*active=*/true);
492 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
493 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
494 layers);
495 // Verify test assumption that VP8 is first in the list, but don't modify the
496 // codec preferences because we want the sender to think SVC is a possibility.
497 std::vector<RtpCodecCapability> codecs =
498 local_pc_wrapper->pc_factory()
499 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
500 .codecs;
501 EXPECT_THAT(codecs[0].name, StrCaseEq("VP8"));
502 // Attempt SVC (L3T3_KEY), which is not possible with VP8, but the sender does
503 // not yet know which codec we'll use so the parameters will be accepted.
504 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
505 RtpParameters parameters = sender->GetParameters();
506 ASSERT_EQ(parameters.encodings.size(), 1u);
507 parameters.encodings[0].scalability_mode = "L3T3_KEY";
508 parameters.encodings[0].scale_resolution_down_by = 1;
509 EXPECT_TRUE(sender->SetParameters(parameters).ok());
510 // Verify fallback has not happened yet.
511 parameters = sender->GetParameters();
512 ASSERT_EQ(parameters.encodings.size(), 1u);
513 EXPECT_THAT(parameters.encodings[0].scalability_mode,
514 Optional(std::string("L3T3_KEY")));
515
516 // Negotiate, this results in VP8 being picked and fallback happening.
Florent Castelli43a5dd82023-04-12 10:45:07517 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
Henrik Boströmda9e2842023-04-06 13:27:33518 local_pc_wrapper->WaitForConnection();
519 remote_pc_wrapper->WaitForConnection();
520 // `scalaiblity_mode` is assigned the fallback value "L1T2" which is different
521 // than the default of absl::nullopt.
522 parameters = sender->GetParameters();
523 ASSERT_EQ(parameters.encodings.size(), 1u);
524 EXPECT_THAT(parameters.encodings[0].scalability_mode,
525 Optional(std::string("L1T2")));
526
527 // Wait until media is flowing, no significant time needed because we only
528 // have one layer.
529 ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 1u),
530 kDefaultTimeout.ms());
531 // GetStats() confirms "L1T2" is used which is different than the "L1T1"
532 // default or the "L3T3_KEY" that was attempted.
533 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
534 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
535 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
536 ASSERT_THAT(outbound_rtps, SizeIs(1u));
537 EXPECT_THAT(GetCurrentCodecMimeType(report, *outbound_rtps[0]),
538 StrCaseEq("video/VP8"));
539 EXPECT_THAT(*outbound_rtps[0]->scalability_mode, StrEq("L1T2"));
540}
541
Henrik Boströmda9e2842023-04-06 13:27:33542// The legacy SVC path is triggered when VP9 us used, but `scalability_mode` has
543// not been specified.
544// TODO(https://crbug.com/webrtc/14889): When legacy VP9 SVC path has been
545// deprecated and removed, update this test to assert that simulcast is used
546// (i.e. VP9 is not treated differently than VP8).
547TEST_F(PeerConnectionEncodingsIntegrationTest,
Henrik Boströmfb65d232023-04-11 13:32:40548 VP9_LegacySvcWhenScalabilityModeNotSpecified) {
Henrik Boströmda9e2842023-04-06 13:27:33549 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
550 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
551 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
552
553 std::vector<cricket::SimulcastLayer> layers =
554 CreateLayers({"f", "h", "q"}, /*active=*/true);
555 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
556 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
557 layers);
558 std::vector<RtpCodecCapability> codecs =
559 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9");
560 transceiver->SetCodecPreferences(codecs);
561
Florent Castelli43a5dd82023-04-12 10:45:07562 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
Henrik Boströmda9e2842023-04-06 13:27:33563 local_pc_wrapper->WaitForConnection();
564 remote_pc_wrapper->WaitForConnection();
565
566 // Wait until media is flowing. We only expect a single RTP stream.
567 // We expect to see bytes flowing almost immediately on the lowest layer.
568 ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 1u),
569 kDefaultTimeout.ms());
570 // Wait until scalability mode is reported and expected resolution reached.
571 // Ramp up time may be significant.
572 ASSERT_TRUE_WAIT(HasOutboundRtpWithRidAndScalabilityMode(
573 local_pc_wrapper, "f", "L3T3_KEY", 720),
Henrik Boströmfb65d232023-04-11 13:32:40574 kLongTimeoutForRampingUp.ms());
Henrik Boströmda9e2842023-04-06 13:27:33575
576 // Despite SVC being used on a single RTP stream, GetParameters() returns the
577 // three encodings that we configured earlier (this is not spec-compliant but
578 // it is how legacy SVC behaves).
579 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
580 std::vector<RtpEncodingParameters> encodings =
581 sender->GetParameters().encodings;
582 ASSERT_EQ(encodings.size(), 3u);
583 // When legacy SVC is used, `scalability_mode` is not specified.
584 EXPECT_FALSE(encodings[0].scalability_mode.has_value());
585 EXPECT_FALSE(encodings[1].scalability_mode.has_value());
586 EXPECT_FALSE(encodings[2].scalability_mode.has_value());
587}
588
589// The spec-compliant way to configure SVC for a single stream. The expected
590// outcome is the same as for the legacy SVC case except that we only have one
591// encoding in GetParameters().
592TEST_F(PeerConnectionEncodingsIntegrationTest,
Henrik Boströmfb65d232023-04-11 13:32:40593 VP9_StandardSvcWithOnlyOneEncoding) {
Henrik Boströmda9e2842023-04-06 13:27:33594 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
595 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
596 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
597
598 std::vector<cricket::SimulcastLayer> layers =
599 CreateLayers({"f"}, /*active=*/true);
600 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
601 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
602 layers);
603 std::vector<RtpCodecCapability> codecs =
604 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9");
605 transceiver->SetCodecPreferences(codecs);
606 // Configure SVC, a.k.a. "L3T3_KEY".
607 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
608 RtpParameters parameters = sender->GetParameters();
609 ASSERT_EQ(parameters.encodings.size(), 1u);
610 parameters.encodings[0].scalability_mode = "L3T3_KEY";
611 parameters.encodings[0].scale_resolution_down_by = 1;
612 EXPECT_TRUE(sender->SetParameters(parameters).ok());
613
Florent Castelli43a5dd82023-04-12 10:45:07614 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
Henrik Boströmda9e2842023-04-06 13:27:33615 local_pc_wrapper->WaitForConnection();
616 remote_pc_wrapper->WaitForConnection();
617
618 // Wait until media is flowing. We only expect a single RTP stream.
619 // We expect to see bytes flowing almost immediately on the lowest layer.
620 ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 1u),
621 kDefaultTimeout.ms());
622 EXPECT_TRUE(OutboundRtpResolutionsAreLessThanOrEqualToExpectations(
623 local_pc_wrapper, {{"", 1280, 720}}));
624 // Verify codec and scalability mode.
625 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
626 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
627 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
628 ASSERT_THAT(outbound_rtps, SizeIs(1u));
629 EXPECT_THAT(GetCurrentCodecMimeType(report, *outbound_rtps[0]),
630 StrCaseEq("video/VP9"));
631 EXPECT_THAT(*outbound_rtps[0]->scalability_mode, StrEq("L3T3_KEY"));
632
633 // GetParameters() is consistent with what we asked for and got.
634 parameters = sender->GetParameters();
635 ASSERT_EQ(parameters.encodings.size(), 1u);
636 EXPECT_THAT(parameters.encodings[0].scalability_mode,
637 Optional(std::string("L3T3_KEY")));
638}
639
640// The {active,inactive,inactive} case is technically simulcast but since we
641// only have one active stream, we're able to do SVC (multiple spatial layers
642// is not supported if multiple encodings are active). The expected outcome is
643// the same as above except we end up with two inactive RTP streams which are
644// observable in GetStats().
645TEST_F(PeerConnectionEncodingsIntegrationTest,
Henrik Boströmfb65d232023-04-11 13:32:40646 VP9_StandardSvcWithSingleActiveEncoding) {
Henrik Boströmda9e2842023-04-06 13:27:33647 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
648 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
649 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
650
651 std::vector<cricket::SimulcastLayer> layers =
652 CreateLayers({"f", "h", "q"}, /*active=*/true);
653 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
654 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
655 layers);
656 std::vector<RtpCodecCapability> codecs =
657 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9");
658 transceiver->SetCodecPreferences(codecs);
659 // Configure SVC, a.k.a. "L3T3_KEY".
660 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
661 RtpParameters parameters = sender->GetParameters();
Henrik Boströmb515c172023-04-11 08:20:19662 ASSERT_THAT(parameters.encodings, SizeIs(3));
Henrik Boströmda9e2842023-04-06 13:27:33663 parameters.encodings[0].scalability_mode = "L3T3_KEY";
664 parameters.encodings[0].scale_resolution_down_by = 1;
665 parameters.encodings[1].active = false;
666 parameters.encodings[2].active = false;
667 EXPECT_TRUE(sender->SetParameters(parameters).ok());
668
Florent Castelli43a5dd82023-04-12 10:45:07669 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
Henrik Boströmda9e2842023-04-06 13:27:33670 local_pc_wrapper->WaitForConnection();
671 remote_pc_wrapper->WaitForConnection();
672
673 // Since the standard API is configuring simulcast we get three outbound-rtps,
674 // but only one is active.
675 ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 3u, 1u),
676 kDefaultTimeout.ms());
677 // Wait until scalability mode is reported and expected resolution reached.
678 // Ramp up time is significant.
679 ASSERT_TRUE_WAIT(HasOutboundRtpWithRidAndScalabilityMode(
680 local_pc_wrapper, "f", "L3T3_KEY", 720),
Henrik Boströmfb65d232023-04-11 13:32:40681 kLongTimeoutForRampingUp.ms());
Henrik Boströmda9e2842023-04-06 13:27:33682
683 // GetParameters() is consistent with what we asked for and got.
684 parameters = sender->GetParameters();
Henrik Boströmb515c172023-04-11 08:20:19685 ASSERT_THAT(parameters.encodings, SizeIs(3));
Henrik Boströmda9e2842023-04-06 13:27:33686 EXPECT_THAT(parameters.encodings[0].scalability_mode,
687 Optional(std::string("L3T3_KEY")));
688 EXPECT_FALSE(parameters.encodings[1].scalability_mode.has_value());
689 EXPECT_FALSE(parameters.encodings[2].scalability_mode.has_value());
690}
691
Henrik Boströmda9e2842023-04-06 13:27:33692// Exercise common path where `scalability_mode` is not specified until after
693// negotiation, requring us to recreate the stream when the number of streams
694// changes from 1 (legacy SVC) to 3 (standard simulcast).
695TEST_F(PeerConnectionEncodingsIntegrationTest,
Henrik Boströmfb65d232023-04-11 13:32:40696 VP9_SwitchFromLegacySvcToStandardSingleActiveEncodingSvc) {
Henrik Boströmda9e2842023-04-06 13:27:33697 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
698 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
699 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
700
701 std::vector<cricket::SimulcastLayer> layers =
702 CreateLayers({"f", "h", "q"}, /*active=*/true);
703 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
704 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
705 layers);
706 std::vector<RtpCodecCapability> codecs =
707 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9");
708 transceiver->SetCodecPreferences(codecs);
709
710 // The original negotiation triggers legacy SVC because we didn't specify
711 // any scalability mode.
Florent Castelli43a5dd82023-04-12 10:45:07712 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
Henrik Boströmda9e2842023-04-06 13:27:33713 local_pc_wrapper->WaitForConnection();
714 remote_pc_wrapper->WaitForConnection();
715
716 // Switch to the standard mode. Despite only having a single active stream in
717 // both cases, this internally reconfigures from 1 stream to 3 streams.
718 // Test coverage for https://crbug.com/webrtc/15016.
719 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
720 RtpParameters parameters = sender->GetParameters();
Henrik Boströmb515c172023-04-11 08:20:19721 ASSERT_THAT(parameters.encodings, SizeIs(3));
Henrik Boströmda9e2842023-04-06 13:27:33722 parameters.encodings[0].active = true;
723 parameters.encodings[0].scalability_mode = "L2T2_KEY";
724 parameters.encodings[0].scale_resolution_down_by = 2.0;
725 parameters.encodings[1].active = false;
726 parameters.encodings[1].scalability_mode = absl::nullopt;
727 parameters.encodings[2].active = false;
728 parameters.encodings[2].scalability_mode = absl::nullopt;
729 sender->SetParameters(parameters);
730
731 // Since the standard API is configuring simulcast we get three outbound-rtps,
732 // but only one is active.
733 ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 3u, 1u),
734 kDefaultTimeout.ms());
735 // Wait until scalability mode is reported and expected resolution reached.
736 // Ramp up time may be significant.
737 ASSERT_TRUE_WAIT(HasOutboundRtpWithRidAndScalabilityMode(
738 local_pc_wrapper, "f", "L2T2_KEY", 720 / 2),
Henrik Boströmfb65d232023-04-11 13:32:40739 kLongTimeoutForRampingUp.ms());
Henrik Boströmda9e2842023-04-06 13:27:33740
741 // GetParameters() does not report any fallback.
742 parameters = sender->GetParameters();
Henrik Boströmb515c172023-04-11 08:20:19743 ASSERT_THAT(parameters.encodings, SizeIs(3));
Henrik Boströmda9e2842023-04-06 13:27:33744 EXPECT_THAT(parameters.encodings[0].scalability_mode,
745 Optional(std::string("L2T2_KEY")));
746 EXPECT_FALSE(parameters.encodings[1].scalability_mode.has_value());
747 EXPECT_FALSE(parameters.encodings[2].scalability_mode.has_value());
748}
749
750TEST_F(PeerConnectionEncodingsIntegrationTest,
Henrik Boströmfb65d232023-04-11 13:32:40751 VP9_AllLayersInactive_LegacySvc) {
Henrik Boströmb515c172023-04-11 08:20:19752 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
753 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
754 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
755
756 std::vector<cricket::SimulcastLayer> layers =
757 CreateLayers({"f", "h", "q"}, /*active=*/true);
758 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
759 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
760 layers);
761 std::vector<RtpCodecCapability> codecs =
762 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9");
763 transceiver->SetCodecPreferences(codecs);
764
765 // Legacy SVC mode and all layers inactive.
766 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
767 RtpParameters parameters = sender->GetParameters();
768 ASSERT_THAT(parameters.encodings, SizeIs(3));
769 parameters.encodings[0].active = false;
770 parameters.encodings[1].active = false;
771 parameters.encodings[2].active = false;
772 sender->SetParameters(parameters);
773
Florent Castelli43a5dd82023-04-12 10:45:07774 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
Henrik Boströmb515c172023-04-11 08:20:19775 local_pc_wrapper->WaitForConnection();
776 remote_pc_wrapper->WaitForConnection();
777
778 // Ensure no media is flowing (1 second should be enough).
779 rtc::Thread::Current()->SleepMs(1000);
780 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
781 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
782 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
783 ASSERT_THAT(outbound_rtps, SizeIs(1u));
784 EXPECT_EQ(*outbound_rtps[0]->bytes_sent, 0u);
785}
786
787TEST_F(PeerConnectionEncodingsIntegrationTest,
Henrik Boströmfb65d232023-04-11 13:32:40788 VP9_AllLayersInactive_StandardSvc) {
Henrik Boströmb515c172023-04-11 08:20:19789 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
790 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
791 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
792
793 std::vector<cricket::SimulcastLayer> layers =
794 CreateLayers({"f", "h", "q"}, /*active=*/true);
795 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
796 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
797 layers);
798 std::vector<RtpCodecCapability> codecs =
799 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9");
800 transceiver->SetCodecPreferences(codecs);
801
802 // Standard mode and all layers inactive.
803 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
804 RtpParameters parameters = sender->GetParameters();
805 ASSERT_THAT(parameters.encodings, SizeIs(3));
806 parameters.encodings[0].scalability_mode = "L3T3_KEY";
807 parameters.encodings[0].scale_resolution_down_by = 1;
808 parameters.encodings[0].active = false;
809 parameters.encodings[1].active = false;
810 parameters.encodings[2].active = false;
811 sender->SetParameters(parameters);
812
Florent Castelli43a5dd82023-04-12 10:45:07813 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
Henrik Boströmb515c172023-04-11 08:20:19814 local_pc_wrapper->WaitForConnection();
815 remote_pc_wrapper->WaitForConnection();
816
817 // Ensure no media is flowing (1 second should be enough).
818 rtc::Thread::Current()->SleepMs(1000);
819 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
820 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
821 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
822 ASSERT_THAT(outbound_rtps, SizeIs(3u));
823 EXPECT_EQ(*outbound_rtps[0]->bytes_sent, 0u);
824 EXPECT_EQ(*outbound_rtps[1]->bytes_sent, 0u);
825 EXPECT_EQ(*outbound_rtps[2]->bytes_sent, 0u);
826}
827
Henrik Boström2fec6442023-06-15 10:49:26828TEST_F(PeerConnectionEncodingsIntegrationTest, VP9_TargetBitrate_LegacyL1T3) {
829 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
830 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
831 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
832
833 std::vector<cricket::SimulcastLayer> layers =
834 CreateLayers({"f", "h", "q"}, /*active=*/true);
835 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
836 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
837 layers);
838 std::vector<RtpCodecCapability> codecs =
839 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9");
840 transceiver->SetCodecPreferences(codecs);
841
842 // In legacy SVC, disabling the bottom two layers encodings is interpreted as
843 // disabling the bottom two spatial layers resulting in L1T3.
844 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
845 RtpParameters parameters = sender->GetParameters();
846 parameters.encodings[0].active = false;
847 parameters.encodings[1].active = false;
848 parameters.encodings[2].active = true;
849 sender->SetParameters(parameters);
850
Florent Castelli43a5dd82023-04-12 10:45:07851 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
Henrik Boström2fec6442023-06-15 10:49:26852 local_pc_wrapper->WaitForConnection();
853 remote_pc_wrapper->WaitForConnection();
854
855 // Wait until 720p L1T3 has ramped up to 720p. It may take additional time
856 // for the target bitrate to reach its maximum.
857 ASSERT_TRUE_WAIT(HasOutboundRtpWithRidAndScalabilityMode(local_pc_wrapper,
858 "f", "L1T3", 720),
859 kLongTimeoutForRampingUp.ms());
860
861 // The target bitrate typically reaches `kVp9ExpectedMaxBitrateForL1T3`
862 // in a short period of time. However to reduce risk of flakiness in bot
863 // environments, this test only fails if we we exceed the expected target.
864 rtc::Thread::Current()->SleepMs(1000);
865 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
866 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
867 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
868 ASSERT_THAT(outbound_rtps, SizeIs(1));
869 DataRate target_bitrate =
870 DataRate::BitsPerSec(*outbound_rtps[0]->target_bitrate);
871 EXPECT_LE(target_bitrate.kbps(), kVp9ExpectedMaxBitrateForL1T3.kbps());
872}
873
874// Test coverage for https://crbug.com/1455039.
875TEST_F(PeerConnectionEncodingsIntegrationTest, VP9_TargetBitrate_StandardL1T3) {
876 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
877 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
878 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
879
880 std::vector<cricket::SimulcastLayer> layers =
881 CreateLayers({"f", "h", "q"}, /*active=*/true);
882 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
883 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
884 layers);
885 std::vector<RtpCodecCapability> codecs =
886 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP9");
887 transceiver->SetCodecPreferences(codecs);
888
889 // With standard APIs, L1T3 is explicitly specified and the encodings refers
890 // to the RTP streams, not the spatial layers. The end result should be
891 // equivalent to the legacy L1T3 case.
892 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
893 RtpParameters parameters = sender->GetParameters();
894 parameters.encodings[0].active = true;
895 parameters.encodings[0].scale_resolution_down_by = 1.0;
896 parameters.encodings[0].scalability_mode = "L1T3";
897 parameters.encodings[1].active = false;
898 parameters.encodings[2].active = false;
899 sender->SetParameters(parameters);
900
Florent Castelli43a5dd82023-04-12 10:45:07901 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
Henrik Boström2fec6442023-06-15 10:49:26902 local_pc_wrapper->WaitForConnection();
903 remote_pc_wrapper->WaitForConnection();
904
905 // Wait until 720p L1T3 has ramped up to 720p. It may take additional time
906 // for the target bitrate to reach its maximum.
907 ASSERT_TRUE_WAIT(HasOutboundRtpWithRidAndScalabilityMode(local_pc_wrapper,
908 "f", "L1T3", 720),
909 kLongTimeoutForRampingUp.ms());
910
911 // The target bitrate typically reaches `kVp9ExpectedMaxBitrateForL1T3`
912 // in a short period of time. However to reduce risk of flakiness in bot
913 // environments, this test only fails if we we exceed the expected target.
914 rtc::Thread::Current()->SleepMs(1000);
915 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
916 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
917 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
918 ASSERT_THAT(outbound_rtps, SizeIs(3));
919 auto* outbound_rtp = FindOutboundRtpByRid(outbound_rtps, "f");
920 ASSERT_TRUE(outbound_rtp);
921 DataRate target_bitrate = DataRate::BitsPerSec(*outbound_rtp->target_bitrate);
922 EXPECT_LE(target_bitrate.kbps(), kVp9ExpectedMaxBitrateForL1T3.kbps());
923}
924
Florent Castelli43a5dd82023-04-12 10:45:07925TEST_F(PeerConnectionEncodingsIntegrationTest,
Philipp Hancke13b5eb72023-10-04 14:20:05926 SimulcastProducesUniqueSsrcAndRtxSsrcs) {
927 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
928 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
929 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
930
931 std::vector<cricket::SimulcastLayer> layers =
932 CreateLayers({"f", "h", "q"}, /*active=*/true);
933 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
934 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
935 layers);
936 std::vector<RtpCodecCapability> codecs =
937 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, "VP8");
938 transceiver->SetCodecPreferences(codecs);
939
940 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
941 local_pc_wrapper->WaitForConnection();
942 remote_pc_wrapper->WaitForConnection();
943
944 // Wait until media is flowing on all three layers.
945 // Ramp up time is needed before all three layers are sending.
946 ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 3u),
947 kLongTimeoutForRampingUp.ms());
948 // Verify SSRCs and RTX SSRCs.
949 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
950 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
951 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
952 ASSERT_THAT(outbound_rtps, SizeIs(3u));
953
954 std::set<uint32_t> ssrcs;
955 std::set<uint32_t> rtx_ssrcs;
956 for (const auto& outbound_rtp : outbound_rtps) {
957 ASSERT_TRUE(outbound_rtp->ssrc.has_value());
958 ASSERT_TRUE(outbound_rtp->rtx_ssrc.has_value());
959 ssrcs.insert(*outbound_rtp->ssrc);
960 rtx_ssrcs.insert(*outbound_rtp->rtx_ssrc);
961 }
962 EXPECT_EQ(ssrcs.size(), 3u);
963 EXPECT_EQ(rtx_ssrcs.size(), 3u);
964}
965
966TEST_F(PeerConnectionEncodingsIntegrationTest,
Florent Castelli43a5dd82023-04-12 10:45:07967 EncodingParameterCodecIsEmptyWhenCreatedAudio) {
968 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
969
970 auto transceiver_or_error =
971 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
972 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
973 transceiver_or_error.MoveValue();
Harald Alvestranda6544372023-11-13 09:33:56974 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
Florent Castelli43a5dd82023-04-12 10:45:07975 EXPECT_FALSE(parameters.encodings[0].codec.has_value());
976}
977
978TEST_F(PeerConnectionEncodingsIntegrationTest,
979 EncodingParameterCodecIsEmptyWhenCreatedVideo) {
980 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
981
982 auto transceiver_or_error =
983 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
984 rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
985 transceiver_or_error.MoveValue();
Harald Alvestranda6544372023-11-13 09:33:56986 RtpParameters parameters = video_transceiver->sender()->GetParameters();
Florent Castelli43a5dd82023-04-12 10:45:07987 EXPECT_FALSE(parameters.encodings[0].codec.has_value());
988}
989
990TEST_F(PeerConnectionEncodingsIntegrationTest,
991 EncodingParameterCodecIsSetByAddTransceiverAudio) {
992 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
993 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
994 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
995
Harald Alvestranda6544372023-11-13 09:33:56996 rtc::scoped_refptr<MediaStreamInterface> stream =
Florent Castelli43a5dd82023-04-12 10:45:07997 local_pc_wrapper->GetUserMedia(
998 /*audio=*/true, {}, /*video=*/false, {});
999 rtc::scoped_refptr<AudioTrackInterface> track = stream->GetAudioTracks()[0];
1000
Harald Alvestranda6544372023-11-13 09:33:561001 absl::optional<RtpCodecCapability> pcmu =
Florent Castelli43a5dd82023-04-12 10:45:071002 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
1003 "pcmu");
1004 ASSERT_TRUE(pcmu);
1005
Harald Alvestranda6544372023-11-13 09:33:561006 RtpTransceiverInit init;
1007 init.direction = RtpTransceiverDirection::kSendOnly;
1008 RtpEncodingParameters encoding_parameters;
Florent Castelli43a5dd82023-04-12 10:45:071009 encoding_parameters.codec = pcmu;
1010 init.send_encodings.push_back(encoding_parameters);
1011
1012 auto transceiver_or_error =
1013 local_pc_wrapper->pc()->AddTransceiver(track, init);
1014 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1015 transceiver_or_error.MoveValue();
Harald Alvestranda6544372023-11-13 09:33:561016 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
Florent Castelli43a5dd82023-04-12 10:45:071017 EXPECT_EQ(*parameters.encodings[0].codec, *pcmu);
1018
1019 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1020 local_pc_wrapper->WaitForConnection();
1021 remote_pc_wrapper->WaitForConnection();
1022
1023 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
1024 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
1025 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1026 ASSERT_EQ(outbound_rtps.size(), 1u);
1027 std::string codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
1028 EXPECT_STRCASEEQ(("audio/" + pcmu->name).c_str(), codec_name.c_str());
1029}
1030
1031TEST_F(PeerConnectionEncodingsIntegrationTest,
1032 EncodingParameterCodecIsSetByAddTransceiverVideo) {
1033 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1034 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1035 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1036
Harald Alvestranda6544372023-11-13 09:33:561037 rtc::scoped_refptr<MediaStreamInterface> stream =
Florent Castelli43a5dd82023-04-12 10:45:071038 local_pc_wrapper->GetUserMedia(
1039 /*audio=*/false, {}, /*video=*/true, {.width = 1280, .height = 720});
1040 rtc::scoped_refptr<VideoTrackInterface> track = stream->GetVideoTracks()[0];
1041
Harald Alvestranda6544372023-11-13 09:33:561042 absl::optional<RtpCodecCapability> vp9 =
Florent Castelli43a5dd82023-04-12 10:45:071043 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1044 "vp9");
1045 ASSERT_TRUE(vp9);
1046
Harald Alvestranda6544372023-11-13 09:33:561047 RtpTransceiverInit init;
1048 init.direction = RtpTransceiverDirection::kSendOnly;
1049 RtpEncodingParameters encoding_parameters;
Florent Castelli43a5dd82023-04-12 10:45:071050 encoding_parameters.codec = vp9;
1051 encoding_parameters.scalability_mode = "L3T3";
1052 init.send_encodings.push_back(encoding_parameters);
1053
1054 auto transceiver_or_error =
1055 local_pc_wrapper->pc()->AddTransceiver(track, init);
1056 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1057 transceiver_or_error.MoveValue();
Harald Alvestranda6544372023-11-13 09:33:561058 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
Florent Castelli43a5dd82023-04-12 10:45:071059 EXPECT_EQ(*parameters.encodings[0].codec, *vp9);
1060
1061 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1062 local_pc_wrapper->WaitForConnection();
1063 remote_pc_wrapper->WaitForConnection();
1064
1065 EXPECT_TRUE_WAIT(
1066 IsCodecIdDifferentWithScalabilityMode(local_pc_wrapper, 0, "", "L3T3"),
1067 kDefaultTimeout.ms());
1068
1069 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
1070 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
1071 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1072 ASSERT_EQ(outbound_rtps.size(), 1u);
1073 std::string codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
1074 EXPECT_STRCASEEQ(("video/" + vp9->name).c_str(), codec_name.c_str());
1075 EXPECT_EQ(outbound_rtps[0]->scalability_mode.value(), "L3T3");
1076}
1077
1078TEST_F(PeerConnectionEncodingsIntegrationTest,
1079 EncodingParameterCodecIsSetBySetParametersBeforeNegotiationAudio) {
1080 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1081 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1082 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1083
Harald Alvestranda6544372023-11-13 09:33:561084 rtc::scoped_refptr<MediaStreamInterface> stream =
Florent Castelli43a5dd82023-04-12 10:45:071085 local_pc_wrapper->GetUserMedia(
1086 /*audio=*/true, {}, /*video=*/false, {});
1087 rtc::scoped_refptr<AudioTrackInterface> track = stream->GetAudioTracks()[0];
1088
Harald Alvestranda6544372023-11-13 09:33:561089 absl::optional<RtpCodecCapability> pcmu =
Florent Castelli43a5dd82023-04-12 10:45:071090 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
1091 "pcmu");
1092
1093 auto transceiver_or_error = local_pc_wrapper->pc()->AddTransceiver(track);
1094 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1095 transceiver_or_error.MoveValue();
Harald Alvestranda6544372023-11-13 09:33:561096 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
Florent Castelli43a5dd82023-04-12 10:45:071097 parameters.encodings[0].codec = pcmu;
1098 EXPECT_TRUE(audio_transceiver->sender()->SetParameters(parameters).ok());
1099
1100 parameters = audio_transceiver->sender()->GetParameters();
1101 EXPECT_EQ(parameters.encodings[0].codec, pcmu);
1102
1103 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1104 local_pc_wrapper->WaitForConnection();
1105 remote_pc_wrapper->WaitForConnection();
1106
1107 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
1108 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
1109 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1110 ASSERT_EQ(outbound_rtps.size(), 1u);
1111 std::string codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
1112 EXPECT_STRCASEEQ(("audio/" + pcmu->name).c_str(), codec_name.c_str());
1113}
1114
1115TEST_F(PeerConnectionEncodingsIntegrationTest,
1116 EncodingParameterCodecIsSetBySetParametersAfterNegotiationAudio) {
1117 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1118 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1119 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1120
Harald Alvestranda6544372023-11-13 09:33:561121 rtc::scoped_refptr<MediaStreamInterface> stream =
Florent Castelli43a5dd82023-04-12 10:45:071122 local_pc_wrapper->GetUserMedia(
1123 /*audio=*/true, {}, /*video=*/false, {});
1124 rtc::scoped_refptr<AudioTrackInterface> track = stream->GetAudioTracks()[0];
1125
Harald Alvestranda6544372023-11-13 09:33:561126 absl::optional<RtpCodecCapability> pcmu =
Florent Castelli43a5dd82023-04-12 10:45:071127 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
1128 "pcmu");
1129
1130 auto transceiver_or_error = local_pc_wrapper->pc()->AddTransceiver(track);
1131 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1132 transceiver_or_error.MoveValue();
1133
1134 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1135 local_pc_wrapper->WaitForConnection();
1136 remote_pc_wrapper->WaitForConnection();
1137
1138 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
1139 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
1140 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1141 ASSERT_EQ(outbound_rtps.size(), 1u);
1142 std::string codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
1143 EXPECT_STRCASENE(("audio/" + pcmu->name).c_str(), codec_name.c_str());
1144 std::string last_codec_id = outbound_rtps[0]->codec_id.value();
1145
Harald Alvestranda6544372023-11-13 09:33:561146 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
Florent Castelli43a5dd82023-04-12 10:45:071147 parameters.encodings[0].codec = pcmu;
1148 EXPECT_TRUE(audio_transceiver->sender()->SetParameters(parameters).ok());
1149
1150 parameters = audio_transceiver->sender()->GetParameters();
1151 EXPECT_EQ(parameters.encodings[0].codec, pcmu);
1152
1153 EXPECT_TRUE_WAIT(IsCodecIdDifferent(local_pc_wrapper, 0, last_codec_id),
1154 kDefaultTimeout.ms());
1155
1156 report = GetStats(local_pc_wrapper);
1157 outbound_rtps = report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1158 ASSERT_EQ(outbound_rtps.size(), 1u);
1159 codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
1160 EXPECT_STRCASEEQ(("audio/" + pcmu->name).c_str(), codec_name.c_str());
1161}
1162
1163TEST_F(PeerConnectionEncodingsIntegrationTest,
1164 EncodingParameterCodecIsSetBySetParametersBeforeNegotiationVideo) {
1165 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1166 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1167 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1168
Harald Alvestranda6544372023-11-13 09:33:561169 rtc::scoped_refptr<MediaStreamInterface> stream =
Florent Castelli43a5dd82023-04-12 10:45:071170 local_pc_wrapper->GetUserMedia(
1171 /*audio=*/false, {}, /*video=*/true, {.width = 1280, .height = 720});
1172 rtc::scoped_refptr<VideoTrackInterface> track = stream->GetVideoTracks()[0];
1173
Harald Alvestranda6544372023-11-13 09:33:561174 absl::optional<RtpCodecCapability> vp9 =
Florent Castelli43a5dd82023-04-12 10:45:071175 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1176 "vp9");
1177
1178 auto transceiver_or_error = local_pc_wrapper->pc()->AddTransceiver(track);
1179 rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
1180 transceiver_or_error.MoveValue();
Harald Alvestranda6544372023-11-13 09:33:561181 RtpParameters parameters = video_transceiver->sender()->GetParameters();
Florent Castelli43a5dd82023-04-12 10:45:071182 parameters.encodings[0].codec = vp9;
1183 parameters.encodings[0].scalability_mode = "L3T3";
1184 EXPECT_TRUE(video_transceiver->sender()->SetParameters(parameters).ok());
1185
1186 parameters = video_transceiver->sender()->GetParameters();
1187 EXPECT_EQ(parameters.encodings[0].codec, vp9);
1188 EXPECT_EQ(parameters.encodings[0].scalability_mode, "L3T3");
1189
1190 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1191 local_pc_wrapper->WaitForConnection();
1192 remote_pc_wrapper->WaitForConnection();
1193
1194 EXPECT_TRUE_WAIT(
1195 IsCodecIdDifferentWithScalabilityMode(local_pc_wrapper, 0, "", "L3T3"),
1196 kDefaultTimeout.ms());
1197 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
1198 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
1199 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1200 ASSERT_EQ(outbound_rtps.size(), 1u);
1201 std::string codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
1202 EXPECT_STRCASEEQ(("video/" + vp9->name).c_str(), codec_name.c_str());
1203 EXPECT_EQ(outbound_rtps[0]->scalability_mode.ValueOrDefault(""), "L3T3");
1204}
1205
1206TEST_F(PeerConnectionEncodingsIntegrationTest,
1207 EncodingParameterCodecIsSetBySetParametersAfterNegotiationVideo) {
1208 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1209 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1210 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1211
Harald Alvestranda6544372023-11-13 09:33:561212 rtc::scoped_refptr<MediaStreamInterface> stream =
Florent Castelli43a5dd82023-04-12 10:45:071213 local_pc_wrapper->GetUserMedia(
1214 /*audio=*/false, {}, /*video=*/true, {.width = 1280, .height = 720});
1215 rtc::scoped_refptr<VideoTrackInterface> track = stream->GetVideoTracks()[0];
1216
Harald Alvestranda6544372023-11-13 09:33:561217 absl::optional<RtpCodecCapability> vp9 =
Florent Castelli43a5dd82023-04-12 10:45:071218 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1219 "vp9");
1220
1221 auto transceiver_or_error = local_pc_wrapper->pc()->AddTransceiver(track);
1222 rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
1223 transceiver_or_error.MoveValue();
1224
1225 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1226 local_pc_wrapper->WaitForConnection();
1227 remote_pc_wrapper->WaitForConnection();
1228
1229 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
1230 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
1231 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1232 ASSERT_EQ(outbound_rtps.size(), 1u);
1233 std::string codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
1234 EXPECT_STRCASENE(("audio/" + vp9->name).c_str(), codec_name.c_str());
1235 std::string last_codec_id = outbound_rtps[0]->codec_id.value();
1236
Harald Alvestranda6544372023-11-13 09:33:561237 RtpParameters parameters = video_transceiver->sender()->GetParameters();
Florent Castelli43a5dd82023-04-12 10:45:071238 parameters.encodings[0].codec = vp9;
1239 parameters.encodings[0].scalability_mode = "L3T3";
1240 EXPECT_TRUE(video_transceiver->sender()->SetParameters(parameters).ok());
1241
1242 parameters = video_transceiver->sender()->GetParameters();
1243 EXPECT_EQ(parameters.encodings[0].codec, vp9);
1244 EXPECT_EQ(parameters.encodings[0].scalability_mode, "L3T3");
1245
1246 EXPECT_TRUE_WAIT(IsCodecIdDifferentWithScalabilityMode(local_pc_wrapper, 0,
1247 last_codec_id, "L3T3"),
1248 kDefaultTimeout.ms());
1249
1250 report = GetStats(local_pc_wrapper);
1251 outbound_rtps = report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1252 ASSERT_EQ(outbound_rtps.size(), 1u);
1253 codec_name = GetCurrentCodecMimeType(report, *outbound_rtps[0]);
1254 EXPECT_STRCASEEQ(("video/" + vp9->name).c_str(), codec_name.c_str());
1255 EXPECT_EQ(outbound_rtps[0]->scalability_mode.value(), "L3T3");
1256}
1257
1258TEST_F(PeerConnectionEncodingsIntegrationTest,
1259 AddTransceiverRejectsUnknownCodecParameterAudio) {
1260 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1261
Harald Alvestranda6544372023-11-13 09:33:561262 RtpCodec dummy_codec;
Florent Castelli43a5dd82023-04-12 10:45:071263 dummy_codec.kind = cricket::MEDIA_TYPE_AUDIO;
1264 dummy_codec.name = "FOOBAR";
1265 dummy_codec.clock_rate = 90000;
1266 dummy_codec.num_channels = 2;
1267
Harald Alvestranda6544372023-11-13 09:33:561268 RtpTransceiverInit init;
1269 init.direction = RtpTransceiverDirection::kSendOnly;
1270 RtpEncodingParameters encoding_parameters;
Florent Castelli43a5dd82023-04-12 10:45:071271 encoding_parameters.codec = dummy_codec;
1272 init.send_encodings.push_back(encoding_parameters);
1273
1274 auto transceiver_or_error =
1275 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init);
1276 EXPECT_FALSE(transceiver_or_error.ok());
1277 EXPECT_EQ(transceiver_or_error.error().type(),
1278 RTCErrorType::UNSUPPORTED_OPERATION);
1279}
1280
1281TEST_F(PeerConnectionEncodingsIntegrationTest,
1282 AddTransceiverRejectsUnknownCodecParameterVideo) {
1283 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1284
Harald Alvestranda6544372023-11-13 09:33:561285 RtpCodec dummy_codec;
Florent Castelli43a5dd82023-04-12 10:45:071286 dummy_codec.kind = cricket::MEDIA_TYPE_VIDEO;
1287 dummy_codec.name = "FOOBAR";
1288 dummy_codec.clock_rate = 90000;
1289
Harald Alvestranda6544372023-11-13 09:33:561290 RtpTransceiverInit init;
1291 init.direction = RtpTransceiverDirection::kSendOnly;
1292 RtpEncodingParameters encoding_parameters;
Florent Castelli43a5dd82023-04-12 10:45:071293 encoding_parameters.codec = dummy_codec;
1294 init.send_encodings.push_back(encoding_parameters);
1295
1296 auto transceiver_or_error =
1297 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
1298 EXPECT_FALSE(transceiver_or_error.ok());
1299 EXPECT_EQ(transceiver_or_error.error().type(),
1300 RTCErrorType::UNSUPPORTED_OPERATION);
1301}
1302
1303TEST_F(PeerConnectionEncodingsIntegrationTest,
1304 SetParametersRejectsUnknownCodecParameterAudio) {
1305 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1306
Harald Alvestranda6544372023-11-13 09:33:561307 RtpCodec dummy_codec;
Florent Castelli43a5dd82023-04-12 10:45:071308 dummy_codec.kind = cricket::MEDIA_TYPE_AUDIO;
1309 dummy_codec.name = "FOOBAR";
1310 dummy_codec.clock_rate = 90000;
1311 dummy_codec.num_channels = 2;
1312
1313 auto transceiver_or_error =
1314 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
1315 ASSERT_TRUE(transceiver_or_error.ok());
1316 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1317 transceiver_or_error.MoveValue();
1318
Harald Alvestranda6544372023-11-13 09:33:561319 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
Florent Castelli43a5dd82023-04-12 10:45:071320 parameters.encodings[0].codec = dummy_codec;
1321 RTCError error = audio_transceiver->sender()->SetParameters(parameters);
1322 EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
1323}
1324
1325TEST_F(PeerConnectionEncodingsIntegrationTest,
1326 SetParametersRejectsUnknownCodecParameterVideo) {
1327 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1328
Harald Alvestranda6544372023-11-13 09:33:561329 RtpCodec dummy_codec;
Florent Castelli43a5dd82023-04-12 10:45:071330 dummy_codec.kind = cricket::MEDIA_TYPE_VIDEO;
1331 dummy_codec.name = "FOOBAR";
1332 dummy_codec.clock_rate = 90000;
1333
1334 auto transceiver_or_error =
1335 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
1336 ASSERT_TRUE(transceiver_or_error.ok());
1337 rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
1338 transceiver_or_error.MoveValue();
1339
Harald Alvestranda6544372023-11-13 09:33:561340 RtpParameters parameters = video_transceiver->sender()->GetParameters();
Florent Castelli43a5dd82023-04-12 10:45:071341 parameters.encodings[0].codec = dummy_codec;
1342 RTCError error = video_transceiver->sender()->SetParameters(parameters);
1343 EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
1344}
1345
1346TEST_F(PeerConnectionEncodingsIntegrationTest,
1347 SetParametersRejectsNonPreferredCodecParameterAudio) {
1348 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1349
Harald Alvestranda6544372023-11-13 09:33:561350 absl::optional<RtpCodecCapability> opus =
Florent Castelli43a5dd82023-04-12 10:45:071351 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
1352 "opus");
1353 ASSERT_TRUE(opus);
1354
Harald Alvestranda6544372023-11-13 09:33:561355 std::vector<RtpCodecCapability> not_opus_codecs =
Florent Castelli43a5dd82023-04-12 10:45:071356 local_pc_wrapper->pc_factory()
1357 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
1358 .codecs;
1359 not_opus_codecs.erase(
1360 std::remove_if(not_opus_codecs.begin(), not_opus_codecs.end(),
1361 [&](const auto& codec) {
1362 return absl::EqualsIgnoreCase(codec.name, opus->name);
1363 }),
1364 not_opus_codecs.end());
1365
1366 auto transceiver_or_error =
1367 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
1368 ASSERT_TRUE(transceiver_or_error.ok());
1369 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1370 transceiver_or_error.MoveValue();
1371 ASSERT_TRUE(audio_transceiver->SetCodecPreferences(not_opus_codecs).ok());
1372
Harald Alvestranda6544372023-11-13 09:33:561373 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
Florent Castelli43a5dd82023-04-12 10:45:071374 parameters.encodings[0].codec = opus;
1375 RTCError error = audio_transceiver->sender()->SetParameters(parameters);
1376 EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
1377}
1378
1379TEST_F(PeerConnectionEncodingsIntegrationTest,
1380 SetParametersRejectsNonPreferredCodecParameterVideo) {
1381 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1382
Harald Alvestranda6544372023-11-13 09:33:561383 absl::optional<RtpCodecCapability> vp8 =
Florent Castelli43a5dd82023-04-12 10:45:071384 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1385 "vp8");
1386 ASSERT_TRUE(vp8);
1387
Harald Alvestranda6544372023-11-13 09:33:561388 std::vector<RtpCodecCapability> not_vp8_codecs =
Florent Castelli43a5dd82023-04-12 10:45:071389 local_pc_wrapper->pc_factory()
1390 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1391 .codecs;
1392 not_vp8_codecs.erase(
1393 std::remove_if(not_vp8_codecs.begin(), not_vp8_codecs.end(),
1394 [&](const auto& codec) {
1395 return absl::EqualsIgnoreCase(codec.name, vp8->name);
1396 }),
1397 not_vp8_codecs.end());
1398
1399 auto transceiver_or_error =
1400 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
1401 ASSERT_TRUE(transceiver_or_error.ok());
1402 rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
1403 transceiver_or_error.MoveValue();
1404 ASSERT_TRUE(video_transceiver->SetCodecPreferences(not_vp8_codecs).ok());
1405
Harald Alvestranda6544372023-11-13 09:33:561406 RtpParameters parameters = video_transceiver->sender()->GetParameters();
Florent Castelli43a5dd82023-04-12 10:45:071407 parameters.encodings[0].codec = vp8;
1408 RTCError error = video_transceiver->sender()->SetParameters(parameters);
1409 EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
1410}
1411
1412TEST_F(PeerConnectionEncodingsIntegrationTest,
1413 SetParametersRejectsNonNegotiatedCodecParameterAudio) {
1414 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1415 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1416 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1417
Harald Alvestranda6544372023-11-13 09:33:561418 absl::optional<RtpCodecCapability> opus =
Florent Castelli43a5dd82023-04-12 10:45:071419 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
1420 "opus");
1421 ASSERT_TRUE(opus);
1422
Harald Alvestranda6544372023-11-13 09:33:561423 std::vector<RtpCodecCapability> not_opus_codecs =
Florent Castelli43a5dd82023-04-12 10:45:071424 local_pc_wrapper->pc_factory()
1425 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
1426 .codecs;
1427 not_opus_codecs.erase(
1428 std::remove_if(not_opus_codecs.begin(), not_opus_codecs.end(),
1429 [&](const auto& codec) {
1430 return absl::EqualsIgnoreCase(codec.name, opus->name);
1431 }),
1432 not_opus_codecs.end());
1433
1434 auto transceiver_or_error =
1435 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
1436 ASSERT_TRUE(transceiver_or_error.ok());
1437 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1438 transceiver_or_error.MoveValue();
1439 ASSERT_TRUE(audio_transceiver->SetCodecPreferences(not_opus_codecs).ok());
1440
1441 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1442 local_pc_wrapper->WaitForConnection();
1443 remote_pc_wrapper->WaitForConnection();
1444
Harald Alvestranda6544372023-11-13 09:33:561445 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
Florent Castelli43a5dd82023-04-12 10:45:071446 parameters.encodings[0].codec = opus;
1447 RTCError error = audio_transceiver->sender()->SetParameters(parameters);
1448 EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
1449}
1450
1451TEST_F(PeerConnectionEncodingsIntegrationTest,
Florent Castelli1adea982023-10-16 09:07:351452 SetParametersRejectsNonRemotelyNegotiatedCodecParameterAudio) {
1453 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1454 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1455 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1456
Harald Alvestranda6544372023-11-13 09:33:561457 absl::optional<RtpCodecCapability> opus =
Florent Castelli1adea982023-10-16 09:07:351458 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
1459 "opus");
1460 ASSERT_TRUE(opus);
1461
Harald Alvestranda6544372023-11-13 09:33:561462 std::vector<RtpCodecCapability> not_opus_codecs =
Florent Castelli1adea982023-10-16 09:07:351463 local_pc_wrapper->pc_factory()
1464 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
1465 .codecs;
1466 not_opus_codecs.erase(
1467 std::remove_if(not_opus_codecs.begin(), not_opus_codecs.end(),
1468 [&](const auto& codec) {
1469 return absl::EqualsIgnoreCase(codec.name, opus->name);
1470 }),
1471 not_opus_codecs.end());
1472
1473 auto transceiver_or_error =
1474 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
1475 ASSERT_TRUE(transceiver_or_error.ok());
1476 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1477 transceiver_or_error.MoveValue();
1478
1479 // Negotiation, create offer and apply it
1480 std::unique_ptr<SessionDescriptionInterface> offer =
1481 CreateOffer(local_pc_wrapper);
1482 rtc::scoped_refptr<MockSetSessionDescriptionObserver> p1 =
1483 SetLocalDescription(local_pc_wrapper, offer.get());
1484 rtc::scoped_refptr<MockSetSessionDescriptionObserver> p2 =
1485 SetRemoteDescription(remote_pc_wrapper, offer.get());
1486 EXPECT_TRUE(Await({p1, p2}));
1487
1488 // Update the remote transceiver to reject Opus
1489 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>> remote_transceivers =
1490 remote_pc_wrapper->pc()->GetTransceivers();
1491 ASSERT_TRUE(!remote_transceivers.empty());
1492 rtc::scoped_refptr<RtpTransceiverInterface> remote_audio_transceiver =
1493 remote_transceivers[0];
1494 ASSERT_TRUE(
1495 remote_audio_transceiver->SetCodecPreferences(not_opus_codecs).ok());
1496
1497 // Create answer and apply it
1498 std::unique_ptr<SessionDescriptionInterface> answer =
1499 CreateAnswer(remote_pc_wrapper);
1500 p1 = SetLocalDescription(remote_pc_wrapper, answer.get());
1501 p2 = SetRemoteDescription(local_pc_wrapper, answer.get());
1502 EXPECT_TRUE(Await({p1, p2}));
1503
1504 local_pc_wrapper->WaitForConnection();
1505 remote_pc_wrapper->WaitForConnection();
1506
Harald Alvestranda6544372023-11-13 09:33:561507 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
Florent Castelli1adea982023-10-16 09:07:351508 parameters.encodings[0].codec = opus;
1509 RTCError error = audio_transceiver->sender()->SetParameters(parameters);
1510 EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
1511}
1512
1513TEST_F(PeerConnectionEncodingsIntegrationTest,
Florent Castelli43a5dd82023-04-12 10:45:071514 SetParametersRejectsNonNegotiatedCodecParameterVideo) {
1515 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1516 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1517 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1518
Harald Alvestranda6544372023-11-13 09:33:561519 absl::optional<RtpCodecCapability> vp8 =
Florent Castelli43a5dd82023-04-12 10:45:071520 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1521 "vp8");
1522 ASSERT_TRUE(vp8);
1523
Harald Alvestranda6544372023-11-13 09:33:561524 std::vector<RtpCodecCapability> not_vp8_codecs =
Florent Castelli43a5dd82023-04-12 10:45:071525 local_pc_wrapper->pc_factory()
1526 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1527 .codecs;
1528 not_vp8_codecs.erase(
1529 std::remove_if(not_vp8_codecs.begin(), not_vp8_codecs.end(),
1530 [&](const auto& codec) {
1531 return absl::EqualsIgnoreCase(codec.name, vp8->name);
1532 }),
1533 not_vp8_codecs.end());
1534
1535 auto transceiver_or_error =
1536 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
1537 ASSERT_TRUE(transceiver_or_error.ok());
1538 rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
1539 transceiver_or_error.MoveValue();
1540 ASSERT_TRUE(video_transceiver->SetCodecPreferences(not_vp8_codecs).ok());
1541
1542 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1543 local_pc_wrapper->WaitForConnection();
1544 remote_pc_wrapper->WaitForConnection();
1545
Harald Alvestranda6544372023-11-13 09:33:561546 RtpParameters parameters = video_transceiver->sender()->GetParameters();
Florent Castelli43a5dd82023-04-12 10:45:071547 parameters.encodings[0].codec = vp8;
1548 RTCError error = video_transceiver->sender()->SetParameters(parameters);
1549 EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
1550}
1551
1552TEST_F(PeerConnectionEncodingsIntegrationTest,
Florent Castelli1adea982023-10-16 09:07:351553 SetParametersRejectsNonRemotelyNegotiatedCodecParameterVideo) {
1554 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1555 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1556 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1557
Harald Alvestranda6544372023-11-13 09:33:561558 absl::optional<RtpCodecCapability> vp8 =
Florent Castelli1adea982023-10-16 09:07:351559 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1560 "vp8");
1561 ASSERT_TRUE(vp8);
1562
Harald Alvestranda6544372023-11-13 09:33:561563 std::vector<RtpCodecCapability> not_vp8_codecs =
Florent Castelli1adea982023-10-16 09:07:351564 local_pc_wrapper->pc_factory()
1565 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1566 .codecs;
1567 not_vp8_codecs.erase(
1568 std::remove_if(not_vp8_codecs.begin(), not_vp8_codecs.end(),
1569 [&](const auto& codec) {
1570 return absl::EqualsIgnoreCase(codec.name, vp8->name);
1571 }),
1572 not_vp8_codecs.end());
1573
1574 auto transceiver_or_error =
1575 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
1576 ASSERT_TRUE(transceiver_or_error.ok());
1577 rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
1578 transceiver_or_error.MoveValue();
1579
1580 // Negotiation, create offer and apply it
1581 std::unique_ptr<SessionDescriptionInterface> offer =
1582 CreateOffer(local_pc_wrapper);
1583 rtc::scoped_refptr<MockSetSessionDescriptionObserver> p1 =
1584 SetLocalDescription(local_pc_wrapper, offer.get());
1585 rtc::scoped_refptr<MockSetSessionDescriptionObserver> p2 =
1586 SetRemoteDescription(remote_pc_wrapper, offer.get());
1587 EXPECT_TRUE(Await({p1, p2}));
1588
1589 // Update the remote transceiver to reject VP8
1590 std::vector<rtc::scoped_refptr<RtpTransceiverInterface>> remote_transceivers =
1591 remote_pc_wrapper->pc()->GetTransceivers();
1592 ASSERT_TRUE(!remote_transceivers.empty());
1593 rtc::scoped_refptr<RtpTransceiverInterface> remote_video_transceiver =
1594 remote_transceivers[0];
1595 ASSERT_TRUE(
1596 remote_video_transceiver->SetCodecPreferences(not_vp8_codecs).ok());
1597
1598 // Create answer and apply it
1599 std::unique_ptr<SessionDescriptionInterface> answer =
1600 CreateAnswer(remote_pc_wrapper);
1601 p1 = SetLocalDescription(remote_pc_wrapper, answer.get());
1602 p2 = SetRemoteDescription(local_pc_wrapper, answer.get());
1603 EXPECT_TRUE(Await({p1, p2}));
1604
1605 local_pc_wrapper->WaitForConnection();
1606 remote_pc_wrapper->WaitForConnection();
1607
Harald Alvestranda6544372023-11-13 09:33:561608 RtpParameters parameters = video_transceiver->sender()->GetParameters();
Florent Castelli1adea982023-10-16 09:07:351609 parameters.encodings[0].codec = vp8;
1610 RTCError error = video_transceiver->sender()->SetParameters(parameters);
1611 EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
1612}
1613
1614TEST_F(PeerConnectionEncodingsIntegrationTest,
Florent Castelli43a5dd82023-04-12 10:45:071615 EncodingParametersCodecRemovedAfterNegotiationAudio) {
1616 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1617 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1618 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1619
Harald Alvestranda6544372023-11-13 09:33:561620 absl::optional<RtpCodecCapability> opus =
Florent Castelli43a5dd82023-04-12 10:45:071621 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
1622 "opus");
1623 ASSERT_TRUE(opus);
1624
Harald Alvestranda6544372023-11-13 09:33:561625 std::vector<RtpCodecCapability> not_opus_codecs =
Florent Castelli43a5dd82023-04-12 10:45:071626 local_pc_wrapper->pc_factory()
1627 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
1628 .codecs;
1629 not_opus_codecs.erase(
1630 std::remove_if(not_opus_codecs.begin(), not_opus_codecs.end(),
1631 [&](const auto& codec) {
1632 return absl::EqualsIgnoreCase(codec.name, opus->name);
1633 }),
1634 not_opus_codecs.end());
1635
Harald Alvestranda6544372023-11-13 09:33:561636 RtpTransceiverInit init;
1637 init.direction = RtpTransceiverDirection::kSendOnly;
1638 RtpEncodingParameters encoding_parameters;
Florent Castelli43a5dd82023-04-12 10:45:071639 encoding_parameters.codec = opus;
1640 init.send_encodings.push_back(encoding_parameters);
1641
1642 auto transceiver_or_error =
1643 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init);
1644 ASSERT_TRUE(transceiver_or_error.ok());
1645 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1646 transceiver_or_error.MoveValue();
1647
1648 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1649 local_pc_wrapper->WaitForConnection();
1650 remote_pc_wrapper->WaitForConnection();
1651
Harald Alvestranda6544372023-11-13 09:33:561652 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
Florent Castelli43a5dd82023-04-12 10:45:071653 EXPECT_EQ(parameters.encodings[0].codec, opus);
1654
1655 ASSERT_TRUE(audio_transceiver->SetCodecPreferences(not_opus_codecs).ok());
1656 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1657
1658 parameters = audio_transceiver->sender()->GetParameters();
1659 EXPECT_FALSE(parameters.encodings[0].codec);
1660}
1661
1662TEST_F(PeerConnectionEncodingsIntegrationTest,
Tomas Lundqvista26d6ed2023-10-27 12:25:571663 EncodingParametersRedEnabledBeforeNegotiationAudio) {
1664 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1665 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1666 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1667
Harald Alvestranda6544372023-11-13 09:33:561668 std::vector<RtpCodecCapability> send_codecs =
Tomas Lundqvista26d6ed2023-10-27 12:25:571669 local_pc_wrapper->pc_factory()
1670 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
1671 .codecs;
1672
Harald Alvestranda6544372023-11-13 09:33:561673 absl::optional<RtpCodecCapability> opus =
Tomas Lundqvista26d6ed2023-10-27 12:25:571674 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
1675 "opus");
1676 ASSERT_TRUE(opus);
1677
Harald Alvestranda6544372023-11-13 09:33:561678 absl::optional<RtpCodecCapability> red =
Tomas Lundqvista26d6ed2023-10-27 12:25:571679 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_AUDIO,
1680 "red");
1681 ASSERT_TRUE(red);
1682
Harald Alvestranda6544372023-11-13 09:33:561683 RtpTransceiverInit init;
1684 init.direction = RtpTransceiverDirection::kSendOnly;
1685 RtpEncodingParameters encoding_parameters;
Tomas Lundqvista26d6ed2023-10-27 12:25:571686 encoding_parameters.codec = opus;
1687 init.send_encodings.push_back(encoding_parameters);
1688
1689 auto transceiver_or_error =
1690 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init);
1691 ASSERT_TRUE(transceiver_or_error.ok());
1692 rtc::scoped_refptr<RtpTransceiverInterface> audio_transceiver =
1693 transceiver_or_error.MoveValue();
1694
1695 // Preferring RED over Opus should enable RED with Opus encoding.
1696 send_codecs[0] = red.value();
1697 send_codecs[1] = opus.value();
1698
1699 ASSERT_TRUE(audio_transceiver->SetCodecPreferences(send_codecs).ok());
1700 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1701 local_pc_wrapper->WaitForConnection();
1702 remote_pc_wrapper->WaitForConnection();
1703
Harald Alvestranda6544372023-11-13 09:33:561704 RtpParameters parameters = audio_transceiver->sender()->GetParameters();
Tomas Lundqvista26d6ed2023-10-27 12:25:571705 EXPECT_EQ(parameters.encodings[0].codec, opus);
1706 EXPECT_EQ(parameters.codecs[0].payload_type, red->preferred_payload_type);
1707 EXPECT_EQ(parameters.codecs[0].name, red->name);
1708
1709 // Check that it's possible to switch back to Opus without RED.
1710 send_codecs[0] = opus.value();
1711 send_codecs[1] = red.value();
1712
1713 ASSERT_TRUE(audio_transceiver->SetCodecPreferences(send_codecs).ok());
1714 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1715
1716 parameters = audio_transceiver->sender()->GetParameters();
1717 EXPECT_EQ(parameters.encodings[0].codec, opus);
1718 EXPECT_EQ(parameters.codecs[0].payload_type, opus->preferred_payload_type);
1719 EXPECT_EQ(parameters.codecs[0].name, opus->name);
1720}
1721
1722TEST_F(PeerConnectionEncodingsIntegrationTest,
Florent Castelli43a5dd82023-04-12 10:45:071723 SetParametersRejectsScalabilityModeForSelectedCodec) {
1724 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1725
Harald Alvestranda6544372023-11-13 09:33:561726 absl::optional<RtpCodecCapability> vp8 =
Florent Castelli43a5dd82023-04-12 10:45:071727 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1728 "vp8");
1729 ASSERT_TRUE(vp8);
1730
Harald Alvestranda6544372023-11-13 09:33:561731 RtpTransceiverInit init;
1732 init.direction = RtpTransceiverDirection::kSendOnly;
1733 RtpEncodingParameters encoding_parameters;
Florent Castelli43a5dd82023-04-12 10:45:071734 encoding_parameters.codec = vp8;
1735 encoding_parameters.scalability_mode = "L1T3";
1736 init.send_encodings.push_back(encoding_parameters);
1737
1738 auto transceiver_or_error =
1739 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
1740 ASSERT_TRUE(transceiver_or_error.ok());
1741 rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
1742 transceiver_or_error.MoveValue();
1743
Harald Alvestranda6544372023-11-13 09:33:561744 RtpParameters parameters = video_transceiver->sender()->GetParameters();
Florent Castelli43a5dd82023-04-12 10:45:071745 parameters.encodings[0].scalability_mode = "L3T3";
1746 RTCError error = video_transceiver->sender()->SetParameters(parameters);
1747 EXPECT_EQ(error.type(), RTCErrorType::INVALID_MODIFICATION);
1748}
1749
1750TEST_F(PeerConnectionEncodingsIntegrationTest,
1751 EncodingParametersCodecRemovedByNegotiationVideo) {
1752 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1753 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1754 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1755
Harald Alvestranda6544372023-11-13 09:33:561756 absl::optional<RtpCodecCapability> vp8 =
Florent Castelli43a5dd82023-04-12 10:45:071757 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1758 "vp8");
1759 ASSERT_TRUE(vp8);
1760
Harald Alvestranda6544372023-11-13 09:33:561761 std::vector<RtpCodecCapability> not_vp8_codecs =
Florent Castelli43a5dd82023-04-12 10:45:071762 local_pc_wrapper->pc_factory()
1763 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1764 .codecs;
1765 not_vp8_codecs.erase(
1766 std::remove_if(not_vp8_codecs.begin(), not_vp8_codecs.end(),
1767 [&](const auto& codec) {
1768 return absl::EqualsIgnoreCase(codec.name, vp8->name);
1769 }),
1770 not_vp8_codecs.end());
1771
Harald Alvestranda6544372023-11-13 09:33:561772 RtpTransceiverInit init;
1773 init.direction = RtpTransceiverDirection::kSendOnly;
1774 RtpEncodingParameters encoding_parameters;
Florent Castelli43a5dd82023-04-12 10:45:071775 encoding_parameters.rid = "h";
1776 encoding_parameters.codec = vp8;
1777 encoding_parameters.scale_resolution_down_by = 2;
1778 init.send_encodings.push_back(encoding_parameters);
1779 encoding_parameters.rid = "f";
1780 encoding_parameters.scale_resolution_down_by = 1;
1781 init.send_encodings.push_back(encoding_parameters);
1782
1783 auto transceiver_or_error =
1784 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
1785 ASSERT_TRUE(transceiver_or_error.ok());
1786 rtc::scoped_refptr<RtpTransceiverInterface> video_transceiver =
1787 transceiver_or_error.MoveValue();
1788
1789 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1790 local_pc_wrapper->WaitForConnection();
1791 remote_pc_wrapper->WaitForConnection();
1792
Harald Alvestranda6544372023-11-13 09:33:561793 RtpParameters parameters = video_transceiver->sender()->GetParameters();
Florent Castelli43a5dd82023-04-12 10:45:071794 ASSERT_EQ(parameters.encodings.size(), 2u);
1795 EXPECT_EQ(parameters.encodings[0].codec, vp8);
1796 EXPECT_EQ(parameters.encodings[1].codec, vp8);
1797
1798 ASSERT_TRUE(video_transceiver->SetCodecPreferences(not_vp8_codecs).ok());
1799 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
1800
1801 parameters = video_transceiver->sender()->GetParameters();
1802 EXPECT_FALSE(parameters.encodings[0].codec);
1803 EXPECT_FALSE(parameters.encodings[1].codec);
1804}
1805
1806TEST_F(PeerConnectionEncodingsIntegrationTest,
1807 AddTransceiverRejectsMixedCodecSimulcast) {
1808 // Mixed Codec Simulcast is not yet supported, so we ensure that we reject
1809 // such parameters.
1810 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1811 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1812 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1813
Harald Alvestranda6544372023-11-13 09:33:561814 absl::optional<RtpCodecCapability> vp8 =
Florent Castelli43a5dd82023-04-12 10:45:071815 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1816 "vp8");
1817 ASSERT_TRUE(vp8);
Harald Alvestranda6544372023-11-13 09:33:561818 absl::optional<RtpCodecCapability> vp9 =
Florent Castelli43a5dd82023-04-12 10:45:071819 local_pc_wrapper->FindFirstSendCodecWithName(cricket::MEDIA_TYPE_VIDEO,
1820 "vp9");
1821
Harald Alvestranda6544372023-11-13 09:33:561822 RtpTransceiverInit init;
1823 init.direction = RtpTransceiverDirection::kSendOnly;
1824 RtpEncodingParameters encoding_parameters;
Florent Castelli43a5dd82023-04-12 10:45:071825 encoding_parameters.rid = "h";
1826 encoding_parameters.codec = vp8;
1827 encoding_parameters.scale_resolution_down_by = 2;
1828 init.send_encodings.push_back(encoding_parameters);
1829 encoding_parameters.rid = "f";
1830 encoding_parameters.codec = vp9;
1831 encoding_parameters.scale_resolution_down_by = 1;
1832 init.send_encodings.push_back(encoding_parameters);
1833
1834 auto transceiver_or_error =
1835 local_pc_wrapper->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
1836 ASSERT_FALSE(transceiver_or_error.ok());
1837 EXPECT_EQ(transceiver_or_error.error().type(),
1838 RTCErrorType::UNSUPPORTED_OPERATION);
1839}
1840
Henrik Boströmfb65d232023-04-11 13:32:401841// Tests that use the standard path (specifying both `scalability_mode` and
1842// `scale_resolution_down_by`) should pass for all codecs.
1843class PeerConnectionEncodingsIntegrationParameterizedTest
1844 : public PeerConnectionEncodingsIntegrationTest,
1845 public ::testing::WithParamInterface<std::string> {
1846 public:
1847 PeerConnectionEncodingsIntegrationParameterizedTest()
1848 : codec_name_(GetParam()), mime_type_("video/" + codec_name_) {}
1849
1850 // Work-around for the fact that whether or not AV1 is supported is not known
1851 // at compile-time so we have to skip tests early if missing.
1852 // TODO(https://crbug.com/webrtc/15011): Increase availability of AV1 or make
1853 // it possible to check support at compile-time.
1854 bool SkipTestDueToAv1Missing(
1855 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper) {
1856 if (codec_name_ == "AV1" &&
1857 !HasSenderVideoCodecCapability(local_pc_wrapper, "AV1")) {
1858 RTC_LOG(LS_WARNING) << "\n***\nAV1 is not available, skipping test.\n***";
1859 return true;
1860 }
1861 return false;
1862 }
1863
1864 protected:
1865 const std::string codec_name_; // E.g. "VP9"
1866 const std::string mime_type_; // E.g. "video/VP9"
1867};
1868
Henrik Boströmeb993002023-04-11 14:11:231869TEST_P(PeerConnectionEncodingsIntegrationParameterizedTest, AllLayersInactive) {
1870 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
1871 if (SkipTestDueToAv1Missing(local_pc_wrapper)) {
1872 return;
1873 }
1874 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1875 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1876
1877 std::vector<cricket::SimulcastLayer> layers =
1878 CreateLayers({"f", "h", "q"}, /*active=*/true);
1879 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
1880 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
1881 layers);
1882 std::vector<RtpCodecCapability> codecs =
1883 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, codec_name_);
1884 transceiver->SetCodecPreferences(codecs);
1885
1886 // Standard mode and all layers inactive.
1887 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
1888 RtpParameters parameters = sender->GetParameters();
1889 ASSERT_THAT(parameters.encodings, SizeIs(3));
1890 parameters.encodings[0].scalability_mode = "L1T3";
1891 parameters.encodings[0].scale_resolution_down_by = 1;
1892 parameters.encodings[0].active = false;
1893 parameters.encodings[1].active = false;
1894 parameters.encodings[2].active = false;
1895 sender->SetParameters(parameters);
1896
Florent Castelli43a5dd82023-04-12 10:45:071897 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
Henrik Boströmeb993002023-04-11 14:11:231898 local_pc_wrapper->WaitForConnection();
1899 remote_pc_wrapper->WaitForConnection();
1900
1901 // Ensure no media is flowing (1 second should be enough).
1902 rtc::Thread::Current()->SleepMs(1000);
1903 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
1904 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
1905 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1906 ASSERT_THAT(outbound_rtps, SizeIs(3u));
1907 EXPECT_EQ(*outbound_rtps[0]->bytes_sent, 0u);
1908 EXPECT_EQ(*outbound_rtps[1]->bytes_sent, 0u);
1909 EXPECT_EQ(*outbound_rtps[2]->bytes_sent, 0u);
1910}
1911
Henrik Boströmfb65d232023-04-11 13:32:401912TEST_P(PeerConnectionEncodingsIntegrationParameterizedTest, Simulcast) {
Henrik Boströmda9e2842023-04-06 13:27:331913 rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
Henrik Boströmfb65d232023-04-11 13:32:401914 if (SkipTestDueToAv1Missing(local_pc_wrapper)) {
Henrik Boströmda9e2842023-04-06 13:27:331915 return;
1916 }
1917 rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
1918 ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
1919
1920 std::vector<cricket::SimulcastLayer> layers =
1921 CreateLayers({"f", "h", "q"}, /*active=*/true);
1922 rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
1923 AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
1924 layers);
1925 std::vector<RtpCodecCapability> codecs =
Henrik Boströmfb65d232023-04-11 13:32:401926 GetCapabilitiesAndRestrictToCodec(local_pc_wrapper, codec_name_);
Henrik Boströmda9e2842023-04-06 13:27:331927 transceiver->SetCodecPreferences(codecs);
1928
Henrik Boströmda9e2842023-04-06 13:27:331929 rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
1930 RtpParameters parameters = sender->GetParameters();
Henrik Boströmb515c172023-04-11 08:20:191931 ASSERT_THAT(parameters.encodings, SizeIs(3));
Henrik Boströmda9e2842023-04-06 13:27:331932 parameters.encodings[0].scalability_mode = "L1T3";
1933 parameters.encodings[0].scale_resolution_down_by = 4;
1934 parameters.encodings[1].scalability_mode = "L1T3";
1935 parameters.encodings[1].scale_resolution_down_by = 2;
1936 parameters.encodings[2].scalability_mode = "L1T3";
1937 parameters.encodings[2].scale_resolution_down_by = 1;
1938 sender->SetParameters(parameters);
1939
Florent Castelli43a5dd82023-04-12 10:45:071940 NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
Henrik Boströmda9e2842023-04-06 13:27:331941 local_pc_wrapper->WaitForConnection();
1942 remote_pc_wrapper->WaitForConnection();
1943
1944 // GetParameters() does not report any fallback.
1945 parameters = sender->GetParameters();
Henrik Boströmb515c172023-04-11 08:20:191946 ASSERT_THAT(parameters.encodings, SizeIs(3));
Henrik Boströmda9e2842023-04-06 13:27:331947 EXPECT_THAT(parameters.encodings[0].scalability_mode,
1948 Optional(std::string("L1T3")));
1949 EXPECT_THAT(parameters.encodings[1].scalability_mode,
1950 Optional(std::string("L1T3")));
1951 EXPECT_THAT(parameters.encodings[2].scalability_mode,
1952 Optional(std::string("L1T3")));
1953
1954 // Wait until media is flowing on all three layers.
1955 // Ramp up time is needed before all three layers are sending.
Henrik Boströmda9e2842023-04-06 13:27:331956 ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, 3u),
Henrik Boströmfb65d232023-04-11 13:32:401957 kLongTimeoutForRampingUp.ms());
Henrik Boströmda9e2842023-04-06 13:27:331958 EXPECT_TRUE(OutboundRtpResolutionsAreLessThanOrEqualToExpectations(
1959 local_pc_wrapper, {{"f", 320, 180}, {"h", 640, 360}, {"q", 1280, 720}}));
1960 // Verify codec and scalability mode.
1961 rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
1962 std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
1963 report->GetStatsOfType<RTCOutboundRtpStreamStats>();
1964 ASSERT_THAT(outbound_rtps, SizeIs(3u));
1965 EXPECT_THAT(GetCurrentCodecMimeType(report, *outbound_rtps[0]),
Henrik Boströmfb65d232023-04-11 13:32:401966 StrCaseEq(mime_type_));
Henrik Boströmda9e2842023-04-06 13:27:331967 EXPECT_THAT(GetCurrentCodecMimeType(report, *outbound_rtps[1]),
Henrik Boströmfb65d232023-04-11 13:32:401968 StrCaseEq(mime_type_));
Henrik Boströmda9e2842023-04-06 13:27:331969 EXPECT_THAT(GetCurrentCodecMimeType(report, *outbound_rtps[2]),
Henrik Boströmfb65d232023-04-11 13:32:401970 StrCaseEq(mime_type_));
Henrik Boströmda9e2842023-04-06 13:27:331971 EXPECT_THAT(*outbound_rtps[0]->scalability_mode, StrEq("L1T3"));
1972 EXPECT_THAT(*outbound_rtps[1]->scalability_mode, StrEq("L1T3"));
1973 EXPECT_THAT(*outbound_rtps[2]->scalability_mode, StrEq("L1T3"));
1974}
1975
Henrik Boströmfb65d232023-04-11 13:32:401976INSTANTIATE_TEST_SUITE_P(StandardPath,
1977 PeerConnectionEncodingsIntegrationParameterizedTest,
1978 ::testing::Values("VP8",
1979 "VP9",
1980#if defined(WEBRTC_USE_H264)
1981 "H264",
1982#endif // defined(WEBRTC_USE_H264)
1983 "AV1"),
1984 StringParamToString());
1985
Henrik Boströmda9e2842023-04-06 13:27:331986} // namespace webrtc