blob: 322540fa2386afe2045f34ab4e239cc78d830051 [file] [log] [blame]
Steve Anton8d3444d2017-10-20 15:30:51 -07001/*
2 * Copyright 2017 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// This file contains tests that check the interaction between the
12// PeerConnection and the underlying media engine, as well as tests that check
13// the media-related aspects of SDP.
14
Harald Alvestrandc24a2182022-02-23 13:44:59 +000015#include <algorithm>
16#include <functional>
17#include <iterator>
18#include <map>
Mirko Bonadei317a1f02019-09-17 17:06:18 +020019#include <memory>
Taylor Brandstetterf7fcfb72021-09-09 13:39:38 -070020#include <set>
Harald Alvestrandc24a2182022-02-23 13:44:59 +000021#include <string>
Steve Anton8d3444d2017-10-20 15:30:51 -070022#include <tuple>
Harald Alvestrandc24a2182022-02-23 13:44:59 +000023#include <type_traits>
24#include <utility>
25#include <vector>
Steve Anton8d3444d2017-10-20 15:30:51 -070026
Steve Anton64b626b2019-01-28 17:25:26 -080027#include "absl/algorithm/container.h"
Mirta Dvornicic479a3c02019-06-04 15:38:50 +020028#include "absl/types/optional.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000029#include "api/audio_options.h"
Steve Anton10542f22019-01-11 09:11:00 -080030#include "api/call/call_factory_interface.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000031#include "api/jsep.h"
32#include "api/media_types.h"
33#include "api/peer_connection_interface.h"
34#include "api/rtc_error.h"
Danil Chapovalov9da25bd2019-06-20 10:19:42 +020035#include "api/rtc_event_log/rtc_event_log_factory.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000036#include "api/rtc_event_log/rtc_event_log_factory_interface.h"
37#include "api/rtp_parameters.h"
38#include "api/rtp_sender_interface.h"
39#include "api/rtp_transceiver_direction.h"
40#include "api/rtp_transceiver_interface.h"
41#include "api/scoped_refptr.h"
Danil Chapovalov9da25bd2019-06-20 10:19:42 +020042#include "api/task_queue/default_task_queue_factory.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000043#include "api/task_queue/task_queue_factory.h"
44#include "media/base/codec.h"
Steve Anton10542f22019-01-11 09:11:00 -080045#include "media/base/fake_media_engine.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000046#include "media/base/media_constants.h"
47#include "media/base/media_engine.h"
48#include "media/base/stream_params.h"
Steve Anton10542f22019-01-11 09:11:00 -080049#include "p2p/base/fake_port_allocator.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000050#include "p2p/base/p2p_constants.h"
51#include "p2p/base/port_allocator.h"
52#include "p2p/base/transport_info.h"
Steve Anton10542f22019-01-11 09:11:00 -080053#include "pc/media_session.h"
54#include "pc/peer_connection_wrapper.h"
55#include "pc/rtp_media_utils.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000056#include "pc/session_description.h"
57#include "pc/test/mock_peer_connection_observers.h"
58#include "rtc_base/checks.h"
59#include "rtc_base/rtc_certificate_generator.h"
60#include "rtc_base/thread.h"
61#include "test/gtest.h"
Sameer Vijaykar0793ee72023-01-23 16:31:29 +010062#include "test/scoped_key_value_config.h"
Steve Anton8d3444d2017-10-20 15:30:51 -070063#ifdef WEBRTC_ANDROID
Steve Anton10542f22019-01-11 09:11:00 -080064#include "pc/test/android_test_initializer.h"
Steve Anton8d3444d2017-10-20 15:30:51 -070065#endif
Steve Anton8d3444d2017-10-20 15:30:51 -070066#include "rtc_base/gunit.h"
Steve Anton10542f22019-01-11 09:11:00 -080067#include "rtc_base/virtual_socket_server.h"
Steve Anton8d3444d2017-10-20 15:30:51 -070068#include "test/gmock.h"
69
70namespace webrtc {
71
72using cricket::FakeMediaEngine;
73using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
74using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
75using ::testing::Bool;
76using ::testing::Combine;
Steve Anton8d3444d2017-10-20 15:30:51 -070077using ::testing::ElementsAre;
Jonas Olssona4d87372019-07-05 19:08:33 +020078using ::testing::Values;
Steve Anton8d3444d2017-10-20 15:30:51 -070079
80class PeerConnectionWrapperForMediaTest : public PeerConnectionWrapper {
81 public:
82 using PeerConnectionWrapper::PeerConnectionWrapper;
83
84 FakeMediaEngine* media_engine() { return media_engine_; }
85 void set_media_engine(FakeMediaEngine* media_engine) {
86 media_engine_ = media_engine;
87 }
88
89 private:
90 FakeMediaEngine* media_engine_;
91};
92
Steve Antonad7bffc2018-01-22 10:21:56 -080093class PeerConnectionMediaBaseTest : public ::testing::Test {
Steve Anton8d3444d2017-10-20 15:30:51 -070094 protected:
95 typedef std::unique_ptr<PeerConnectionWrapperForMediaTest> WrapperPtr;
96
Steve Antonad7bffc2018-01-22 10:21:56 -080097 explicit PeerConnectionMediaBaseTest(SdpSemantics sdp_semantics)
98 : vss_(new rtc::VirtualSocketServer()),
99 main_(vss_.get()),
100 sdp_semantics_(sdp_semantics) {
Steve Anton8d3444d2017-10-20 15:30:51 -0700101#ifdef WEBRTC_ANDROID
102 InitializeAndroidObjects();
103#endif
104 }
105
106 WrapperPtr CreatePeerConnection() {
107 return CreatePeerConnection(RTCConfiguration());
108 }
109
Florent Castelli2d9d82e2019-04-23 19:25:51 +0200110 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200111 return CreatePeerConnection(config, std::make_unique<FakeMediaEngine>());
Florent Castelli2d9d82e2019-04-23 19:25:51 +0200112 }
113
114 WrapperPtr CreatePeerConnection(
115 std::unique_ptr<FakeMediaEngine> media_engine) {
116 return CreatePeerConnection(RTCConfiguration(), std::move(media_engine));
117 }
118
Anton Sukhanov98a462c2018-10-17 13:15:42 -0700119 // Creates PeerConnectionFactory and PeerConnection for given configuration.
Florent Castelli2d9d82e2019-04-23 19:25:51 +0200120 WrapperPtr CreatePeerConnection(
121 const RTCConfiguration& config,
122 std::unique_ptr<FakeMediaEngine> media_engine) {
Steve Anton8d3444d2017-10-20 15:30:51 -0700123 auto* media_engine_ptr = media_engine.get();
Anton Sukhanov98a462c2018-10-17 13:15:42 -0700124
125 PeerConnectionFactoryDependencies factory_dependencies;
126
127 factory_dependencies.network_thread = rtc::Thread::Current();
128 factory_dependencies.worker_thread = rtc::Thread::Current();
129 factory_dependencies.signaling_thread = rtc::Thread::Current();
Danil Chapovalov9da25bd2019-06-20 10:19:42 +0200130 factory_dependencies.task_queue_factory = CreateDefaultTaskQueueFactory();
Anton Sukhanov98a462c2018-10-17 13:15:42 -0700131 factory_dependencies.media_engine = std::move(media_engine);
132 factory_dependencies.call_factory = CreateCallFactory();
Danil Chapovalov9da25bd2019-06-20 10:19:42 +0200133 factory_dependencies.event_log_factory =
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200134 std::make_unique<RtcEventLogFactory>(
Danil Chapovalov9da25bd2019-06-20 10:19:42 +0200135 factory_dependencies.task_queue_factory.get());
Anton Sukhanov98a462c2018-10-17 13:15:42 -0700136
137 auto pc_factory =
138 CreateModularPeerConnectionFactory(std::move(factory_dependencies));
Steve Anton8d3444d2017-10-20 15:30:51 -0700139
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200140 auto fake_port_allocator = std::make_unique<cricket::FakePortAllocator>(
Byoungchan Leed58f5262022-06-27 18:05:22 +0900141 rtc::Thread::Current(),
Sameer Vijaykar0793ee72023-01-23 16:31:29 +0100142 std::make_unique<rtc::BasicPacketSocketFactory>(vss_.get()),
143 &field_trials_);
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200144 auto observer = std::make_unique<MockPeerConnectionObserver>();
Steve Antonad7bffc2018-01-22 10:21:56 -0800145 auto modified_config = config;
146 modified_config.sdp_semantics = sdp_semantics_;
Florent Castelli72424402022-04-06 03:45:10 +0200147 PeerConnectionDependencies pc_dependencies(observer.get());
148 pc_dependencies.allocator = std::move(fake_port_allocator);
149 auto result = pc_factory->CreatePeerConnectionOrError(
150 modified_config, std::move(pc_dependencies));
151 if (!result.ok()) {
Steve Anton8d3444d2017-10-20 15:30:51 -0700152 return nullptr;
153 }
154
Florent Castelli72424402022-04-06 03:45:10 +0200155 auto pc = result.MoveValue();
Yves Gerey4e933292018-10-31 15:36:05 +0100156 observer->SetPeerConnectionInterface(pc.get());
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200157 auto wrapper = std::make_unique<PeerConnectionWrapperForMediaTest>(
Steve Anton8d3444d2017-10-20 15:30:51 -0700158 pc_factory, pc, std::move(observer));
159 wrapper->set_media_engine(media_engine_ptr);
160 return wrapper;
161 }
162
163 // Accepts the same arguments as CreatePeerConnection and adds default audio
Piotr (Peter) Slatala10aeb2a2018-11-14 10:57:24 -0800164 // track (but no video).
165 template <typename... Args>
166 WrapperPtr CreatePeerConnectionWithAudio(Args&&... args) {
167 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
168 if (!wrapper) {
169 return nullptr;
170 }
171 wrapper->AddAudioTrack("a");
172 return wrapper;
173 }
174
Florent Castelli2d9d82e2019-04-23 19:25:51 +0200175 // Accepts the same arguments as CreatePeerConnection and adds default video
176 // track (but no audio).
177 template <typename... Args>
178 WrapperPtr CreatePeerConnectionWithVideo(Args&&... args) {
179 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
180 if (!wrapper) {
181 return nullptr;
182 }
183 wrapper->AddVideoTrack("v");
184 return wrapper;
185 }
186
Piotr (Peter) Slatala10aeb2a2018-11-14 10:57:24 -0800187 // Accepts the same arguments as CreatePeerConnection and adds default audio
Steve Anton8d3444d2017-10-20 15:30:51 -0700188 // and video tracks.
189 template <typename... Args>
190 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
191 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
192 if (!wrapper) {
193 return nullptr;
194 }
195 wrapper->AddAudioTrack("a");
196 wrapper->AddVideoTrack("v");
197 return wrapper;
198 }
199
Steve Anton4e70a722017-11-28 14:57:10 -0800200 RtpTransceiverDirection GetMediaContentDirection(
Steve Anton8d3444d2017-10-20 15:30:51 -0700201 const SessionDescriptionInterface* sdesc,
Steve Antonad7bffc2018-01-22 10:21:56 -0800202 cricket::MediaType media_type) {
203 auto* content =
204 cricket::GetFirstMediaContent(sdesc->description(), media_type);
205 RTC_DCHECK(content);
206 return content->media_description()->direction();
207 }
208
209 bool IsUnifiedPlan() const {
210 return sdp_semantics_ == SdpSemantics::kUnifiedPlan;
Steve Anton8d3444d2017-10-20 15:30:51 -0700211 }
212
Sameer Vijaykar0793ee72023-01-23 16:31:29 +0100213 webrtc::test::ScopedKeyValueConfig field_trials_;
Steve Anton8d3444d2017-10-20 15:30:51 -0700214 std::unique_ptr<rtc::VirtualSocketServer> vss_;
215 rtc::AutoSocketServerThread main_;
Steve Antonad7bffc2018-01-22 10:21:56 -0800216 const SdpSemantics sdp_semantics_;
Steve Anton8d3444d2017-10-20 15:30:51 -0700217};
218
Steve Antonad7bffc2018-01-22 10:21:56 -0800219class PeerConnectionMediaTest
220 : public PeerConnectionMediaBaseTest,
221 public ::testing::WithParamInterface<SdpSemantics> {
222 protected:
223 PeerConnectionMediaTest() : PeerConnectionMediaBaseTest(GetParam()) {}
224};
225
226class PeerConnectionMediaTestUnifiedPlan : public PeerConnectionMediaBaseTest {
227 protected:
228 PeerConnectionMediaTestUnifiedPlan()
229 : PeerConnectionMediaBaseTest(SdpSemantics::kUnifiedPlan) {}
230};
231
232class PeerConnectionMediaTestPlanB : public PeerConnectionMediaBaseTest {
233 protected:
234 PeerConnectionMediaTestPlanB()
Florent Castelli15a38de2022-04-06 00:38:21 +0200235 : PeerConnectionMediaBaseTest(SdpSemantics::kPlanB_DEPRECATED) {}
Steve Antonad7bffc2018-01-22 10:21:56 -0800236};
237
238TEST_P(PeerConnectionMediaTest,
Steve Anton8d3444d2017-10-20 15:30:51 -0700239 FailToSetRemoteDescriptionIfCreateMediaChannelFails) {
240 auto caller = CreatePeerConnectionWithAudioVideo();
241 auto callee = CreatePeerConnectionWithAudioVideo();
242 callee->media_engine()->set_fail_create_channel(true);
243
244 std::string error;
245 ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
Steve Antonad7bffc2018-01-22 10:21:56 -0800246 EXPECT_PRED_FORMAT2(AssertStartsWith, error,
247 "Failed to set remote offer sdp: Failed to create");
Steve Anton8d3444d2017-10-20 15:30:51 -0700248}
249
Steve Antonad7bffc2018-01-22 10:21:56 -0800250TEST_P(PeerConnectionMediaTest,
Steve Anton8d3444d2017-10-20 15:30:51 -0700251 FailToSetLocalDescriptionIfCreateMediaChannelFails) {
252 auto caller = CreatePeerConnectionWithAudioVideo();
253 caller->media_engine()->set_fail_create_channel(true);
254
255 std::string error;
256 ASSERT_FALSE(caller->SetLocalDescription(caller->CreateOffer(), &error));
Steve Antonad7bffc2018-01-22 10:21:56 -0800257 EXPECT_PRED_FORMAT2(AssertStartsWith, error,
258 "Failed to set local offer sdp: Failed to create");
Steve Anton8d3444d2017-10-20 15:30:51 -0700259}
260
261std::vector<std::string> GetIds(
262 const std::vector<cricket::StreamParams>& streams) {
263 std::vector<std::string> ids;
Mirko Bonadei649a4c22019-01-29 10:11:53 +0100264 ids.reserve(streams.size());
Steve Anton8d3444d2017-10-20 15:30:51 -0700265 for (const auto& stream : streams) {
266 ids.push_back(stream.id);
267 }
268 return ids;
269}
270
271// Test that exchanging an offer and answer with each side having an audio and
272// video stream creates the appropriate send/recv streams in the underlying
273// media engine on both sides.
Steve Antonad7bffc2018-01-22 10:21:56 -0800274TEST_P(PeerConnectionMediaTest, AudioVideoOfferAnswerCreateSendRecvStreams) {
Steve Anton8d3444d2017-10-20 15:30:51 -0700275 const std::string kCallerAudioId = "caller_a";
276 const std::string kCallerVideoId = "caller_v";
277 const std::string kCalleeAudioId = "callee_a";
278 const std::string kCalleeVideoId = "callee_v";
279
280 auto caller = CreatePeerConnection();
281 caller->AddAudioTrack(kCallerAudioId);
282 caller->AddVideoTrack(kCallerVideoId);
283
284 auto callee = CreatePeerConnection();
285 callee->AddAudioTrack(kCalleeAudioId);
286 callee->AddVideoTrack(kCalleeVideoId);
287
288 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
289 ASSERT_TRUE(
290 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
291
292 auto* caller_voice = caller->media_engine()->GetVoiceChannel(0);
293 EXPECT_THAT(GetIds(caller_voice->recv_streams()),
294 ElementsAre(kCalleeAudioId));
295 EXPECT_THAT(GetIds(caller_voice->send_streams()),
296 ElementsAre(kCallerAudioId));
297
298 auto* caller_video = caller->media_engine()->GetVideoChannel(0);
299 EXPECT_THAT(GetIds(caller_video->recv_streams()),
300 ElementsAre(kCalleeVideoId));
301 EXPECT_THAT(GetIds(caller_video->send_streams()),
302 ElementsAre(kCallerVideoId));
303
304 auto* callee_voice = callee->media_engine()->GetVoiceChannel(0);
305 EXPECT_THAT(GetIds(callee_voice->recv_streams()),
306 ElementsAre(kCallerAudioId));
307 EXPECT_THAT(GetIds(callee_voice->send_streams()),
308 ElementsAre(kCalleeAudioId));
309
310 auto* callee_video = callee->media_engine()->GetVideoChannel(0);
311 EXPECT_THAT(GetIds(callee_video->recv_streams()),
312 ElementsAre(kCallerVideoId));
313 EXPECT_THAT(GetIds(callee_video->send_streams()),
314 ElementsAre(kCalleeVideoId));
315}
316
Steve Antonad7bffc2018-01-22 10:21:56 -0800317// Test that stopping the caller transceivers causes the media channels on the
318// callee to be destroyed after calling SetRemoteDescription on the generated
319// offer.
320// See next test for equivalent behavior with Plan B semantics.
321TEST_F(PeerConnectionMediaTestUnifiedPlan,
322 StoppedRemoteTransceiversRemovesMediaChannels) {
323 auto caller = CreatePeerConnectionWithAudioVideo();
324 auto callee = CreatePeerConnection();
325
326 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
327
328 // Stop both audio and video transceivers on the caller.
329 auto transceivers = caller->pc()->GetTransceivers();
330 ASSERT_EQ(2u, transceivers.size());
Harald Alvestrand6060df52020-08-11 09:54:02 +0200331 transceivers[0]->StopInternal();
332 transceivers[1]->StopInternal();
Steve Antonad7bffc2018-01-22 10:21:56 -0800333
334 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
335
336 ASSERT_FALSE(callee->media_engine()->GetVoiceChannel(0));
337 ASSERT_FALSE(callee->media_engine()->GetVideoChannel(0));
338}
339
Steve Anton8d3444d2017-10-20 15:30:51 -0700340// Test that removing streams from a subsequent offer causes the receive streams
341// on the callee to be removed.
Steve Antonad7bffc2018-01-22 10:21:56 -0800342// See previous test for equivalent behavior with Unified Plan semantics.
343TEST_F(PeerConnectionMediaTestPlanB, EmptyRemoteOfferRemovesRecvStreams) {
Steve Anton8d3444d2017-10-20 15:30:51 -0700344 auto caller = CreatePeerConnection();
345 auto caller_audio_track = caller->AddAudioTrack("a");
346 auto caller_video_track = caller->AddVideoTrack("v");
347 auto callee = CreatePeerConnectionWithAudioVideo();
348
Steve Antonad7bffc2018-01-22 10:21:56 -0800349 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
Steve Anton8d3444d2017-10-20 15:30:51 -0700350
351 // Remove both tracks from caller.
Harald Alvestrand93dd7632022-01-19 12:28:45 +0000352 caller->pc()->RemoveTrackOrError(caller_audio_track);
353 caller->pc()->RemoveTrackOrError(caller_video_track);
Steve Anton8d3444d2017-10-20 15:30:51 -0700354
Steve Antonad7bffc2018-01-22 10:21:56 -0800355 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
Steve Anton8d3444d2017-10-20 15:30:51 -0700356
357 auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
Steve Antonad7bffc2018-01-22 10:21:56 -0800358 auto callee_video = callee->media_engine()->GetVideoChannel(0);
Steve Anton8d3444d2017-10-20 15:30:51 -0700359 EXPECT_EQ(1u, callee_voice->send_streams().size());
360 EXPECT_EQ(0u, callee_voice->recv_streams().size());
Steve Anton8d3444d2017-10-20 15:30:51 -0700361 EXPECT_EQ(1u, callee_video->send_streams().size());
362 EXPECT_EQ(0u, callee_video->recv_streams().size());
363}
364
Jonas Orelandfc1acd22018-08-24 10:58:37 +0200365// Test enabling of simulcast with Plan B semantics.
366// This test creating an offer.
367TEST_F(PeerConnectionMediaTestPlanB, SimulcastOffer) {
368 auto caller = CreatePeerConnection();
369 auto caller_video_track = caller->AddVideoTrack("v");
370 RTCOfferAnswerOptions options;
371 options.num_simulcast_layers = 3;
372 auto offer = caller->CreateOffer(options);
Jonas Olssona4d87372019-07-05 19:08:33 +0200373 auto* description = cricket::GetFirstMediaContent(offer->description(),
374 cricket::MEDIA_TYPE_VIDEO)
375 ->media_description();
Jonas Orelandfc1acd22018-08-24 10:58:37 +0200376 ASSERT_EQ(1u, description->streams().size());
377 ASSERT_TRUE(description->streams()[0].get_ssrc_group("SIM"));
378 EXPECT_EQ(3u, description->streams()[0].get_ssrc_group("SIM")->ssrcs.size());
379
380 // Check that it actually creates simulcast aswell.
381 caller->SetLocalDescription(std::move(offer));
382 auto senders = caller->pc()->GetSenders();
383 ASSERT_EQ(1u, senders.size());
384 EXPECT_EQ(cricket::MediaType::MEDIA_TYPE_VIDEO, senders[0]->media_type());
385 EXPECT_EQ(3u, senders[0]->GetParameters().encodings.size());
386}
387
388// Test enabling of simulcast with Plan B semantics.
389// This test creating an answer.
390TEST_F(PeerConnectionMediaTestPlanB, SimulcastAnswer) {
391 auto caller = CreatePeerConnection();
392 caller->AddVideoTrack("v0");
393 auto offer = caller->CreateOffer();
394 auto callee = CreatePeerConnection();
395 auto callee_video_track = callee->AddVideoTrack("v1");
396 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
397 RTCOfferAnswerOptions options;
398 options.num_simulcast_layers = 3;
399 auto answer = callee->CreateAnswer(options);
Jonas Olssona4d87372019-07-05 19:08:33 +0200400 auto* description = cricket::GetFirstMediaContent(answer->description(),
401 cricket::MEDIA_TYPE_VIDEO)
402 ->media_description();
Jonas Orelandfc1acd22018-08-24 10:58:37 +0200403 ASSERT_EQ(1u, description->streams().size());
404 ASSERT_TRUE(description->streams()[0].get_ssrc_group("SIM"));
405 EXPECT_EQ(3u, description->streams()[0].get_ssrc_group("SIM")->ssrcs.size());
406
407 // Check that it actually creates simulcast aswell.
408 callee->SetLocalDescription(std::move(answer));
409 auto senders = callee->pc()->GetSenders();
410 ASSERT_EQ(1u, senders.size());
411 EXPECT_EQ(cricket::MediaType::MEDIA_TYPE_VIDEO, senders[0]->media_type());
412 EXPECT_EQ(3u, senders[0]->GetParameters().encodings.size());
413}
414
Steve Antonad7bffc2018-01-22 10:21:56 -0800415// Test that stopping the callee transceivers causes the media channels to be
416// destroyed on the callee after calling SetLocalDescription on the local
417// answer.
418// See next test for equivalent behavior with Plan B semantics.
419TEST_F(PeerConnectionMediaTestUnifiedPlan,
420 StoppedLocalTransceiversRemovesMediaChannels) {
421 auto caller = CreatePeerConnectionWithAudioVideo();
422 auto callee = CreatePeerConnectionWithAudioVideo();
423
424 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
425
426 // Stop both audio and video transceivers on the callee.
427 auto transceivers = callee->pc()->GetTransceivers();
428 ASSERT_EQ(2u, transceivers.size());
Harald Alvestrand6060df52020-08-11 09:54:02 +0200429 transceivers[0]->StopInternal();
430 transceivers[1]->StopInternal();
Steve Antonad7bffc2018-01-22 10:21:56 -0800431
432 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
433
434 EXPECT_FALSE(callee->media_engine()->GetVoiceChannel(0));
435 EXPECT_FALSE(callee->media_engine()->GetVideoChannel(0));
436}
437
Steve Anton8d3444d2017-10-20 15:30:51 -0700438// Test that removing streams from a subsequent answer causes the send streams
439// on the callee to be removed when applied locally.
Steve Antonad7bffc2018-01-22 10:21:56 -0800440// See previous test for equivalent behavior with Unified Plan semantics.
441TEST_F(PeerConnectionMediaTestPlanB, EmptyLocalAnswerRemovesSendStreams) {
Steve Anton8d3444d2017-10-20 15:30:51 -0700442 auto caller = CreatePeerConnectionWithAudioVideo();
443 auto callee = CreatePeerConnection();
444 auto callee_audio_track = callee->AddAudioTrack("a");
445 auto callee_video_track = callee->AddVideoTrack("v");
446
Steve Antonad7bffc2018-01-22 10:21:56 -0800447 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
Steve Anton8d3444d2017-10-20 15:30:51 -0700448
449 // Remove both tracks from callee.
Harald Alvestrand93dd7632022-01-19 12:28:45 +0000450 callee->pc()->RemoveTrackOrError(callee_audio_track);
451 callee->pc()->RemoveTrackOrError(callee_video_track);
Steve Anton8d3444d2017-10-20 15:30:51 -0700452
Steve Antonad7bffc2018-01-22 10:21:56 -0800453 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
Steve Anton8d3444d2017-10-20 15:30:51 -0700454
455 auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
Steve Antonad7bffc2018-01-22 10:21:56 -0800456 auto callee_video = callee->media_engine()->GetVideoChannel(0);
Steve Anton8d3444d2017-10-20 15:30:51 -0700457 EXPECT_EQ(0u, callee_voice->send_streams().size());
458 EXPECT_EQ(1u, callee_voice->recv_streams().size());
Steve Anton8d3444d2017-10-20 15:30:51 -0700459 EXPECT_EQ(0u, callee_video->send_streams().size());
460 EXPECT_EQ(1u, callee_video->recv_streams().size());
461}
462
463// Test that a new stream in a subsequent offer causes a new receive stream to
464// be created on the callee.
Steve Antonad7bffc2018-01-22 10:21:56 -0800465TEST_P(PeerConnectionMediaTest, NewStreamInRemoteOfferAddsRecvStreams) {
Steve Anton8d3444d2017-10-20 15:30:51 -0700466 auto caller = CreatePeerConnectionWithAudioVideo();
467 auto callee = CreatePeerConnection();
468
Steve Antonad7bffc2018-01-22 10:21:56 -0800469 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
Steve Anton8d3444d2017-10-20 15:30:51 -0700470
471 // Add second set of tracks to the caller.
472 caller->AddAudioTrack("a2");
473 caller->AddVideoTrack("v2");
474
Steve Antonad7bffc2018-01-22 10:21:56 -0800475 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
Steve Anton8d3444d2017-10-20 15:30:51 -0700476
Steve Antonad7bffc2018-01-22 10:21:56 -0800477 auto a1 = callee->media_engine()->GetVoiceChannel(0);
478 auto a2 = callee->media_engine()->GetVoiceChannel(1);
479 auto v1 = callee->media_engine()->GetVideoChannel(0);
480 auto v2 = callee->media_engine()->GetVideoChannel(1);
481 if (IsUnifiedPlan()) {
482 ASSERT_TRUE(a1);
483 EXPECT_EQ(1u, a1->recv_streams().size());
484 ASSERT_TRUE(a2);
485 EXPECT_EQ(1u, a2->recv_streams().size());
486 ASSERT_TRUE(v1);
487 EXPECT_EQ(1u, v1->recv_streams().size());
488 ASSERT_TRUE(v2);
489 EXPECT_EQ(1u, v2->recv_streams().size());
490 } else {
491 ASSERT_TRUE(a1);
492 EXPECT_EQ(2u, a1->recv_streams().size());
493 ASSERT_FALSE(a2);
494 ASSERT_TRUE(v1);
495 EXPECT_EQ(2u, v1->recv_streams().size());
496 ASSERT_FALSE(v2);
497 }
Steve Anton8d3444d2017-10-20 15:30:51 -0700498}
499
500// Test that a new stream in a subsequent answer causes a new send stream to be
501// created on the callee when added locally.
Steve Antonad7bffc2018-01-22 10:21:56 -0800502TEST_P(PeerConnectionMediaTest, NewStreamInLocalAnswerAddsSendStreams) {
Steve Anton8d3444d2017-10-20 15:30:51 -0700503 auto caller = CreatePeerConnection();
504 auto callee = CreatePeerConnectionWithAudioVideo();
505
Steve Anton22da89f2018-01-25 13:58:07 -0800506 RTCOfferAnswerOptions offer_options;
507 offer_options.offer_to_receive_audio =
Steve Anton8d3444d2017-10-20 15:30:51 -0700508 RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
Steve Anton22da89f2018-01-25 13:58:07 -0800509 offer_options.offer_to_receive_video =
Steve Anton8d3444d2017-10-20 15:30:51 -0700510 RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
Steve Anton22da89f2018-01-25 13:58:07 -0800511 RTCOfferAnswerOptions answer_options;
Steve Anton8d3444d2017-10-20 15:30:51 -0700512
Steve Anton22da89f2018-01-25 13:58:07 -0800513 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get(), offer_options,
514 answer_options));
Steve Anton8d3444d2017-10-20 15:30:51 -0700515
516 // Add second set of tracks to the callee.
517 callee->AddAudioTrack("a2");
518 callee->AddVideoTrack("v2");
519
Steve Anton22da89f2018-01-25 13:58:07 -0800520 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get(), offer_options,
521 answer_options));
Steve Anton8d3444d2017-10-20 15:30:51 -0700522
523 auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
Steve Anton22da89f2018-01-25 13:58:07 -0800524 ASSERT_TRUE(callee_voice);
Steve Anton8d3444d2017-10-20 15:30:51 -0700525 auto callee_video = callee->media_engine()->GetVideoChannel(0);
Steve Anton22da89f2018-01-25 13:58:07 -0800526 ASSERT_TRUE(callee_video);
527
528 if (IsUnifiedPlan()) {
529 EXPECT_EQ(1u, callee_voice->send_streams().size());
530 EXPECT_EQ(1u, callee_video->send_streams().size());
531 } else {
532 EXPECT_EQ(2u, callee_voice->send_streams().size());
533 EXPECT_EQ(2u, callee_video->send_streams().size());
534 }
Steve Anton8d3444d2017-10-20 15:30:51 -0700535}
536
537// A PeerConnection with no local streams and no explicit answer constraints
538// should not reject any offered media sections.
Steve Antonad7bffc2018-01-22 10:21:56 -0800539TEST_P(PeerConnectionMediaTest,
Steve Anton8d3444d2017-10-20 15:30:51 -0700540 CreateAnswerWithNoStreamsAndDefaultOptionsDoesNotReject) {
541 auto caller = CreatePeerConnectionWithAudioVideo();
542 auto callee = CreatePeerConnection();
543 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
544 auto answer = callee->CreateAnswer();
545
546 const auto* audio_content =
547 cricket::GetFirstAudioContent(answer->description());
548 ASSERT_TRUE(audio_content);
549 EXPECT_FALSE(audio_content->rejected);
550
551 const auto* video_content =
552 cricket::GetFirstVideoContent(answer->description());
553 ASSERT_TRUE(video_content);
554 EXPECT_FALSE(video_content->rejected);
555}
556
Mirta Dvornicic479a3c02019-06-04 15:38:50 +0200557// Test that raw packetization is not set in the offer by default.
558TEST_P(PeerConnectionMediaTest, RawPacketizationNotSetInOffer) {
559 std::vector<cricket::VideoCodec> fake_codecs;
560 fake_codecs.push_back(cricket::VideoCodec(111, cricket::kVp8CodecName));
561 fake_codecs.push_back(cricket::VideoCodec(112, cricket::kRtxCodecName));
562 fake_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "111";
563 fake_codecs.push_back(cricket::VideoCodec(113, cricket::kVp9CodecName));
564 fake_codecs.push_back(cricket::VideoCodec(114, cricket::kH264CodecName));
565 fake_codecs.push_back(cricket::VideoCodec(115, "HEVC"));
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200566 auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
Mirta Dvornicic479a3c02019-06-04 15:38:50 +0200567 caller_fake_engine->SetVideoCodecs(fake_codecs);
568
569 auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
570 auto offer = caller->CreateOfferAndSetAsLocal();
571 auto* offer_description =
572 cricket::GetFirstVideoContentDescription(offer->description());
573 for (const auto& codec : offer_description->codecs()) {
574 EXPECT_EQ(codec.packetization, absl::nullopt);
575 }
576}
577
578// Test that raw packetization is set in the offer and answer for all
579// video payload when raw_packetization_for_video is true.
580TEST_P(PeerConnectionMediaTest, RawPacketizationSetInOfferAndAnswer) {
581 std::vector<cricket::VideoCodec> fake_codecs;
582 fake_codecs.push_back(cricket::VideoCodec(111, cricket::kVp8CodecName));
583 fake_codecs.push_back(cricket::VideoCodec(112, cricket::kRtxCodecName));
584 fake_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "111";
585 fake_codecs.push_back(cricket::VideoCodec(113, cricket::kVp9CodecName));
586 fake_codecs.push_back(cricket::VideoCodec(114, cricket::kH264CodecName));
587 fake_codecs.push_back(cricket::VideoCodec(115, "HEVC"));
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200588 auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
Mirta Dvornicic479a3c02019-06-04 15:38:50 +0200589 caller_fake_engine->SetVideoCodecs(fake_codecs);
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200590 auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
Mirta Dvornicic479a3c02019-06-04 15:38:50 +0200591 callee_fake_engine->SetVideoCodecs(fake_codecs);
592
593 RTCOfferAnswerOptions options;
594 options.raw_packetization_for_video = true;
595
596 auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
597 auto offer = caller->CreateOfferAndSetAsLocal(options);
598 auto* offer_description =
599 cricket::GetFirstVideoContentDescription(offer->description());
600 for (const auto& codec : offer_description->codecs()) {
601 if (codec.GetCodecType() == cricket::VideoCodec::CODEC_VIDEO) {
602 EXPECT_EQ(codec.packetization, cricket::kPacketizationParamRaw);
603 }
604 }
605
606 auto callee = CreatePeerConnectionWithVideo(std::move(callee_fake_engine));
607 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
608 auto answer = callee->CreateAnswerAndSetAsLocal(options);
609 auto* answer_description =
610 cricket::GetFirstVideoContentDescription(answer->description());
611 for (const auto& codec : answer_description->codecs()) {
612 if (codec.GetCodecType() == cricket::VideoCodec::CODEC_VIDEO) {
613 EXPECT_EQ(codec.packetization, cricket::kPacketizationParamRaw);
614 }
615 }
616
617 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
618}
619
620// Test that raw packetization is not set in the answer when
621// raw_packetization_for_video is true if it was not set in the offer.
622TEST_P(PeerConnectionMediaTest,
623 RawPacketizationNotSetInAnswerWhenNotSetInOffer) {
624 std::vector<cricket::VideoCodec> fake_codecs;
625 fake_codecs.push_back(cricket::VideoCodec(111, cricket::kVp8CodecName));
626 fake_codecs.push_back(cricket::VideoCodec(112, cricket::kRtxCodecName));
627 fake_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "111";
628 fake_codecs.push_back(cricket::VideoCodec(113, cricket::kVp9CodecName));
629 fake_codecs.push_back(cricket::VideoCodec(114, cricket::kH264CodecName));
630 fake_codecs.push_back(cricket::VideoCodec(115, "HEVC"));
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200631 auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
Mirta Dvornicic479a3c02019-06-04 15:38:50 +0200632 caller_fake_engine->SetVideoCodecs(fake_codecs);
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200633 auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
Mirta Dvornicic479a3c02019-06-04 15:38:50 +0200634 callee_fake_engine->SetVideoCodecs(fake_codecs);
635
636 RTCOfferAnswerOptions caller_options;
637 caller_options.raw_packetization_for_video = false;
638 RTCOfferAnswerOptions callee_options;
639 callee_options.raw_packetization_for_video = true;
640
641 auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
642 auto offer = caller->CreateOfferAndSetAsLocal(caller_options);
643
644 auto callee = CreatePeerConnectionWithVideo(std::move(callee_fake_engine));
645 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
646 auto answer = callee->CreateAnswerAndSetAsLocal(callee_options);
647
648 auto* answer_description =
649 cricket::GetFirstVideoContentDescription(answer->description());
650 for (const auto& codec : answer_description->codecs()) {
651 EXPECT_EQ(codec.packetization, absl::nullopt);
652 }
653
654 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
655}
656
Steve Anton8d3444d2017-10-20 15:30:51 -0700657class PeerConnectionMediaOfferDirectionTest
Steve Antonad7bffc2018-01-22 10:21:56 -0800658 : public PeerConnectionMediaBaseTest,
Steve Anton8d3444d2017-10-20 15:30:51 -0700659 public ::testing::WithParamInterface<
Steve Antonad7bffc2018-01-22 10:21:56 -0800660 std::tuple<SdpSemantics,
661 std::tuple<bool, int, RtpTransceiverDirection>>> {
Steve Anton8d3444d2017-10-20 15:30:51 -0700662 protected:
Steve Antonad7bffc2018-01-22 10:21:56 -0800663 PeerConnectionMediaOfferDirectionTest()
664 : PeerConnectionMediaBaseTest(std::get<0>(GetParam())) {
665 auto param = std::get<1>(GetParam());
666 send_media_ = std::get<0>(param);
667 offer_to_receive_ = std::get<1>(param);
668 expected_direction_ = std::get<2>(param);
Steve Anton8d3444d2017-10-20 15:30:51 -0700669 }
670
671 bool send_media_;
672 int offer_to_receive_;
Steve Anton4e70a722017-11-28 14:57:10 -0800673 RtpTransceiverDirection expected_direction_;
Steve Anton8d3444d2017-10-20 15:30:51 -0700674};
675
676// Tests that the correct direction is set on the media description according
677// to the presence of a local media track and the offer_to_receive setting.
678TEST_P(PeerConnectionMediaOfferDirectionTest, VerifyDirection) {
679 auto caller = CreatePeerConnection();
680 if (send_media_) {
681 caller->AddAudioTrack("a");
682 }
683
684 RTCOfferAnswerOptions options;
685 options.offer_to_receive_audio = offer_to_receive_;
686 auto offer = caller->CreateOffer(options);
687
Steve Antonad7bffc2018-01-22 10:21:56 -0800688 auto* content = cricket::GetFirstMediaContent(offer->description(),
689 cricket::MEDIA_TYPE_AUDIO);
Steve Anton4e70a722017-11-28 14:57:10 -0800690 if (expected_direction_ == RtpTransceiverDirection::kInactive) {
Steve Antonad7bffc2018-01-22 10:21:56 -0800691 EXPECT_FALSE(content);
Steve Anton8d3444d2017-10-20 15:30:51 -0700692 } else {
Steve Antonad7bffc2018-01-22 10:21:56 -0800693 EXPECT_EQ(expected_direction_, content->media_description()->direction());
Steve Anton8d3444d2017-10-20 15:30:51 -0700694 }
695}
696
697// Note that in these tests, MD_INACTIVE indicates that no media section is
698// included in the offer, not that the media direction is inactive.
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100699INSTANTIATE_TEST_SUITE_P(
Steve Anton4e70a722017-11-28 14:57:10 -0800700 PeerConnectionMediaTest,
701 PeerConnectionMediaOfferDirectionTest,
Steve Antonad7bffc2018-01-22 10:21:56 -0800702 Combine(
Florent Castelli15a38de2022-04-06 00:38:21 +0200703 Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan),
Steve Antonad7bffc2018-01-22 10:21:56 -0800704 Values(std::make_tuple(false, -1, RtpTransceiverDirection::kInactive),
705 std::make_tuple(false, 0, RtpTransceiverDirection::kInactive),
706 std::make_tuple(false, 1, RtpTransceiverDirection::kRecvOnly),
707 std::make_tuple(true, -1, RtpTransceiverDirection::kSendRecv),
708 std::make_tuple(true, 0, RtpTransceiverDirection::kSendOnly),
709 std::make_tuple(true, 1, RtpTransceiverDirection::kSendRecv))));
Steve Anton8d3444d2017-10-20 15:30:51 -0700710
711class PeerConnectionMediaAnswerDirectionTest
Steve Antonad7bffc2018-01-22 10:21:56 -0800712 : public PeerConnectionMediaBaseTest,
Steve Anton8d3444d2017-10-20 15:30:51 -0700713 public ::testing::WithParamInterface<
Steve Antonad7bffc2018-01-22 10:21:56 -0800714 std::tuple<SdpSemantics, RtpTransceiverDirection, bool, int>> {
Steve Anton8d3444d2017-10-20 15:30:51 -0700715 protected:
Steve Antonad7bffc2018-01-22 10:21:56 -0800716 PeerConnectionMediaAnswerDirectionTest()
717 : PeerConnectionMediaBaseTest(std::get<0>(GetParam())) {
718 offer_direction_ = std::get<1>(GetParam());
719 send_media_ = std::get<2>(GetParam());
720 offer_to_receive_ = std::get<3>(GetParam());
Steve Anton8d3444d2017-10-20 15:30:51 -0700721 }
722
Steve Anton4e70a722017-11-28 14:57:10 -0800723 RtpTransceiverDirection offer_direction_;
Steve Anton8d3444d2017-10-20 15:30:51 -0700724 bool send_media_;
725 int offer_to_receive_;
726};
727
728// Tests that the direction in an answer is correct according to direction sent
729// in the offer, the presence of a local media track on the receive side and the
730// offer_to_receive setting.
731TEST_P(PeerConnectionMediaAnswerDirectionTest, VerifyDirection) {
Steve Anton22da89f2018-01-25 13:58:07 -0800732 if (IsUnifiedPlan() &&
733 offer_to_receive_ != RTCOfferAnswerOptions::kUndefined) {
734 // offer_to_receive_ is not implemented when creating answers with Unified
735 // Plan semantics specified.
Steve Antonad7bffc2018-01-22 10:21:56 -0800736 return;
737 }
Steve Anton22da89f2018-01-25 13:58:07 -0800738
Steve Anton8d3444d2017-10-20 15:30:51 -0700739 auto caller = CreatePeerConnection();
740 caller->AddAudioTrack("a");
741
742 // Create the offer with an audio section and set its direction.
743 auto offer = caller->CreateOffer();
744 cricket::GetFirstAudioContentDescription(offer->description())
745 ->set_direction(offer_direction_);
746
747 auto callee = CreatePeerConnection();
748 if (send_media_) {
749 callee->AddAudioTrack("a");
750 }
751 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
752
753 // Create the answer according to the test parameters.
754 RTCOfferAnswerOptions options;
755 options.offer_to_receive_audio = offer_to_receive_;
756 auto answer = callee->CreateAnswer(options);
757
758 // The expected direction in the answer is the intersection of each side's
759 // capability to send/recv media.
760 // For the offerer, the direction is given in the offer (offer_direction_).
761 // For the answerer, the direction has two components:
762 // 1. Send if the answerer has a local track to send.
763 // 2. Receive if the answerer has explicitly set the offer_to_receive to 1 or
764 // if it has been left as default.
Steve Anton4e70a722017-11-28 14:57:10 -0800765 bool offer_send = RtpTransceiverDirectionHasSend(offer_direction_);
766 bool offer_recv = RtpTransceiverDirectionHasRecv(offer_direction_);
Steve Anton8d3444d2017-10-20 15:30:51 -0700767
768 // The negotiated components determine the direction set in the answer.
Steve Anton1d03a752017-11-27 14:30:09 -0800769 bool negotiate_send = (send_media_ && offer_recv);
770 bool negotiate_recv = ((offer_to_receive_ != 0) && offer_send);
Steve Anton8d3444d2017-10-20 15:30:51 -0700771
772 auto expected_direction =
Steve Anton4e70a722017-11-28 14:57:10 -0800773 RtpTransceiverDirectionFromSendRecv(negotiate_send, negotiate_recv);
Steve Anton8d3444d2017-10-20 15:30:51 -0700774 EXPECT_EQ(expected_direction,
Steve Antonad7bffc2018-01-22 10:21:56 -0800775 GetMediaContentDirection(answer.get(), cricket::MEDIA_TYPE_AUDIO));
Steve Anton8d3444d2017-10-20 15:30:51 -0700776}
777
778// Tests that the media section is rejected if and only if the callee has no
779// local media track and has set offer_to_receive to 0, no matter which
780// direction the caller indicated in the offer.
781TEST_P(PeerConnectionMediaAnswerDirectionTest, VerifyRejected) {
Steve Anton22da89f2018-01-25 13:58:07 -0800782 if (IsUnifiedPlan() &&
783 offer_to_receive_ != RTCOfferAnswerOptions::kUndefined) {
784 // offer_to_receive_ is not implemented when creating answers with Unified
785 // Plan semantics specified.
Steve Antonad7bffc2018-01-22 10:21:56 -0800786 return;
787 }
Steve Anton22da89f2018-01-25 13:58:07 -0800788
Steve Anton8d3444d2017-10-20 15:30:51 -0700789 auto caller = CreatePeerConnection();
790 caller->AddAudioTrack("a");
791
792 // Create the offer with an audio section and set its direction.
793 auto offer = caller->CreateOffer();
794 cricket::GetFirstAudioContentDescription(offer->description())
795 ->set_direction(offer_direction_);
796
797 auto callee = CreatePeerConnection();
798 if (send_media_) {
799 callee->AddAudioTrack("a");
800 }
801 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
802
803 // Create the answer according to the test parameters.
804 RTCOfferAnswerOptions options;
805 options.offer_to_receive_audio = offer_to_receive_;
806 auto answer = callee->CreateAnswer(options);
807
808 // The media section is rejected if and only if offer_to_receive is explicitly
809 // set to 0 and there is no media to send.
810 auto* audio_content = cricket::GetFirstAudioContent(answer->description());
811 ASSERT_TRUE(audio_content);
812 EXPECT_EQ((offer_to_receive_ == 0 && !send_media_), audio_content->rejected);
813}
814
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100815INSTANTIATE_TEST_SUITE_P(PeerConnectionMediaTest,
816 PeerConnectionMediaAnswerDirectionTest,
Florent Castelli15a38de2022-04-06 00:38:21 +0200817 Combine(Values(SdpSemantics::kPlanB_DEPRECATED,
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100818 SdpSemantics::kUnifiedPlan),
819 Values(RtpTransceiverDirection::kInactive,
820 RtpTransceiverDirection::kSendOnly,
821 RtpTransceiverDirection::kRecvOnly,
822 RtpTransceiverDirection::kSendRecv),
823 Bool(),
824 Values(-1, 0, 1)));
Steve Anton8d3444d2017-10-20 15:30:51 -0700825
Steve Antonad7bffc2018-01-22 10:21:56 -0800826TEST_P(PeerConnectionMediaTest, OfferHasDifferentDirectionForAudioVideo) {
Steve Anton8d3444d2017-10-20 15:30:51 -0700827 auto caller = CreatePeerConnection();
828 caller->AddVideoTrack("v");
829
830 RTCOfferAnswerOptions options;
831 options.offer_to_receive_audio = 1;
832 options.offer_to_receive_video = 0;
833 auto offer = caller->CreateOffer(options);
834
Steve Anton4e70a722017-11-28 14:57:10 -0800835 EXPECT_EQ(RtpTransceiverDirection::kRecvOnly,
Steve Antonad7bffc2018-01-22 10:21:56 -0800836 GetMediaContentDirection(offer.get(), cricket::MEDIA_TYPE_AUDIO));
Steve Anton4e70a722017-11-28 14:57:10 -0800837 EXPECT_EQ(RtpTransceiverDirection::kSendOnly,
Steve Antonad7bffc2018-01-22 10:21:56 -0800838 GetMediaContentDirection(offer.get(), cricket::MEDIA_TYPE_VIDEO));
Steve Anton8d3444d2017-10-20 15:30:51 -0700839}
840
Steve Antonad7bffc2018-01-22 10:21:56 -0800841TEST_P(PeerConnectionMediaTest, AnswerHasDifferentDirectionsForAudioVideo) {
Steve Antonad7bffc2018-01-22 10:21:56 -0800842 if (IsUnifiedPlan()) {
Steve Anton22da89f2018-01-25 13:58:07 -0800843 // offer_to_receive_ is not implemented when creating answers with Unified
844 // Plan semantics specified.
Steve Antonad7bffc2018-01-22 10:21:56 -0800845 return;
846 }
847
Steve Anton8d3444d2017-10-20 15:30:51 -0700848 auto caller = CreatePeerConnectionWithAudioVideo();
849 auto callee = CreatePeerConnection();
850 callee->AddVideoTrack("v");
851
852 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
853
854 RTCOfferAnswerOptions options;
855 options.offer_to_receive_audio = 1;
856 options.offer_to_receive_video = 0;
857 auto answer = callee->CreateAnswer(options);
858
Steve Anton4e70a722017-11-28 14:57:10 -0800859 EXPECT_EQ(RtpTransceiverDirection::kRecvOnly,
Steve Antonad7bffc2018-01-22 10:21:56 -0800860 GetMediaContentDirection(answer.get(), cricket::MEDIA_TYPE_AUDIO));
Steve Anton4e70a722017-11-28 14:57:10 -0800861 EXPECT_EQ(RtpTransceiverDirection::kSendOnly,
Steve Antonad7bffc2018-01-22 10:21:56 -0800862 GetMediaContentDirection(answer.get(), cricket::MEDIA_TYPE_VIDEO));
Steve Anton8d3444d2017-10-20 15:30:51 -0700863}
864
865void AddComfortNoiseCodecsToSend(cricket::FakeMediaEngine* media_engine) {
Philipp Hancke8498c252020-06-05 13:28:57 +0200866 const cricket::AudioCodec kComfortNoiseCodec8k(102, cricket::kCnCodecName,
867 8000, 0, 1);
868 const cricket::AudioCodec kComfortNoiseCodec16k(103, cricket::kCnCodecName,
869 16000, 0, 1);
Steve Anton8d3444d2017-10-20 15:30:51 -0700870
Sebastian Jansson6eb8a162018-11-16 11:29:55 +0100871 auto codecs = media_engine->voice().send_codecs();
Steve Anton8d3444d2017-10-20 15:30:51 -0700872 codecs.push_back(kComfortNoiseCodec8k);
873 codecs.push_back(kComfortNoiseCodec16k);
874 media_engine->SetAudioCodecs(codecs);
875}
876
877bool HasAnyComfortNoiseCodecs(const cricket::SessionDescription* desc) {
878 const auto* audio_desc = cricket::GetFirstAudioContentDescription(desc);
879 for (const auto& codec : audio_desc->codecs()) {
Philipp Hancke8498c252020-06-05 13:28:57 +0200880 if (codec.name == cricket::kCnCodecName) {
Steve Anton8d3444d2017-10-20 15:30:51 -0700881 return true;
882 }
883 }
884 return false;
885}
886
Taylor Brandstetterf7fcfb72021-09-09 13:39:38 -0700887bool HasPayloadTypeConflict(const cricket::SessionDescription* desc) {
888 std::set<int> payload_types;
889 const auto* audio_desc = cricket::GetFirstAudioContentDescription(desc);
890 if (audio_desc) {
891 for (const auto& codec : audio_desc->codecs()) {
892 if (payload_types.count(codec.id) > 0) {
893 return true;
894 }
895 payload_types.insert(codec.id);
896 }
897 }
898 const auto* video_desc = cricket::GetFirstVideoContentDescription(desc);
899 if (video_desc) {
900 for (const auto& codec : video_desc->codecs()) {
901 if (payload_types.count(codec.id) > 0) {
902 return true;
903 }
904 payload_types.insert(codec.id);
905 }
906 }
907 return false;
908}
909
Steve Antonad7bffc2018-01-22 10:21:56 -0800910TEST_P(PeerConnectionMediaTest,
Steve Anton8d3444d2017-10-20 15:30:51 -0700911 CreateOfferWithNoVoiceActivityDetectionIncludesNoComfortNoiseCodecs) {
Philipp Hancke718acf62021-05-12 14:18:03 +0200912 auto fake_engine = std::make_unique<FakeMediaEngine>();
913 AddComfortNoiseCodecsToSend(fake_engine.get());
914 auto caller = CreatePeerConnectionWithAudioVideo(std::move(fake_engine));
Steve Anton8d3444d2017-10-20 15:30:51 -0700915
916 RTCOfferAnswerOptions options;
917 options.voice_activity_detection = false;
918 auto offer = caller->CreateOffer(options);
919
920 EXPECT_FALSE(HasAnyComfortNoiseCodecs(offer->description()));
921}
922
Steve Antonad7bffc2018-01-22 10:21:56 -0800923TEST_P(PeerConnectionMediaTest,
Philipp Hancke718acf62021-05-12 14:18:03 +0200924 CreateOfferWithVoiceActivityDetectionIncludesComfortNoiseCodecs) {
925 auto fake_engine = std::make_unique<FakeMediaEngine>();
926 AddComfortNoiseCodecsToSend(fake_engine.get());
927 auto caller = CreatePeerConnectionWithAudioVideo(std::move(fake_engine));
928
929 RTCOfferAnswerOptions options;
930 options.voice_activity_detection = true;
931 auto offer = caller->CreateOffer(options);
932
933 EXPECT_TRUE(HasAnyComfortNoiseCodecs(offer->description()));
934}
935
936TEST_P(PeerConnectionMediaTest,
937 CreateAnswerWithVoiceActivityDetectionIncludesNoComfortNoiseCodecs) {
Steve Anton8d3444d2017-10-20 15:30:51 -0700938 auto caller = CreatePeerConnectionWithAudioVideo();
Philipp Hancke718acf62021-05-12 14:18:03 +0200939
940 auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
941 AddComfortNoiseCodecsToSend(callee_fake_engine.get());
942 auto callee =
943 CreatePeerConnectionWithAudioVideo(std::move(callee_fake_engine));
944
945 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
946
947 RTCOfferAnswerOptions options;
948 options.voice_activity_detection = true;
949 auto answer = callee->CreateAnswer(options);
950
951 EXPECT_FALSE(HasAnyComfortNoiseCodecs(answer->description()));
952}
953
954TEST_P(PeerConnectionMediaTest,
955 CreateAnswerWithNoVoiceActivityDetectionIncludesNoComfortNoiseCodecs) {
956 auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
957 AddComfortNoiseCodecsToSend(caller_fake_engine.get());
958 auto caller =
959 CreatePeerConnectionWithAudioVideo(std::move(caller_fake_engine));
960
961 auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
962 AddComfortNoiseCodecsToSend(callee_fake_engine.get());
963 auto callee =
964 CreatePeerConnectionWithAudioVideo(std::move(callee_fake_engine));
Steve Anton8d3444d2017-10-20 15:30:51 -0700965
966 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
967
968 RTCOfferAnswerOptions options;
969 options.voice_activity_detection = false;
970 auto answer = callee->CreateAnswer(options);
971
972 EXPECT_FALSE(HasAnyComfortNoiseCodecs(answer->description()));
973}
974
975// The following test group verifies that we reject answers with invalid media
976// sections as per RFC 3264.
977
978class PeerConnectionMediaInvalidMediaTest
Steve Antonad7bffc2018-01-22 10:21:56 -0800979 : public PeerConnectionMediaBaseTest,
980 public ::testing::WithParamInterface<std::tuple<
981 SdpSemantics,
Steve Anton8d3444d2017-10-20 15:30:51 -0700982 std::tuple<std::string,
983 std::function<void(cricket::SessionDescription*)>,
Steve Antonad7bffc2018-01-22 10:21:56 -0800984 std::string>>> {
Steve Anton8d3444d2017-10-20 15:30:51 -0700985 protected:
Steve Antonad7bffc2018-01-22 10:21:56 -0800986 PeerConnectionMediaInvalidMediaTest()
987 : PeerConnectionMediaBaseTest(std::get<0>(GetParam())) {
988 auto param = std::get<1>(GetParam());
989 mutator_ = std::get<1>(param);
990 expected_error_ = std::get<2>(param);
Steve Anton8d3444d2017-10-20 15:30:51 -0700991 }
992
993 std::function<void(cricket::SessionDescription*)> mutator_;
994 std::string expected_error_;
995};
996
997TEST_P(PeerConnectionMediaInvalidMediaTest, FailToSetRemoteAnswer) {
998 auto caller = CreatePeerConnectionWithAudioVideo();
999 auto callee = CreatePeerConnectionWithAudioVideo();
1000
1001 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1002
1003 auto answer = callee->CreateAnswer();
1004 mutator_(answer->description());
1005
1006 std::string error;
1007 ASSERT_FALSE(caller->SetRemoteDescription(std::move(answer), &error));
1008 EXPECT_EQ("Failed to set remote answer sdp: " + expected_error_, error);
1009}
1010
1011TEST_P(PeerConnectionMediaInvalidMediaTest, FailToSetLocalAnswer) {
1012 auto caller = CreatePeerConnectionWithAudioVideo();
1013 auto callee = CreatePeerConnectionWithAudioVideo();
1014
1015 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1016
1017 auto answer = callee->CreateAnswer();
1018 mutator_(answer->description());
1019
1020 std::string error;
1021 ASSERT_FALSE(callee->SetLocalDescription(std::move(answer), &error));
1022 EXPECT_EQ("Failed to set local answer sdp: " + expected_error_, error);
1023}
1024
1025void RemoveVideoContent(cricket::SessionDescription* desc) {
1026 auto content_name = cricket::GetFirstVideoContent(desc)->name;
1027 desc->RemoveContentByName(content_name);
1028 desc->RemoveTransportInfoByName(content_name);
1029}
1030
1031void RenameVideoContent(cricket::SessionDescription* desc) {
1032 auto* video_content = cricket::GetFirstVideoContent(desc);
1033 auto* transport_info = desc->GetTransportInfoByName(video_content->name);
1034 video_content->name = "video_renamed";
1035 transport_info->content_name = video_content->name;
1036}
1037
1038void ReverseMediaContent(cricket::SessionDescription* desc) {
Steve Anton64b626b2019-01-28 17:25:26 -08001039 absl::c_reverse(desc->contents());
1040 absl::c_reverse(desc->transport_infos());
Steve Anton8d3444d2017-10-20 15:30:51 -07001041}
1042
1043void ChangeMediaTypeAudioToVideo(cricket::SessionDescription* desc) {
Steve Antonad7bffc2018-01-22 10:21:56 -08001044 std::string audio_mid = cricket::GetFirstAudioContent(desc)->name;
1045 desc->RemoveContentByName(audio_mid);
1046 auto* video_content = cricket::GetFirstVideoContent(desc);
1047 desc->AddContent(audio_mid, video_content->type,
Harald Alvestrand1716d392019-06-03 20:35:45 +02001048 video_content->media_description()->Clone());
Steve Anton8d3444d2017-10-20 15:30:51 -07001049}
1050
1051constexpr char kMLinesOutOfOrder[] =
1052 "The order of m-lines in answer doesn't match order in offer. Rejecting "
1053 "answer.";
1054
Mirko Bonadeic84f6612019-01-31 12:20:57 +01001055INSTANTIATE_TEST_SUITE_P(
Steve Anton8d3444d2017-10-20 15:30:51 -07001056 PeerConnectionMediaTest,
1057 PeerConnectionMediaInvalidMediaTest,
Florent Castelli15a38de2022-04-06 00:38:21 +02001058 Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan),
Steve Antonad7bffc2018-01-22 10:21:56 -08001059 Values(std::make_tuple("remove video",
1060 RemoveVideoContent,
1061 kMLinesOutOfOrder),
1062 std::make_tuple("rename video",
1063 RenameVideoContent,
1064 kMLinesOutOfOrder),
1065 std::make_tuple("reverse media sections",
1066 ReverseMediaContent,
1067 kMLinesOutOfOrder),
1068 std::make_tuple("change audio type to video type",
1069 ChangeMediaTypeAudioToVideo,
1070 kMLinesOutOfOrder))));
Steve Anton8d3444d2017-10-20 15:30:51 -07001071
1072// Test that the correct media engine send/recv streams are created when doing
1073// a series of offer/answers where audio/video are both sent, then audio is
1074// rejected, then both audio/video sent again.
Steve Antonad7bffc2018-01-22 10:21:56 -08001075TEST_P(PeerConnectionMediaTest, TestAVOfferWithAudioOnlyAnswer) {
Steve Antonad7bffc2018-01-22 10:21:56 -08001076 if (IsUnifiedPlan()) {
Steve Anton22da89f2018-01-25 13:58:07 -08001077 // offer_to_receive_ is not implemented when creating answers with Unified
1078 // Plan semantics specified.
Steve Antonad7bffc2018-01-22 10:21:56 -08001079 return;
1080 }
1081
Steve Anton8d3444d2017-10-20 15:30:51 -07001082 RTCOfferAnswerOptions options_reject_video;
1083 options_reject_video.offer_to_receive_audio =
1084 RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
1085 options_reject_video.offer_to_receive_video = 0;
1086
1087 auto caller = CreatePeerConnection();
1088 caller->AddAudioTrack("a");
1089 caller->AddVideoTrack("v");
1090 auto callee = CreatePeerConnection();
1091
1092 // Caller initially offers to send/recv audio and video.
1093 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1094 // Callee accepts the audio as recv only but rejects the video.
1095 ASSERT_TRUE(caller->SetRemoteDescription(
1096 callee->CreateAnswerAndSetAsLocal(options_reject_video)));
1097
1098 auto caller_voice = caller->media_engine()->GetVoiceChannel(0);
1099 ASSERT_TRUE(caller_voice);
1100 EXPECT_EQ(0u, caller_voice->recv_streams().size());
1101 EXPECT_EQ(1u, caller_voice->send_streams().size());
1102 auto caller_video = caller->media_engine()->GetVideoChannel(0);
1103 EXPECT_FALSE(caller_video);
1104
1105 // Callee adds its own audio/video stream and offers to receive audio/video
1106 // too.
1107 callee->AddAudioTrack("a");
1108 auto callee_video_track = callee->AddVideoTrack("v");
1109 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1110 ASSERT_TRUE(
1111 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
1112
1113 auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
1114 ASSERT_TRUE(callee_voice);
1115 EXPECT_EQ(1u, callee_voice->recv_streams().size());
1116 EXPECT_EQ(1u, callee_voice->send_streams().size());
1117 auto callee_video = callee->media_engine()->GetVideoChannel(0);
1118 ASSERT_TRUE(callee_video);
1119 EXPECT_EQ(1u, callee_video->recv_streams().size());
1120 EXPECT_EQ(1u, callee_video->send_streams().size());
1121
1122 // Callee removes video but keeps audio and rejects the video once again.
Harald Alvestrand93dd7632022-01-19 12:28:45 +00001123 callee->pc()->RemoveTrackOrError(callee_video_track);
Steve Anton8d3444d2017-10-20 15:30:51 -07001124 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1125 ASSERT_TRUE(
1126 callee->SetLocalDescription(callee->CreateAnswer(options_reject_video)));
1127
1128 callee_voice = callee->media_engine()->GetVoiceChannel(0);
1129 ASSERT_TRUE(callee_voice);
1130 EXPECT_EQ(1u, callee_voice->recv_streams().size());
1131 EXPECT_EQ(1u, callee_voice->send_streams().size());
1132 callee_video = callee->media_engine()->GetVideoChannel(0);
1133 EXPECT_FALSE(callee_video);
1134}
1135
1136// Test that the correct media engine send/recv streams are created when doing
1137// a series of offer/answers where audio/video are both sent, then video is
1138// rejected, then both audio/video sent again.
Steve Antonad7bffc2018-01-22 10:21:56 -08001139TEST_P(PeerConnectionMediaTest, TestAVOfferWithVideoOnlyAnswer) {
Steve Antonad7bffc2018-01-22 10:21:56 -08001140 if (IsUnifiedPlan()) {
Steve Anton22da89f2018-01-25 13:58:07 -08001141 // offer_to_receive_ is not implemented when creating answers with Unified
1142 // Plan semantics specified.
Steve Antonad7bffc2018-01-22 10:21:56 -08001143 return;
1144 }
1145
Steve Anton8d3444d2017-10-20 15:30:51 -07001146 // Disable the bundling here. If the media is bundled on audio
1147 // transport, then we can't reject the audio because switching the bundled
1148 // transport is not currently supported.
1149 // (https://bugs.chromium.org/p/webrtc/issues/detail?id=6704)
1150 RTCOfferAnswerOptions options_no_bundle;
1151 options_no_bundle.use_rtp_mux = false;
1152 RTCOfferAnswerOptions options_reject_audio = options_no_bundle;
1153 options_reject_audio.offer_to_receive_audio = 0;
1154 options_reject_audio.offer_to_receive_video =
1155 RTCOfferAnswerOptions::kMaxOfferToReceiveMedia;
1156
1157 auto caller = CreatePeerConnection();
1158 caller->AddAudioTrack("a");
1159 caller->AddVideoTrack("v");
1160 auto callee = CreatePeerConnection();
1161
1162 // Caller initially offers to send/recv audio and video.
1163 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1164 // Callee accepts the video as recv only but rejects the audio.
1165 ASSERT_TRUE(caller->SetRemoteDescription(
1166 callee->CreateAnswerAndSetAsLocal(options_reject_audio)));
1167
1168 auto caller_voice = caller->media_engine()->GetVoiceChannel(0);
1169 EXPECT_FALSE(caller_voice);
1170 auto caller_video = caller->media_engine()->GetVideoChannel(0);
1171 ASSERT_TRUE(caller_video);
1172 EXPECT_EQ(0u, caller_video->recv_streams().size());
1173 EXPECT_EQ(1u, caller_video->send_streams().size());
1174
1175 // Callee adds its own audio/video stream and offers to receive audio/video
1176 // too.
1177 auto callee_audio_track = callee->AddAudioTrack("a");
1178 callee->AddVideoTrack("v");
1179 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1180 ASSERT_TRUE(caller->SetRemoteDescription(
1181 callee->CreateAnswerAndSetAsLocal(options_no_bundle)));
1182
1183 auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
1184 ASSERT_TRUE(callee_voice);
1185 EXPECT_EQ(1u, callee_voice->recv_streams().size());
1186 EXPECT_EQ(1u, callee_voice->send_streams().size());
1187 auto callee_video = callee->media_engine()->GetVideoChannel(0);
1188 ASSERT_TRUE(callee_video);
1189 EXPECT_EQ(1u, callee_video->recv_streams().size());
1190 EXPECT_EQ(1u, callee_video->send_streams().size());
1191
1192 // Callee removes audio but keeps video and rejects the audio once again.
Harald Alvestrand93dd7632022-01-19 12:28:45 +00001193 callee->pc()->RemoveTrackOrError(callee_audio_track);
Steve Anton8d3444d2017-10-20 15:30:51 -07001194 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1195 ASSERT_TRUE(
1196 callee->SetLocalDescription(callee->CreateAnswer(options_reject_audio)));
1197
1198 callee_voice = callee->media_engine()->GetVoiceChannel(0);
1199 EXPECT_FALSE(callee_voice);
1200 callee_video = callee->media_engine()->GetVideoChannel(0);
1201 ASSERT_TRUE(callee_video);
1202 EXPECT_EQ(1u, callee_video->recv_streams().size());
1203 EXPECT_EQ(1u, callee_video->send_streams().size());
1204}
1205
1206// Tests that if the underlying video encoder fails to be initialized (signaled
1207// by failing to set send codecs), the PeerConnection signals the error to the
1208// client.
Steve Antonad7bffc2018-01-22 10:21:56 -08001209TEST_P(PeerConnectionMediaTest, MediaEngineErrorPropagatedToClients) {
Steve Anton8d3444d2017-10-20 15:30:51 -07001210 auto caller = CreatePeerConnectionWithAudioVideo();
1211 auto callee = CreatePeerConnectionWithAudioVideo();
1212
1213 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1214
1215 auto video_channel = caller->media_engine()->GetVideoChannel(0);
1216 video_channel->set_fail_set_send_codecs(true);
1217
1218 std::string error;
1219 ASSERT_FALSE(caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(),
1220 &error));
Yura Yaroshevichbc1f9102020-06-03 21:15:22 +00001221 EXPECT_EQ(std::string("Failed to set remote answer sdp: Failed to set remote "
1222 "video description "
1223 "send parameters for m-section with mid='") +
1224 (IsUnifiedPlan() ? "1" : "video") + "'.",
1225 error);
Steve Anton8d3444d2017-10-20 15:30:51 -07001226}
1227
1228// Tests that if the underlying video encoder fails once then subsequent
1229// attempts at setting the local/remote description will also fail, even if
1230// SetSendCodecs no longer fails.
Steve Antonad7bffc2018-01-22 10:21:56 -08001231TEST_P(PeerConnectionMediaTest,
Steve Anton8d3444d2017-10-20 15:30:51 -07001232 FailToApplyDescriptionIfVideoEncoderHasEverFailed) {
1233 auto caller = CreatePeerConnectionWithAudioVideo();
1234 auto callee = CreatePeerConnectionWithAudioVideo();
1235
1236 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1237
1238 auto video_channel = caller->media_engine()->GetVideoChannel(0);
1239 video_channel->set_fail_set_send_codecs(true);
1240
1241 EXPECT_FALSE(
1242 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
1243
1244 video_channel->set_fail_set_send_codecs(false);
1245
1246 EXPECT_FALSE(caller->SetRemoteDescription(callee->CreateAnswer()));
1247 EXPECT_FALSE(caller->SetLocalDescription(caller->CreateOffer()));
1248}
1249
1250void RenameContent(cricket::SessionDescription* desc,
Steve Antonad7bffc2018-01-22 10:21:56 -08001251 cricket::MediaType media_type,
Steve Anton8d3444d2017-10-20 15:30:51 -07001252 const std::string& new_name) {
Steve Antonad7bffc2018-01-22 10:21:56 -08001253 auto* content = cricket::GetFirstMediaContent(desc, media_type);
Steve Anton8d3444d2017-10-20 15:30:51 -07001254 RTC_DCHECK(content);
Steve Antonad7bffc2018-01-22 10:21:56 -08001255 std::string old_name = content->name;
Steve Anton8d3444d2017-10-20 15:30:51 -07001256 content->name = new_name;
1257 auto* transport = desc->GetTransportInfoByName(old_name);
1258 RTC_DCHECK(transport);
1259 transport->content_name = new_name;
Zhi Huangd2248f82018-04-10 14:41:03 -07001260
1261 // Rename the content name in the BUNDLE group.
1262 cricket::ContentGroup new_bundle_group =
1263 *desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
1264 new_bundle_group.RemoveContentName(old_name);
1265 new_bundle_group.AddContentName(new_name);
1266 desc->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1267 desc->AddGroup(new_bundle_group);
Steve Anton8d3444d2017-10-20 15:30:51 -07001268}
1269
1270// Tests that an answer responds with the same MIDs as the offer.
Steve Antonad7bffc2018-01-22 10:21:56 -08001271TEST_P(PeerConnectionMediaTest, AnswerHasSameMidsAsOffer) {
Zhi Huang365381f2018-04-13 16:44:34 -07001272 const std::string kAudioMid = "notdefault1";
1273 const std::string kVideoMid = "notdefault2";
Steve Anton8d3444d2017-10-20 15:30:51 -07001274
1275 auto caller = CreatePeerConnectionWithAudioVideo();
1276 auto callee = CreatePeerConnectionWithAudioVideo();
1277
1278 auto offer = caller->CreateOffer();
Steve Antonad7bffc2018-01-22 10:21:56 -08001279 RenameContent(offer->description(), cricket::MEDIA_TYPE_AUDIO, kAudioMid);
1280 RenameContent(offer->description(), cricket::MEDIA_TYPE_VIDEO, kVideoMid);
Steve Anton8d3444d2017-10-20 15:30:51 -07001281 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
1282
1283 auto answer = callee->CreateAnswer();
1284 EXPECT_EQ(kAudioMid,
1285 cricket::GetFirstAudioContent(answer->description())->name);
1286 EXPECT_EQ(kVideoMid,
1287 cricket::GetFirstVideoContent(answer->description())->name);
1288}
1289
1290// Test that if the callee creates a re-offer, the MIDs are the same as the
1291// original offer.
Steve Antonad7bffc2018-01-22 10:21:56 -08001292TEST_P(PeerConnectionMediaTest, ReOfferHasSameMidsAsFirstOffer) {
Zhi Huang365381f2018-04-13 16:44:34 -07001293 const std::string kAudioMid = "notdefault1";
1294 const std::string kVideoMid = "notdefault2";
Steve Anton8d3444d2017-10-20 15:30:51 -07001295
1296 auto caller = CreatePeerConnectionWithAudioVideo();
1297 auto callee = CreatePeerConnectionWithAudioVideo();
1298
1299 auto offer = caller->CreateOffer();
Steve Antonad7bffc2018-01-22 10:21:56 -08001300 RenameContent(offer->description(), cricket::MEDIA_TYPE_AUDIO, kAudioMid);
1301 RenameContent(offer->description(), cricket::MEDIA_TYPE_VIDEO, kVideoMid);
Steve Anton8d3444d2017-10-20 15:30:51 -07001302 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
1303 ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
1304
1305 auto reoffer = callee->CreateOffer();
1306 EXPECT_EQ(kAudioMid,
1307 cricket::GetFirstAudioContent(reoffer->description())->name);
1308 EXPECT_EQ(kVideoMid,
1309 cricket::GetFirstVideoContent(reoffer->description())->name);
1310}
1311
Steve Anton06817cd2018-12-18 15:55:30 -08001312// Test that SetRemoteDescription returns an error if there are two m= sections
1313// with the same MID value.
1314TEST_P(PeerConnectionMediaTest, SetRemoteDescriptionFailsWithDuplicateMids) {
1315 auto caller = CreatePeerConnectionWithAudioVideo();
1316 auto callee = CreatePeerConnectionWithAudioVideo();
1317
1318 auto offer = caller->CreateOffer();
1319 RenameContent(offer->description(), cricket::MEDIA_TYPE_AUDIO, "same");
1320 RenameContent(offer->description(), cricket::MEDIA_TYPE_VIDEO, "same");
1321
1322 std::string error;
1323 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer), &error));
1324 EXPECT_EQ(error,
1325 "Failed to set remote offer sdp: Duplicate a=mid value 'same'.");
1326}
1327
Steve Antonad7bffc2018-01-22 10:21:56 -08001328TEST_P(PeerConnectionMediaTest,
Steve Anton8d3444d2017-10-20 15:30:51 -07001329 CombinedAudioVideoBweConfigPropagatedToMediaEngine) {
1330 RTCConfiguration config;
1331 config.combined_audio_video_bwe.emplace(true);
1332 auto caller = CreatePeerConnectionWithAudioVideo(config);
1333
1334 ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
1335
1336 auto caller_voice = caller->media_engine()->GetVoiceChannel(0);
1337 ASSERT_TRUE(caller_voice);
1338 const cricket::AudioOptions& audio_options = caller_voice->options();
1339 EXPECT_EQ(config.combined_audio_video_bwe,
1340 audio_options.combined_audio_video_bwe);
1341}
1342
Philipp Hancke7145a142021-09-28 07:46:06 +02001343// Test that if a RED codec refers to another codec in its fmtp line, but that
1344// codec's payload type was reassigned for some reason (either the remote
1345// endpoint selected a different payload type or there was a conflict), the RED
1346// fmtp line is modified to refer to the correct payload type.
1347TEST_P(PeerConnectionMediaTest, RedFmtpPayloadTypeReassigned) {
1348 std::vector<cricket::AudioCodec> caller_fake_codecs;
1349 caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1));
1350 auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
1351 caller_fake_engine->SetAudioCodecs(caller_fake_codecs);
1352 auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine));
1353
1354 std::vector<cricket::AudioCodec> callee_fake_codecs;
1355 callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1));
1356 callee_fake_codecs.push_back(
1357 cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1));
1358 callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
1359 "120/120");
1360 auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
1361 callee_fake_engine->SetAudioCodecs(callee_fake_codecs);
1362 auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
1363
1364 // Offer from the caller establishes 100 as the "foo" payload type.
1365 auto offer = caller->CreateOfferAndSetAsLocal();
1366 callee->SetRemoteDescription(std::move(offer));
1367 auto answer = callee->CreateAnswerAndSetAsLocal();
1368 auto answer_description =
1369 cricket::GetFirstAudioContentDescription(answer->description());
1370 ASSERT_EQ(1u, answer_description->codecs().size());
1371
1372 // Offer from the callee should respect the established payload type, and
1373 // attempt to add RED, which should refer to the correct payload type.
1374 offer = callee->CreateOfferAndSetAsLocal();
1375 auto* offer_description =
1376 cricket::GetFirstAudioContentDescription(offer->description());
1377 ASSERT_EQ(2u, offer_description->codecs().size());
1378 for (const auto& codec : offer_description->codecs()) {
1379 if (codec.name == "foo") {
1380 ASSERT_EQ(100, codec.id);
1381 } else if (codec.name == cricket::kRedCodecName) {
1382 std::string fmtp;
1383 ASSERT_TRUE(codec.GetParam("", &fmtp));
1384 EXPECT_EQ("100/100", fmtp);
1385 }
1386 }
1387}
1388
1389// Test that RED without fmtp does match RED without fmtp.
1390TEST_P(PeerConnectionMediaTest, RedFmtpPayloadTypeNoFmtpMatchNoFmtp) {
1391 std::vector<cricket::AudioCodec> caller_fake_codecs;
1392 caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1));
1393 caller_fake_codecs.push_back(
1394 cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1));
1395 auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
1396 caller_fake_engine->SetAudioCodecs(caller_fake_codecs);
1397 auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine));
1398
1399 std::vector<cricket::AudioCodec> callee_fake_codecs;
1400 callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1));
1401 callee_fake_codecs.push_back(
1402 cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1));
1403 auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
1404 callee_fake_engine->SetAudioCodecs(callee_fake_codecs);
1405 auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
1406
1407 // Offer from the caller establishes 100 as the "foo" payload type.
1408 // Red (without fmtp) is negotiated.
1409 auto offer = caller->CreateOfferAndSetAsLocal();
1410 callee->SetRemoteDescription(std::move(offer));
1411 auto answer = callee->CreateAnswerAndSetAsLocal();
1412 auto answer_description =
1413 cricket::GetFirstAudioContentDescription(answer->description());
1414 ASSERT_EQ(2u, answer_description->codecs().size());
1415
1416 // Offer from the callee should respect the established payload type, and
1417 // attempt to add RED.
1418 offer = callee->CreateOfferAndSetAsLocal();
1419 auto* offer_description =
1420 cricket::GetFirstAudioContentDescription(offer->description());
1421 ASSERT_EQ(2u, offer_description->codecs().size());
1422 for (const auto& codec : offer_description->codecs()) {
1423 if (codec.name == "foo") {
1424 ASSERT_EQ(100, codec.id);
1425 } else if (codec.name == cricket::kRedCodecName) {
1426 ASSERT_EQ(101, codec.id);
1427 }
1428 }
1429}
1430
1431// Test that RED without fmtp does not match RED with fmtp.
1432TEST_P(PeerConnectionMediaTest, RedFmtpPayloadTypeNoFmtpNoMatchFmtp) {
1433 std::vector<cricket::AudioCodec> caller_fake_codecs;
1434 caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1));
1435 caller_fake_codecs.push_back(
1436 cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1));
1437 auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
1438 caller_fake_engine->SetAudioCodecs(caller_fake_codecs);
1439 auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine));
1440
1441 std::vector<cricket::AudioCodec> callee_fake_codecs;
1442 callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1));
1443 callee_fake_codecs.push_back(
1444 cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1));
1445 callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
1446 "120/120");
1447 auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
1448 callee_fake_engine->SetAudioCodecs(callee_fake_codecs);
1449 auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
1450
1451 // Offer from the caller establishes 100 as the "foo" payload type.
1452 // It should not negotiate RED.
1453 auto offer = caller->CreateOfferAndSetAsLocal();
1454 callee->SetRemoteDescription(std::move(offer));
1455 auto answer = callee->CreateAnswerAndSetAsLocal();
1456 auto answer_description =
1457 cricket::GetFirstAudioContentDescription(answer->description());
1458 ASSERT_EQ(1u, answer_description->codecs().size());
1459
1460 // Offer from the callee should respect the established payload type, and
1461 // attempt to add RED, which should refer to the correct payload type.
1462 offer = callee->CreateOfferAndSetAsLocal();
1463 auto* offer_description =
1464 cricket::GetFirstAudioContentDescription(offer->description());
1465 ASSERT_EQ(2u, offer_description->codecs().size());
1466 for (const auto& codec : offer_description->codecs()) {
1467 if (codec.name == "foo") {
1468 ASSERT_EQ(100, codec.id);
1469 } else if (codec.name == cricket::kRedCodecName) {
1470 std::string fmtp;
1471 ASSERT_TRUE(
1472 codec.GetParam(cricket::kCodecParamNotInNameValueFormat, &fmtp));
1473 EXPECT_EQ("100/100", fmtp);
1474 }
1475 }
1476}
1477
1478// Test that RED with fmtp must match base codecs.
1479TEST_P(PeerConnectionMediaTest, RedFmtpPayloadTypeMustMatchBaseCodecs) {
1480 std::vector<cricket::AudioCodec> caller_fake_codecs;
1481 caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1));
1482 caller_fake_codecs.push_back(
1483 cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1));
1484 caller_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
1485 "100/100");
1486 auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
1487 caller_fake_engine->SetAudioCodecs(caller_fake_codecs);
1488 auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine));
1489
1490 std::vector<cricket::AudioCodec> callee_fake_codecs;
1491 callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1));
1492 callee_fake_codecs.push_back(
1493 cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1));
1494 callee_fake_codecs.push_back(cricket::AudioCodec(122, "bar", 0, 0, 1));
1495 callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
1496 "122/122");
1497 auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
1498 callee_fake_engine->SetAudioCodecs(callee_fake_codecs);
1499 auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
1500
1501 // Offer from the caller establishes 100 as the "foo" payload type.
1502 // It should not negotiate RED since RED is associated with foo, not bar.
1503 auto offer = caller->CreateOfferAndSetAsLocal();
1504 callee->SetRemoteDescription(std::move(offer));
1505 auto answer = callee->CreateAnswerAndSetAsLocal();
1506 auto answer_description =
1507 cricket::GetFirstAudioContentDescription(answer->description());
1508 ASSERT_EQ(1u, answer_description->codecs().size());
1509}
1510
1511// Test behaviour when the RED fmtp attempts to specify different codecs
1512// which is not supported.
1513TEST_P(PeerConnectionMediaTest, RedFmtpPayloadMixed) {
1514 std::vector<cricket::AudioCodec> caller_fake_codecs;
1515 caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1));
1516 caller_fake_codecs.push_back(cricket::AudioCodec(102, "bar", 0, 0, 1));
1517 caller_fake_codecs.push_back(
1518 cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1));
1519 caller_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
1520 "100/102");
1521 auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
1522 caller_fake_engine->SetAudioCodecs(caller_fake_codecs);
1523 auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine));
1524
1525 std::vector<cricket::AudioCodec> callee_fake_codecs;
1526 callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1));
1527 callee_fake_codecs.push_back(
1528 cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1));
1529 callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
1530 "120/120");
1531 auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
1532 callee_fake_engine->SetAudioCodecs(callee_fake_codecs);
1533 auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
1534
1535 // Offer from the caller establishes 100 as the "foo" payload type.
1536 auto offer = caller->CreateOfferAndSetAsLocal();
1537 callee->SetRemoteDescription(std::move(offer));
1538 auto answer = callee->CreateAnswerAndSetAsLocal();
1539 auto answer_description =
1540 cricket::GetFirstAudioContentDescription(answer->description());
1541 // RED is not negotiated.
1542 ASSERT_EQ(1u, answer_description->codecs().size());
1543}
1544
1545// Test behaviour when the RED fmtp attempts to negotiate different levels of
1546// redundancy.
1547TEST_P(PeerConnectionMediaTest, RedFmtpPayloadDifferentRedundancy) {
1548 std::vector<cricket::AudioCodec> caller_fake_codecs;
1549 caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1));
1550 caller_fake_codecs.push_back(
1551 cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1));
1552 caller_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
1553 "100/100");
1554 auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
1555 caller_fake_engine->SetAudioCodecs(caller_fake_codecs);
1556 auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine));
1557
1558 std::vector<cricket::AudioCodec> callee_fake_codecs;
1559 callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1));
1560 callee_fake_codecs.push_back(
1561 cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1));
1562 callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
1563 "120/120/120");
1564 auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
1565 callee_fake_engine->SetAudioCodecs(callee_fake_codecs);
1566 auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
1567
1568 // Offer from the caller establishes 100 as the "foo" payload type.
1569 auto offer = caller->CreateOfferAndSetAsLocal();
1570 callee->SetRemoteDescription(std::move(offer));
1571 auto answer = callee->CreateAnswerAndSetAsLocal();
1572 auto answer_description =
1573 cricket::GetFirstAudioContentDescription(answer->description());
1574 // RED is negotiated.
1575 ASSERT_EQ(2u, answer_description->codecs().size());
1576
1577 // Offer from the callee should respect the established payload type, and
1578 // attempt to add RED, which should refer to the correct payload type.
1579 offer = callee->CreateOfferAndSetAsLocal();
1580 auto* offer_description =
1581 cricket::GetFirstAudioContentDescription(offer->description());
1582 ASSERT_EQ(2u, offer_description->codecs().size());
1583 for (const auto& codec : offer_description->codecs()) {
1584 if (codec.name == "foo") {
1585 ASSERT_EQ(100, codec.id);
1586 } else if (codec.name == cricket::kRedCodecName) {
1587 std::string fmtp;
1588 ASSERT_TRUE(
1589 codec.GetParam(cricket::kCodecParamNotInNameValueFormat, &fmtp));
1590 EXPECT_EQ("100/100", fmtp);
1591 }
1592 }
1593}
1594
Florent Castelli2d9d82e2019-04-23 19:25:51 +02001595template <typename C>
1596bool CompareCodecs(const std::vector<webrtc::RtpCodecCapability>& capabilities,
1597 const std::vector<C>& codecs) {
1598 bool capability_has_rtx =
1599 absl::c_any_of(capabilities, [](const webrtc::RtpCodecCapability& codec) {
1600 return codec.name == cricket::kRtxCodecName;
1601 });
1602 bool codecs_has_rtx = absl::c_any_of(codecs, [](const C& codec) {
1603 return codec.name == cricket::kRtxCodecName;
1604 });
1605
1606 std::vector<C> codecs_no_rtx;
1607 absl::c_copy_if(
1608 codecs, std::back_inserter(codecs_no_rtx),
1609 [](const C& codec) { return codec.name != cricket::kRtxCodecName; });
1610
1611 std::vector<webrtc::RtpCodecCapability> capabilities_no_rtx;
1612 absl::c_copy_if(capabilities, std::back_inserter(capabilities_no_rtx),
1613 [](const webrtc::RtpCodecCapability& codec) {
1614 return codec.name != cricket::kRtxCodecName;
1615 });
1616
1617 return capability_has_rtx == codecs_has_rtx &&
1618 absl::c_equal(
1619 capabilities_no_rtx, codecs_no_rtx,
1620 [](const webrtc::RtpCodecCapability& capability, const C& codec) {
1621 return codec.MatchesCapability(capability);
1622 });
1623}
1624
1625TEST_F(PeerConnectionMediaTestUnifiedPlan,
1626 SetCodecPreferencesAudioMissingRecvCodec) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +02001627 auto fake_engine = std::make_unique<FakeMediaEngine>();
Florent Castelli2d9d82e2019-04-23 19:25:51 +02001628 auto send_codecs = fake_engine->voice().send_codecs();
1629 send_codecs.push_back(cricket::AudioCodec(send_codecs.back().id + 1,
1630 "send_only_codec", 0, 0, 1));
1631 fake_engine->SetAudioSendCodecs(send_codecs);
1632
1633 auto caller = CreatePeerConnectionWithAudio(std::move(fake_engine));
1634
1635 auto transceiver = caller->pc()->GetTransceivers().front();
1636 auto capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
1637 cricket::MediaType::MEDIA_TYPE_AUDIO);
1638
1639 std::vector<webrtc::RtpCodecCapability> codecs;
1640 absl::c_copy_if(capabilities.codecs, std::back_inserter(codecs),
1641 [](const webrtc::RtpCodecCapability& codec) {
1642 return codec.name.find("_only_") != std::string::npos;
1643 });
1644
1645 auto result = transceiver->SetCodecPreferences(codecs);
1646 EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
1647}
1648
1649TEST_F(PeerConnectionMediaTestUnifiedPlan,
1650 SetCodecPreferencesAudioMissingSendCodec) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +02001651 auto fake_engine = std::make_unique<FakeMediaEngine>();
Florent Castelli2d9d82e2019-04-23 19:25:51 +02001652 auto recv_codecs = fake_engine->voice().recv_codecs();
1653 recv_codecs.push_back(cricket::AudioCodec(recv_codecs.back().id + 1,
1654 "recv_only_codec", 0, 0, 1));
1655 fake_engine->SetAudioRecvCodecs(recv_codecs);
1656 auto caller = CreatePeerConnectionWithAudio(std::move(fake_engine));
1657
1658 auto transceiver = caller->pc()->GetTransceivers().front();
1659 auto capabilities = caller->pc_factory()->GetRtpReceiverCapabilities(
1660 cricket::MediaType::MEDIA_TYPE_AUDIO);
1661
1662 std::vector<webrtc::RtpCodecCapability> codecs;
1663 absl::c_copy_if(capabilities.codecs, std::back_inserter(codecs),
1664 [](const webrtc::RtpCodecCapability& codec) {
1665 return codec.name.find("_only_") != std::string::npos;
1666 });
1667
1668 auto result = transceiver->SetCodecPreferences(codecs);
1669 EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
1670}
1671
1672TEST_F(PeerConnectionMediaTestUnifiedPlan,
1673 SetCodecPreferencesAudioRejectsVideoCodec) {
1674 auto caller = CreatePeerConnectionWithAudio();
1675
1676 auto transceiver = caller->pc()->GetTransceivers().front();
1677 auto video_codecs =
1678 caller->pc_factory()
1679 ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
1680 .codecs;
1681 auto codecs =
1682 caller->pc_factory()
1683 ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_AUDIO)
1684 .codecs;
1685 codecs.insert(codecs.end(), video_codecs.begin(), video_codecs.end());
1686 auto result = transceiver->SetCodecPreferences(codecs);
1687 EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
1688}
1689
1690TEST_F(PeerConnectionMediaTestUnifiedPlan,
1691 SetCodecPreferencesAudioRejectsOnlyRtxRedFec) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +02001692 auto fake_engine = std::make_unique<FakeMediaEngine>();
Florent Castelli2d9d82e2019-04-23 19:25:51 +02001693 auto audio_codecs = fake_engine->voice().send_codecs();
1694 audio_codecs.push_back(cricket::AudioCodec(audio_codecs.back().id + 1,
1695 cricket::kRtxCodecName, 0, 0, 1));
1696 audio_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1697 std::to_string(audio_codecs.back().id - 1);
1698 audio_codecs.push_back(cricket::AudioCodec(audio_codecs.back().id + 1,
1699 cricket::kRedCodecName, 0, 0, 1));
1700 audio_codecs.push_back(cricket::AudioCodec(
1701 audio_codecs.back().id + 1, cricket::kUlpfecCodecName, 0, 0, 1));
1702 fake_engine->SetAudioCodecs(audio_codecs);
1703
1704 auto caller = CreatePeerConnectionWithAudio(std::move(fake_engine));
1705
1706 auto transceiver = caller->pc()->GetTransceivers().front();
1707 auto codecs =
1708 caller->pc_factory()
1709 ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_AUDIO)
1710 .codecs;
1711 auto codecs_only_rtx_red_fec = codecs;
1712 auto it = std::remove_if(codecs_only_rtx_red_fec.begin(),
1713 codecs_only_rtx_red_fec.end(),
1714 [](const webrtc::RtpCodecCapability& codec) {
1715 return !(codec.name == cricket::kRtxCodecName ||
1716 codec.name == cricket::kRedCodecName ||
1717 codec.name == cricket::kUlpfecCodecName);
1718 });
1719 codecs_only_rtx_red_fec.erase(it, codecs_only_rtx_red_fec.end());
1720
1721 auto result = transceiver->SetCodecPreferences(codecs_only_rtx_red_fec);
1722 EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
1723}
1724
1725TEST_F(PeerConnectionMediaTestUnifiedPlan, SetCodecPreferencesAllAudioCodecs) {
1726 auto caller = CreatePeerConnectionWithAudio();
1727
1728 auto sender_audio_codecs =
1729 caller->pc_factory()
1730 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
1731 .codecs;
1732
1733 auto audio_transceiver = caller->pc()->GetTransceivers().front();
1734
1735 // Normal case, set all capabilities as preferences
1736 EXPECT_TRUE(audio_transceiver->SetCodecPreferences(sender_audio_codecs).ok());
1737 auto offer = caller->CreateOffer();
1738 auto codecs = offer->description()
1739 ->contents()[0]
1740 .media_description()
1741 ->as_audio()
1742 ->codecs();
1743 EXPECT_TRUE(CompareCodecs(sender_audio_codecs, codecs));
1744}
1745
1746TEST_F(PeerConnectionMediaTestUnifiedPlan,
1747 SetCodecPreferencesResetAudioCodecs) {
1748 auto caller = CreatePeerConnectionWithAudio();
1749
1750 auto sender_audio_codecs =
1751 caller->pc_factory()
1752 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
1753 .codecs;
1754 std::vector<webrtc::RtpCodecCapability> empty_codecs = {};
1755
1756 auto audio_transceiver = caller->pc()->GetTransceivers().front();
1757
1758 // Normal case, reset codec preferences
1759 EXPECT_TRUE(audio_transceiver->SetCodecPreferences(empty_codecs).ok());
1760 auto offer = caller->CreateOffer();
1761 auto codecs = offer->description()
1762 ->contents()[0]
1763 .media_description()
1764 ->as_audio()
1765 ->codecs();
1766 EXPECT_TRUE(CompareCodecs(sender_audio_codecs, codecs));
1767}
1768
1769TEST_F(PeerConnectionMediaTestUnifiedPlan,
1770 SetCodecPreferencesVideoRejectsAudioCodec) {
1771 auto caller = CreatePeerConnectionWithVideo();
1772
1773 auto transceiver = caller->pc()->GetTransceivers().front();
1774 auto audio_codecs =
1775 caller->pc_factory()
1776 ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_AUDIO)
1777 .codecs;
1778 auto codecs =
1779 caller->pc_factory()
1780 ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
1781 .codecs;
1782 codecs.insert(codecs.end(), audio_codecs.begin(), audio_codecs.end());
1783 auto result = transceiver->SetCodecPreferences(codecs);
1784 EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
1785}
1786
1787TEST_F(PeerConnectionMediaTestUnifiedPlan,
1788 SetCodecPreferencesVideoRejectsOnlyRtxRedFec) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +02001789 auto fake_engine = std::make_unique<FakeMediaEngine>();
Johannes Kron3e983682020-03-29 22:17:00 +02001790 auto video_codecs = fake_engine->video().send_codecs();
Florent Castelli2d9d82e2019-04-23 19:25:51 +02001791 video_codecs.push_back(
1792 cricket::VideoCodec(video_codecs.back().id + 1, cricket::kRtxCodecName));
Johannes Kron3e983682020-03-29 22:17:00 +02001793 video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1794 std::to_string(video_codecs.back().id - 1);
Florent Castelli2d9d82e2019-04-23 19:25:51 +02001795 video_codecs.push_back(
1796 cricket::VideoCodec(video_codecs.back().id + 1, cricket::kRedCodecName));
1797 video_codecs.push_back(cricket::VideoCodec(video_codecs.back().id + 1,
1798 cricket::kUlpfecCodecName));
1799 fake_engine->SetVideoCodecs(video_codecs);
1800
1801 auto caller = CreatePeerConnectionWithVideo(std::move(fake_engine));
1802
1803 auto transceiver = caller->pc()->GetTransceivers().front();
1804 auto codecs =
1805 caller->pc_factory()
1806 ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
1807 .codecs;
1808 auto codecs_only_rtx_red_fec = codecs;
1809 auto it = std::remove_if(codecs_only_rtx_red_fec.begin(),
1810 codecs_only_rtx_red_fec.end(),
1811 [](const webrtc::RtpCodecCapability& codec) {
1812 return !(codec.name == cricket::kRtxCodecName ||
1813 codec.name == cricket::kRedCodecName ||
1814 codec.name == cricket::kUlpfecCodecName);
1815 });
1816 codecs_only_rtx_red_fec.erase(it, codecs_only_rtx_red_fec.end());
1817
1818 auto result = transceiver->SetCodecPreferences(codecs_only_rtx_red_fec);
1819 EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
1820}
1821
1822TEST_F(PeerConnectionMediaTestUnifiedPlan, SetCodecPreferencesAllVideoCodecs) {
1823 auto caller = CreatePeerConnectionWithVideo();
1824
1825 auto sender_video_codecs =
1826 caller->pc_factory()
1827 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1828 .codecs;
1829
1830 auto video_transceiver = caller->pc()->GetTransceivers().front();
1831
1832 // Normal case, setting preferences to normal capabilities
1833 EXPECT_TRUE(video_transceiver->SetCodecPreferences(sender_video_codecs).ok());
1834 auto offer = caller->CreateOffer();
1835 auto codecs = offer->description()
1836 ->contents()[0]
1837 .media_description()
1838 ->as_video()
1839 ->codecs();
1840 EXPECT_TRUE(CompareCodecs(sender_video_codecs, codecs));
1841}
1842
1843TEST_F(PeerConnectionMediaTestUnifiedPlan,
1844 SetCodecPreferencesResetVideoCodecs) {
1845 auto caller = CreatePeerConnectionWithVideo();
1846
1847 auto sender_video_codecs =
1848 caller->pc_factory()
1849 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1850 .codecs;
1851
1852 std::vector<webrtc::RtpCodecCapability> empty_codecs = {};
1853
1854 auto video_transceiver = caller->pc()->GetTransceivers().front();
1855
1856 // Normal case, resetting preferences with empty list of codecs
1857 EXPECT_TRUE(video_transceiver->SetCodecPreferences(empty_codecs).ok());
1858 auto offer = caller->CreateOffer();
1859 auto codecs = offer->description()
1860 ->contents()[0]
1861 .media_description()
1862 ->as_video()
1863 ->codecs();
1864 EXPECT_TRUE(CompareCodecs(sender_video_codecs, codecs));
1865}
1866
1867TEST_F(PeerConnectionMediaTestUnifiedPlan,
1868 SetCodecPreferencesVideoCodecDuplicatesRemoved) {
1869 auto caller = CreatePeerConnectionWithVideo();
1870
1871 auto sender_video_codecs =
1872 caller->pc_factory()
1873 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1874 .codecs;
1875
1876 auto video_transceiver = caller->pc()->GetTransceivers().front();
1877
1878 // Check duplicates are removed
1879 auto single_codec = sender_video_codecs;
1880 single_codec.resize(1);
1881 auto duplicate_codec = single_codec;
1882 duplicate_codec.push_back(duplicate_codec.front());
1883 duplicate_codec.push_back(duplicate_codec.front());
1884 duplicate_codec.push_back(duplicate_codec.front());
1885
1886 EXPECT_TRUE(video_transceiver->SetCodecPreferences(duplicate_codec).ok());
1887 auto offer = caller->CreateOffer();
1888 auto codecs = offer->description()
1889 ->contents()[0]
1890 .media_description()
1891 ->as_video()
1892 ->codecs();
1893 EXPECT_TRUE(CompareCodecs(single_codec, codecs));
1894}
1895
1896TEST_F(PeerConnectionMediaTestUnifiedPlan, SetCodecPreferencesVideoWithRtx) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +02001897 auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
Johannes Kron3e983682020-03-29 22:17:00 +02001898 auto caller_video_codecs = caller_fake_engine->video().send_codecs();
Florent Castelli2d9d82e2019-04-23 19:25:51 +02001899 caller_video_codecs.push_back(cricket::VideoCodec(
1900 caller_video_codecs.back().id + 1, cricket::kVp8CodecName));
1901 caller_video_codecs.push_back(cricket::VideoCodec(
1902 caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
1903 caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1904 std::to_string(caller_video_codecs.back().id - 1);
1905 caller_video_codecs.push_back(cricket::VideoCodec(
1906 caller_video_codecs.back().id + 1, cricket::kVp9CodecName));
1907 caller_video_codecs.push_back(cricket::VideoCodec(
1908 caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
1909 caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1910 std::to_string(caller_video_codecs.back().id - 1);
1911 caller_fake_engine->SetVideoCodecs(caller_video_codecs);
1912
1913 auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
1914
1915 auto sender_video_codecs =
1916 caller->pc_factory()
1917 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1918 .codecs;
1919
1920 auto video_transceiver = caller->pc()->GetTransceivers().front();
1921
1922 // Check that RTX codec is properly added
1923 auto video_codecs_vpx_rtx = sender_video_codecs;
1924 auto it =
1925 std::remove_if(video_codecs_vpx_rtx.begin(), video_codecs_vpx_rtx.end(),
1926 [](const webrtc::RtpCodecCapability& codec) {
1927 return codec.name != cricket::kRtxCodecName &&
1928 codec.name != cricket::kVp8CodecName &&
1929 codec.name != cricket::kVp9CodecName;
1930 });
1931 video_codecs_vpx_rtx.erase(it, video_codecs_vpx_rtx.end());
1932 absl::c_reverse(video_codecs_vpx_rtx);
1933 EXPECT_EQ(video_codecs_vpx_rtx.size(), 3u); // VP8, VP9, RTX
1934 EXPECT_TRUE(
1935 video_transceiver->SetCodecPreferences(video_codecs_vpx_rtx).ok());
1936 auto offer = caller->CreateOffer();
1937 auto codecs = offer->description()
1938 ->contents()[0]
1939 .media_description()
1940 ->as_video()
1941 ->codecs();
1942
1943 EXPECT_TRUE(CompareCodecs(video_codecs_vpx_rtx, codecs));
1944 EXPECT_EQ(codecs.size(), 4u);
1945}
1946
1947TEST_F(PeerConnectionMediaTestUnifiedPlan,
1948 SetCodecPreferencesVideoCodecsNegotiation) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +02001949 auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
Johannes Kron3e983682020-03-29 22:17:00 +02001950 auto caller_video_codecs = caller_fake_engine->video().send_codecs();
Florent Castelli2d9d82e2019-04-23 19:25:51 +02001951 caller_video_codecs.push_back(cricket::VideoCodec(
1952 caller_video_codecs.back().id + 1, cricket::kVp8CodecName));
1953 caller_video_codecs.push_back(cricket::VideoCodec(
1954 caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
1955 caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1956 std::to_string(caller_video_codecs.back().id - 1);
1957 caller_video_codecs.push_back(cricket::VideoCodec(
1958 caller_video_codecs.back().id + 1, cricket::kVp9CodecName));
1959 caller_video_codecs.push_back(cricket::VideoCodec(
1960 caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
1961 caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1962 std::to_string(caller_video_codecs.back().id - 1);
1963 caller_fake_engine->SetVideoCodecs(caller_video_codecs);
1964
Mirko Bonadei317a1f02019-09-17 17:06:18 +02001965 auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
Florent Castelli2d9d82e2019-04-23 19:25:51 +02001966 callee_fake_engine->SetVideoCodecs(caller_video_codecs);
1967
1968 auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
1969 auto callee = CreatePeerConnection(std::move(callee_fake_engine));
1970
1971 auto video_codecs = caller->pc_factory()
1972 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1973 .codecs;
1974
1975 auto send_transceiver = caller->pc()->GetTransceivers().front();
1976
1977 auto video_codecs_vpx = video_codecs;
1978 auto it = std::remove_if(video_codecs_vpx.begin(), video_codecs_vpx.end(),
1979 [](const webrtc::RtpCodecCapability& codec) {
1980 return codec.name != cricket::kVp8CodecName &&
1981 codec.name != cricket::kVp9CodecName;
1982 });
1983 video_codecs_vpx.erase(it, video_codecs_vpx.end());
1984 EXPECT_EQ(video_codecs_vpx.size(), 2u); // VP8, VP9
1985 EXPECT_TRUE(send_transceiver->SetCodecPreferences(video_codecs_vpx).ok());
1986
1987 auto offer = caller->CreateOfferAndSetAsLocal();
1988 auto codecs = offer->description()
1989 ->contents()[0]
1990 .media_description()
1991 ->as_video()
1992 ->codecs();
1993
1994 EXPECT_EQ(codecs.size(), 2u); // VP8, VP9
1995 EXPECT_TRUE(CompareCodecs(video_codecs_vpx, codecs));
1996
1997 callee->SetRemoteDescription(std::move(offer));
1998
1999 auto recv_transceiver = callee->pc()->GetTransceivers().front();
2000 auto video_codecs_vp8_rtx = video_codecs;
2001 it = std::remove_if(video_codecs_vp8_rtx.begin(), video_codecs_vp8_rtx.end(),
2002 [](const webrtc::RtpCodecCapability& codec) {
2003 bool r = codec.name != cricket::kVp8CodecName &&
2004 codec.name != cricket::kRtxCodecName;
2005 return r;
2006 });
2007 video_codecs_vp8_rtx.erase(it, video_codecs_vp8_rtx.end());
2008 EXPECT_EQ(video_codecs_vp8_rtx.size(), 2u); // VP8, RTX
2009 recv_transceiver->SetCodecPreferences(video_codecs_vp8_rtx);
2010
2011 auto answer = callee->CreateAnswerAndSetAsLocal();
2012
2013 auto recv_codecs = answer->description()
2014 ->contents()[0]
2015 .media_description()
2016 ->as_video()
2017 ->codecs();
2018 EXPECT_EQ(recv_codecs.size(), 1u); // VP8
2019}
2020
2021TEST_F(PeerConnectionMediaTestUnifiedPlan,
2022 SetCodecPreferencesVideoCodecsNegotiationReverseOrder) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +02002023 auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
Johannes Kron3e983682020-03-29 22:17:00 +02002024 auto caller_video_codecs = caller_fake_engine->video().send_codecs();
Florent Castelli2d9d82e2019-04-23 19:25:51 +02002025 caller_video_codecs.push_back(cricket::VideoCodec(
2026 caller_video_codecs.back().id + 1, cricket::kVp8CodecName));
2027 caller_video_codecs.push_back(cricket::VideoCodec(
2028 caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
2029 caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
2030 std::to_string(caller_video_codecs.back().id - 1);
2031 caller_video_codecs.push_back(cricket::VideoCodec(
2032 caller_video_codecs.back().id + 1, cricket::kVp9CodecName));
2033 caller_video_codecs.push_back(cricket::VideoCodec(
2034 caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
2035 caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
2036 std::to_string(caller_video_codecs.back().id - 1);
2037 caller_fake_engine->SetVideoCodecs(caller_video_codecs);
2038
Mirko Bonadei317a1f02019-09-17 17:06:18 +02002039 auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
Florent Castelli2d9d82e2019-04-23 19:25:51 +02002040 callee_fake_engine->SetVideoCodecs(caller_video_codecs);
2041
2042 auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
2043 auto callee = CreatePeerConnection(std::move(callee_fake_engine));
2044
2045 auto video_codecs = caller->pc_factory()
2046 ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
2047 .codecs;
2048
2049 auto send_transceiver = caller->pc()->GetTransceivers().front();
2050
2051 auto video_codecs_vpx = video_codecs;
2052 auto it = std::remove_if(video_codecs_vpx.begin(), video_codecs_vpx.end(),
2053 [](const webrtc::RtpCodecCapability& codec) {
2054 return codec.name != cricket::kVp8CodecName &&
2055 codec.name != cricket::kVp9CodecName;
2056 });
2057 video_codecs_vpx.erase(it, video_codecs_vpx.end());
2058 EXPECT_EQ(video_codecs_vpx.size(), 2u); // VP8, VP9
2059 EXPECT_TRUE(send_transceiver->SetCodecPreferences(video_codecs_vpx).ok());
2060
2061 auto video_codecs_vpx_reverse = video_codecs_vpx;
2062 absl::c_reverse(video_codecs_vpx_reverse);
2063
2064 auto offer = caller->CreateOfferAndSetAsLocal();
2065 auto codecs = offer->description()
2066 ->contents()[0]
2067 .media_description()
2068 ->as_video()
2069 ->codecs();
2070 EXPECT_EQ(codecs.size(), 2u); // VP9, VP8
2071 EXPECT_TRUE(CompareCodecs(video_codecs_vpx, codecs));
2072
2073 callee->SetRemoteDescription(std::move(offer));
2074
2075 auto recv_transceiver = callee->pc()->GetTransceivers().front();
2076 recv_transceiver->SetCodecPreferences(video_codecs_vpx_reverse);
2077
2078 auto answer = callee->CreateAnswerAndSetAsLocal();
2079
2080 auto recv_codecs = answer->description()
2081 ->contents()[0]
2082 .media_description()
2083 ->as_video()
2084 ->codecs();
2085
2086 EXPECT_TRUE(CompareCodecs(video_codecs_vpx_reverse, recv_codecs));
2087}
2088
Philipp Hancke3ac73bd2021-05-11 14:13:06 +02002089TEST_F(PeerConnectionMediaTestUnifiedPlan,
2090 SetCodecPreferencesVoiceActivityDetection) {
2091 auto fake_engine = std::make_unique<FakeMediaEngine>();
2092 AddComfortNoiseCodecsToSend(fake_engine.get());
2093 auto caller = CreatePeerConnectionWithAudio(std::move(fake_engine));
2094
2095 RTCOfferAnswerOptions options;
2096 auto offer = caller->CreateOffer(options);
2097 EXPECT_TRUE(HasAnyComfortNoiseCodecs(offer->description()));
2098
2099 auto transceiver = caller->pc()->GetTransceivers().front();
2100 auto capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
2101 cricket::MediaType::MEDIA_TYPE_AUDIO);
2102 EXPECT_TRUE(transceiver->SetCodecPreferences(capabilities.codecs).ok());
2103
2104 options.voice_activity_detection = false;
2105 offer = caller->CreateOffer(options);
2106 EXPECT_FALSE(HasAnyComfortNoiseCodecs(offer->description()));
2107}
2108
Taylor Brandstetterf7fcfb72021-09-09 13:39:38 -07002109// If the "default" payload types of audio/video codecs are the same, and
2110// audio/video are bundled (as is the default), payload types should be
2111// remapped to avoid conflict, as normally happens without using
2112// SetCodecPreferences.
2113TEST_F(PeerConnectionMediaTestUnifiedPlan,
2114 SetCodecPreferencesAvoidsPayloadTypeConflictInOffer) {
2115 auto fake_engine = std::make_unique<cricket::FakeMediaEngine>();
2116
2117 std::vector<cricket::AudioCodec> audio_codecs;
2118 audio_codecs.emplace_back(100, "foo", 0, 0, 1);
2119 audio_codecs.emplace_back(101, cricket::kRtxCodecName, 0, 0, 1);
2120 audio_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "100";
2121 fake_engine->SetAudioCodecs(audio_codecs);
2122
2123 std::vector<cricket::VideoCodec> video_codecs;
2124 video_codecs.emplace_back(100, "bar");
2125 video_codecs.emplace_back(101, cricket::kRtxCodecName);
2126 video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "100";
2127 fake_engine->SetVideoCodecs(video_codecs);
2128
2129 auto caller = CreatePeerConnectionWithAudioVideo(std::move(fake_engine));
2130 auto transceivers = caller->pc()->GetTransceivers();
2131 ASSERT_EQ(2u, transceivers.size());
2132
2133 auto audio_transceiver = caller->pc()->GetTransceivers()[0];
2134 auto capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
2135 cricket::MediaType::MEDIA_TYPE_AUDIO);
2136 EXPECT_TRUE(audio_transceiver->SetCodecPreferences(capabilities.codecs).ok());
2137
2138 auto video_transceiver = caller->pc()->GetTransceivers()[1];
2139 capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
2140 cricket::MediaType::MEDIA_TYPE_VIDEO);
2141 EXPECT_TRUE(video_transceiver->SetCodecPreferences(capabilities.codecs).ok());
2142
2143 RTCOfferAnswerOptions options;
2144 auto offer = caller->CreateOffer(options);
2145 EXPECT_FALSE(HasPayloadTypeConflict(offer->description()));
2146 // Sanity check that we got the primary codec and RTX.
2147 EXPECT_EQ(2u, cricket::GetFirstAudioContentDescription(offer->description())
2148 ->codecs()
2149 .size());
2150 EXPECT_EQ(2u, cricket::GetFirstVideoContentDescription(offer->description())
2151 ->codecs()
2152 .size());
2153}
2154
2155// Same as above, but preferences set for the answer.
2156TEST_F(PeerConnectionMediaTestUnifiedPlan,
2157 SetCodecPreferencesAvoidsPayloadTypeConflictInAnswer) {
2158 auto fake_engine = std::make_unique<cricket::FakeMediaEngine>();
2159
2160 std::vector<cricket::AudioCodec> audio_codecs;
2161 audio_codecs.emplace_back(100, "foo", 0, 0, 1);
2162 audio_codecs.emplace_back(101, cricket::kRtxCodecName, 0, 0, 1);
2163 audio_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "100";
2164 fake_engine->SetAudioCodecs(audio_codecs);
2165
2166 std::vector<cricket::VideoCodec> video_codecs;
2167 video_codecs.emplace_back(100, "bar");
2168 video_codecs.emplace_back(101, cricket::kRtxCodecName);
2169 video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "100";
2170 fake_engine->SetVideoCodecs(video_codecs);
2171
2172 auto caller = CreatePeerConnectionWithAudioVideo(std::move(fake_engine));
2173
2174 RTCOfferAnswerOptions options;
2175 caller->SetRemoteDescription(caller->CreateOffer(options));
2176
2177 auto transceivers = caller->pc()->GetTransceivers();
2178 ASSERT_EQ(2u, transceivers.size());
2179
2180 auto audio_transceiver = caller->pc()->GetTransceivers()[0];
2181 auto capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
2182 cricket::MediaType::MEDIA_TYPE_AUDIO);
2183 EXPECT_TRUE(audio_transceiver->SetCodecPreferences(capabilities.codecs).ok());
2184
2185 auto video_transceiver = caller->pc()->GetTransceivers()[1];
2186 capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
2187 cricket::MediaType::MEDIA_TYPE_VIDEO);
2188 EXPECT_TRUE(video_transceiver->SetCodecPreferences(capabilities.codecs).ok());
2189
2190 auto answer = caller->CreateAnswer(options);
2191
2192 EXPECT_FALSE(HasPayloadTypeConflict(answer->description()));
2193 // Sanity check that we got the primary codec and RTX.
2194 EXPECT_EQ(2u, cricket::GetFirstAudioContentDescription(answer->description())
2195 ->codecs()
2196 .size());
2197 EXPECT_EQ(2u, cricket::GetFirstVideoContentDescription(answer->description())
2198 ->codecs()
2199 .size());
2200}
2201
2202// Same as above, but preferences set for a subsequent offer.
2203TEST_F(PeerConnectionMediaTestUnifiedPlan,
2204 SetCodecPreferencesAvoidsPayloadTypeConflictInSubsequentOffer) {
2205 auto fake_engine = std::make_unique<cricket::FakeMediaEngine>();
2206
2207 std::vector<cricket::AudioCodec> audio_codecs;
2208 audio_codecs.emplace_back(100, "foo", 0, 0, 1);
2209 audio_codecs.emplace_back(101, cricket::kRtxCodecName, 0, 0, 1);
2210 audio_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "100";
2211 fake_engine->SetAudioCodecs(audio_codecs);
2212
2213 std::vector<cricket::VideoCodec> video_codecs;
2214 video_codecs.emplace_back(100, "bar");
2215 video_codecs.emplace_back(101, cricket::kRtxCodecName);
2216 video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "100";
2217 fake_engine->SetVideoCodecs(video_codecs);
2218
2219 auto caller = CreatePeerConnectionWithAudioVideo(std::move(fake_engine));
2220
2221 RTCOfferAnswerOptions options;
2222 caller->SetRemoteDescription(caller->CreateOffer(options));
2223 caller->SetLocalDescription(caller->CreateAnswer(options));
2224
2225 auto transceivers = caller->pc()->GetTransceivers();
2226 ASSERT_EQ(2u, transceivers.size());
2227
2228 auto audio_transceiver = caller->pc()->GetTransceivers()[0];
2229 auto capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
2230 cricket::MediaType::MEDIA_TYPE_AUDIO);
2231 EXPECT_TRUE(audio_transceiver->SetCodecPreferences(capabilities.codecs).ok());
2232
2233 auto video_transceiver = caller->pc()->GetTransceivers()[1];
2234 capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
2235 cricket::MediaType::MEDIA_TYPE_VIDEO);
2236 EXPECT_TRUE(video_transceiver->SetCodecPreferences(capabilities.codecs).ok());
2237
2238 auto reoffer = caller->CreateOffer(options);
2239
2240 EXPECT_FALSE(HasPayloadTypeConflict(reoffer->description()));
2241 // Sanity check that we got the primary codec and RTX.
2242 EXPECT_EQ(2u, cricket::GetFirstAudioContentDescription(reoffer->description())
2243 ->codecs()
2244 .size());
2245 EXPECT_EQ(2u, cricket::GetFirstVideoContentDescription(reoffer->description())
2246 ->codecs()
2247 .size());
2248}
2249
Mirko Bonadeic84f6612019-01-31 12:20:57 +01002250INSTANTIATE_TEST_SUITE_P(PeerConnectionMediaTest,
2251 PeerConnectionMediaTest,
Florent Castelli15a38de2022-04-06 00:38:21 +02002252 Values(SdpSemantics::kPlanB_DEPRECATED,
Mirko Bonadeic84f6612019-01-31 12:20:57 +01002253 SdpSemantics::kUnifiedPlan));
Steve Antonad7bffc2018-01-22 10:21:56 -08002254
Steve Anton8d3444d2017-10-20 15:30:51 -07002255} // namespace webrtc