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