blob: d00ad1c90e11aa1058de1e2a841142b70596533b [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:361/*
kjellander65c7f672016-02-12 08:05:012 * Copyright 2004 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:363 *
kjellander65c7f672016-02-12 08:05:014 * 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.
henrike@webrtc.org28e20752013-07-10 00:45:369 */
10
Jonas Olssona4d87372019-07-05 17:08:3311#include "pc/media_session.h"
12
Harald Alvestrandc24a2182022-02-23 13:44:5913#include <stddef.h>
14
Amit Hilbuch77938e62018-12-21 17:23:3815#include <algorithm>
Harald Alvestrandc24a2182022-02-23 13:44:5916#include <cstdint>
17#include <map>
kwiberg31022942016-03-11 22:18:2118#include <memory>
henrike@webrtc.org28e20752013-07-10 00:45:3619#include <string>
Harald Alvestrandc24a2182022-02-23 13:44:5920#include <tuple>
henrike@webrtc.org28e20752013-07-10 00:45:3621#include <vector>
22
Steve Anton64b626b2019-01-29 01:25:2623#include "absl/algorithm/container.h"
Mirko Bonadei57cabed2020-04-01 10:03:1124#include "absl/strings/match.h"
Harald Alvestrandc24a2182022-02-23 13:44:5925#include "absl/strings/string_view.h"
Florent Castelli811e24a2023-05-31 12:35:4726#include "absl/types/optional.h"
Harald Alvestrandc24a2182022-02-23 13:44:5927#include "api/candidate.h"
Tomas Lundqvista26d6ed2023-10-27 12:25:5728#include "api/rtp_parameters.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3129#include "media/base/codec.h"
Harald Alvestrandc24a2182022-02-23 13:44:5930#include "media/base/media_constants.h"
Steve Anton10542f22019-01-11 17:11:0031#include "media/base/test_utils.h"
Harald Alvestrand8d3d6cf2019-05-16 09:49:1732#include "media/sctp/sctp_transport_internal.h"
Steve Anton10542f22019-01-11 17:11:0033#include "p2p/base/p2p_constants.h"
34#include "p2p/base/transport_description.h"
35#include "p2p/base/transport_info.h"
Harald Alvestrandc24a2182022-02-23 13:44:5936#include "pc/media_protocol_names.h"
Steve Anton10542f22019-01-11 17:11:0037#include "pc/rtp_media_utils.h"
Tomas Lundqvista26d6ed2023-10-27 12:25:5738#include "pc/rtp_parameters_conversion.h"
Harald Alvestrandc24a2182022-02-23 13:44:5939#include "rtc_base/arraysize.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3140#include "rtc_base/checks.h"
Steve Anton10542f22019-01-11 17:11:0041#include "rtc_base/fake_ssl_identity.h"
Harald Alvestrandc24a2182022-02-23 13:44:5942#include "rtc_base/rtc_certificate.h"
43#include "rtc_base/ssl_identity.h"
44#include "rtc_base/ssl_stream_adapter.h"
45#include "rtc_base/string_encode.h"
Jonas Olsson366a50c2018-09-06 11:41:3046#include "rtc_base/strings/string_builder.h"
Amit Hilbuchbcd39d42019-01-26 01:13:5647#include "rtc_base/unique_id_generator.h"
Steve Antone38a5a12018-11-22 00:05:1548#include "test/gmock.h"
Harald Alvestrandc24a2182022-02-23 13:44:5949#include "test/gtest.h"
Jonas Orelanded99dae2022-03-09 08:28:1050#include "test/scoped_key_value_config.h"
henrike@webrtc.org28e20752013-07-10 00:45:3651
Emil Lundmark7d2e6162023-11-08 15:27:4552namespace cricket {
53namespace {
henrike@webrtc.org28e20752013-07-10 00:45:3654
Emil Lundmark7d2e6162023-11-08 15:27:4555using ::rtc::kCsAeadAes128Gcm;
56using ::rtc::kCsAeadAes256Gcm;
57using ::rtc::kCsAesCm128HmacSha1_32;
58using ::rtc::kCsAesCm128HmacSha1_80;
59using ::rtc::UniqueRandomIdGenerator;
60using ::testing::Bool;
61using ::testing::Combine;
Mirko Bonadei6a489f22019-04-09 13:11:1262using ::testing::Contains;
63using ::testing::Each;
Danil Chapovalov5f999a72020-02-20 15:39:0564using ::testing::ElementsAre;
Mirko Bonadei6a489f22019-04-09 13:11:1265using ::testing::ElementsAreArray;
66using ::testing::Eq;
67using ::testing::Field;
68using ::testing::IsEmpty;
69using ::testing::IsFalse;
70using ::testing::Ne;
71using ::testing::Not;
72using ::testing::Pointwise;
73using ::testing::SizeIs;
Emil Lundmark7d2e6162023-11-08 15:27:4574using ::testing::Values;
75using ::testing::ValuesIn;
76using ::webrtc::RtpExtension;
77using ::webrtc::RtpTransceiverDirection;
henrike@webrtc.org28e20752013-07-10 00:45:3678
Emil Lundmark7d2e6162023-11-08 15:27:4579using Candidates = std::vector<Candidate>;
80
81AudioCodec CreateRedAudioCodec(absl::string_view encoding_id) {
82 AudioCodec red = CreateAudioCodec(63, "red", 48000, 2);
83 red.SetParam(kCodecParamNotInNameValueFormat,
Tomas Lundqvista26d6ed2023-10-27 12:25:5784 std::string(encoding_id) + '/' + std::string(encoding_id));
85 return red;
86}
87
Emil Lundmark7d2e6162023-11-08 15:27:4588const AudioCodec kAudioCodecs1[] = {CreateAudioCodec(111, "opus", 48000, 2),
89 CreateRedAudioCodec("111"),
90 CreateAudioCodec(102, "iLBC", 8000, 1),
91 CreateAudioCodec(0, "PCMU", 8000, 1),
92 CreateAudioCodec(8, "PCMA", 8000, 1),
93 CreateAudioCodec(117, "red", 8000, 1),
94 CreateAudioCodec(107, "CN", 48000, 1)};
henrike@webrtc.org28e20752013-07-10 00:45:3695
Emil Lundmark7d2e6162023-11-08 15:27:4596const AudioCodec kAudioCodecs2[] = {
97 CreateAudioCodec(126, "foo", 16000, 1),
98 CreateAudioCodec(0, "PCMU", 8000, 1),
99 CreateAudioCodec(127, "iLBC", 8000, 1),
henrike@webrtc.org28e20752013-07-10 00:45:36100};
101
Emil Lundmark7d2e6162023-11-08 15:27:45102const AudioCodec kAudioCodecsAnswer[] = {
103 CreateAudioCodec(102, "iLBC", 8000, 1),
104 CreateAudioCodec(0, "PCMU", 8000, 1),
henrike@webrtc.org28e20752013-07-10 00:45:36105};
106
Emil Lundmark7d2e6162023-11-08 15:27:45107const VideoCodec kVideoCodecs1[] = {CreateVideoCodec(96, "H264-SVC"),
108 CreateVideoCodec(97, "H264")};
henrike@webrtc.org28e20752013-07-10 00:45:36109
Emil Lundmark7d2e6162023-11-08 15:27:45110const VideoCodec kVideoCodecs1Reverse[] = {CreateVideoCodec(97, "H264"),
111 CreateVideoCodec(96, "H264-SVC")};
zhihuang1c378ed2017-08-17 21:10:50112
Emil Lundmark7d2e6162023-11-08 15:27:45113const VideoCodec kVideoCodecs2[] = {CreateVideoCodec(126, "H264"),
114 CreateVideoCodec(127, "H263")};
henrike@webrtc.org28e20752013-07-10 00:45:36115
Emil Lundmark7d2e6162023-11-08 15:27:45116const VideoCodec kVideoCodecsAnswer[] = {CreateVideoCodec(97, "H264")};
henrike@webrtc.org28e20752013-07-10 00:45:36117
Emil Lundmark7d2e6162023-11-08 15:27:45118const RtpExtension kAudioRtpExtension1[] = {
isheriff6f8d6862016-05-26 18:24:55119 RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 8),
120 RtpExtension("http://google.com/testing/audio_something", 10),
henrike@webrtc.org28e20752013-07-10 00:45:36121};
122
Emil Lundmark7d2e6162023-11-08 15:27:45123const RtpExtension kAudioRtpExtensionEncrypted1[] = {
jbauch5869f502017-06-29 19:31:36124 RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 8),
125 RtpExtension("http://google.com/testing/audio_something", 10),
126 RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 12, true),
Lennart Grahl0d0ed762021-05-17 14:06:37127 RtpExtension("http://google.com/testing/audio_something", 11, true),
jbauch5869f502017-06-29 19:31:36128};
129
Emil Lundmark7d2e6162023-11-08 15:27:45130const RtpExtension kAudioRtpExtension2[] = {
isheriff6f8d6862016-05-26 18:24:55131 RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 2),
132 RtpExtension("http://google.com/testing/audio_something_else", 8),
133 RtpExtension("http://google.com/testing/both_audio_and_video", 7),
henrike@webrtc.org28e20752013-07-10 00:45:36134};
135
Emil Lundmark7d2e6162023-11-08 15:27:45136const RtpExtension kAudioRtpExtension3[] = {
isheriff6f8d6862016-05-26 18:24:55137 RtpExtension("http://google.com/testing/audio_something", 2),
138 RtpExtension("http://google.com/testing/both_audio_and_video", 3),
deadbeefa5b273a2015-08-21 00:30:13139};
140
Emil Lundmark7d2e6162023-11-08 15:27:45141const RtpExtension kAudioRtpExtension3ForEncryption[] = {
jbauch5869f502017-06-29 19:31:36142 RtpExtension("http://google.com/testing/audio_something", 2),
143 // Use RTP extension that supports encryption.
144 RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 3),
145};
146
Emil Lundmark7d2e6162023-11-08 15:27:45147const RtpExtension kAudioRtpExtension3ForEncryptionOffer[] = {
jbauch5869f502017-06-29 19:31:36148 RtpExtension("http://google.com/testing/audio_something", 2),
149 RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 3),
Lennart Grahl0d0ed762021-05-17 14:06:37150 RtpExtension("http://google.com/testing/audio_something", 14, true),
151 RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 13, true),
152};
153
Emil Lundmark7d2e6162023-11-08 15:27:45154const RtpExtension kVideoRtpExtension3ForEncryptionOffer[] = {
Lennart Grahl0d0ed762021-05-17 14:06:37155 RtpExtension("http://google.com/testing/video_something", 4),
156 RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 3),
157 RtpExtension("http://google.com/testing/video_something", 12, true),
158 RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 13, true),
jbauch5869f502017-06-29 19:31:36159};
160
Emil Lundmark7d2e6162023-11-08 15:27:45161const RtpExtension kAudioRtpExtensionAnswer[] = {
isheriff6f8d6862016-05-26 18:24:55162 RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 8),
henrike@webrtc.org28e20752013-07-10 00:45:36163};
164
Emil Lundmark7d2e6162023-11-08 15:27:45165const RtpExtension kAudioRtpExtensionEncryptedAnswer[] = {
jbauch5869f502017-06-29 19:31:36166 RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 12, true),
167};
168
Emil Lundmark7d2e6162023-11-08 15:27:45169const RtpExtension kVideoRtpExtension1[] = {
isheriff6f8d6862016-05-26 18:24:55170 RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 14),
171 RtpExtension("http://google.com/testing/video_something", 13),
henrike@webrtc.org28e20752013-07-10 00:45:36172};
173
Emil Lundmark7d2e6162023-11-08 15:27:45174const RtpExtension kVideoRtpExtensionEncrypted1[] = {
jbauch5869f502017-06-29 19:31:36175 RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 14),
176 RtpExtension("http://google.com/testing/video_something", 13),
Lennart Grahl0d0ed762021-05-17 14:06:37177 RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 9, true),
178 RtpExtension("http://google.com/testing/video_something", 7, true),
jbauch5869f502017-06-29 19:31:36179};
180
Emil Lundmark7d2e6162023-11-08 15:27:45181const RtpExtension kVideoRtpExtension2[] = {
isheriff6f8d6862016-05-26 18:24:55182 RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 2),
183 RtpExtension("http://google.com/testing/video_something_else", 14),
184 RtpExtension("http://google.com/testing/both_audio_and_video", 7),
henrike@webrtc.org28e20752013-07-10 00:45:36185};
186
Emil Lundmark7d2e6162023-11-08 15:27:45187const RtpExtension kVideoRtpExtension3[] = {
isheriff6f8d6862016-05-26 18:24:55188 RtpExtension("http://google.com/testing/video_something", 4),
189 RtpExtension("http://google.com/testing/both_audio_and_video", 5),
deadbeefa5b273a2015-08-21 00:30:13190};
191
Emil Lundmark7d2e6162023-11-08 15:27:45192const RtpExtension kVideoRtpExtension3ForEncryption[] = {
jbauch5869f502017-06-29 19:31:36193 RtpExtension("http://google.com/testing/video_something", 4),
194 // Use RTP extension that supports encryption.
195 RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 5),
196};
197
Emil Lundmark7d2e6162023-11-08 15:27:45198const RtpExtension kVideoRtpExtensionAnswer[] = {
isheriff6f8d6862016-05-26 18:24:55199 RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 14),
henrike@webrtc.org28e20752013-07-10 00:45:36200};
201
Emil Lundmark7d2e6162023-11-08 15:27:45202const RtpExtension kVideoRtpExtensionEncryptedAnswer[] = {
Lennart Grahl0d0ed762021-05-17 14:06:37203 RtpExtension("urn:ietf:params:rtp-hdrext:toffset", 9, true),
jbauch5869f502017-06-29 19:31:36204};
205
Emil Lundmark7d2e6162023-11-08 15:27:45206const RtpExtension kRtpExtensionTransportSequenceNumber01[] = {
Johannes Kronce8e8672019-02-22 12:06:44207 RtpExtension("http://www.ietf.org/id/"
208 "draft-holmer-rmcat-transport-wide-cc-extensions-01",
209 1),
210};
211
Emil Lundmark7d2e6162023-11-08 15:27:45212const RtpExtension kRtpExtensionTransportSequenceNumber01And02[] = {
Johannes Kronce8e8672019-02-22 12:06:44213 RtpExtension("http://www.ietf.org/id/"
214 "draft-holmer-rmcat-transport-wide-cc-extensions-01",
215 1),
Johannes Kron8cc711a2019-03-07 21:36:35216 RtpExtension(
217 "http://www.webrtc.org/experiments/rtp-hdrext/transport-wide-cc-02",
218 2),
Johannes Kronce8e8672019-02-22 12:06:44219};
220
Emil Lundmark7d2e6162023-11-08 15:27:45221const RtpExtension kRtpExtensionTransportSequenceNumber02[] = {
Johannes Kron8cc711a2019-03-07 21:36:35222 RtpExtension(
223 "http://www.webrtc.org/experiments/rtp-hdrext/transport-wide-cc-02",
224 2),
Johannes Kronce8e8672019-02-22 12:06:44225};
226
Emil Lundmark7d2e6162023-11-08 15:27:45227const RtpExtension kRtpExtensionGenericFrameDescriptorUri00[] = {
Markus Handellc1cbf6b2020-02-17 19:03:57228 RtpExtension("http://www.webrtc.org/experiments/rtp-hdrext/"
229 "generic-frame-descriptor-00",
230 3),
231};
232
Emil Lundmark7d2e6162023-11-08 15:27:45233const uint32_t kSimulcastParamsSsrc[] = {10, 11, 20, 21, 30, 31};
234const uint32_t kSimSsrc[] = {10, 20, 30};
235const uint32_t kFec1Ssrc[] = {10, 11};
236const uint32_t kFec2Ssrc[] = {20, 21};
237const uint32_t kFec3Ssrc[] = {30, 31};
henrike@webrtc.org28e20752013-07-10 00:45:36238
Emil Lundmark7d2e6162023-11-08 15:27:45239const char kMediaStream1[] = "stream_1";
240const char kMediaStream2[] = "stream_2";
241const char kVideoTrack1[] = "video_1";
242const char kVideoTrack2[] = "video_2";
243const char kAudioTrack1[] = "audio_1";
244const char kAudioTrack2[] = "audio_2";
245const char kAudioTrack3[] = "audio_3";
henrike@webrtc.org28e20752013-07-10 00:45:36246
Emil Lundmark7d2e6162023-11-08 15:27:45247const char* kMediaProtocols[] = {"RTP/AVP", "RTP/SAVP", "RTP/AVPF",
248 "RTP/SAVPF"};
249const char* kMediaProtocolsDtls[] = {"TCP/TLS/RTP/SAVPF", "TCP/TLS/RTP/SAVP",
250 "UDP/TLS/RTP/SAVPF", "UDP/TLS/RTP/SAVP"};
zhihuangcf5b37c2016-05-05 18:44:35251
Amit Hilbuchc63ddb22019-01-02 18:13:58252// These constants are used to make the code using "AddMediaDescriptionOptions"
253// more readable.
Emil Lundmark7d2e6162023-11-08 15:27:45254constexpr bool kStopped = true;
255constexpr bool kActive = false;
zhihuang1c378ed2017-08-17 21:10:50256
Emil Lundmark7d2e6162023-11-08 15:27:45257bool IsMediaContentOfType(const ContentInfo* content, MediaType media_type) {
Steve Antonb1c1de12017-12-21 23:14:30258 RTC_DCHECK(content);
259 return content->media_description()->type() == media_type;
jiayl@webrtc.orge7d47a12014-08-05 19:19:05260}
261
Emil Lundmark7d2e6162023-11-08 15:27:45262RtpTransceiverDirection GetMediaDirection(const ContentInfo* content) {
Steve Antonb1c1de12017-12-21 23:14:30263 RTC_DCHECK(content);
264 return content->media_description()->direction();
jiayl@webrtc.org742922b2014-10-07 21:32:43265}
266
Emil Lundmark7d2e6162023-11-08 15:27:45267void AddRtxCodec(const VideoCodec& rtx_codec, std::vector<VideoCodec>* codecs) {
268 ASSERT_FALSE(FindCodecById(*codecs, rtx_codec.id));
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34269 codecs->push_back(rtx_codec);
270}
271
Emil Lundmark7d2e6162023-11-08 15:27:45272std::vector<std::string> GetCodecNames(const std::vector<Codec>& codecs) {
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34273 std::vector<std::string> codec_names;
Mirko Bonadei649a4c22019-01-29 09:11:53274 codec_names.reserve(codecs.size());
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34275 for (const auto& codec : codecs) {
276 codec_names.push_back(codec.name);
277 }
278 return codec_names;
279}
280
zhihuang1c378ed2017-08-17 21:10:50281// This is used for test only. MIDs are not the identification of the
282// MediaDescriptionOptions since some end points may not support MID and the SDP
283// may not contain 'mid'.
284std::vector<MediaDescriptionOptions>::iterator FindFirstMediaDescriptionByMid(
285 const std::string& mid,
286 MediaSessionOptions* opts) {
Steve Anton64b626b2019-01-29 01:25:26287 return absl::c_find_if(
288 opts->media_description_options,
Steve Anton36b29d12017-10-30 16:57:42289 [&mid](const MediaDescriptionOptions& t) { return t.mid == mid; });
290}
291
292std::vector<MediaDescriptionOptions>::const_iterator
293FindFirstMediaDescriptionByMid(const std::string& mid,
294 const MediaSessionOptions& opts) {
Steve Anton64b626b2019-01-29 01:25:26295 return absl::c_find_if(
296 opts.media_description_options,
Steve Anton36b29d12017-10-30 16:57:42297 [&mid](const MediaDescriptionOptions& t) { return t.mid == mid; });
zhihuang1c378ed2017-08-17 21:10:50298}
299
Artem Titov880fa812021-07-30 20:30:23300// Add a media section to the `session_options`.
Emil Lundmark7d2e6162023-11-08 15:27:45301void AddMediaDescriptionOptions(MediaType type,
302 const std::string& mid,
303 RtpTransceiverDirection direction,
304 bool stopped,
305 MediaSessionOptions* opts) {
Steve Anton4e70a722017-11-28 22:57:10306 opts->media_description_options.push_back(
307 MediaDescriptionOptions(type, mid, direction, stopped));
zhihuang1c378ed2017-08-17 21:10:50308}
309
Emil Lundmark7d2e6162023-11-08 15:27:45310void AddAudioVideoSections(RtpTransceiverDirection direction,
311 MediaSessionOptions* opts) {
Amit Hilbuchc63ddb22019-01-02 18:13:58312 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", direction, kActive,
313 opts);
314 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", direction, kActive,
315 opts);
zhihuang1c378ed2017-08-17 21:10:50316}
317
Emil Lundmark7d2e6162023-11-08 15:27:45318void AddDataSection(RtpTransceiverDirection direction,
319 MediaSessionOptions* opts) {
Amit Hilbuchc63ddb22019-01-02 18:13:58320 AddMediaDescriptionOptions(MEDIA_TYPE_DATA, "data", direction, kActive, opts);
zhihuang1c378ed2017-08-17 21:10:50321}
322
Emil Lundmark7d2e6162023-11-08 15:27:45323void AttachSenderToMediaDescriptionOptions(
Steve Anton8ffb9c32017-08-31 22:45:38324 const std::string& mid,
325 MediaType type,
326 const std::string& track_id,
327 const std::vector<std::string>& stream_ids,
Amit Hilbuchc63ddb22019-01-02 18:13:58328 const std::vector<RidDescription>& rids,
329 const SimulcastLayerList& simulcast_layers,
Steve Anton8ffb9c32017-08-31 22:45:38330 int num_sim_layer,
331 MediaSessionOptions* session_options) {
zhihuang1c378ed2017-08-17 21:10:50332 auto it = FindFirstMediaDescriptionByMid(mid, session_options);
333 switch (type) {
334 case MEDIA_TYPE_AUDIO:
Steve Anton8ffb9c32017-08-31 22:45:38335 it->AddAudioSender(track_id, stream_ids);
zhihuang1c378ed2017-08-17 21:10:50336 break;
337 case MEDIA_TYPE_VIDEO:
Amit Hilbuchc63ddb22019-01-02 18:13:58338 it->AddVideoSender(track_id, stream_ids, rids, simulcast_layers,
339 num_sim_layer);
zhihuang1c378ed2017-08-17 21:10:50340 break;
zhihuang1c378ed2017-08-17 21:10:50341 default:
Artem Titovd3251962021-11-15 15:57:07342 RTC_DCHECK_NOTREACHED();
zhihuang1c378ed2017-08-17 21:10:50343 }
344}
345
Emil Lundmark7d2e6162023-11-08 15:27:45346void AttachSenderToMediaDescriptionOptions(
Amit Hilbuchc63ddb22019-01-02 18:13:58347 const std::string& mid,
348 MediaType type,
349 const std::string& track_id,
350 const std::vector<std::string>& stream_ids,
351 int num_sim_layer,
352 MediaSessionOptions* session_options) {
353 AttachSenderToMediaDescriptionOptions(mid, type, track_id, stream_ids, {},
354 SimulcastLayerList(), num_sim_layer,
355 session_options);
356}
357
Emil Lundmark7d2e6162023-11-08 15:27:45358void DetachSenderFromMediaSection(const std::string& mid,
359 const std::string& track_id,
360 MediaSessionOptions* session_options) {
361 std::vector<SenderOptions>& sender_options_list =
Steve Anton3a66edf2018-09-10 19:57:37362 FindFirstMediaDescriptionByMid(mid, session_options)->sender_options;
Emil Lundmark7d2e6162023-11-08 15:27:45363 auto sender_it = absl::c_find_if(
364 sender_options_list, [track_id](const SenderOptions& sender_options) {
365 return sender_options.track_id == track_id;
366 });
Steve Anton3a66edf2018-09-10 19:57:37367 RTC_DCHECK(sender_it != sender_options_list.end());
368 sender_options_list.erase(sender_it);
zhihuang1c378ed2017-08-17 21:10:50369}
370
Philipp Hancke7fbcc8c2023-11-02 09:53:21371// Helper function used to create recv-only audio MediaSessionOptions.
Emil Lundmark7d2e6162023-11-08 15:27:45372MediaSessionOptions CreateAudioMediaSession() {
zhihuang1c378ed2017-08-17 21:10:50373 MediaSessionOptions session_options;
Amit Hilbuchc63ddb22019-01-02 18:13:58374 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
375 RtpTransceiverDirection::kRecvOnly, kActive,
376 &session_options);
zhihuang1c378ed2017-08-17 21:10:50377 return session_options;
378}
379
380// TODO(zhihuang): Most of these tests were written while MediaSessionOptions
381// was designed for Plan B SDP, where only one audio "m=" section and one video
382// "m=" section could be generated, and ordering couldn't be controlled. Many of
383// these tests may be obsolete as a result, and should be refactored or removed.
Emil Lundmark7d2e6162023-11-08 15:27:45384class MediaSessionDescriptionFactoryTest : public testing::Test {
henrike@webrtc.org28e20752013-07-10 00:45:36385 public:
Amit Hilbuchbcd39d42019-01-26 01:13:56386 MediaSessionDescriptionFactoryTest()
Jonas Orelanded99dae2022-03-09 08:28:10387 : tdf1_(field_trials),
388 tdf2_(field_trials),
Philipp Hancke332c56f2023-09-27 08:06:37389 f1_(nullptr, false, &ssrc_generator1, &tdf1_),
390 f2_(nullptr, false, &ssrc_generator2, &tdf2_) {
ossu075af922016-06-14 10:29:38391 f1_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs1),
392 MAKE_VECTOR(kAudioCodecs1));
Johannes Kron3e983682020-03-29 20:17:00393 f1_.set_video_codecs(MAKE_VECTOR(kVideoCodecs1),
394 MAKE_VECTOR(kVideoCodecs1));
ossu075af922016-06-14 10:29:38395 f2_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs2),
396 MAKE_VECTOR(kAudioCodecs2));
Johannes Kron3e983682020-03-29 20:17:00397 f2_.set_video_codecs(MAKE_VECTOR(kVideoCodecs2),
398 MAKE_VECTOR(kVideoCodecs2));
Harald Alvestrand0d018412021-11-04 13:52:31399 tdf1_.set_certificate(rtc::RTCCertificate::Create(
400 std::unique_ptr<rtc::SSLIdentity>(new rtc::FakeSSLIdentity("id1"))));
401 tdf2_.set_certificate(rtc::RTCCertificate::Create(
402 std::unique_ptr<rtc::SSLIdentity>(new rtc::FakeSSLIdentity("id2"))));
henrike@webrtc.org28e20752013-07-10 00:45:36403 }
404
henrike@webrtc.org704bf9e2014-02-27 17:52:04405 // Create a video StreamParamsVec object with:
406 // - one video stream with 3 simulcast streams and FEC,
407 StreamParamsVec CreateComplexVideoStreamParamsVec() {
408 SsrcGroup sim_group("SIM", MAKE_VECTOR(kSimSsrc));
409 SsrcGroup fec_group1("FEC", MAKE_VECTOR(kFec1Ssrc));
410 SsrcGroup fec_group2("FEC", MAKE_VECTOR(kFec2Ssrc));
411 SsrcGroup fec_group3("FEC", MAKE_VECTOR(kFec3Ssrc));
412
413 std::vector<SsrcGroup> ssrc_groups;
414 ssrc_groups.push_back(sim_group);
415 ssrc_groups.push_back(fec_group1);
416 ssrc_groups.push_back(fec_group2);
417 ssrc_groups.push_back(fec_group3);
418
419 StreamParams simulcast_params;
420 simulcast_params.id = kVideoTrack1;
421 simulcast_params.ssrcs = MAKE_VECTOR(kSimulcastParamsSsrc);
422 simulcast_params.ssrc_groups = ssrc_groups;
423 simulcast_params.cname = "Video_SIM_FEC";
Seth Hampson845e8782018-03-02 19:34:10424 simulcast_params.set_stream_ids({kMediaStream1});
henrike@webrtc.org704bf9e2014-02-27 17:52:04425
426 StreamParamsVec video_streams;
427 video_streams.push_back(simulcast_params);
428
429 return video_streams;
430 }
henrike@webrtc.org28e20752013-07-10 00:45:36431
Honghai Zhang4cedf2b2016-08-31 15:18:11432 // Returns true if the transport info contains "renomination" as an
433 // ICE option.
434 bool GetIceRenomination(const TransportInfo* transport_info) {
Steve Anton64b626b2019-01-29 01:25:26435 return absl::c_linear_search(transport_info->description.transport_options,
436 "renomination");
Honghai Zhang4cedf2b2016-08-31 15:18:11437 }
438
zhihuang1c378ed2017-08-17 21:10:50439 void TestTransportInfo(bool offer,
Steve Anton36b29d12017-10-30 16:57:42440 const MediaSessionOptions& options,
henrike@webrtc.org28e20752013-07-10 00:45:36441 bool has_current_desc) {
442 const std::string current_audio_ufrag = "current_audio_ufrag";
443 const std::string current_audio_pwd = "current_audio_pwd";
444 const std::string current_video_ufrag = "current_video_ufrag";
445 const std::string current_video_pwd = "current_video_pwd";
446 const std::string current_data_ufrag = "current_data_ufrag";
447 const std::string current_data_pwd = "current_data_pwd";
kwiberg31022942016-03-11 22:18:21448 std::unique_ptr<SessionDescription> current_desc;
449 std::unique_ptr<SessionDescription> desc;
henrike@webrtc.org28e20752013-07-10 00:45:36450 if (has_current_desc) {
Mirko Bonadei317a1f02019-09-17 15:06:18451 current_desc = std::make_unique<SessionDescription>();
Steve Anton06817cd2018-12-18 23:55:30452 current_desc->AddTransportInfo(TransportInfo(
Yves Gerey665174f2018-06-19 13:03:05453 "audio",
Steve Anton06817cd2018-12-18 23:55:30454 TransportDescription(current_audio_ufrag, current_audio_pwd)));
455 current_desc->AddTransportInfo(TransportInfo(
Yves Gerey665174f2018-06-19 13:03:05456 "video",
Steve Anton06817cd2018-12-18 23:55:30457 TransportDescription(current_video_ufrag, current_video_pwd)));
458 current_desc->AddTransportInfo(TransportInfo(
459 "data", TransportDescription(current_data_ufrag, current_data_pwd)));
henrike@webrtc.org28e20752013-07-10 00:45:36460 }
461 if (offer) {
Philipp Hancke2bf1b992023-09-26 07:04:46462 desc = f1_.CreateOfferOrError(options, current_desc.get()).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:36463 } else {
kwiberg31022942016-03-11 22:18:21464 std::unique_ptr<SessionDescription> offer;
Philipp Hancke2bf1b992023-09-26 07:04:46465 offer = f1_.CreateOfferOrError(options, nullptr).MoveValue();
466 desc = f1_.CreateAnswerOrError(offer.get(), options, current_desc.get())
467 .MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:36468 }
Philipp Hancke2bf1b992023-09-26 07:04:46469 ASSERT_TRUE(desc);
henrike@webrtc.org28e20752013-07-10 00:45:36470 const TransportInfo* ti_audio = desc->GetTransportInfoByName("audio");
jiayl@webrtc.org742922b2014-10-07 21:32:43471 if (options.has_audio()) {
henrike@webrtc.org28e20752013-07-10 00:45:36472 if (has_current_desc) {
473 EXPECT_EQ(current_audio_ufrag, ti_audio->description.ice_ufrag);
474 EXPECT_EQ(current_audio_pwd, ti_audio->description.ice_pwd);
475 } else {
Emil Lundmark7d2e6162023-11-08 15:27:45476 EXPECT_EQ(static_cast<size_t>(ICE_UFRAG_LENGTH),
henrike@webrtc.org28e20752013-07-10 00:45:36477 ti_audio->description.ice_ufrag.size());
Emil Lundmark7d2e6162023-11-08 15:27:45478 EXPECT_EQ(static_cast<size_t>(ICE_PWD_LENGTH),
henrike@webrtc.org28e20752013-07-10 00:45:36479 ti_audio->description.ice_pwd.size());
480 }
zhihuang1c378ed2017-08-17 21:10:50481 auto media_desc_options_it =
Steve Anton36b29d12017-10-30 16:57:42482 FindFirstMediaDescriptionByMid("audio", options);
zhihuang1c378ed2017-08-17 21:10:50483 EXPECT_EQ(
484 media_desc_options_it->transport_options.enable_ice_renomination,
485 GetIceRenomination(ti_audio));
henrike@webrtc.org28e20752013-07-10 00:45:36486 }
487 const TransportInfo* ti_video = desc->GetTransportInfoByName("video");
jiayl@webrtc.org742922b2014-10-07 21:32:43488 if (options.has_video()) {
Bjorn A Mellemc85ebbe2019-06-07 17:28:06489 auto media_desc_options_it =
490 FindFirstMediaDescriptionByMid("video", options);
henrike@webrtc.org28e20752013-07-10 00:45:36491 if (options.bundle_enabled) {
492 EXPECT_EQ(ti_audio->description.ice_ufrag,
493 ti_video->description.ice_ufrag);
Yves Gerey665174f2018-06-19 13:03:05494 EXPECT_EQ(ti_audio->description.ice_pwd, ti_video->description.ice_pwd);
henrike@webrtc.org28e20752013-07-10 00:45:36495 } else {
496 if (has_current_desc) {
497 EXPECT_EQ(current_video_ufrag, ti_video->description.ice_ufrag);
498 EXPECT_EQ(current_video_pwd, ti_video->description.ice_pwd);
499 } else {
Emil Lundmark7d2e6162023-11-08 15:27:45500 EXPECT_EQ(static_cast<size_t>(ICE_UFRAG_LENGTH),
henrike@webrtc.org28e20752013-07-10 00:45:36501 ti_video->description.ice_ufrag.size());
Emil Lundmark7d2e6162023-11-08 15:27:45502 EXPECT_EQ(static_cast<size_t>(ICE_PWD_LENGTH),
henrike@webrtc.org28e20752013-07-10 00:45:36503 ti_video->description.ice_pwd.size());
504 }
505 }
zhihuang1c378ed2017-08-17 21:10:50506 EXPECT_EQ(
507 media_desc_options_it->transport_options.enable_ice_renomination,
508 GetIceRenomination(ti_video));
henrike@webrtc.org28e20752013-07-10 00:45:36509 }
510 const TransportInfo* ti_data = desc->GetTransportInfoByName("data");
511 if (options.has_data()) {
henrike@webrtc.org28e20752013-07-10 00:45:36512 if (options.bundle_enabled) {
513 EXPECT_EQ(ti_audio->description.ice_ufrag,
514 ti_data->description.ice_ufrag);
Yves Gerey665174f2018-06-19 13:03:05515 EXPECT_EQ(ti_audio->description.ice_pwd, ti_data->description.ice_pwd);
henrike@webrtc.org28e20752013-07-10 00:45:36516 } else {
517 if (has_current_desc) {
518 EXPECT_EQ(current_data_ufrag, ti_data->description.ice_ufrag);
519 EXPECT_EQ(current_data_pwd, ti_data->description.ice_pwd);
520 } else {
Emil Lundmark7d2e6162023-11-08 15:27:45521 EXPECT_EQ(static_cast<size_t>(ICE_UFRAG_LENGTH),
henrike@webrtc.org28e20752013-07-10 00:45:36522 ti_data->description.ice_ufrag.size());
Emil Lundmark7d2e6162023-11-08 15:27:45523 EXPECT_EQ(static_cast<size_t>(ICE_PWD_LENGTH),
henrike@webrtc.org28e20752013-07-10 00:45:36524 ti_data->description.ice_pwd.size());
525 }
526 }
zhihuang1c378ed2017-08-17 21:10:50527 auto media_desc_options_it =
Steve Anton36b29d12017-10-30 16:57:42528 FindFirstMediaDescriptionByMid("data", options);
zhihuang1c378ed2017-08-17 21:10:50529 EXPECT_EQ(
530 media_desc_options_it->transport_options.enable_ice_renomination,
531 GetIceRenomination(ti_data));
henrike@webrtc.org28e20752013-07-10 00:45:36532 }
533 }
534
henrike@webrtc.org28e20752013-07-10 00:45:36535 // This test that the audio and video media direction is set to
Artem Titov880fa812021-07-30 20:30:23536 // `expected_direction_in_answer` in an answer if the offer direction is set
537 // to `direction_in_offer` and the answer is willing to both send and receive.
henrike@webrtc.org28e20752013-07-10 00:45:36538 void TestMediaDirectionInAnswer(
Steve Anton4e70a722017-11-28 22:57:10539 RtpTransceiverDirection direction_in_offer,
540 RtpTransceiverDirection expected_direction_in_answer) {
zhihuang1c378ed2017-08-17 21:10:50541 MediaSessionOptions offer_opts;
542 AddAudioVideoSections(direction_in_offer, &offer_opts);
543
Steve Anton6fe1fba2018-12-11 18:15:23544 std::unique_ptr<SessionDescription> offer =
Philipp Hancke2bf1b992023-09-26 07:04:46545 f1_.CreateOfferOrError(offer_opts, nullptr).MoveValue();
546 ASSERT_TRUE(offer.get());
terelius8c011e52016-04-26 12:28:11547 ContentInfo* ac_offer = offer->GetContentByName("audio");
Philipp Hancke2bf1b992023-09-26 07:04:46548 ASSERT_TRUE(ac_offer);
terelius8c011e52016-04-26 12:28:11549 ContentInfo* vc_offer = offer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:46550 ASSERT_TRUE(vc_offer);
henrike@webrtc.org28e20752013-07-10 00:45:36551
zhihuang1c378ed2017-08-17 21:10:50552 MediaSessionOptions answer_opts;
Steve Anton4e70a722017-11-28 22:57:10553 AddAudioVideoSections(RtpTransceiverDirection::kSendRecv, &answer_opts);
Steve Anton6fe1fba2018-12-11 18:15:23554 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:46555 f2_.CreateAnswerOrError(offer.get(), answer_opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:36556 const AudioContentDescription* acd_answer =
557 GetFirstAudioContentDescription(answer.get());
558 EXPECT_EQ(expected_direction_in_answer, acd_answer->direction());
559 const VideoContentDescription* vcd_answer =
560 GetFirstVideoContentDescription(answer.get());
561 EXPECT_EQ(expected_direction_in_answer, vcd_answer->direction());
562 }
563
Emil Lundmark7d2e6162023-11-08 15:27:45564 bool VerifyNoCNCodecs(const ContentInfo* content) {
Steve Antonb1c1de12017-12-21 23:14:30565 RTC_DCHECK(content);
566 RTC_CHECK(content->media_description());
Emil Lundmark7d2e6162023-11-08 15:27:45567 for (const Codec& codec : content->media_description()->codecs()) {
Steve Antonb1c1de12017-12-21 23:14:30568 if (codec.name == "CN") {
henrike@webrtc.org28e20752013-07-10 00:45:36569 return false;
Steve Antonb1c1de12017-12-21 23:14:30570 }
henrike@webrtc.org28e20752013-07-10 00:45:36571 }
572 return true;
573 }
574
Johannes Kronce8e8672019-02-22 12:06:44575 void TestTransportSequenceNumberNegotiation(
Emil Lundmark7d2e6162023-11-08 15:27:45576 const RtpHeaderExtensions& local,
577 const RtpHeaderExtensions& offered,
578 const RtpHeaderExtensions& expectedAnswer) {
Johannes Kronce8e8672019-02-22 12:06:44579 MediaSessionOptions opts;
580 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
Markus Handell755c65d2020-06-23 23:06:10581 SetAudioVideoRtpHeaderExtensions(offered, offered, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:46582 std::unique_ptr<SessionDescription> offer =
583 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
584 ASSERT_TRUE(offer.get());
Markus Handell755c65d2020-06-23 23:06:10585 SetAudioVideoRtpHeaderExtensions(local, local, &opts);
Johannes Kronce8e8672019-02-22 12:06:44586 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:46587 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Johannes Kronce8e8672019-02-22 12:06:44588
589 EXPECT_EQ(
590 expectedAnswer,
591 GetFirstAudioContentDescription(answer.get())->rtp_header_extensions());
592 EXPECT_EQ(
593 expectedAnswer,
594 GetFirstVideoContentDescription(answer.get())->rtp_header_extensions());
595 }
596
Markus Handell755c65d2020-06-23 23:06:10597 std::vector<webrtc::RtpHeaderExtensionCapability>
Emil Lundmark7d2e6162023-11-08 15:27:45598 HeaderExtensionCapabilitiesFromRtpExtensions(RtpHeaderExtensions extensions) {
Markus Handell755c65d2020-06-23 23:06:10599 std::vector<webrtc::RtpHeaderExtensionCapability> capabilities;
600 for (const auto& extension : extensions) {
601 webrtc::RtpHeaderExtensionCapability capability(
602 extension.uri, extension.id,
603 webrtc::RtpTransceiverDirection::kSendRecv);
604 capabilities.push_back(capability);
605 }
606 return capabilities;
607 }
608
Emil Lundmark7d2e6162023-11-08 15:27:45609 void SetAudioVideoRtpHeaderExtensions(RtpHeaderExtensions audio_exts,
610 RtpHeaderExtensions video_exts,
Markus Handell755c65d2020-06-23 23:06:10611 MediaSessionOptions* opts) {
612 auto audio_caps = HeaderExtensionCapabilitiesFromRtpExtensions(audio_exts);
613 auto video_caps = HeaderExtensionCapabilitiesFromRtpExtensions(video_exts);
614 for (auto& entry : opts->media_description_options) {
615 switch (entry.type) {
616 case MEDIA_TYPE_AUDIO:
617 entry.header_extensions = audio_caps;
618 break;
619 case MEDIA_TYPE_VIDEO:
620 entry.header_extensions = video_caps;
621 break;
622 default:
623 break;
624 }
625 }
626 }
627
henrike@webrtc.org28e20752013-07-10 00:45:36628 protected:
Jonas Orelanded99dae2022-03-09 08:28:10629 webrtc::test::ScopedKeyValueConfig field_trials;
Amit Hilbuchbcd39d42019-01-26 01:13:56630 UniqueRandomIdGenerator ssrc_generator1;
631 UniqueRandomIdGenerator ssrc_generator2;
henrike@webrtc.org28e20752013-07-10 00:45:36632 TransportDescriptionFactory tdf1_;
633 TransportDescriptionFactory tdf2_;
Jonas Orelanded99dae2022-03-09 08:28:10634 MediaSessionDescriptionFactory f1_;
635 MediaSessionDescriptionFactory f2_;
henrike@webrtc.org28e20752013-07-10 00:45:36636};
637
638// Create a typical audio offer, and ensure it matches what we expect.
639TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioOffer) {
Steve Anton6fe1fba2018-12-11 18:15:23640 std::unique_ptr<SessionDescription> offer =
Philipp Hancke7fbcc8c2023-11-02 09:53:21641 f1_.CreateOfferOrError(CreateAudioMediaSession(), nullptr).MoveValue();
Philipp Hancke2bf1b992023-09-26 07:04:46642 ASSERT_TRUE(offer.get());
henrike@webrtc.org28e20752013-07-10 00:45:36643 const ContentInfo* ac = offer->GetContentByName("audio");
644 const ContentInfo* vc = offer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:46645 ASSERT_TRUE(ac);
646 EXPECT_FALSE(vc);
Steve Anton5adfafd2017-12-21 00:34:00647 EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
Philipp Hancked0f0f382023-11-23 19:21:05648 const MediaContentDescription* acd = ac->media_description();
henrike@webrtc.org28e20752013-07-10 00:45:36649 EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
ossudedfd282016-06-14 14:12:39650 EXPECT_EQ(f1_.audio_sendrecv_codecs(), acd->codecs());
zhihuang1c378ed2017-08-17 21:10:50651 EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached.
henrike@webrtc.org28e20752013-07-10 00:45:36652 EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto)
653 EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on
Harald Alvestrand974044e2024-02-08 13:15:51654 EXPECT_EQ(kMediaProtocolDtlsSavpf, acd->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:36655}
656
Tomas Lundqvista26d6ed2023-10-27 12:25:57657// Create an offer with just Opus and RED.
658TEST_F(MediaSessionDescriptionFactoryTest,
659 TestCreateAudioOfferWithJustOpusAndRed) {
Tomas Lundqvista26d6ed2023-10-27 12:25:57660 // First, prefer to only use opus and red.
661 std::vector<webrtc::RtpCodecCapability> preferences;
662 preferences.push_back(
663 webrtc::ToRtpCodecCapability(f1_.audio_sendrecv_codecs()[0]));
664 preferences.push_back(
665 webrtc::ToRtpCodecCapability(f1_.audio_sendrecv_codecs()[1]));
666 EXPECT_EQ("opus", preferences[0].name);
667 EXPECT_EQ("red", preferences[1].name);
668
Philipp Hancke7fbcc8c2023-11-02 09:53:21669 auto opts = CreateAudioMediaSession();
Tomas Lundqvista26d6ed2023-10-27 12:25:57670 opts.media_description_options.at(0).codec_preferences = preferences;
671 std::unique_ptr<SessionDescription> offer =
672 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
673 ASSERT_TRUE(offer.get());
674 const ContentInfo* ac = offer->GetContentByName("audio");
675 const ContentInfo* vc = offer->GetContentByName("video");
676 ASSERT_TRUE(ac != NULL);
677 ASSERT_TRUE(vc == NULL);
678 EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
Philipp Hancked0f0f382023-11-23 19:21:05679 const MediaContentDescription* acd = ac->media_description();
Tomas Lundqvista26d6ed2023-10-27 12:25:57680 EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
681 EXPECT_EQ(2U, acd->codecs().size());
682 EXPECT_EQ("opus", acd->codecs()[0].name);
683 EXPECT_EQ("red", acd->codecs()[1].name);
684}
685
686// Create an offer with RED before Opus, which enables RED with Opus encoding.
687TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioOfferWithRedForOpus) {
Tomas Lundqvista26d6ed2023-10-27 12:25:57688 // First, prefer to only use opus and red.
689 std::vector<webrtc::RtpCodecCapability> preferences;
690 preferences.push_back(
691 webrtc::ToRtpCodecCapability(f1_.audio_sendrecv_codecs()[1]));
692 preferences.push_back(
693 webrtc::ToRtpCodecCapability(f1_.audio_sendrecv_codecs()[0]));
694 EXPECT_EQ("red", preferences[0].name);
695 EXPECT_EQ("opus", preferences[1].name);
696
Philipp Hancke7fbcc8c2023-11-02 09:53:21697 auto opts = CreateAudioMediaSession();
Tomas Lundqvista26d6ed2023-10-27 12:25:57698 opts.media_description_options.at(0).codec_preferences = preferences;
699 std::unique_ptr<SessionDescription> offer =
700 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
701 ASSERT_TRUE(offer.get());
702 const ContentInfo* ac = offer->GetContentByName("audio");
703 const ContentInfo* vc = offer->GetContentByName("video");
704 ASSERT_TRUE(ac != NULL);
705 ASSERT_TRUE(vc == NULL);
706 EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
Philipp Hancked0f0f382023-11-23 19:21:05707 const MediaContentDescription* acd = ac->media_description();
Tomas Lundqvista26d6ed2023-10-27 12:25:57708 EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
709 EXPECT_EQ(2U, acd->codecs().size());
710 EXPECT_EQ("red", acd->codecs()[0].name);
711 EXPECT_EQ("opus", acd->codecs()[1].name);
712}
713
henrike@webrtc.org28e20752013-07-10 00:45:36714// Create a typical video offer, and ensure it matches what we expect.
715TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoOffer) {
716 MediaSessionOptions opts;
Steve Anton4e70a722017-11-28 22:57:10717 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:46718 std::unique_ptr<SessionDescription> offer =
719 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
720 ASSERT_TRUE(offer.get());
henrike@webrtc.org28e20752013-07-10 00:45:36721 const ContentInfo* ac = offer->GetContentByName("audio");
722 const ContentInfo* vc = offer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:46723 ASSERT_TRUE(ac);
724 ASSERT_TRUE(vc);
Steve Anton5adfafd2017-12-21 00:34:00725 EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
726 EXPECT_EQ(MediaProtocolType::kRtp, vc->type);
Philipp Hancked0f0f382023-11-23 19:21:05727 const MediaContentDescription* acd = ac->media_description();
728 const MediaContentDescription* vcd = vc->media_description();
henrike@webrtc.org28e20752013-07-10 00:45:36729 EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
ossudedfd282016-06-14 14:12:39730 EXPECT_EQ(f1_.audio_sendrecv_codecs(), acd->codecs());
zhihuang1c378ed2017-08-17 21:10:50731 EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
henrike@webrtc.org28e20752013-07-10 00:45:36732 EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto)
733 EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on
Harald Alvestrand974044e2024-02-08 13:15:51734 EXPECT_EQ(kMediaProtocolDtlsSavpf, acd->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:36735 EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type());
Johannes Kron3e983682020-03-29 20:17:00736 EXPECT_EQ(f1_.video_sendrecv_codecs(), vcd->codecs());
zhihuang1c378ed2017-08-17 21:10:50737 EXPECT_EQ(0U, vcd->first_ssrc()); // no sender is attached
henrike@webrtc.org28e20752013-07-10 00:45:36738 EXPECT_EQ(kAutoBandwidth, vcd->bandwidth()); // default bandwidth (auto)
739 EXPECT_TRUE(vcd->rtcp_mux()); // rtcp-mux defaults on
Harald Alvestrand974044e2024-02-08 13:15:51740 EXPECT_EQ(kMediaProtocolDtlsSavpf, vcd->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:36741}
742
743// Test creating an offer with bundle where the Codecs have the same dynamic
744// RTP playlod type. The test verifies that the offer don't contain the
745// duplicate RTP payload types.
746TEST_F(MediaSessionDescriptionFactoryTest, TestBundleOfferWithSameCodecPlType) {
Johannes Kron3e983682020-03-29 20:17:00747 const VideoCodec& offered_video_codec = f2_.video_sendrecv_codecs()[0];
ossudedfd282016-06-14 14:12:39748 const AudioCodec& offered_audio_codec = f2_.audio_sendrecv_codecs()[0];
henrike@webrtc.org28e20752013-07-10 00:45:36749 ASSERT_EQ(offered_video_codec.id, offered_audio_codec.id);
henrike@webrtc.org28e20752013-07-10 00:45:36750
751 MediaSessionOptions opts;
Steve Anton4e70a722017-11-28 22:57:10752 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
henrike@webrtc.org28e20752013-07-10 00:45:36753 opts.bundle_enabled = true;
Philipp Hancke2bf1b992023-09-26 07:04:46754 std::unique_ptr<SessionDescription> offer =
755 f2_.CreateOfferOrError(opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:36756 const VideoContentDescription* vcd =
757 GetFirstVideoContentDescription(offer.get());
758 const AudioContentDescription* acd =
759 GetFirstAudioContentDescription(offer.get());
Philipp Hancke2bf1b992023-09-26 07:04:46760 ASSERT_TRUE(vcd);
761 ASSERT_TRUE(acd);
henrike@webrtc.org28e20752013-07-10 00:45:36762 EXPECT_NE(vcd->codecs()[0].id, acd->codecs()[0].id);
henrike@webrtc.org28e20752013-07-10 00:45:36763 EXPECT_EQ(vcd->codecs()[0].name, offered_video_codec.name);
764 EXPECT_EQ(acd->codecs()[0].name, offered_audio_codec.name);
henrike@webrtc.org28e20752013-07-10 00:45:36765}
766
zhihuang1c378ed2017-08-17 21:10:50767// Test creating an updated offer with bundle, audio, video and data
henrike@webrtc.org28e20752013-07-10 00:45:36768// after an audio only session has been negotiated.
769TEST_F(MediaSessionDescriptionFactoryTest,
770 TestCreateUpdatedVideoOfferWithBundle) {
henrike@webrtc.org28e20752013-07-10 00:45:36771 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:58772 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
773 RtpTransceiverDirection::kRecvOnly, kActive,
774 &opts);
775 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
776 RtpTransceiverDirection::kInactive, kStopped,
777 &opts);
henrike@webrtc.org28e20752013-07-10 00:45:36778 opts.bundle_enabled = true;
Philipp Hancke2bf1b992023-09-26 07:04:46779 std::unique_ptr<SessionDescription> offer =
780 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Steve Anton6fe1fba2018-12-11 18:15:23781 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:46782 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:36783
784 MediaSessionOptions updated_opts;
Steve Anton4e70a722017-11-28 22:57:10785 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &updated_opts);
henrike@webrtc.org28e20752013-07-10 00:45:36786 updated_opts.bundle_enabled = true;
kwiberg31022942016-03-11 22:18:21787 std::unique_ptr<SessionDescription> updated_offer(
Philipp Hancke2bf1b992023-09-26 07:04:46788 f1_.CreateOfferOrError(updated_opts, answer.get()).MoveValue());
henrike@webrtc.org28e20752013-07-10 00:45:36789
790 const AudioContentDescription* acd =
791 GetFirstAudioContentDescription(updated_offer.get());
792 const VideoContentDescription* vcd =
793 GetFirstVideoContentDescription(updated_offer.get());
Philipp Hancke2bf1b992023-09-26 07:04:46794 EXPECT_TRUE(vcd);
795 EXPECT_TRUE(acd);
henrike@webrtc.org28e20752013-07-10 00:45:36796
Harald Alvestrand974044e2024-02-08 13:15:51797 EXPECT_EQ(kMediaProtocolDtlsSavpf, acd->protocol());
798 EXPECT_EQ(kMediaProtocolDtlsSavpf, vcd->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:36799}
800
wu@webrtc.org78187522013-10-07 23:32:02801// Create an SCTP data offer with bundle without error.
802TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSctpDataOffer) {
803 MediaSessionOptions opts;
wu@webrtc.org78187522013-10-07 23:32:02804 opts.bundle_enabled = true;
Florent Castelli516e2842021-04-19 13:29:50805 AddDataSection(RtpTransceiverDirection::kSendRecv, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:46806 std::unique_ptr<SessionDescription> offer =
807 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
808 EXPECT_TRUE(offer.get());
809 EXPECT_TRUE(offer->GetContentByName("data"));
Guido Urdanetacecf87f2019-05-31 10:17:38810 auto dcd = GetFirstSctpDataContentDescription(offer.get());
811 ASSERT_TRUE(dcd);
812 // Since this transport is insecure, the protocol should be "SCTP".
Harald Alvestrand974044e2024-02-08 13:15:51813 EXPECT_EQ(kMediaProtocolUdpDtlsSctp, dcd->protocol());
Guido Urdanetacecf87f2019-05-31 10:17:38814}
815
816// Create an SCTP data offer with bundle without error.
817TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSecureSctpDataOffer) {
818 MediaSessionOptions opts;
819 opts.bundle_enabled = true;
Florent Castelli516e2842021-04-19 13:29:50820 AddDataSection(RtpTransceiverDirection::kSendRecv, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:46821 std::unique_ptr<SessionDescription> offer =
822 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
823 EXPECT_TRUE(offer.get());
824 EXPECT_TRUE(offer->GetContentByName("data"));
Guido Urdanetacecf87f2019-05-31 10:17:38825 auto dcd = GetFirstSctpDataContentDescription(offer.get());
826 ASSERT_TRUE(dcd);
827 // The protocol should now be "UDP/DTLS/SCTP"
Emil Lundmark7d2e6162023-11-08 15:27:45828 EXPECT_EQ(kMediaProtocolUdpDtlsSctp, dcd->protocol());
wu@webrtc.org78187522013-10-07 23:32:02829}
830
tommi@webrtc.orgf15dee62014-10-27 22:15:04831// Test creating an sctp data channel from an already generated offer.
832TEST_F(MediaSessionDescriptionFactoryTest, TestCreateImplicitSctpDataOffer) {
833 MediaSessionOptions opts;
tommi@webrtc.orgf15dee62014-10-27 22:15:04834 opts.bundle_enabled = true;
Florent Castelli516e2842021-04-19 13:29:50835 AddDataSection(RtpTransceiverDirection::kSendRecv, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:46836 std::unique_ptr<SessionDescription> offer1(
837 f1_.CreateOfferOrError(opts, nullptr).MoveValue());
838 ASSERT_TRUE(offer1.get());
tommi@webrtc.orgf15dee62014-10-27 22:15:04839 const ContentInfo* data = offer1->GetContentByName("data");
Philipp Hancke2bf1b992023-09-26 07:04:46840 ASSERT_TRUE(data);
Harald Alvestrand974044e2024-02-08 13:15:51841 ASSERT_EQ(kMediaProtocolUdpDtlsSctp, data->media_description()->protocol());
tommi@webrtc.orgf15dee62014-10-27 22:15:04842
kwiberg31022942016-03-11 22:18:21843 std::unique_ptr<SessionDescription> offer2(
Philipp Hancke2bf1b992023-09-26 07:04:46844 f1_.CreateOfferOrError(opts, offer1.get()).MoveValue());
tommi@webrtc.orgf15dee62014-10-27 22:15:04845 data = offer2->GetContentByName("data");
Philipp Hancke2bf1b992023-09-26 07:04:46846 ASSERT_TRUE(data);
Harald Alvestrand974044e2024-02-08 13:15:51847 EXPECT_EQ(kMediaProtocolUdpDtlsSctp, data->media_description()->protocol());
tommi@webrtc.orgf15dee62014-10-27 22:15:04848}
849
Steve Anton2bed3972019-01-05 01:04:30850// Test that if BUNDLE is enabled and all media sections are rejected then the
851// BUNDLE group is not present in the re-offer.
852TEST_F(MediaSessionDescriptionFactoryTest, ReOfferNoBundleGroupIfAllRejected) {
853 MediaSessionOptions opts;
854 opts.bundle_enabled = true;
855 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
856 RtpTransceiverDirection::kSendRecv, kActive,
857 &opts);
Philipp Hancke2bf1b992023-09-26 07:04:46858 std::unique_ptr<SessionDescription> offer =
859 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Steve Anton2bed3972019-01-05 01:04:30860
861 opts.media_description_options[0].stopped = true;
862 std::unique_ptr<SessionDescription> reoffer =
Philipp Hancke2bf1b992023-09-26 07:04:46863 f1_.CreateOfferOrError(opts, offer.get()).MoveValue();
Steve Anton2bed3972019-01-05 01:04:30864
Emil Lundmark7d2e6162023-11-08 15:27:45865 EXPECT_FALSE(reoffer->GetGroupByName(GROUP_TYPE_BUNDLE));
Steve Anton2bed3972019-01-05 01:04:30866}
867
868// Test that if BUNDLE is enabled and the remote re-offer does not include a
869// BUNDLE group since all media sections are rejected, then the re-answer also
870// does not include a BUNDLE group.
871TEST_F(MediaSessionDescriptionFactoryTest, ReAnswerNoBundleGroupIfAllRejected) {
872 MediaSessionOptions opts;
873 opts.bundle_enabled = true;
874 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
875 RtpTransceiverDirection::kSendRecv, kActive,
876 &opts);
Philipp Hancke2bf1b992023-09-26 07:04:46877 std::unique_ptr<SessionDescription> offer =
878 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Steve Anton2bed3972019-01-05 01:04:30879 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:46880 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Steve Anton2bed3972019-01-05 01:04:30881
882 opts.media_description_options[0].stopped = true;
883 std::unique_ptr<SessionDescription> reoffer =
Philipp Hancke2bf1b992023-09-26 07:04:46884 f1_.CreateOfferOrError(opts, offer.get()).MoveValue();
Steve Anton2bed3972019-01-05 01:04:30885 std::unique_ptr<SessionDescription> reanswer =
Philipp Hancke2bf1b992023-09-26 07:04:46886 f2_.CreateAnswerOrError(reoffer.get(), opts, answer.get()).MoveValue();
Steve Anton2bed3972019-01-05 01:04:30887
Emil Lundmark7d2e6162023-11-08 15:27:45888 EXPECT_FALSE(reanswer->GetGroupByName(GROUP_TYPE_BUNDLE));
Steve Anton2bed3972019-01-05 01:04:30889}
890
891// Test that if BUNDLE is enabled and the previous offerer-tagged media section
892// was rejected then the new offerer-tagged media section is the non-rejected
893// media section.
894TEST_F(MediaSessionDescriptionFactoryTest, ReOfferChangeBundleOffererTagged) {
895 MediaSessionOptions opts;
896 opts.bundle_enabled = true;
897 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
898 RtpTransceiverDirection::kSendRecv, kActive,
899 &opts);
Philipp Hancke2bf1b992023-09-26 07:04:46900 std::unique_ptr<SessionDescription> offer =
901 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Steve Anton2bed3972019-01-05 01:04:30902
903 // Reject the audio m= section and add a video m= section.
904 opts.media_description_options[0].stopped = true;
905 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
906 RtpTransceiverDirection::kSendRecv, kActive,
907 &opts);
908 std::unique_ptr<SessionDescription> reoffer =
Philipp Hancke2bf1b992023-09-26 07:04:46909 f1_.CreateOfferOrError(opts, offer.get()).MoveValue();
Steve Anton2bed3972019-01-05 01:04:30910
Emil Lundmark7d2e6162023-11-08 15:27:45911 const ContentGroup* bundle_group = reoffer->GetGroupByName(GROUP_TYPE_BUNDLE);
Steve Anton2bed3972019-01-05 01:04:30912 ASSERT_TRUE(bundle_group);
913 EXPECT_FALSE(bundle_group->HasContentName("audio"));
914 EXPECT_TRUE(bundle_group->HasContentName("video"));
915}
916
917// Test that if BUNDLE is enabled and the previous offerer-tagged media section
918// was rejected and a new media section is added, then the re-answer BUNDLE
919// group will contain only the non-rejected media section.
920TEST_F(MediaSessionDescriptionFactoryTest, ReAnswerChangedBundleOffererTagged) {
921 MediaSessionOptions opts;
922 opts.bundle_enabled = true;
923 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
924 RtpTransceiverDirection::kSendRecv, kActive,
925 &opts);
Philipp Hancke2bf1b992023-09-26 07:04:46926 std::unique_ptr<SessionDescription> offer =
927 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Steve Anton2bed3972019-01-05 01:04:30928 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:46929 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Steve Anton2bed3972019-01-05 01:04:30930
931 // Reject the audio m= section and add a video m= section.
932 opts.media_description_options[0].stopped = true;
933 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
934 RtpTransceiverDirection::kSendRecv, kActive,
935 &opts);
936 std::unique_ptr<SessionDescription> reoffer =
Philipp Hancke2bf1b992023-09-26 07:04:46937 f1_.CreateOfferOrError(opts, offer.get()).MoveValue();
Steve Anton2bed3972019-01-05 01:04:30938 std::unique_ptr<SessionDescription> reanswer =
Philipp Hancke2bf1b992023-09-26 07:04:46939 f2_.CreateAnswerOrError(reoffer.get(), opts, answer.get()).MoveValue();
Steve Anton2bed3972019-01-05 01:04:30940
Emil Lundmark7d2e6162023-11-08 15:27:45941 const ContentGroup* bundle_group =
942 reanswer->GetGroupByName(GROUP_TYPE_BUNDLE);
Steve Anton2bed3972019-01-05 01:04:30943 ASSERT_TRUE(bundle_group);
944 EXPECT_FALSE(bundle_group->HasContentName("audio"));
945 EXPECT_TRUE(bundle_group->HasContentName("video"));
946}
947
Henrik Boströmf8187e02021-04-26 19:04:26948TEST_F(MediaSessionDescriptionFactoryTest,
949 CreateAnswerForOfferWithMultipleBundleGroups) {
950 // Create an offer with 4 m= sections, initially without BUNDLE groups.
951 MediaSessionOptions opts;
952 opts.bundle_enabled = false;
953 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "1",
954 RtpTransceiverDirection::kSendRecv, kActive,
955 &opts);
956 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "2",
957 RtpTransceiverDirection::kSendRecv, kActive,
958 &opts);
959 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "3",
960 RtpTransceiverDirection::kSendRecv, kActive,
961 &opts);
962 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "4",
963 RtpTransceiverDirection::kSendRecv, kActive,
964 &opts);
Philipp Hancke2bf1b992023-09-26 07:04:46965 std::unique_ptr<SessionDescription> offer =
966 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Henrik Boströmf8187e02021-04-26 19:04:26967 ASSERT_TRUE(offer->groups().empty());
968
969 // Munge the offer to have two groups. Offers like these cannot be generated
970 // without munging, but it is valid to receive such offers from remote
971 // endpoints.
Emil Lundmark7d2e6162023-11-08 15:27:45972 ContentGroup bundle_group1(GROUP_TYPE_BUNDLE);
Henrik Boströmf8187e02021-04-26 19:04:26973 bundle_group1.AddContentName("1");
974 bundle_group1.AddContentName("2");
Emil Lundmark7d2e6162023-11-08 15:27:45975 ContentGroup bundle_group2(GROUP_TYPE_BUNDLE);
Henrik Boströmf8187e02021-04-26 19:04:26976 bundle_group2.AddContentName("3");
977 bundle_group2.AddContentName("4");
978 offer->AddGroup(bundle_group1);
979 offer->AddGroup(bundle_group2);
980
981 // If BUNDLE is enabled, the answer to this offer should accept both BUNDLE
982 // groups.
983 opts.bundle_enabled = true;
984 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:46985 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Henrik Boströmf8187e02021-04-26 19:04:26986
Emil Lundmark7d2e6162023-11-08 15:27:45987 std::vector<const ContentGroup*> answer_groups =
988 answer->GetGroupsByName(GROUP_TYPE_BUNDLE);
Henrik Boströmf8187e02021-04-26 19:04:26989 ASSERT_EQ(answer_groups.size(), 2u);
990 EXPECT_EQ(answer_groups[0]->content_names().size(), 2u);
991 EXPECT_TRUE(answer_groups[0]->HasContentName("1"));
992 EXPECT_TRUE(answer_groups[0]->HasContentName("2"));
993 EXPECT_EQ(answer_groups[1]->content_names().size(), 2u);
994 EXPECT_TRUE(answer_groups[1]->HasContentName("3"));
995 EXPECT_TRUE(answer_groups[1]->HasContentName("4"));
996
997 // If BUNDLE is disabled, the answer to this offer should reject both BUNDLE
998 // groups.
999 opts.bundle_enabled = false;
Philipp Hancke2bf1b992023-09-26 07:04:461000 answer = f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Henrik Boströmf8187e02021-04-26 19:04:261001
Emil Lundmark7d2e6162023-11-08 15:27:451002 answer_groups = answer->GetGroupsByName(GROUP_TYPE_BUNDLE);
Henrik Boströmf8187e02021-04-26 19:04:261003 // Rejected groups are still listed, but they are empty.
1004 ASSERT_EQ(answer_groups.size(), 2u);
1005 EXPECT_TRUE(answer_groups[0]->content_names().empty());
1006 EXPECT_TRUE(answer_groups[1]->content_names().empty());
1007}
1008
Steve Anton2bed3972019-01-05 01:04:301009// Test that if the BUNDLE offerer-tagged media section is changed in a reoffer
1010// and there is still a non-rejected media section that was in the initial
1011// offer, then the ICE credentials do not change in the reoffer offerer-tagged
1012// media section.
1013TEST_F(MediaSessionDescriptionFactoryTest,
1014 ReOfferChangeBundleOffererTaggedKeepsIceCredentials) {
1015 MediaSessionOptions opts;
1016 opts.bundle_enabled = true;
1017 AddAudioVideoSections(RtpTransceiverDirection::kSendRecv, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461018 std::unique_ptr<SessionDescription> offer =
1019 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Steve Anton2bed3972019-01-05 01:04:301020 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461021 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Steve Anton2bed3972019-01-05 01:04:301022
1023 // Reject the audio m= section.
1024 opts.media_description_options[0].stopped = true;
1025 std::unique_ptr<SessionDescription> reoffer =
Philipp Hancke2bf1b992023-09-26 07:04:461026 f1_.CreateOfferOrError(opts, offer.get()).MoveValue();
Steve Anton2bed3972019-01-05 01:04:301027
1028 const TransportDescription* offer_tagged =
1029 offer->GetTransportDescriptionByName("audio");
1030 ASSERT_TRUE(offer_tagged);
1031 const TransportDescription* reoffer_tagged =
1032 reoffer->GetTransportDescriptionByName("video");
1033 ASSERT_TRUE(reoffer_tagged);
1034 EXPECT_EQ(offer_tagged->ice_ufrag, reoffer_tagged->ice_ufrag);
1035 EXPECT_EQ(offer_tagged->ice_pwd, reoffer_tagged->ice_pwd);
1036}
1037
1038// Test that if the BUNDLE offerer-tagged media section is changed in a reoffer
1039// and there is still a non-rejected media section that was in the initial
1040// offer, then the ICE credentials do not change in the reanswer answerer-tagged
1041// media section.
1042TEST_F(MediaSessionDescriptionFactoryTest,
1043 ReAnswerChangeBundleOffererTaggedKeepsIceCredentials) {
1044 MediaSessionOptions opts;
1045 opts.bundle_enabled = true;
1046 AddAudioVideoSections(RtpTransceiverDirection::kSendRecv, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461047 std::unique_ptr<SessionDescription> offer =
1048 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Steve Anton2bed3972019-01-05 01:04:301049 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461050 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Steve Anton2bed3972019-01-05 01:04:301051
1052 // Reject the audio m= section.
1053 opts.media_description_options[0].stopped = true;
1054 std::unique_ptr<SessionDescription> reoffer =
Philipp Hancke2bf1b992023-09-26 07:04:461055 f1_.CreateOfferOrError(opts, offer.get()).MoveValue();
Steve Anton2bed3972019-01-05 01:04:301056 std::unique_ptr<SessionDescription> reanswer =
Philipp Hancke2bf1b992023-09-26 07:04:461057 f2_.CreateAnswerOrError(reoffer.get(), opts, answer.get()).MoveValue();
Steve Anton2bed3972019-01-05 01:04:301058
1059 const TransportDescription* answer_tagged =
1060 answer->GetTransportDescriptionByName("audio");
1061 ASSERT_TRUE(answer_tagged);
1062 const TransportDescription* reanswer_tagged =
1063 reanswer->GetTransportDescriptionByName("video");
1064 ASSERT_TRUE(reanswer_tagged);
1065 EXPECT_EQ(answer_tagged->ice_ufrag, reanswer_tagged->ice_ufrag);
1066 EXPECT_EQ(answer_tagged->ice_pwd, reanswer_tagged->ice_pwd);
1067}
1068
henrike@webrtc.org28e20752013-07-10 00:45:361069// Create an audio, video offer without legacy StreamParams.
1070TEST_F(MediaSessionDescriptionFactoryTest,
1071 TestCreateOfferWithoutLegacyStreams) {
1072 MediaSessionOptions opts;
Steve Anton4e70a722017-11-28 22:57:101073 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461074 std::unique_ptr<SessionDescription> offer =
1075 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
1076 ASSERT_TRUE(offer.get());
henrike@webrtc.org28e20752013-07-10 00:45:361077 const ContentInfo* ac = offer->GetContentByName("audio");
1078 const ContentInfo* vc = offer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:461079 ASSERT_TRUE(ac);
1080 ASSERT_TRUE(vc);
Philipp Hancked0f0f382023-11-23 19:21:051081 const MediaContentDescription* acd = ac->media_description();
1082 const MediaContentDescription* vcd = vc->media_description();
henrike@webrtc.org28e20752013-07-10 00:45:361083
Yves Gerey665174f2018-06-19 13:03:051084 EXPECT_FALSE(vcd->has_ssrcs()); // No StreamParams.
1085 EXPECT_FALSE(acd->has_ssrcs()); // No StreamParams.
henrike@webrtc.org28e20752013-07-10 00:45:361086}
1087
jiayl@webrtc.org742922b2014-10-07 21:32:431088// Creates an audio+video sendonly offer.
1089TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSendOnlyOffer) {
zhihuang1c378ed2017-08-17 21:10:501090 MediaSessionOptions opts;
Steve Anton4e70a722017-11-28 22:57:101091 AddAudioVideoSections(RtpTransceiverDirection::kSendOnly, &opts);
Amit Hilbuchc63ddb22019-01-02 18:13:581092 AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1,
1093 {kMediaStream1}, 1, &opts);
1094 AttachSenderToMediaDescriptionOptions("audio", MEDIA_TYPE_AUDIO, kAudioTrack1,
1095 {kMediaStream1}, 1, &opts);
jiayl@webrtc.org742922b2014-10-07 21:32:431096
Philipp Hancke2bf1b992023-09-26 07:04:461097 std::unique_ptr<SessionDescription> offer =
1098 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
1099 ASSERT_TRUE(offer.get());
jiayl@webrtc.org742922b2014-10-07 21:32:431100 EXPECT_EQ(2u, offer->contents().size());
1101 EXPECT_TRUE(IsMediaContentOfType(&offer->contents()[0], MEDIA_TYPE_AUDIO));
1102 EXPECT_TRUE(IsMediaContentOfType(&offer->contents()[1], MEDIA_TYPE_VIDEO));
1103
Steve Anton4e70a722017-11-28 22:57:101104 EXPECT_EQ(RtpTransceiverDirection::kSendOnly,
1105 GetMediaDirection(&offer->contents()[0]));
1106 EXPECT_EQ(RtpTransceiverDirection::kSendOnly,
1107 GetMediaDirection(&offer->contents()[1]));
jiayl@webrtc.org742922b2014-10-07 21:32:431108}
1109
jiayl@webrtc.orge7d47a12014-08-05 19:19:051110// Verifies that the order of the media contents in the current
1111// SessionDescription is preserved in the new SessionDescription.
1112TEST_F(MediaSessionDescriptionFactoryTest, TestCreateOfferContentOrder) {
1113 MediaSessionOptions opts;
Florent Castelli516e2842021-04-19 13:29:501114 AddDataSection(RtpTransceiverDirection::kSendRecv, &opts);
jiayl@webrtc.orge7d47a12014-08-05 19:19:051115
Philipp Hancke2bf1b992023-09-26 07:04:461116 std::unique_ptr<SessionDescription> offer1(
1117 f1_.CreateOfferOrError(opts, nullptr).MoveValue());
1118 ASSERT_TRUE(offer1.get());
jiayl@webrtc.orge7d47a12014-08-05 19:19:051119 EXPECT_EQ(1u, offer1->contents().size());
1120 EXPECT_TRUE(IsMediaContentOfType(&offer1->contents()[0], MEDIA_TYPE_DATA));
1121
Amit Hilbuchc63ddb22019-01-02 18:13:581122 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
1123 RtpTransceiverDirection::kRecvOnly, kActive,
1124 &opts);
kwiberg31022942016-03-11 22:18:211125 std::unique_ptr<SessionDescription> offer2(
Philipp Hancke2bf1b992023-09-26 07:04:461126 f1_.CreateOfferOrError(opts, offer1.get()).MoveValue());
1127 ASSERT_TRUE(offer2.get());
jiayl@webrtc.orge7d47a12014-08-05 19:19:051128 EXPECT_EQ(2u, offer2->contents().size());
1129 EXPECT_TRUE(IsMediaContentOfType(&offer2->contents()[0], MEDIA_TYPE_DATA));
1130 EXPECT_TRUE(IsMediaContentOfType(&offer2->contents()[1], MEDIA_TYPE_VIDEO));
1131
Amit Hilbuchc63ddb22019-01-02 18:13:581132 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
1133 RtpTransceiverDirection::kRecvOnly, kActive,
1134 &opts);
kwiberg31022942016-03-11 22:18:211135 std::unique_ptr<SessionDescription> offer3(
Philipp Hancke2bf1b992023-09-26 07:04:461136 f1_.CreateOfferOrError(opts, offer2.get()).MoveValue());
1137 ASSERT_TRUE(offer3.get());
jiayl@webrtc.orge7d47a12014-08-05 19:19:051138 EXPECT_EQ(3u, offer3->contents().size());
1139 EXPECT_TRUE(IsMediaContentOfType(&offer3->contents()[0], MEDIA_TYPE_DATA));
1140 EXPECT_TRUE(IsMediaContentOfType(&offer3->contents()[1], MEDIA_TYPE_VIDEO));
1141 EXPECT_TRUE(IsMediaContentOfType(&offer3->contents()[2], MEDIA_TYPE_AUDIO));
jiayl@webrtc.orge7d47a12014-08-05 19:19:051142}
1143
henrike@webrtc.org28e20752013-07-10 00:45:361144// Create a typical audio answer, and ensure it matches what we expect.
1145TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswer) {
Steve Anton6fe1fba2018-12-11 18:15:231146 std::unique_ptr<SessionDescription> offer =
Philipp Hancke7fbcc8c2023-11-02 09:53:211147 f1_.CreateOfferOrError(CreateAudioMediaSession(), nullptr).MoveValue();
Philipp Hancke2bf1b992023-09-26 07:04:461148 ASSERT_TRUE(offer.get());
Steve Anton6fe1fba2018-12-11 18:15:231149 std::unique_ptr<SessionDescription> answer =
Philipp Hancke7fbcc8c2023-11-02 09:53:211150 f2_.CreateAnswerOrError(offer.get(), CreateAudioMediaSession(), nullptr)
Philipp Hancke2bf1b992023-09-26 07:04:461151 .MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:361152 const ContentInfo* ac = answer->GetContentByName("audio");
1153 const ContentInfo* vc = answer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:461154 ASSERT_TRUE(ac);
1155 EXPECT_FALSE(vc);
Steve Anton5adfafd2017-12-21 00:34:001156 EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
Philipp Hancked0f0f382023-11-23 19:21:051157 const MediaContentDescription* acd = ac->media_description();
henrike@webrtc.org28e20752013-07-10 00:45:361158 EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
Steve Antone38a5a12018-11-22 00:05:151159 EXPECT_THAT(acd->codecs(), ElementsAreArray(kAudioCodecsAnswer));
zhihuang1c378ed2017-08-17 21:10:501160 EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
henrike@webrtc.org28e20752013-07-10 00:45:361161 EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw
1162 EXPECT_TRUE(acd->rtcp_mux()); // negotiated rtcp-mux
Harald Alvestrand974044e2024-02-08 13:15:511163 EXPECT_EQ(kMediaProtocolDtlsSavpf, acd->protocol());
Harald Alvestrand0d018412021-11-04 13:52:311164}
1165
1166// Create a typical audio answer with GCM ciphers enabled, and ensure it
1167// matches what we expect.
1168TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswerGcm) {
Philipp Hancke7fbcc8c2023-11-02 09:53:211169 MediaSessionOptions opts = CreateAudioMediaSession();
Philipp Hancke2bf1b992023-09-26 07:04:461170 std::unique_ptr<SessionDescription> offer =
1171 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
1172 ASSERT_TRUE(offer.get());
Harald Alvestrand0d018412021-11-04 13:52:311173 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461174 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Harald Alvestrand0d018412021-11-04 13:52:311175 const ContentInfo* ac = answer->GetContentByName("audio");
1176 const ContentInfo* vc = answer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:461177 ASSERT_TRUE(ac);
1178 EXPECT_FALSE(vc);
Harald Alvestrand0d018412021-11-04 13:52:311179 EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
Philipp Hancked0f0f382023-11-23 19:21:051180 const MediaContentDescription* acd = ac->media_description();
Harald Alvestrand0d018412021-11-04 13:52:311181 EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
1182 EXPECT_THAT(acd->codecs(), ElementsAreArray(kAudioCodecsAnswer));
1183 EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
1184 EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw
1185 EXPECT_TRUE(acd->rtcp_mux()); // negotiated rtcp-mux
Harald Alvestrand974044e2024-02-08 13:15:511186 EXPECT_EQ(kMediaProtocolDtlsSavpf, acd->protocol());
jbauchcb560652016-08-04 12:20:321187}
1188
Philipp Hancke2f3168f2022-05-16 12:41:321189// Create an audio answer with no common codecs, and ensure it is rejected.
1190TEST_F(MediaSessionDescriptionFactoryTest,
1191 TestCreateAudioAnswerWithNoCommonCodecs) {
1192 MediaSessionOptions opts;
1193 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
1194 RtpTransceiverDirection::kSendRecv, kActive,
1195 &opts);
Emil Lundmark7d2e6162023-11-08 15:27:451196 std::vector f1_codecs = {CreateAudioCodec(96, "opus", 48000, 1)};
Philipp Hancke2f3168f2022-05-16 12:41:321197 f1_.set_audio_codecs(f1_codecs, f1_codecs);
1198
Emil Lundmark7d2e6162023-11-08 15:27:451199 std::vector f2_codecs = {CreateAudioCodec(0, "PCMU", 8000, 1)};
Philipp Hancke2f3168f2022-05-16 12:41:321200 f2_.set_audio_codecs(f2_codecs, f2_codecs);
1201
Philipp Hancke2bf1b992023-09-26 07:04:461202 std::unique_ptr<SessionDescription> offer =
1203 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Philipp Hancke2f3168f2022-05-16 12:41:321204 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461205 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Philipp Hancke2f3168f2022-05-16 12:41:321206 const ContentInfo* ac = answer->GetContentByName("audio");
Philipp Hancke2bf1b992023-09-26 07:04:461207 ASSERT_TRUE(ac);
Philipp Hancke2f3168f2022-05-16 12:41:321208 EXPECT_TRUE(ac->rejected);
1209}
1210
henrike@webrtc.org28e20752013-07-10 00:45:361211// Create a typical video answer, and ensure it matches what we expect.
1212TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswer) {
1213 MediaSessionOptions opts;
Steve Anton4e70a722017-11-28 22:57:101214 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461215 std::unique_ptr<SessionDescription> offer =
1216 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
1217 ASSERT_TRUE(offer.get());
Steve Anton6fe1fba2018-12-11 18:15:231218 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461219 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:361220 const ContentInfo* ac = answer->GetContentByName("audio");
1221 const ContentInfo* vc = answer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:461222 ASSERT_TRUE(ac);
1223 ASSERT_TRUE(vc);
Steve Anton5adfafd2017-12-21 00:34:001224 EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
1225 EXPECT_EQ(MediaProtocolType::kRtp, vc->type);
Philipp Hancked0f0f382023-11-23 19:21:051226 const MediaContentDescription* acd = ac->media_description();
1227 const MediaContentDescription* vcd = vc->media_description();
henrike@webrtc.org28e20752013-07-10 00:45:361228 EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
Steve Antone38a5a12018-11-22 00:05:151229 EXPECT_THAT(acd->codecs(), ElementsAreArray(kAudioCodecsAnswer));
henrike@webrtc.org28e20752013-07-10 00:45:361230 EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw
zhihuang1c378ed2017-08-17 21:10:501231 EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached
henrike@webrtc.org28e20752013-07-10 00:45:361232 EXPECT_TRUE(acd->rtcp_mux()); // negotiated rtcp-mux
henrike@webrtc.org28e20752013-07-10 00:45:361233 EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type());
Steve Antone38a5a12018-11-22 00:05:151234 EXPECT_THAT(vcd->codecs(), ElementsAreArray(kVideoCodecsAnswer));
Yves Gerey665174f2018-06-19 13:03:051235 EXPECT_EQ(0U, vcd->first_ssrc()); // no sender is attached
1236 EXPECT_TRUE(vcd->rtcp_mux()); // negotiated rtcp-mux
Harald Alvestrand974044e2024-02-08 13:15:511237 EXPECT_EQ(kMediaProtocolDtlsSavpf, vcd->protocol());
jbauchcb560652016-08-04 12:20:321238}
1239
Philipp Hancke2f3168f2022-05-16 12:41:321240// Create a video answer with no common codecs, and ensure it is rejected.
1241TEST_F(MediaSessionDescriptionFactoryTest,
1242 TestCreateVideoAnswerWithNoCommonCodecs) {
1243 MediaSessionOptions opts;
1244 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
1245 RtpTransceiverDirection::kSendRecv, kActive,
1246 &opts);
Emil Lundmark7d2e6162023-11-08 15:27:451247 std::vector f1_codecs = {CreateVideoCodec(96, "H264")};
Philipp Hancke2f3168f2022-05-16 12:41:321248 f1_.set_video_codecs(f1_codecs, f1_codecs);
1249
Emil Lundmark7d2e6162023-11-08 15:27:451250 std::vector f2_codecs = {CreateVideoCodec(97, "VP8")};
Philipp Hancke2f3168f2022-05-16 12:41:321251 f2_.set_video_codecs(f2_codecs, f2_codecs);
1252
Philipp Hancke2bf1b992023-09-26 07:04:461253 std::unique_ptr<SessionDescription> offer =
1254 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Philipp Hancke2f3168f2022-05-16 12:41:321255 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461256 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Philipp Hancke2f3168f2022-05-16 12:41:321257 const ContentInfo* vc = answer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:461258 ASSERT_TRUE(vc);
Philipp Hancke2f3168f2022-05-16 12:41:321259 EXPECT_TRUE(vc->rejected);
1260}
1261
1262// Create a video answer with no common codecs (but a common FEC codec), and
1263// ensure it is rejected.
1264TEST_F(MediaSessionDescriptionFactoryTest,
1265 TestCreateVideoAnswerWithOnlyFecCodecsCommon) {
1266 MediaSessionOptions opts;
1267 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
1268 RtpTransceiverDirection::kSendRecv, kActive,
1269 &opts);
Emil Lundmark7d2e6162023-11-08 15:27:451270 std::vector f1_codecs = {CreateVideoCodec(96, "H264"),
1271 CreateVideoCodec(118, "flexfec-03")};
Philipp Hancke2f3168f2022-05-16 12:41:321272 f1_.set_video_codecs(f1_codecs, f1_codecs);
1273
Emil Lundmark7d2e6162023-11-08 15:27:451274 std::vector f2_codecs = {CreateVideoCodec(97, "VP8"),
1275 CreateVideoCodec(118, "flexfec-03")};
Philipp Hancke2f3168f2022-05-16 12:41:321276 f2_.set_video_codecs(f2_codecs, f2_codecs);
1277
Philipp Hancke2bf1b992023-09-26 07:04:461278 std::unique_ptr<SessionDescription> offer =
1279 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Philipp Hancke2f3168f2022-05-16 12:41:321280 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461281 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Philipp Hancke2f3168f2022-05-16 12:41:321282 const ContentInfo* vc = answer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:461283 ASSERT_TRUE(vc);
Philipp Hancke2f3168f2022-05-16 12:41:321284 EXPECT_TRUE(vc->rejected);
1285}
1286
Harald Alvestrandc5effc22019-06-11 09:46:591287// The use_sctpmap flag should be set in an Sctp DataContentDescription by
1288// default. The answer's use_sctpmap flag should match the offer's.
zstein4b2e0822017-02-18 03:48:381289TEST_F(MediaSessionDescriptionFactoryTest, TestCreateDataAnswerUsesSctpmap) {
1290 MediaSessionOptions opts;
Florent Castelli516e2842021-04-19 13:29:501291 AddDataSection(RtpTransceiverDirection::kSendRecv, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461292 std::unique_ptr<SessionDescription> offer =
1293 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
1294 ASSERT_TRUE(offer.get());
zstein4b2e0822017-02-18 03:48:381295 ContentInfo* dc_offer = offer->GetContentByName("data");
Philipp Hancke2bf1b992023-09-26 07:04:461296 ASSERT_TRUE(dc_offer);
Harald Alvestrand5fc28b12019-05-13 11:36:161297 SctpDataContentDescription* dcd_offer =
1298 dc_offer->media_description()->as_sctp();
zstein4b2e0822017-02-18 03:48:381299 EXPECT_TRUE(dcd_offer->use_sctpmap());
1300
Steve Anton6fe1fba2018-12-11 18:15:231301 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461302 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
zstein4b2e0822017-02-18 03:48:381303 const ContentInfo* dc_answer = answer->GetContentByName("data");
Philipp Hancke2bf1b992023-09-26 07:04:461304 ASSERT_TRUE(dc_answer);
Harald Alvestrand5fc28b12019-05-13 11:36:161305 const SctpDataContentDescription* dcd_answer =
1306 dc_answer->media_description()->as_sctp();
zstein4b2e0822017-02-18 03:48:381307 EXPECT_TRUE(dcd_answer->use_sctpmap());
1308}
1309
1310// The answer's use_sctpmap flag should match the offer's.
1311TEST_F(MediaSessionDescriptionFactoryTest, TestCreateDataAnswerWithoutSctpmap) {
1312 MediaSessionOptions opts;
Florent Castelli516e2842021-04-19 13:29:501313 AddDataSection(RtpTransceiverDirection::kSendRecv, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461314 std::unique_ptr<SessionDescription> offer =
1315 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
1316 ASSERT_TRUE(offer.get());
zstein4b2e0822017-02-18 03:48:381317 ContentInfo* dc_offer = offer->GetContentByName("data");
Philipp Hancke2bf1b992023-09-26 07:04:461318 ASSERT_TRUE(dc_offer);
Harald Alvestrand5fc28b12019-05-13 11:36:161319 SctpDataContentDescription* dcd_offer =
1320 dc_offer->media_description()->as_sctp();
zstein4b2e0822017-02-18 03:48:381321 dcd_offer->set_use_sctpmap(false);
1322
Steve Anton6fe1fba2018-12-11 18:15:231323 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461324 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
zstein4b2e0822017-02-18 03:48:381325 const ContentInfo* dc_answer = answer->GetContentByName("data");
Philipp Hancke2bf1b992023-09-26 07:04:461326 ASSERT_TRUE(dc_answer);
Harald Alvestrand5fc28b12019-05-13 11:36:161327 const SctpDataContentDescription* dcd_answer =
1328 dc_answer->media_description()->as_sctp();
zstein4b2e0822017-02-18 03:48:381329 EXPECT_FALSE(dcd_answer->use_sctpmap());
jbauchcb560652016-08-04 12:20:321330}
1331
deadbeef8b7e9ad2017-05-25 16:38:551332// Test that a valid answer will be created for "DTLS/SCTP", "UDP/DTLS/SCTP"
1333// and "TCP/DTLS/SCTP" offers.
1334TEST_F(MediaSessionDescriptionFactoryTest,
1335 TestCreateDataAnswerToDifferentOfferedProtos) {
deadbeef8b7e9ad2017-05-25 16:38:551336 MediaSessionOptions opts;
Florent Castelli516e2842021-04-19 13:29:501337 AddDataSection(RtpTransceiverDirection::kSendRecv, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461338 std::unique_ptr<SessionDescription> offer =
1339 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
1340 ASSERT_TRUE(offer.get());
deadbeef8b7e9ad2017-05-25 16:38:551341 ContentInfo* dc_offer = offer->GetContentByName("data");
Philipp Hancke2bf1b992023-09-26 07:04:461342 ASSERT_TRUE(dc_offer);
Harald Alvestrand5fc28b12019-05-13 11:36:161343 SctpDataContentDescription* dcd_offer =
1344 dc_offer->media_description()->as_sctp();
1345 ASSERT_TRUE(dcd_offer);
deadbeef8b7e9ad2017-05-25 16:38:551346
1347 std::vector<std::string> protos = {"DTLS/SCTP", "UDP/DTLS/SCTP",
1348 "TCP/DTLS/SCTP"};
1349 for (const std::string& proto : protos) {
1350 dcd_offer->set_protocol(proto);
Steve Anton6fe1fba2018-12-11 18:15:231351 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461352 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
deadbeef8b7e9ad2017-05-25 16:38:551353 const ContentInfo* dc_answer = answer->GetContentByName("data");
Philipp Hancke2bf1b992023-09-26 07:04:461354 ASSERT_TRUE(dc_answer);
Harald Alvestrand5fc28b12019-05-13 11:36:161355 const SctpDataContentDescription* dcd_answer =
1356 dc_answer->media_description()->as_sctp();
deadbeef8b7e9ad2017-05-25 16:38:551357 EXPECT_FALSE(dc_answer->rejected);
1358 EXPECT_EQ(proto, dcd_answer->protocol());
1359 }
1360}
1361
Harald Alvestrand8d3d6cf2019-05-16 09:49:171362TEST_F(MediaSessionDescriptionFactoryTest,
1363 TestCreateDataAnswerToOfferWithDefinedMessageSize) {
Harald Alvestrand8d3d6cf2019-05-16 09:49:171364 MediaSessionOptions opts;
Florent Castelli516e2842021-04-19 13:29:501365 AddDataSection(RtpTransceiverDirection::kSendRecv, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461366 std::unique_ptr<SessionDescription> offer =
1367 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
1368 ASSERT_TRUE(offer.get());
Harald Alvestrand8d3d6cf2019-05-16 09:49:171369 ContentInfo* dc_offer = offer->GetContentByName("data");
Philipp Hancke2bf1b992023-09-26 07:04:461370 ASSERT_TRUE(dc_offer);
Harald Alvestrand8d3d6cf2019-05-16 09:49:171371 SctpDataContentDescription* dcd_offer =
1372 dc_offer->media_description()->as_sctp();
1373 ASSERT_TRUE(dcd_offer);
1374 dcd_offer->set_max_message_size(1234);
1375 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461376 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Harald Alvestrand8d3d6cf2019-05-16 09:49:171377 const ContentInfo* dc_answer = answer->GetContentByName("data");
Philipp Hancke2bf1b992023-09-26 07:04:461378 ASSERT_TRUE(dc_answer);
Harald Alvestrand8d3d6cf2019-05-16 09:49:171379 const SctpDataContentDescription* dcd_answer =
1380 dc_answer->media_description()->as_sctp();
1381 EXPECT_FALSE(dc_answer->rejected);
1382 EXPECT_EQ(1234, dcd_answer->max_message_size());
1383}
1384
1385TEST_F(MediaSessionDescriptionFactoryTest,
1386 TestCreateDataAnswerToOfferWithZeroMessageSize) {
Harald Alvestrand8d3d6cf2019-05-16 09:49:171387 MediaSessionOptions opts;
Florent Castelli516e2842021-04-19 13:29:501388 AddDataSection(RtpTransceiverDirection::kSendRecv, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461389 std::unique_ptr<SessionDescription> offer =
1390 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
1391 ASSERT_TRUE(offer.get());
Harald Alvestrand8d3d6cf2019-05-16 09:49:171392 ContentInfo* dc_offer = offer->GetContentByName("data");
Philipp Hancke2bf1b992023-09-26 07:04:461393 ASSERT_TRUE(dc_offer);
Harald Alvestrand8d3d6cf2019-05-16 09:49:171394 SctpDataContentDescription* dcd_offer =
1395 dc_offer->media_description()->as_sctp();
1396 ASSERT_TRUE(dcd_offer);
1397 dcd_offer->set_max_message_size(0);
1398 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461399 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Harald Alvestrand8d3d6cf2019-05-16 09:49:171400 const ContentInfo* dc_answer = answer->GetContentByName("data");
Philipp Hancke2bf1b992023-09-26 07:04:461401 ASSERT_TRUE(dc_answer);
Harald Alvestrand8d3d6cf2019-05-16 09:49:171402 const SctpDataContentDescription* dcd_answer =
1403 dc_answer->media_description()->as_sctp();
1404 EXPECT_FALSE(dc_answer->rejected);
Emil Lundmark7d2e6162023-11-08 15:27:451405 EXPECT_EQ(kSctpSendBufferSize, dcd_answer->max_message_size());
Harald Alvestrand8d3d6cf2019-05-16 09:49:171406}
1407
jiayl@webrtc.orge7d47a12014-08-05 19:19:051408// Verifies that the order of the media contents in the offer is preserved in
1409// the answer.
1410TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAnswerContentOrder) {
1411 MediaSessionOptions opts;
1412
1413 // Creates a data only offer.
Florent Castelli516e2842021-04-19 13:29:501414 AddDataSection(RtpTransceiverDirection::kSendRecv, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461415 std::unique_ptr<SessionDescription> offer1(
1416 f1_.CreateOfferOrError(opts, nullptr).MoveValue());
1417 ASSERT_TRUE(offer1.get());
jiayl@webrtc.orge7d47a12014-08-05 19:19:051418
1419 // Appends audio to the offer.
Amit Hilbuchc63ddb22019-01-02 18:13:581420 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
1421 RtpTransceiverDirection::kRecvOnly, kActive,
1422 &opts);
kwiberg31022942016-03-11 22:18:211423 std::unique_ptr<SessionDescription> offer2(
Philipp Hancke2bf1b992023-09-26 07:04:461424 f1_.CreateOfferOrError(opts, offer1.get()).MoveValue());
1425 ASSERT_TRUE(offer2.get());
jiayl@webrtc.orge7d47a12014-08-05 19:19:051426
1427 // Appends video to the offer.
Amit Hilbuchc63ddb22019-01-02 18:13:581428 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
1429 RtpTransceiverDirection::kRecvOnly, kActive,
1430 &opts);
kwiberg31022942016-03-11 22:18:211431 std::unique_ptr<SessionDescription> offer3(
Philipp Hancke2bf1b992023-09-26 07:04:461432 f1_.CreateOfferOrError(opts, offer2.get()).MoveValue());
1433 ASSERT_TRUE(offer3.get());
jiayl@webrtc.orge7d47a12014-08-05 19:19:051434
Steve Anton6fe1fba2018-12-11 18:15:231435 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461436 f2_.CreateAnswerOrError(offer3.get(), opts, nullptr).MoveValue();
1437 ASSERT_TRUE(answer.get());
jiayl@webrtc.orge7d47a12014-08-05 19:19:051438 EXPECT_EQ(3u, answer->contents().size());
1439 EXPECT_TRUE(IsMediaContentOfType(&answer->contents()[0], MEDIA_TYPE_DATA));
1440 EXPECT_TRUE(IsMediaContentOfType(&answer->contents()[1], MEDIA_TYPE_AUDIO));
1441 EXPECT_TRUE(IsMediaContentOfType(&answer->contents()[2], MEDIA_TYPE_VIDEO));
1442}
1443
ossu075af922016-06-14 10:29:381444// TODO(deadbeef): Extend these tests to ensure the correct direction with other
1445// answerer settings.
1446
henrike@webrtc.org28e20752013-07-10 00:45:361447// This test that the media direction is set to send/receive in an answer if
1448// the offer is send receive.
1449TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerToSendReceiveOffer) {
Steve Anton4e70a722017-11-28 22:57:101450 TestMediaDirectionInAnswer(RtpTransceiverDirection::kSendRecv,
1451 RtpTransceiverDirection::kSendRecv);
henrike@webrtc.org28e20752013-07-10 00:45:361452}
1453
1454// This test that the media direction is set to receive only in an answer if
1455// the offer is send only.
1456TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerToSendOnlyOffer) {
Steve Anton4e70a722017-11-28 22:57:101457 TestMediaDirectionInAnswer(RtpTransceiverDirection::kSendOnly,
1458 RtpTransceiverDirection::kRecvOnly);
henrike@webrtc.org28e20752013-07-10 00:45:361459}
1460
1461// This test that the media direction is set to send only in an answer if
1462// the offer is recv only.
1463TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerToRecvOnlyOffer) {
Steve Anton4e70a722017-11-28 22:57:101464 TestMediaDirectionInAnswer(RtpTransceiverDirection::kRecvOnly,
1465 RtpTransceiverDirection::kSendOnly);
henrike@webrtc.org28e20752013-07-10 00:45:361466}
1467
1468// This test that the media direction is set to inactive in an answer if
1469// the offer is inactive.
1470TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerToInactiveOffer) {
Steve Anton4e70a722017-11-28 22:57:101471 TestMediaDirectionInAnswer(RtpTransceiverDirection::kInactive,
1472 RtpTransceiverDirection::kInactive);
henrike@webrtc.org28e20752013-07-10 00:45:361473}
1474
Harald Alvestrand974044e2024-02-08 13:15:511475// Test that the media protocol is RTP/AVPF if DTLS is disabled.
henrike@webrtc.org28e20752013-07-10 00:45:361476TEST_F(MediaSessionDescriptionFactoryTest, AudioOfferAnswerWithCryptoDisabled) {
Philipp Hancke7fbcc8c2023-11-02 09:53:211477 MediaSessionOptions opts = CreateAudioMediaSession();
Harald Alvestrand974044e2024-02-08 13:15:511478 tdf1_.SetInsecureForTesting();
1479 tdf1_.set_certificate(nullptr);
1480 tdf2_.SetInsecureForTesting();
1481 tdf2_.set_certificate(nullptr);
henrike@webrtc.org28e20752013-07-10 00:45:361482
Philipp Hancke2bf1b992023-09-26 07:04:461483 std::unique_ptr<SessionDescription> offer =
1484 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:361485 const AudioContentDescription* offer_acd =
1486 GetFirstAudioContentDescription(offer.get());
Philipp Hancke2bf1b992023-09-26 07:04:461487 ASSERT_TRUE(offer_acd);
Emil Lundmark7d2e6162023-11-08 15:27:451488 EXPECT_EQ(kMediaProtocolAvpf, offer_acd->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:361489
Steve Anton6fe1fba2018-12-11 18:15:231490 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461491 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:361492
1493 const ContentInfo* ac_answer = answer->GetContentByName("audio");
Philipp Hancke2bf1b992023-09-26 07:04:461494 ASSERT_TRUE(ac_answer);
henrike@webrtc.org28e20752013-07-10 00:45:361495 EXPECT_FALSE(ac_answer->rejected);
1496
1497 const AudioContentDescription* answer_acd =
1498 GetFirstAudioContentDescription(answer.get());
Philipp Hancke2bf1b992023-09-26 07:04:461499 ASSERT_TRUE(answer_acd);
Emil Lundmark7d2e6162023-11-08 15:27:451500 EXPECT_EQ(kMediaProtocolAvpf, answer_acd->protocol());
henrike@webrtc.org28e20752013-07-10 00:45:361501}
1502
1503// Create a video offer and answer and ensure the RTP header extensions
1504// matches what we expect.
1505TEST_F(MediaSessionDescriptionFactoryTest, TestOfferAnswerWithRtpExtensions) {
1506 MediaSessionOptions opts;
Steve Anton4e70a722017-11-28 22:57:101507 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
Markus Handell755c65d2020-06-23 23:06:101508 SetAudioVideoRtpHeaderExtensions(MAKE_VECTOR(kAudioRtpExtension1),
1509 MAKE_VECTOR(kVideoRtpExtension1), &opts);
henrike@webrtc.org28e20752013-07-10 00:45:361510
Philipp Hancke2bf1b992023-09-26 07:04:461511 std::unique_ptr<SessionDescription> offer =
1512 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
1513 ASSERT_TRUE(offer.get());
Markus Handell755c65d2020-06-23 23:06:101514 SetAudioVideoRtpHeaderExtensions(MAKE_VECTOR(kAudioRtpExtension2),
1515 MAKE_VECTOR(kVideoRtpExtension2), &opts);
Steve Anton6fe1fba2018-12-11 18:15:231516 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461517 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:361518
Yves Gerey665174f2018-06-19 13:03:051519 EXPECT_EQ(
1520 MAKE_VECTOR(kAudioRtpExtension1),
1521 GetFirstAudioContentDescription(offer.get())->rtp_header_extensions());
1522 EXPECT_EQ(
1523 MAKE_VECTOR(kVideoRtpExtension1),
1524 GetFirstVideoContentDescription(offer.get())->rtp_header_extensions());
1525 EXPECT_EQ(
1526 MAKE_VECTOR(kAudioRtpExtensionAnswer),
1527 GetFirstAudioContentDescription(answer.get())->rtp_header_extensions());
1528 EXPECT_EQ(
1529 MAKE_VECTOR(kVideoRtpExtensionAnswer),
1530 GetFirstVideoContentDescription(answer.get())->rtp_header_extensions());
henrike@webrtc.org28e20752013-07-10 00:45:361531}
1532
Johannes Kronce8e8672019-02-22 12:06:441533// Create a audio/video offer and answer and ensure that the
Philipp Hancke657b65f2024-01-29 08:50:011534// TransportSequenceNumber RTP v1 and v2 header extensions are handled
1535// correctly.
Johannes Kronce8e8672019-02-22 12:06:441536TEST_F(MediaSessionDescriptionFactoryTest,
Philipp Hancke657b65f2024-01-29 08:50:011537 TestOfferAnswerWithTransportSequenceNumberV1LocalAndV1InOffer) {
Johannes Kronce8e8672019-02-22 12:06:441538 TestTransportSequenceNumberNegotiation(
1539 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber01), // Local.
1540 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber01), // Offer.
1541 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber01)); // Expected answer.
1542}
1543TEST_F(MediaSessionDescriptionFactoryTest,
Philipp Hancke657b65f2024-01-29 08:50:011544 TestOfferAnswerWithTransportSequenceNumberV1LocalAndV1V2InOffer) {
Johannes Kronce8e8672019-02-22 12:06:441545 TestTransportSequenceNumberNegotiation(
1546 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber01), // Local.
1547 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber01And02), // Offer.
Philipp Hancke657b65f2024-01-29 08:50:011548 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber01)); // Expected answer.
1549}
1550TEST_F(MediaSessionDescriptionFactoryTest,
1551 TestOfferAnswerWithTransportSequenceNumberV1LocalAndV2InOffer) {
1552 TestTransportSequenceNumberNegotiation(
1553 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber01), // Local.
1554 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber02), // Offer.
1555 {}); // Expected answer.
1556}
1557TEST_F(MediaSessionDescriptionFactoryTest,
1558 TestOfferAnswerWithTransportSequenceNumberV2LocalAndV1InOffer) {
1559 TestTransportSequenceNumberNegotiation(
1560 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber02), // Local.
1561 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber01), // Offer.
1562 {}); // Expected answer.
1563}
1564TEST_F(MediaSessionDescriptionFactoryTest,
1565 TestOfferAnswerWithTransportSequenceNumberV2LocalAndV1V2InOffer) {
1566 TestTransportSequenceNumberNegotiation(
1567 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber02), // Local.
1568 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber01And02), // Offer.
Johannes Kronce8e8672019-02-22 12:06:441569 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber02)); // Expected answer.
1570}
1571TEST_F(MediaSessionDescriptionFactoryTest,
Philipp Hancke657b65f2024-01-29 08:50:011572 TestOfferAnswerWithTransportSequenceNumberV2LocalAndV2InOffer) {
Johannes Kronce8e8672019-02-22 12:06:441573 TestTransportSequenceNumberNegotiation(
Philipp Hancke657b65f2024-01-29 08:50:011574 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber02), // Local.
Johannes Kronce8e8672019-02-22 12:06:441575 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber02), // Offer.
1576 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber02)); // Expected answer.
1577}
Philipp Hancke657b65f2024-01-29 08:50:011578TEST_F(MediaSessionDescriptionFactoryTest,
1579 TestOfferAnswerWithTransportSequenceNumberV1V2LocalAndV1InOffer) {
1580 TestTransportSequenceNumberNegotiation(
1581 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber01And02), // Local.
1582 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber01), // Offer.
1583 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber01)); // Expected answer.
1584}
1585TEST_F(MediaSessionDescriptionFactoryTest,
1586 TestOfferAnswerWithTransportSequenceNumberV1V2LocalAndV2InOffer) {
1587 TestTransportSequenceNumberNegotiation(
1588 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber01And02), // Local.
1589 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber02), // Offer.
1590 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber02)); // Expected answer.
1591}
1592TEST_F(MediaSessionDescriptionFactoryTest,
1593 TestOfferAnswerWithTransportSequenceNumberV1V2LocalAndV1V2InOffer) {
1594 TestTransportSequenceNumberNegotiation(
1595 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber01And02), // Local.
1596 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber01And02), // Offer.
1597 MAKE_VECTOR(
1598 kRtpExtensionTransportSequenceNumber01And02)); // Expected answer.
1599}
Johannes Kronce8e8672019-02-22 12:06:441600
jbauch5869f502017-06-29 19:31:361601TEST_F(MediaSessionDescriptionFactoryTest,
Markus Handellc1cbf6b2020-02-17 19:03:571602 TestNegotiateFrameDescriptorWhenUnexposedLocally) {
1603 MediaSessionOptions opts;
1604 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
1605
Markus Handell755c65d2020-06-23 23:06:101606 SetAudioVideoRtpHeaderExtensions(
1607 MAKE_VECTOR(kRtpExtensionGenericFrameDescriptorUri00),
1608 MAKE_VECTOR(kRtpExtensionGenericFrameDescriptorUri00), &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461609 std::unique_ptr<SessionDescription> offer =
1610 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Markus Handell755c65d2020-06-23 23:06:101611 SetAudioVideoRtpHeaderExtensions(
1612 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber01),
1613 MAKE_VECTOR(kRtpExtensionTransportSequenceNumber01), &opts);
Markus Handellc1cbf6b2020-02-17 19:03:571614 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461615 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Markus Handellc1cbf6b2020-02-17 19:03:571616 EXPECT_THAT(
1617 GetFirstAudioContentDescription(answer.get())->rtp_header_extensions(),
Markus Handell755c65d2020-06-23 23:06:101618 ElementsAreArray(kRtpExtensionGenericFrameDescriptorUri00));
Markus Handellc1cbf6b2020-02-17 19:03:571619 EXPECT_THAT(
1620 GetFirstVideoContentDescription(answer.get())->rtp_header_extensions(),
Markus Handell755c65d2020-06-23 23:06:101621 ElementsAreArray(kRtpExtensionGenericFrameDescriptorUri00));
Markus Handellc1cbf6b2020-02-17 19:03:571622}
1623
1624TEST_F(MediaSessionDescriptionFactoryTest,
1625 TestNegotiateFrameDescriptorWhenExposedLocally) {
1626 MediaSessionOptions opts;
1627 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
1628
Markus Handell755c65d2020-06-23 23:06:101629 SetAudioVideoRtpHeaderExtensions(
1630 MAKE_VECTOR(kRtpExtensionGenericFrameDescriptorUri00),
1631 MAKE_VECTOR(kRtpExtensionGenericFrameDescriptorUri00), &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461632 std::unique_ptr<SessionDescription> offer =
1633 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Markus Handellc1cbf6b2020-02-17 19:03:571634 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461635 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Markus Handellc1cbf6b2020-02-17 19:03:571636 EXPECT_THAT(
1637 GetFirstAudioContentDescription(answer.get())->rtp_header_extensions(),
Markus Handell755c65d2020-06-23 23:06:101638 ElementsAreArray(kRtpExtensionGenericFrameDescriptorUri00));
Markus Handellc1cbf6b2020-02-17 19:03:571639 EXPECT_THAT(
1640 GetFirstVideoContentDescription(answer.get())->rtp_header_extensions(),
Markus Handell755c65d2020-06-23 23:06:101641 ElementsAreArray(kRtpExtensionGenericFrameDescriptorUri00));
Markus Handellc1cbf6b2020-02-17 19:03:571642}
1643
1644TEST_F(MediaSessionDescriptionFactoryTest,
Danil Chapovalov5f999a72020-02-20 15:39:051645 NegotiateDependencyDescriptorWhenUnexposedLocally) {
1646 MediaSessionOptions opts;
1647 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
1648
1649 RtpExtension offer_dd(RtpExtension::kDependencyDescriptorUri, 7);
Markus Handell755c65d2020-06-23 23:06:101650 SetAudioVideoRtpHeaderExtensions({}, {offer_dd}, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461651 std::unique_ptr<SessionDescription> offer =
1652 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Markus Handell755c65d2020-06-23 23:06:101653 RtpExtension local_tsn(RtpExtension::kTransportSequenceNumberUri, 5);
1654 SetAudioVideoRtpHeaderExtensions({}, {local_tsn}, &opts);
Danil Chapovalov5f999a72020-02-20 15:39:051655 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461656 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Danil Chapovalov5f999a72020-02-20 15:39:051657 EXPECT_THAT(
1658 GetFirstVideoContentDescription(answer.get())->rtp_header_extensions(),
1659 ElementsAre(offer_dd));
1660}
1661
1662TEST_F(MediaSessionDescriptionFactoryTest,
1663 NegotiateDependencyDescriptorWhenExposedLocally) {
1664 MediaSessionOptions opts;
1665 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
1666
1667 RtpExtension offer_dd(RtpExtension::kDependencyDescriptorUri, 7);
1668 RtpExtension local_dd(RtpExtension::kDependencyDescriptorUri, 5);
Markus Handell755c65d2020-06-23 23:06:101669 SetAudioVideoRtpHeaderExtensions({}, {offer_dd}, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461670 std::unique_ptr<SessionDescription> offer =
1671 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Markus Handell755c65d2020-06-23 23:06:101672 SetAudioVideoRtpHeaderExtensions({}, {local_dd}, &opts);
Danil Chapovalov5f999a72020-02-20 15:39:051673 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461674 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Danil Chapovalov5f999a72020-02-20 15:39:051675 EXPECT_THAT(
1676 GetFirstVideoContentDescription(answer.get())->rtp_header_extensions(),
1677 ElementsAre(offer_dd));
1678}
1679
1680TEST_F(MediaSessionDescriptionFactoryTest,
Minyue Li430e4a02020-03-10 09:59:371681 NegotiateAbsoluteCaptureTimeWhenUnexposedLocally) {
1682 MediaSessionOptions opts;
1683 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
1684
Emil Lundmark7d2e6162023-11-08 15:27:451685 const RtpHeaderExtensions offered_extensions = {
Minyue Li430e4a02020-03-10 09:59:371686 RtpExtension(RtpExtension::kAbsoluteCaptureTimeUri, 7)};
Emil Lundmark7d2e6162023-11-08 15:27:451687 const RtpHeaderExtensions local_extensions = {
Minyue Li430e4a02020-03-10 09:59:371688 RtpExtension(RtpExtension::kTransportSequenceNumberUri, 5)};
Markus Handell755c65d2020-06-23 23:06:101689 SetAudioVideoRtpHeaderExtensions(offered_extensions, offered_extensions,
1690 &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461691 std::unique_ptr<SessionDescription> offer =
1692 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Markus Handell755c65d2020-06-23 23:06:101693 SetAudioVideoRtpHeaderExtensions(local_extensions, local_extensions, &opts);
Minyue Li430e4a02020-03-10 09:59:371694 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461695 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Minyue Li430e4a02020-03-10 09:59:371696 EXPECT_THAT(
1697 GetFirstVideoContentDescription(answer.get())->rtp_header_extensions(),
1698 ElementsAreArray(offered_extensions));
1699 EXPECT_THAT(
1700 GetFirstAudioContentDescription(answer.get())->rtp_header_extensions(),
1701 ElementsAreArray(offered_extensions));
1702}
1703
1704TEST_F(MediaSessionDescriptionFactoryTest,
1705 NegotiateAbsoluteCaptureTimeWhenExposedLocally) {
1706 MediaSessionOptions opts;
1707 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
1708
Emil Lundmark7d2e6162023-11-08 15:27:451709 const RtpHeaderExtensions offered_extensions = {
Minyue Li430e4a02020-03-10 09:59:371710 RtpExtension(RtpExtension::kAbsoluteCaptureTimeUri, 7)};
Emil Lundmark7d2e6162023-11-08 15:27:451711 const RtpHeaderExtensions local_extensions = {
Minyue Li430e4a02020-03-10 09:59:371712 RtpExtension(RtpExtension::kAbsoluteCaptureTimeUri, 5)};
Markus Handell755c65d2020-06-23 23:06:101713 SetAudioVideoRtpHeaderExtensions(offered_extensions, offered_extensions,
1714 &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461715 std::unique_ptr<SessionDescription> offer =
1716 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Markus Handell755c65d2020-06-23 23:06:101717 SetAudioVideoRtpHeaderExtensions(local_extensions, local_extensions, &opts);
Minyue Li430e4a02020-03-10 09:59:371718 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461719 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Minyue Li430e4a02020-03-10 09:59:371720 EXPECT_THAT(
1721 GetFirstVideoContentDescription(answer.get())->rtp_header_extensions(),
1722 ElementsAreArray(offered_extensions));
1723 EXPECT_THAT(
1724 GetFirstAudioContentDescription(answer.get())->rtp_header_extensions(),
1725 ElementsAreArray(offered_extensions));
1726}
1727
1728TEST_F(MediaSessionDescriptionFactoryTest,
1729 DoNotNegotiateAbsoluteCaptureTimeWhenNotOffered) {
1730 MediaSessionOptions opts;
1731 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
1732
Emil Lundmark7d2e6162023-11-08 15:27:451733 const RtpHeaderExtensions offered_extensions = {
Minyue Li430e4a02020-03-10 09:59:371734 RtpExtension(RtpExtension::kTransportSequenceNumberUri, 7)};
Emil Lundmark7d2e6162023-11-08 15:27:451735 const RtpHeaderExtensions local_extensions = {
Minyue Li430e4a02020-03-10 09:59:371736 RtpExtension(RtpExtension::kAbsoluteCaptureTimeUri, 5)};
Markus Handell755c65d2020-06-23 23:06:101737 SetAudioVideoRtpHeaderExtensions(offered_extensions, offered_extensions,
1738 &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461739 std::unique_ptr<SessionDescription> offer =
1740 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Markus Handell755c65d2020-06-23 23:06:101741 SetAudioVideoRtpHeaderExtensions(local_extensions, local_extensions, &opts);
Minyue Li430e4a02020-03-10 09:59:371742 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461743 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Minyue Li430e4a02020-03-10 09:59:371744 EXPECT_THAT(
1745 GetFirstVideoContentDescription(answer.get())->rtp_header_extensions(),
1746 IsEmpty());
1747 EXPECT_THAT(
1748 GetFirstAudioContentDescription(answer.get())->rtp_header_extensions(),
1749 IsEmpty());
1750}
1751
1752TEST_F(MediaSessionDescriptionFactoryTest,
Markus Handell755c65d2020-06-23 23:06:101753 OffersUnstoppedExtensionsWithAudioVideoExtensionStopped) {
1754 MediaSessionOptions opts;
1755 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
1756 RtpTransceiverDirection::kSendRecv, kActive,
1757 &opts);
1758 opts.media_description_options.back().header_extensions = {
1759 webrtc::RtpHeaderExtensionCapability("uri1", 1,
1760 RtpTransceiverDirection::kStopped),
1761 webrtc::RtpHeaderExtensionCapability("uri2", 3,
1762 RtpTransceiverDirection::kSendOnly)};
1763 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video1",
1764 RtpTransceiverDirection::kSendRecv, kActive,
1765 &opts);
1766 opts.media_description_options.back().header_extensions = {
1767 webrtc::RtpHeaderExtensionCapability("uri1", 1,
1768 RtpTransceiverDirection::kStopped),
1769 webrtc::RtpHeaderExtensionCapability("uri3", 7,
1770 RtpTransceiverDirection::kSendOnly)};
Philipp Hancke2bf1b992023-09-26 07:04:461771 auto offer = f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Markus Handell755c65d2020-06-23 23:06:101772 EXPECT_THAT(
1773 offer->contents(),
1774 ElementsAre(
1775 Property(&ContentInfo::media_description,
1776 Pointee(Property(
1777 &MediaContentDescription::rtp_header_extensions,
1778 ElementsAre(Field(&RtpExtension::uri, "uri2"))))),
1779 Property(&ContentInfo::media_description,
1780 Pointee(Property(
1781 &MediaContentDescription::rtp_header_extensions,
1782 ElementsAre(Field(&RtpExtension::uri, "uri3")))))));
1783}
1784
1785TEST_F(MediaSessionDescriptionFactoryTest,
1786 OffersUnstoppedExtensionsWithAudioExtensionStopped) {
1787 MediaSessionOptions opts;
1788 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
1789 RtpTransceiverDirection::kSendRecv, kActive,
1790 &opts);
1791 opts.media_description_options.back().header_extensions = {
1792 webrtc::RtpHeaderExtensionCapability("uri1", 1,
1793 RtpTransceiverDirection::kSendOnly),
1794 webrtc::RtpHeaderExtensionCapability("uri2", 3,
1795 RtpTransceiverDirection::kStopped)};
1796 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video1",
1797 RtpTransceiverDirection::kSendRecv, kActive,
1798 &opts);
1799 opts.media_description_options.back().header_extensions = {
1800 webrtc::RtpHeaderExtensionCapability("uri42", 42,
1801 RtpTransceiverDirection::kSendRecv),
1802 webrtc::RtpHeaderExtensionCapability("uri3", 7,
1803 RtpTransceiverDirection::kSendOnly)};
Philipp Hancke2bf1b992023-09-26 07:04:461804 auto offer = f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Markus Handell755c65d2020-06-23 23:06:101805 EXPECT_THAT(
1806 offer->contents(),
1807 ElementsAre(
1808 Property(&ContentInfo::media_description,
1809 Pointee(Property(
1810 &MediaContentDescription::rtp_header_extensions,
1811 ElementsAre(Field(&RtpExtension::uri, "uri1"))))),
1812 Property(
1813 &ContentInfo::media_description,
1814 Pointee(Property(
1815 &MediaContentDescription::rtp_header_extensions,
1816 UnorderedElementsAre(Field(&RtpExtension::uri, "uri3"),
1817 Field(&RtpExtension::uri, "uri42")))))));
1818}
1819
1820TEST_F(MediaSessionDescriptionFactoryTest,
1821 OffersUnstoppedExtensionsWithVideoExtensionStopped) {
1822 MediaSessionOptions opts;
1823 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
1824 RtpTransceiverDirection::kSendRecv, kActive,
1825 &opts);
1826 opts.media_description_options.back().header_extensions = {
1827 webrtc::RtpHeaderExtensionCapability("uri1", 5,
1828 RtpTransceiverDirection::kSendOnly),
1829 webrtc::RtpHeaderExtensionCapability("uri2", 7,
1830 RtpTransceiverDirection::kSendRecv)};
1831 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video1",
1832 RtpTransceiverDirection::kSendRecv, kActive,
1833 &opts);
1834 opts.media_description_options.back().header_extensions = {
1835 webrtc::RtpHeaderExtensionCapability("uri42", 42,
1836 RtpTransceiverDirection::kSendRecv),
1837 webrtc::RtpHeaderExtensionCapability("uri3", 7,
1838 RtpTransceiverDirection::kStopped)};
Philipp Hancke2bf1b992023-09-26 07:04:461839 auto offer = f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Markus Handell755c65d2020-06-23 23:06:101840 EXPECT_THAT(
1841 offer->contents(),
1842 ElementsAre(
1843 Property(
1844 &ContentInfo::media_description,
1845 Pointee(Property(
1846 &MediaContentDescription::rtp_header_extensions,
1847 UnorderedElementsAre(Field(&RtpExtension::uri, "uri1"),
1848 Field(&RtpExtension::uri, "uri2"))))),
1849 Property(&ContentInfo::media_description,
1850 Pointee(Property(
1851 &MediaContentDescription::rtp_header_extensions,
1852 ElementsAre(Field(&RtpExtension::uri, "uri42")))))));
1853}
1854
1855TEST_F(MediaSessionDescriptionFactoryTest, AnswersUnstoppedExtensions) {
1856 MediaSessionOptions opts;
1857 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
1858 RtpTransceiverDirection::kSendRecv, kActive,
1859 &opts);
1860 opts.media_description_options.back().header_extensions = {
1861 webrtc::RtpHeaderExtensionCapability("uri1", 4,
1862 RtpTransceiverDirection::kStopped),
1863 webrtc::RtpHeaderExtensionCapability("uri2", 3,
1864 RtpTransceiverDirection::kSendOnly),
1865 webrtc::RtpHeaderExtensionCapability("uri3", 2,
1866 RtpTransceiverDirection::kRecvOnly),
1867 webrtc::RtpHeaderExtensionCapability("uri4", 1,
1868 RtpTransceiverDirection::kSendRecv)};
Philipp Hancke2bf1b992023-09-26 07:04:461869 auto offer = f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Markus Handell755c65d2020-06-23 23:06:101870 opts.media_description_options.back().header_extensions = {
1871 webrtc::RtpHeaderExtensionCapability("uri1", 4,
1872 RtpTransceiverDirection::kSendOnly),
1873 webrtc::RtpHeaderExtensionCapability("uri2", 3,
1874 RtpTransceiverDirection::kRecvOnly),
1875 webrtc::RtpHeaderExtensionCapability("uri3", 2,
1876 RtpTransceiverDirection::kStopped),
1877 webrtc::RtpHeaderExtensionCapability("uri4", 1,
1878 RtpTransceiverDirection::kSendRecv)};
Philipp Hancke2bf1b992023-09-26 07:04:461879 auto answer = f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Markus Handell755c65d2020-06-23 23:06:101880 EXPECT_THAT(
1881 answer->contents(),
1882 ElementsAre(Property(
1883 &ContentInfo::media_description,
1884 Pointee(Property(&MediaContentDescription::rtp_header_extensions,
1885 ElementsAre(Field(&RtpExtension::uri, "uri2"),
1886 Field(&RtpExtension::uri, "uri4")))))));
1887}
1888
1889TEST_F(MediaSessionDescriptionFactoryTest,
1890 AppendsUnstoppedExtensionsToCurrentDescription) {
1891 MediaSessionOptions opts;
1892 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
1893 RtpTransceiverDirection::kSendRecv, kActive,
1894 &opts);
1895 opts.media_description_options.back().header_extensions = {
1896 webrtc::RtpHeaderExtensionCapability("uri1", 1,
1897 RtpTransceiverDirection::kSendRecv)};
Philipp Hancke2bf1b992023-09-26 07:04:461898 auto offer = f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Markus Handell755c65d2020-06-23 23:06:101899 opts.media_description_options.back().header_extensions = {
1900 webrtc::RtpHeaderExtensionCapability("uri1", 2,
1901 RtpTransceiverDirection::kSendRecv),
1902 webrtc::RtpHeaderExtensionCapability("uri2", 3,
1903 RtpTransceiverDirection::kRecvOnly),
1904 webrtc::RtpHeaderExtensionCapability("uri3", 5,
1905 RtpTransceiverDirection::kStopped),
1906 webrtc::RtpHeaderExtensionCapability("uri4", 6,
1907 RtpTransceiverDirection::kSendRecv)};
Philipp Hancke2bf1b992023-09-26 07:04:461908 auto offer2 = f1_.CreateOfferOrError(opts, offer.get()).MoveValue();
Markus Handell755c65d2020-06-23 23:06:101909 EXPECT_THAT(
1910 offer2->contents(),
1911 ElementsAre(Property(
1912 &ContentInfo::media_description,
1913 Pointee(Property(&MediaContentDescription::rtp_header_extensions,
1914 ElementsAre(Field(&RtpExtension::uri, "uri1"),
1915 Field(&RtpExtension::uri, "uri2"),
1916 Field(&RtpExtension::uri, "uri4")))))));
1917}
1918
1919TEST_F(MediaSessionDescriptionFactoryTest,
Philipp Hancke49e55872023-03-30 11:51:391920 AllowsStoppedExtensionsToBeRemovedFromSubsequentOffer) {
Markus Handell755c65d2020-06-23 23:06:101921 MediaSessionOptions opts;
1922 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
1923 RtpTransceiverDirection::kSendRecv, kActive,
1924 &opts);
1925 opts.media_description_options.back().header_extensions = {
1926 webrtc::RtpHeaderExtensionCapability("uri1", 1,
1927 RtpTransceiverDirection::kSendRecv),
Philipp Hancke49e55872023-03-30 11:51:391928 webrtc::RtpHeaderExtensionCapability("uri2", 2,
Markus Handell755c65d2020-06-23 23:06:101929 RtpTransceiverDirection::kSendRecv)};
Philipp Hancke2bf1b992023-09-26 07:04:461930 auto offer = f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Markus Handell755c65d2020-06-23 23:06:101931
Philipp Hancke49e55872023-03-30 11:51:391932 // Check that a subsequent offer after setting "uri2" to stopped no longer
1933 // contains the extension.
Markus Handell755c65d2020-06-23 23:06:101934 opts.media_description_options.back().header_extensions = {
1935 webrtc::RtpHeaderExtensionCapability("uri1", 1,
1936 RtpTransceiverDirection::kSendRecv),
1937 webrtc::RtpHeaderExtensionCapability("uri2", 2,
1938 RtpTransceiverDirection::kStopped)};
Philipp Hancke2bf1b992023-09-26 07:04:461939 auto offer2 = f1_.CreateOfferOrError(opts, offer.get()).MoveValue();
Markus Handell755c65d2020-06-23 23:06:101940 EXPECT_THAT(
1941 offer2->contents(),
1942 ElementsAre(Property(
1943 &ContentInfo::media_description,
1944 Pointee(Property(&MediaContentDescription::rtp_header_extensions,
Philipp Hancke49e55872023-03-30 11:51:391945 ElementsAre(Field(&RtpExtension::uri, "uri1")))))));
Markus Handell755c65d2020-06-23 23:06:101946}
1947
1948TEST_F(MediaSessionDescriptionFactoryTest,
Yves Gerey665174f2018-06-19 13:03:051949 TestOfferAnswerWithEncryptedRtpExtensionsBoth) {
jbauch5869f502017-06-29 19:31:361950 MediaSessionOptions opts;
Steve Anton4e70a722017-11-28 22:57:101951 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
jbauch5869f502017-06-29 19:31:361952
1953 f1_.set_enable_encrypted_rtp_header_extensions(true);
1954 f2_.set_enable_encrypted_rtp_header_extensions(true);
1955
Markus Handell755c65d2020-06-23 23:06:101956 SetAudioVideoRtpHeaderExtensions(MAKE_VECTOR(kAudioRtpExtension1),
1957 MAKE_VECTOR(kVideoRtpExtension1), &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461958 std::unique_ptr<SessionDescription> offer =
1959 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
1960 ASSERT_TRUE(offer.get());
Markus Handell755c65d2020-06-23 23:06:101961 SetAudioVideoRtpHeaderExtensions(MAKE_VECTOR(kAudioRtpExtension2),
1962 MAKE_VECTOR(kVideoRtpExtension2), &opts);
Steve Anton6fe1fba2018-12-11 18:15:231963 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461964 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
jbauch5869f502017-06-29 19:31:361965
Yves Gerey665174f2018-06-19 13:03:051966 EXPECT_EQ(
1967 MAKE_VECTOR(kAudioRtpExtensionEncrypted1),
1968 GetFirstAudioContentDescription(offer.get())->rtp_header_extensions());
1969 EXPECT_EQ(
1970 MAKE_VECTOR(kVideoRtpExtensionEncrypted1),
1971 GetFirstVideoContentDescription(offer.get())->rtp_header_extensions());
1972 EXPECT_EQ(
1973 MAKE_VECTOR(kAudioRtpExtensionEncryptedAnswer),
1974 GetFirstAudioContentDescription(answer.get())->rtp_header_extensions());
1975 EXPECT_EQ(
1976 MAKE_VECTOR(kVideoRtpExtensionEncryptedAnswer),
1977 GetFirstVideoContentDescription(answer.get())->rtp_header_extensions());
jbauch5869f502017-06-29 19:31:361978}
1979
1980TEST_F(MediaSessionDescriptionFactoryTest,
Yves Gerey665174f2018-06-19 13:03:051981 TestOfferAnswerWithEncryptedRtpExtensionsOffer) {
jbauch5869f502017-06-29 19:31:361982 MediaSessionOptions opts;
Steve Anton4e70a722017-11-28 22:57:101983 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
jbauch5869f502017-06-29 19:31:361984
1985 f1_.set_enable_encrypted_rtp_header_extensions(true);
1986
Markus Handell755c65d2020-06-23 23:06:101987 SetAudioVideoRtpHeaderExtensions(MAKE_VECTOR(kAudioRtpExtension1),
1988 MAKE_VECTOR(kVideoRtpExtension1), &opts);
Philipp Hancke2bf1b992023-09-26 07:04:461989 std::unique_ptr<SessionDescription> offer =
1990 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
1991 ASSERT_TRUE(offer.get());
Markus Handell755c65d2020-06-23 23:06:101992 SetAudioVideoRtpHeaderExtensions(MAKE_VECTOR(kAudioRtpExtension2),
1993 MAKE_VECTOR(kVideoRtpExtension2), &opts);
Steve Anton6fe1fba2018-12-11 18:15:231994 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:461995 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
jbauch5869f502017-06-29 19:31:361996
Yves Gerey665174f2018-06-19 13:03:051997 EXPECT_EQ(
1998 MAKE_VECTOR(kAudioRtpExtensionEncrypted1),
1999 GetFirstAudioContentDescription(offer.get())->rtp_header_extensions());
2000 EXPECT_EQ(
2001 MAKE_VECTOR(kVideoRtpExtensionEncrypted1),
2002 GetFirstVideoContentDescription(offer.get())->rtp_header_extensions());
2003 EXPECT_EQ(
2004 MAKE_VECTOR(kAudioRtpExtensionAnswer),
2005 GetFirstAudioContentDescription(answer.get())->rtp_header_extensions());
2006 EXPECT_EQ(
2007 MAKE_VECTOR(kVideoRtpExtensionAnswer),
2008 GetFirstVideoContentDescription(answer.get())->rtp_header_extensions());
jbauch5869f502017-06-29 19:31:362009}
2010
2011TEST_F(MediaSessionDescriptionFactoryTest,
Yves Gerey665174f2018-06-19 13:03:052012 TestOfferAnswerWithEncryptedRtpExtensionsAnswer) {
jbauch5869f502017-06-29 19:31:362013 MediaSessionOptions opts;
Steve Anton4e70a722017-11-28 22:57:102014 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
jbauch5869f502017-06-29 19:31:362015
2016 f2_.set_enable_encrypted_rtp_header_extensions(true);
2017
Markus Handell755c65d2020-06-23 23:06:102018 SetAudioVideoRtpHeaderExtensions(MAKE_VECTOR(kAudioRtpExtension1),
2019 MAKE_VECTOR(kVideoRtpExtension1), &opts);
Philipp Hancke2bf1b992023-09-26 07:04:462020 std::unique_ptr<SessionDescription> offer =
2021 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
2022 ASSERT_TRUE(offer.get());
Markus Handell755c65d2020-06-23 23:06:102023 SetAudioVideoRtpHeaderExtensions(MAKE_VECTOR(kAudioRtpExtension2),
2024 MAKE_VECTOR(kVideoRtpExtension2), &opts);
Steve Anton6fe1fba2018-12-11 18:15:232025 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:462026 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
jbauch5869f502017-06-29 19:31:362027
Yves Gerey665174f2018-06-19 13:03:052028 EXPECT_EQ(
2029 MAKE_VECTOR(kAudioRtpExtension1),
2030 GetFirstAudioContentDescription(offer.get())->rtp_header_extensions());
2031 EXPECT_EQ(
2032 MAKE_VECTOR(kVideoRtpExtension1),
2033 GetFirstVideoContentDescription(offer.get())->rtp_header_extensions());
2034 EXPECT_EQ(
2035 MAKE_VECTOR(kAudioRtpExtensionAnswer),
2036 GetFirstAudioContentDescription(answer.get())->rtp_header_extensions());
2037 EXPECT_EQ(
2038 MAKE_VECTOR(kVideoRtpExtensionAnswer),
2039 GetFirstVideoContentDescription(answer.get())->rtp_header_extensions());
jbauch5869f502017-06-29 19:31:362040}
2041
henrike@webrtc.org28e20752013-07-10 00:45:362042// Create an audio, video, data answer without legacy StreamParams.
2043TEST_F(MediaSessionDescriptionFactoryTest,
2044 TestCreateAnswerWithoutLegacyStreams) {
2045 MediaSessionOptions opts;
Steve Anton4e70a722017-11-28 22:57:102046 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:462047 std::unique_ptr<SessionDescription> offer =
2048 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
2049 ASSERT_TRUE(offer.get());
Steve Anton6fe1fba2018-12-11 18:15:232050 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:462051 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:362052 const ContentInfo* ac = answer->GetContentByName("audio");
2053 const ContentInfo* vc = answer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:462054 ASSERT_TRUE(ac);
2055 ASSERT_TRUE(vc);
Philipp Hancked0f0f382023-11-23 19:21:052056 const MediaContentDescription* acd = ac->media_description();
2057 const MediaContentDescription* vcd = vc->media_description();
henrike@webrtc.org28e20752013-07-10 00:45:362058
2059 EXPECT_FALSE(acd->has_ssrcs()); // No StreamParams.
2060 EXPECT_FALSE(vcd->has_ssrcs()); // No StreamParams.
henrike@webrtc.org28e20752013-07-10 00:45:362061}
2062
henrike@webrtc.org28e20752013-07-10 00:45:362063// Create a typical video answer, and ensure it matches what we expect.
2064TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswerRtcpMux) {
2065 MediaSessionOptions offer_opts;
Steve Anton4e70a722017-11-28 22:57:102066 AddAudioVideoSections(RtpTransceiverDirection::kSendRecv, &offer_opts);
zhihuang1c378ed2017-08-17 21:10:502067
henrike@webrtc.org28e20752013-07-10 00:45:362068 MediaSessionOptions answer_opts;
Steve Anton4e70a722017-11-28 22:57:102069 AddAudioVideoSections(RtpTransceiverDirection::kSendRecv, &answer_opts);
henrike@webrtc.org28e20752013-07-10 00:45:362070
kwiberg31022942016-03-11 22:18:212071 std::unique_ptr<SessionDescription> offer;
2072 std::unique_ptr<SessionDescription> answer;
henrike@webrtc.org28e20752013-07-10 00:45:362073
2074 offer_opts.rtcp_mux_enabled = true;
2075 answer_opts.rtcp_mux_enabled = true;
Philipp Hancke2bf1b992023-09-26 07:04:462076 offer = f1_.CreateOfferOrError(offer_opts, nullptr).MoveValue();
2077 answer =
2078 f2_.CreateAnswerOrError(offer.get(), answer_opts, nullptr).MoveValue();
2079 ASSERT_TRUE(GetFirstAudioContentDescription(offer.get()));
2080 ASSERT_TRUE(GetFirstVideoContentDescription(offer.get()));
2081 ASSERT_TRUE(GetFirstAudioContentDescription(answer.get()));
2082 ASSERT_TRUE(GetFirstVideoContentDescription(answer.get()));
henrike@webrtc.org28e20752013-07-10 00:45:362083 EXPECT_TRUE(GetFirstAudioContentDescription(offer.get())->rtcp_mux());
2084 EXPECT_TRUE(GetFirstVideoContentDescription(offer.get())->rtcp_mux());
henrike@webrtc.org28e20752013-07-10 00:45:362085 EXPECT_TRUE(GetFirstAudioContentDescription(answer.get())->rtcp_mux());
2086 EXPECT_TRUE(GetFirstVideoContentDescription(answer.get())->rtcp_mux());
henrike@webrtc.org28e20752013-07-10 00:45:362087
2088 offer_opts.rtcp_mux_enabled = true;
2089 answer_opts.rtcp_mux_enabled = false;
Philipp Hancke2bf1b992023-09-26 07:04:462090 offer = f1_.CreateOfferOrError(offer_opts, nullptr).MoveValue();
2091 answer =
2092 f2_.CreateAnswerOrError(offer.get(), answer_opts, nullptr).MoveValue();
2093 ASSERT_TRUE(GetFirstAudioContentDescription(offer.get()));
2094 ASSERT_TRUE(GetFirstVideoContentDescription(offer.get()));
2095 ASSERT_TRUE(GetFirstAudioContentDescription(answer.get()));
2096 ASSERT_TRUE(GetFirstVideoContentDescription(answer.get()));
henrike@webrtc.org28e20752013-07-10 00:45:362097 EXPECT_TRUE(GetFirstAudioContentDescription(offer.get())->rtcp_mux());
2098 EXPECT_TRUE(GetFirstVideoContentDescription(offer.get())->rtcp_mux());
henrike@webrtc.org28e20752013-07-10 00:45:362099 EXPECT_FALSE(GetFirstAudioContentDescription(answer.get())->rtcp_mux());
2100 EXPECT_FALSE(GetFirstVideoContentDescription(answer.get())->rtcp_mux());
henrike@webrtc.org28e20752013-07-10 00:45:362101
2102 offer_opts.rtcp_mux_enabled = false;
2103 answer_opts.rtcp_mux_enabled = true;
Philipp Hancke2bf1b992023-09-26 07:04:462104 offer = f1_.CreateOfferOrError(offer_opts, nullptr).MoveValue();
2105 answer =
2106 f2_.CreateAnswerOrError(offer.get(), answer_opts, nullptr).MoveValue();
2107 ASSERT_TRUE(GetFirstAudioContentDescription(offer.get()));
2108 ASSERT_TRUE(GetFirstVideoContentDescription(offer.get()));
2109 ASSERT_TRUE(GetFirstAudioContentDescription(answer.get()));
2110 ASSERT_TRUE(GetFirstVideoContentDescription(answer.get()));
henrike@webrtc.org28e20752013-07-10 00:45:362111 EXPECT_FALSE(GetFirstAudioContentDescription(offer.get())->rtcp_mux());
2112 EXPECT_FALSE(GetFirstVideoContentDescription(offer.get())->rtcp_mux());
henrike@webrtc.org28e20752013-07-10 00:45:362113 EXPECT_FALSE(GetFirstAudioContentDescription(answer.get())->rtcp_mux());
2114 EXPECT_FALSE(GetFirstVideoContentDescription(answer.get())->rtcp_mux());
henrike@webrtc.org28e20752013-07-10 00:45:362115
2116 offer_opts.rtcp_mux_enabled = false;
2117 answer_opts.rtcp_mux_enabled = false;
Philipp Hancke2bf1b992023-09-26 07:04:462118 offer = f1_.CreateOfferOrError(offer_opts, nullptr).MoveValue();
2119 answer =
2120 f2_.CreateAnswerOrError(offer.get(), answer_opts, nullptr).MoveValue();
2121 ASSERT_TRUE(GetFirstAudioContentDescription(offer.get()));
2122 ASSERT_TRUE(GetFirstVideoContentDescription(offer.get()));
2123 ASSERT_TRUE(GetFirstAudioContentDescription(answer.get()));
2124 ASSERT_TRUE(GetFirstVideoContentDescription(answer.get()));
henrike@webrtc.org28e20752013-07-10 00:45:362125 EXPECT_FALSE(GetFirstAudioContentDescription(offer.get())->rtcp_mux());
2126 EXPECT_FALSE(GetFirstVideoContentDescription(offer.get())->rtcp_mux());
henrike@webrtc.org28e20752013-07-10 00:45:362127 EXPECT_FALSE(GetFirstAudioContentDescription(answer.get())->rtcp_mux());
2128 EXPECT_FALSE(GetFirstVideoContentDescription(answer.get())->rtcp_mux());
henrike@webrtc.org28e20752013-07-10 00:45:362129}
2130
2131// Create an audio-only answer to a video offer.
2132TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswerToVideo) {
2133 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:582134 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
2135 RtpTransceiverDirection::kRecvOnly, kActive,
2136 &opts);
2137 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
2138 RtpTransceiverDirection::kRecvOnly, kActive,
2139 &opts);
Philipp Hancke2bf1b992023-09-26 07:04:462140 std::unique_ptr<SessionDescription> offer =
2141 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
2142 ASSERT_TRUE(offer.get());
zhihuang1c378ed2017-08-17 21:10:502143
2144 opts.media_description_options[1].stopped = true;
Steve Anton6fe1fba2018-12-11 18:15:232145 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:462146 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:362147 const ContentInfo* ac = answer->GetContentByName("audio");
2148 const ContentInfo* vc = answer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:462149 ASSERT_TRUE(ac);
2150 ASSERT_TRUE(vc);
2151 ASSERT_TRUE(vc->media_description());
henrike@webrtc.org28e20752013-07-10 00:45:362152 EXPECT_TRUE(vc->rejected);
2153}
2154
henrike@webrtc.org28e20752013-07-10 00:45:362155// Create an answer that rejects the contents which are rejected in the offer.
2156TEST_F(MediaSessionDescriptionFactoryTest,
2157 CreateAnswerToOfferWithRejectedMedia) {
2158 MediaSessionOptions opts;
Steve Anton4e70a722017-11-28 22:57:102159 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:462160 std::unique_ptr<SessionDescription> offer =
2161 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
2162 ASSERT_TRUE(offer.get());
henrike@webrtc.org28e20752013-07-10 00:45:362163 ContentInfo* ac = offer->GetContentByName("audio");
2164 ContentInfo* vc = offer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:462165 ASSERT_TRUE(ac);
2166 ASSERT_TRUE(vc);
henrike@webrtc.org28e20752013-07-10 00:45:362167 ac->rejected = true;
2168 vc->rejected = true;
Steve Anton6fe1fba2018-12-11 18:15:232169 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:462170 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:362171 ac = answer->GetContentByName("audio");
2172 vc = answer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:462173 ASSERT_TRUE(ac);
2174 ASSERT_TRUE(vc);
henrike@webrtc.org28e20752013-07-10 00:45:362175 EXPECT_TRUE(ac->rejected);
2176 EXPECT_TRUE(vc->rejected);
henrike@webrtc.org28e20752013-07-10 00:45:362177}
2178
Johannes Kron0854eb62018-10-10 20:33:202179TEST_F(MediaSessionDescriptionFactoryTest,
Emil Lundmark801c9992021-01-19 12:06:322180 OfferAndAnswerDoesNotHaveMixedByteSessionAttribute) {
Johannes Kron0854eb62018-10-10 20:33:202181 MediaSessionOptions opts;
Emil Lundmark801c9992021-01-19 12:06:322182 std::unique_ptr<SessionDescription> offer =
Philipp Hancke2bf1b992023-09-26 07:04:462183 f1_.CreateOfferOrError(opts, /*current_description=*/nullptr).MoveValue();
Johannes Kron9581bc42018-10-23 08:17:392184 offer->set_extmap_allow_mixed(false);
Johannes Kron0854eb62018-10-10 20:33:202185
Emil Lundmark801c9992021-01-19 12:06:322186 std::unique_ptr<SessionDescription> answer(
Philipp Hancke2bf1b992023-09-26 07:04:462187 f2_.CreateAnswerOrError(offer.get(), opts,
2188 /*current_description=*/nullptr)
2189 .MoveValue());
Emil Lundmark801c9992021-01-19 12:06:322190
2191 EXPECT_FALSE(answer->extmap_allow_mixed());
2192}
2193
2194TEST_F(MediaSessionDescriptionFactoryTest,
2195 OfferAndAnswerHaveMixedByteSessionAttribute) {
2196 MediaSessionOptions opts;
2197 std::unique_ptr<SessionDescription> offer =
Philipp Hancke2bf1b992023-09-26 07:04:462198 f1_.CreateOfferOrError(opts, /*current_description=*/nullptr).MoveValue();
Johannes Kron9581bc42018-10-23 08:17:392199 offer->set_extmap_allow_mixed(true);
Emil Lundmark801c9992021-01-19 12:06:322200
Johannes Kron0854eb62018-10-10 20:33:202201 std::unique_ptr<SessionDescription> answer_support(
Philipp Hancke2bf1b992023-09-26 07:04:462202 f2_.CreateAnswerOrError(offer.get(), opts,
2203 /*current_description=*/nullptr)
2204 .MoveValue());
Emil Lundmark801c9992021-01-19 12:06:322205
Johannes Kron9581bc42018-10-23 08:17:392206 EXPECT_TRUE(answer_support->extmap_allow_mixed());
Johannes Kron0854eb62018-10-10 20:33:202207}
2208
2209TEST_F(MediaSessionDescriptionFactoryTest,
Emil Lundmark801c9992021-01-19 12:06:322210 OfferAndAnswerDoesNotHaveMixedByteMediaAttributes) {
Johannes Kron0854eb62018-10-10 20:33:202211 MediaSessionOptions opts;
2212 AddAudioVideoSections(RtpTransceiverDirection::kSendRecv, &opts);
Emil Lundmark801c9992021-01-19 12:06:322213 std::unique_ptr<SessionDescription> offer =
Philipp Hancke2bf1b992023-09-26 07:04:462214 f1_.CreateOfferOrError(opts, /*current_description=*/nullptr).MoveValue();
Emil Lundmark801c9992021-01-19 12:06:322215 offer->set_extmap_allow_mixed(false);
Johannes Kron0854eb62018-10-10 20:33:202216 MediaContentDescription* audio_offer =
2217 offer->GetContentDescriptionByName("audio");
Emil Lundmark801c9992021-01-19 12:06:322218 MediaContentDescription* video_offer =
2219 offer->GetContentDescriptionByName("video");
2220 ASSERT_EQ(MediaContentDescription::kNo,
2221 audio_offer->extmap_allow_mixed_enum());
2222 ASSERT_EQ(MediaContentDescription::kNo,
2223 video_offer->extmap_allow_mixed_enum());
Johannes Kron0854eb62018-10-10 20:33:202224
Emil Lundmark801c9992021-01-19 12:06:322225 std::unique_ptr<SessionDescription> answer(
Philipp Hancke2bf1b992023-09-26 07:04:462226 f2_.CreateAnswerOrError(offer.get(), opts,
2227 /*current_description=*/nullptr)
2228 .MoveValue());
Johannes Kron0854eb62018-10-10 20:33:202229
Johannes Kron0854eb62018-10-10 20:33:202230 MediaContentDescription* audio_answer =
Emil Lundmark801c9992021-01-19 12:06:322231 answer->GetContentDescriptionByName("audio");
2232 MediaContentDescription* video_answer =
2233 answer->GetContentDescriptionByName("video");
Johannes Kron0854eb62018-10-10 20:33:202234 EXPECT_EQ(MediaContentDescription::kNo,
Johannes Kron9581bc42018-10-23 08:17:392235 audio_answer->extmap_allow_mixed_enum());
Emil Lundmark801c9992021-01-19 12:06:322236 EXPECT_EQ(MediaContentDescription::kNo,
2237 video_answer->extmap_allow_mixed_enum());
2238}
Johannes Kron0854eb62018-10-10 20:33:202239
Emil Lundmark801c9992021-01-19 12:06:322240TEST_F(MediaSessionDescriptionFactoryTest,
2241 OfferAndAnswerHaveSameMixedByteMediaAttributes) {
2242 MediaSessionOptions opts;
2243 AddAudioVideoSections(RtpTransceiverDirection::kSendRecv, &opts);
2244 std::unique_ptr<SessionDescription> offer =
Philipp Hancke2bf1b992023-09-26 07:04:462245 f1_.CreateOfferOrError(opts, /*current_description=*/nullptr).MoveValue();
Emil Lundmark801c9992021-01-19 12:06:322246 offer->set_extmap_allow_mixed(false);
2247 MediaContentDescription* audio_offer =
2248 offer->GetContentDescriptionByName("audio");
Johannes Kron9581bc42018-10-23 08:17:392249 audio_offer->set_extmap_allow_mixed_enum(MediaContentDescription::kMedia);
Emil Lundmark801c9992021-01-19 12:06:322250 MediaContentDescription* video_offer =
2251 offer->GetContentDescriptionByName("video");
2252 video_offer->set_extmap_allow_mixed_enum(MediaContentDescription::kMedia);
2253
2254 std::unique_ptr<SessionDescription> answer(
Philipp Hancke2bf1b992023-09-26 07:04:462255 f2_.CreateAnswerOrError(offer.get(), opts,
2256 /*current_description=*/nullptr)
2257 .MoveValue());
Emil Lundmark801c9992021-01-19 12:06:322258
2259 MediaContentDescription* audio_answer =
2260 answer->GetContentDescriptionByName("audio");
2261 MediaContentDescription* video_answer =
2262 answer->GetContentDescriptionByName("video");
Johannes Kron0854eb62018-10-10 20:33:202263 EXPECT_EQ(MediaContentDescription::kMedia,
Johannes Kron9581bc42018-10-23 08:17:392264 audio_answer->extmap_allow_mixed_enum());
Emil Lundmark801c9992021-01-19 12:06:322265 EXPECT_EQ(MediaContentDescription::kMedia,
2266 video_answer->extmap_allow_mixed_enum());
2267}
2268
2269TEST_F(MediaSessionDescriptionFactoryTest,
2270 OfferAndAnswerHaveDifferentMixedByteMediaAttributes) {
2271 MediaSessionOptions opts;
2272 AddAudioVideoSections(RtpTransceiverDirection::kSendRecv, &opts);
2273 std::unique_ptr<SessionDescription> offer =
Philipp Hancke2bf1b992023-09-26 07:04:462274 f1_.CreateOfferOrError(opts, /*current_description=*/nullptr).MoveValue();
Emil Lundmark801c9992021-01-19 12:06:322275 offer->set_extmap_allow_mixed(false);
2276 MediaContentDescription* audio_offer =
2277 offer->GetContentDescriptionByName("audio");
2278 audio_offer->set_extmap_allow_mixed_enum(MediaContentDescription::kNo);
2279 MediaContentDescription* video_offer =
2280 offer->GetContentDescriptionByName("video");
2281 video_offer->set_extmap_allow_mixed_enum(MediaContentDescription::kMedia);
2282
2283 std::unique_ptr<SessionDescription> answer(
Philipp Hancke2bf1b992023-09-26 07:04:462284 f2_.CreateAnswerOrError(offer.get(), opts,
2285 /*current_description=*/nullptr)
2286 .MoveValue());
Emil Lundmark801c9992021-01-19 12:06:322287
2288 MediaContentDescription* audio_answer =
2289 answer->GetContentDescriptionByName("audio");
2290 MediaContentDescription* video_answer =
2291 answer->GetContentDescriptionByName("video");
2292 EXPECT_EQ(MediaContentDescription::kNo,
2293 audio_answer->extmap_allow_mixed_enum());
2294 EXPECT_EQ(MediaContentDescription::kMedia,
2295 video_answer->extmap_allow_mixed_enum());
Johannes Kron0854eb62018-10-10 20:33:202296}
2297
henrike@webrtc.org28e20752013-07-10 00:45:362298// Create an audio and video offer with:
2299// - one video track
2300// - two audio tracks
henrike@webrtc.org28e20752013-07-10 00:45:362301// and ensure it matches what we expect. Also updates the initial offer by
2302// adding a new video track and replaces one of the audio tracks.
2303TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoOffer) {
2304 MediaSessionOptions opts;
Steve Anton4e70a722017-11-28 22:57:102305 AddAudioVideoSections(RtpTransceiverDirection::kSendRecv, &opts);
Amit Hilbuchc63ddb22019-01-02 18:13:582306 AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1,
2307 {kMediaStream1}, 1, &opts);
2308 AttachSenderToMediaDescriptionOptions("audio", MEDIA_TYPE_AUDIO, kAudioTrack1,
2309 {kMediaStream1}, 1, &opts);
2310 AttachSenderToMediaDescriptionOptions("audio", MEDIA_TYPE_AUDIO, kAudioTrack2,
2311 {kMediaStream1}, 1, &opts);
zhihuang1c378ed2017-08-17 21:10:502312
Philipp Hancke2bf1b992023-09-26 07:04:462313 std::unique_ptr<SessionDescription> offer =
2314 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:362315
Philipp Hancke2bf1b992023-09-26 07:04:462316 ASSERT_TRUE(offer.get());
henrike@webrtc.org28e20752013-07-10 00:45:362317 const ContentInfo* ac = offer->GetContentByName("audio");
2318 const ContentInfo* vc = offer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:462319 ASSERT_TRUE(ac);
2320 ASSERT_TRUE(vc);
Philipp Hancked0f0f382023-11-23 19:21:052321 const MediaContentDescription* acd = ac->media_description();
2322 const MediaContentDescription* vcd = vc->media_description();
henrike@webrtc.org28e20752013-07-10 00:45:362323 EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
ossudedfd282016-06-14 14:12:392324 EXPECT_EQ(f1_.audio_sendrecv_codecs(), acd->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:362325
2326 const StreamParamsVec& audio_streams = acd->streams();
2327 ASSERT_EQ(2U, audio_streams.size());
Yves Gerey665174f2018-06-19 13:03:052328 EXPECT_EQ(audio_streams[0].cname, audio_streams[1].cname);
henrike@webrtc.org28e20752013-07-10 00:45:362329 EXPECT_EQ(kAudioTrack1, audio_streams[0].id);
2330 ASSERT_EQ(1U, audio_streams[0].ssrcs.size());
2331 EXPECT_NE(0U, audio_streams[0].ssrcs[0]);
2332 EXPECT_EQ(kAudioTrack2, audio_streams[1].id);
2333 ASSERT_EQ(1U, audio_streams[1].ssrcs.size());
2334 EXPECT_NE(0U, audio_streams[1].ssrcs[0]);
2335
2336 EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto)
2337 EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on
henrike@webrtc.org28e20752013-07-10 00:45:362338
2339 EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type());
Johannes Kron3e983682020-03-29 20:17:002340 EXPECT_EQ(f1_.video_sendrecv_codecs(), vcd->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:362341
2342 const StreamParamsVec& video_streams = vcd->streams();
2343 ASSERT_EQ(1U, video_streams.size());
2344 EXPECT_EQ(video_streams[0].cname, audio_streams[0].cname);
2345 EXPECT_EQ(kVideoTrack1, video_streams[0].id);
2346 EXPECT_EQ(kAutoBandwidth, vcd->bandwidth()); // default bandwidth (auto)
2347 EXPECT_TRUE(vcd->rtcp_mux()); // rtcp-mux defaults on
2348
henrike@webrtc.org28e20752013-07-10 00:45:362349 // Update the offer. Add a new video track that is not synched to the
2350 // other tracks and replace audio track 2 with audio track 3.
Amit Hilbuchc63ddb22019-01-02 18:13:582351 AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack2,
2352 {kMediaStream2}, 1, &opts);
zhihuang1c378ed2017-08-17 21:10:502353 DetachSenderFromMediaSection("audio", kAudioTrack2, &opts);
Amit Hilbuchc63ddb22019-01-02 18:13:582354 AttachSenderToMediaDescriptionOptions("audio", MEDIA_TYPE_AUDIO, kAudioTrack3,
2355 {kMediaStream1}, 1, &opts);
kwiberg31022942016-03-11 22:18:212356 std::unique_ptr<SessionDescription> updated_offer(
Philipp Hancke2bf1b992023-09-26 07:04:462357 f1_.CreateOfferOrError(opts, offer.get()).MoveValue());
henrike@webrtc.org28e20752013-07-10 00:45:362358
Philipp Hancke2bf1b992023-09-26 07:04:462359 ASSERT_TRUE(updated_offer.get());
henrike@webrtc.org28e20752013-07-10 00:45:362360 ac = updated_offer->GetContentByName("audio");
2361 vc = updated_offer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:462362 ASSERT_TRUE(ac);
2363 ASSERT_TRUE(vc);
Philipp Hancked0f0f382023-11-23 19:21:052364 const MediaContentDescription* updated_acd = ac->media_description();
2365 const MediaContentDescription* updated_vcd = vc->media_description();
henrike@webrtc.org28e20752013-07-10 00:45:362366
2367 EXPECT_EQ(acd->type(), updated_acd->type());
2368 EXPECT_EQ(acd->codecs(), updated_acd->codecs());
2369 EXPECT_EQ(vcd->type(), updated_vcd->type());
2370 EXPECT_EQ(vcd->codecs(), updated_vcd->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:362371
2372 const StreamParamsVec& updated_audio_streams = updated_acd->streams();
2373 ASSERT_EQ(2U, updated_audio_streams.size());
2374 EXPECT_EQ(audio_streams[0], updated_audio_streams[0]);
2375 EXPECT_EQ(kAudioTrack3, updated_audio_streams[1].id); // New audio track.
2376 ASSERT_EQ(1U, updated_audio_streams[1].ssrcs.size());
2377 EXPECT_NE(0U, updated_audio_streams[1].ssrcs[0]);
2378 EXPECT_EQ(updated_audio_streams[0].cname, updated_audio_streams[1].cname);
2379
2380 const StreamParamsVec& updated_video_streams = updated_vcd->streams();
2381 ASSERT_EQ(2U, updated_video_streams.size());
2382 EXPECT_EQ(video_streams[0], updated_video_streams[0]);
2383 EXPECT_EQ(kVideoTrack2, updated_video_streams[1].id);
zhihuang8f65cdf2016-05-07 01:40:302384 // All the media streams in one PeerConnection share one RTCP CNAME.
2385 EXPECT_EQ(updated_video_streams[1].cname, updated_video_streams[0].cname);
henrike@webrtc.org28e20752013-07-10 00:45:362386}
2387
wu@webrtc.orgcecfd182013-10-30 05:18:122388// Create an offer with simulcast video stream.
2389TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSimulcastVideoOffer) {
2390 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:582391 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
2392 RtpTransceiverDirection::kRecvOnly, kActive,
2393 &opts);
2394 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
2395 RtpTransceiverDirection::kSendRecv, kActive,
2396 &opts);
wu@webrtc.orgcecfd182013-10-30 05:18:122397 const int num_sim_layers = 3;
Amit Hilbuchc63ddb22019-01-02 18:13:582398 AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1,
2399 {kMediaStream1}, num_sim_layers, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:462400 std::unique_ptr<SessionDescription> offer =
2401 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
wu@webrtc.orgcecfd182013-10-30 05:18:122402
Philipp Hancke2bf1b992023-09-26 07:04:462403 ASSERT_TRUE(offer.get());
wu@webrtc.orgcecfd182013-10-30 05:18:122404 const ContentInfo* vc = offer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:462405 ASSERT_TRUE(vc);
Philipp Hancked0f0f382023-11-23 19:21:052406 const MediaContentDescription* vcd = vc->media_description();
wu@webrtc.orgcecfd182013-10-30 05:18:122407
2408 const StreamParamsVec& video_streams = vcd->streams();
2409 ASSERT_EQ(1U, video_streams.size());
2410 EXPECT_EQ(kVideoTrack1, video_streams[0].id);
2411 const SsrcGroup* sim_ssrc_group =
Emil Lundmark7d2e6162023-11-08 15:27:452412 video_streams[0].get_ssrc_group(kSimSsrcGroupSemantics);
Philipp Hancke2bf1b992023-09-26 07:04:462413 ASSERT_TRUE(sim_ssrc_group);
wu@webrtc.orgcecfd182013-10-30 05:18:122414 EXPECT_EQ(static_cast<size_t>(num_sim_layers), sim_ssrc_group->ssrcs.size());
2415}
2416
Amit Hilbuchc63ddb22019-01-02 18:13:582417MATCHER(RidDescriptionEquals, "Verifies that two RidDescriptions are equal.") {
Emil Lundmark7d2e6162023-11-08 15:27:452418 const RidDescription& rid1 = std::get<0>(arg);
2419 const RidDescription& rid2 = std::get<1>(arg);
Amit Hilbuchc63ddb22019-01-02 18:13:582420 return rid1.rid == rid2.rid && rid1.direction == rid2.direction;
2421}
2422
Emil Lundmark7d2e6162023-11-08 15:27:452423void CheckSimulcastInSessionDescription(
Amit Hilbuchc63ddb22019-01-02 18:13:582424 const SessionDescription* description,
2425 const std::string& content_name,
2426 const std::vector<RidDescription>& send_rids,
Amit Hilbuchb7446ed2019-01-28 20:25:252427 const SimulcastLayerList& send_layers) {
Amit Hilbuchc63ddb22019-01-02 18:13:582428 ASSERT_NE(description, nullptr);
2429 const ContentInfo* content = description->GetContentByName(content_name);
2430 ASSERT_NE(content, nullptr);
2431 const MediaContentDescription* cd = content->media_description();
2432 ASSERT_NE(cd, nullptr);
2433 const StreamParamsVec& streams = cd->streams();
2434 ASSERT_THAT(streams, SizeIs(1));
2435 const StreamParams& stream = streams[0];
2436 ASSERT_THAT(stream.ssrcs, IsEmpty());
2437 EXPECT_TRUE(stream.has_rids());
2438 const std::vector<RidDescription> rids = stream.rids();
2439
2440 EXPECT_THAT(rids, Pointwise(RidDescriptionEquals(), send_rids));
2441
Amit Hilbuchc63ddb22019-01-02 18:13:582442 EXPECT_TRUE(cd->HasSimulcast());
2443 const SimulcastDescription& simulcast = cd->simulcast_description();
2444 EXPECT_THAT(simulcast.send_layers(), SizeIs(send_layers.size()));
2445 EXPECT_THAT(simulcast.send_layers(), Pointwise(Eq(), send_layers));
2446
Amit Hilbuchb7446ed2019-01-28 20:25:252447 ASSERT_THAT(simulcast.receive_layers().GetAllLayers(), SizeIs(0));
Amit Hilbuchc63ddb22019-01-02 18:13:582448}
2449
2450// Create an offer with spec-compliant simulcast video stream.
2451TEST_F(MediaSessionDescriptionFactoryTest, TestCreateCompliantSimulcastOffer) {
2452 MediaSessionOptions opts;
2453 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
2454 RtpTransceiverDirection::kSendRecv, kActive,
2455 &opts);
Amit Hilbuchc63ddb22019-01-02 18:13:582456 std::vector<RidDescription> send_rids;
2457 send_rids.push_back(RidDescription("f", RidDirection::kSend));
2458 send_rids.push_back(RidDescription("h", RidDirection::kSend));
2459 send_rids.push_back(RidDescription("q", RidDirection::kSend));
2460 SimulcastLayerList simulcast_layers;
2461 simulcast_layers.AddLayer(SimulcastLayer(send_rids[0].rid, false));
2462 simulcast_layers.AddLayer(SimulcastLayer(send_rids[1].rid, true));
2463 simulcast_layers.AddLayer(SimulcastLayer(send_rids[2].rid, false));
2464 AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1,
2465 {kMediaStream1}, send_rids,
2466 simulcast_layers, 0, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:462467 std::unique_ptr<SessionDescription> offer =
2468 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Amit Hilbuchc63ddb22019-01-02 18:13:582469
2470 CheckSimulcastInSessionDescription(offer.get(), "video", send_rids,
Amit Hilbuchb7446ed2019-01-28 20:25:252471 simulcast_layers);
Amit Hilbuchc63ddb22019-01-02 18:13:582472}
2473
2474// Create an offer that signals RIDs (not SSRCs) without Simulcast.
2475// In this scenario, RIDs do not need to be negotiated (there is only one).
2476TEST_F(MediaSessionDescriptionFactoryTest, TestOfferWithRidsNoSimulcast) {
2477 MediaSessionOptions opts;
2478 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
2479 RtpTransceiverDirection::kSendRecv, kActive,
2480 &opts);
2481 RidDescription rid("f", RidDirection::kSend);
2482 AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1,
2483 {kMediaStream1}, {rid},
2484 SimulcastLayerList(), 0, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:462485 std::unique_ptr<SessionDescription> offer =
2486 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Amit Hilbuchc63ddb22019-01-02 18:13:582487
2488 ASSERT_NE(offer.get(), nullptr);
2489 const ContentInfo* content = offer->GetContentByName("video");
2490 ASSERT_NE(content, nullptr);
2491 const MediaContentDescription* cd = content->media_description();
2492 ASSERT_NE(cd, nullptr);
Amit Hilbuchc63ddb22019-01-02 18:13:582493 const StreamParamsVec& streams = cd->streams();
2494 ASSERT_THAT(streams, SizeIs(1));
2495 const StreamParams& stream = streams[0];
2496 ASSERT_THAT(stream.ssrcs, IsEmpty());
2497 EXPECT_FALSE(stream.has_rids());
2498 EXPECT_FALSE(cd->HasSimulcast());
2499}
2500
2501// Create an answer with spec-compliant simulcast video stream.
2502// In this scenario, the SFU is the caller requesting that we send Simulcast.
2503TEST_F(MediaSessionDescriptionFactoryTest, TestCreateCompliantSimulcastAnswer) {
2504 MediaSessionOptions offer_opts;
2505 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
2506 RtpTransceiverDirection::kSendRecv, kActive,
2507 &offer_opts);
2508 AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1,
2509 {kMediaStream1}, 1, &offer_opts);
2510 std::unique_ptr<SessionDescription> offer =
Philipp Hancke2bf1b992023-09-26 07:04:462511 f1_.CreateOfferOrError(offer_opts, nullptr).MoveValue();
Amit Hilbuchc63ddb22019-01-02 18:13:582512
2513 MediaSessionOptions answer_opts;
2514 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
2515 RtpTransceiverDirection::kSendRecv, kActive,
2516 &answer_opts);
2517
Amit Hilbuchc63ddb22019-01-02 18:13:582518 std::vector<RidDescription> rid_descriptions{
2519 RidDescription("f", RidDirection::kSend),
2520 RidDescription("h", RidDirection::kSend),
2521 RidDescription("q", RidDirection::kSend),
2522 };
2523 SimulcastLayerList simulcast_layers;
2524 simulcast_layers.AddLayer(SimulcastLayer(rid_descriptions[0].rid, false));
2525 simulcast_layers.AddLayer(SimulcastLayer(rid_descriptions[1].rid, true));
2526 simulcast_layers.AddLayer(SimulcastLayer(rid_descriptions[2].rid, false));
2527 AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1,
2528 {kMediaStream1}, rid_descriptions,
2529 simulcast_layers, 0, &answer_opts);
2530 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:462531 f2_.CreateAnswerOrError(offer.get(), answer_opts, nullptr).MoveValue();
Amit Hilbuchc63ddb22019-01-02 18:13:582532
2533 CheckSimulcastInSessionDescription(answer.get(), "video", rid_descriptions,
Amit Hilbuchb7446ed2019-01-28 20:25:252534 simulcast_layers);
Amit Hilbuchc63ddb22019-01-02 18:13:582535}
2536
2537// Create an answer that signals RIDs (not SSRCs) without Simulcast.
2538// In this scenario, RIDs do not need to be negotiated (there is only one).
2539// Note that RID Direction is not the same as the transceiver direction.
2540TEST_F(MediaSessionDescriptionFactoryTest, TestAnswerWithRidsNoSimulcast) {
2541 MediaSessionOptions offer_opts;
2542 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
2543 RtpTransceiverDirection::kSendRecv, kActive,
2544 &offer_opts);
2545 RidDescription rid_offer("f", RidDirection::kSend);
2546 AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1,
2547 {kMediaStream1}, {rid_offer},
2548 SimulcastLayerList(), 0, &offer_opts);
2549 std::unique_ptr<SessionDescription> offer =
Philipp Hancke2bf1b992023-09-26 07:04:462550 f1_.CreateOfferOrError(offer_opts, nullptr).MoveValue();
Amit Hilbuchc63ddb22019-01-02 18:13:582551
2552 MediaSessionOptions answer_opts;
2553 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
2554 RtpTransceiverDirection::kSendRecv, kActive,
2555 &answer_opts);
2556
2557 RidDescription rid_answer("f", RidDirection::kReceive);
2558 AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1,
2559 {kMediaStream1}, {rid_answer},
2560 SimulcastLayerList(), 0, &answer_opts);
2561 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:462562 f2_.CreateAnswerOrError(offer.get(), answer_opts, nullptr).MoveValue();
Amit Hilbuchc63ddb22019-01-02 18:13:582563
2564 ASSERT_NE(answer.get(), nullptr);
2565 const ContentInfo* content = offer->GetContentByName("video");
2566 ASSERT_NE(content, nullptr);
2567 const MediaContentDescription* cd = content->media_description();
2568 ASSERT_NE(cd, nullptr);
Amit Hilbuchc63ddb22019-01-02 18:13:582569 const StreamParamsVec& streams = cd->streams();
2570 ASSERT_THAT(streams, SizeIs(1));
2571 const StreamParams& stream = streams[0];
2572 ASSERT_THAT(stream.ssrcs, IsEmpty());
2573 EXPECT_FALSE(stream.has_rids());
2574 EXPECT_FALSE(cd->HasSimulcast());
2575}
2576
henrike@webrtc.org28e20752013-07-10 00:45:362577// Create an audio and video answer to a standard video offer with:
2578// - one video track
2579// - two audio tracks
2580// - two data tracks
2581// and ensure it matches what we expect. Also updates the initial answer by
2582// adding a new video track and removes one of the audio tracks.
2583TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoAnswer) {
2584 MediaSessionOptions offer_opts;
Amit Hilbuchc63ddb22019-01-02 18:13:582585 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
2586 RtpTransceiverDirection::kRecvOnly, kActive,
2587 &offer_opts);
2588 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
2589 RtpTransceiverDirection::kRecvOnly, kActive,
2590 &offer_opts);
Philipp Hancke2bf1b992023-09-26 07:04:462591 std::unique_ptr<SessionDescription> offer =
2592 f1_.CreateOfferOrError(offer_opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:362593
zhihuang1c378ed2017-08-17 21:10:502594 MediaSessionOptions answer_opts;
Amit Hilbuchc63ddb22019-01-02 18:13:582595 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
2596 RtpTransceiverDirection::kSendRecv, kActive,
2597 &answer_opts);
2598 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
2599 RtpTransceiverDirection::kSendRecv, kActive,
2600 &answer_opts);
2601 AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1,
2602 {kMediaStream1}, 1, &answer_opts);
2603 AttachSenderToMediaDescriptionOptions("audio", MEDIA_TYPE_AUDIO, kAudioTrack1,
2604 {kMediaStream1}, 1, &answer_opts);
2605 AttachSenderToMediaDescriptionOptions("audio", MEDIA_TYPE_AUDIO, kAudioTrack2,
2606 {kMediaStream1}, 1, &answer_opts);
zhihuang1c378ed2017-08-17 21:10:502607
Steve Anton6fe1fba2018-12-11 18:15:232608 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:462609 f2_.CreateAnswerOrError(offer.get(), answer_opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:362610
Philipp Hancke2bf1b992023-09-26 07:04:462611 ASSERT_TRUE(answer.get());
henrike@webrtc.org28e20752013-07-10 00:45:362612 const ContentInfo* ac = answer->GetContentByName("audio");
2613 const ContentInfo* vc = answer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:462614 ASSERT_TRUE(ac);
2615 ASSERT_TRUE(vc);
Philipp Hancked0f0f382023-11-23 19:21:052616 const MediaContentDescription* acd = ac->media_description();
2617 const MediaContentDescription* vcd = vc->media_description();
henrike@webrtc.org28e20752013-07-10 00:45:362618
2619 EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
Steve Antone38a5a12018-11-22 00:05:152620 EXPECT_THAT(acd->codecs(), ElementsAreArray(kAudioCodecsAnswer));
henrike@webrtc.org28e20752013-07-10 00:45:362621
2622 const StreamParamsVec& audio_streams = acd->streams();
2623 ASSERT_EQ(2U, audio_streams.size());
Yves Gerey665174f2018-06-19 13:03:052624 EXPECT_TRUE(audio_streams[0].cname == audio_streams[1].cname);
henrike@webrtc.org28e20752013-07-10 00:45:362625 EXPECT_EQ(kAudioTrack1, audio_streams[0].id);
2626 ASSERT_EQ(1U, audio_streams[0].ssrcs.size());
2627 EXPECT_NE(0U, audio_streams[0].ssrcs[0]);
2628 EXPECT_EQ(kAudioTrack2, audio_streams[1].id);
2629 ASSERT_EQ(1U, audio_streams[1].ssrcs.size());
2630 EXPECT_NE(0U, audio_streams[1].ssrcs[0]);
2631
2632 EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto)
2633 EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on
2634
2635 EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type());
Steve Antone38a5a12018-11-22 00:05:152636 EXPECT_THAT(vcd->codecs(), ElementsAreArray(kVideoCodecsAnswer));
henrike@webrtc.org28e20752013-07-10 00:45:362637
2638 const StreamParamsVec& video_streams = vcd->streams();
2639 ASSERT_EQ(1U, video_streams.size());
2640 EXPECT_EQ(video_streams[0].cname, audio_streams[0].cname);
2641 EXPECT_EQ(kVideoTrack1, video_streams[0].id);
2642 EXPECT_EQ(kAutoBandwidth, vcd->bandwidth()); // default bandwidth (auto)
2643 EXPECT_TRUE(vcd->rtcp_mux()); // rtcp-mux defaults on
2644
henrike@webrtc.org28e20752013-07-10 00:45:362645 // Update the answer. Add a new video track that is not synched to the
zhihuang8f65cdf2016-05-07 01:40:302646 // other tracks and remove 1 audio track.
Amit Hilbuchc63ddb22019-01-02 18:13:582647 AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack2,
2648 {kMediaStream2}, 1, &answer_opts);
zhihuang1c378ed2017-08-17 21:10:502649 DetachSenderFromMediaSection("audio", kAudioTrack2, &answer_opts);
kwiberg31022942016-03-11 22:18:212650 std::unique_ptr<SessionDescription> updated_answer(
Philipp Hancke2bf1b992023-09-26 07:04:462651 f2_.CreateAnswerOrError(offer.get(), answer_opts, answer.get())
2652 .MoveValue());
henrike@webrtc.org28e20752013-07-10 00:45:362653
Philipp Hancke2bf1b992023-09-26 07:04:462654 ASSERT_TRUE(updated_answer.get());
henrike@webrtc.org28e20752013-07-10 00:45:362655 ac = updated_answer->GetContentByName("audio");
2656 vc = updated_answer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:462657 ASSERT_TRUE(ac);
2658 ASSERT_TRUE(vc);
Philipp Hancked0f0f382023-11-23 19:21:052659 const MediaContentDescription* updated_acd = ac->media_description();
2660 const MediaContentDescription* updated_vcd = vc->media_description();
henrike@webrtc.org28e20752013-07-10 00:45:362661
henrike@webrtc.org28e20752013-07-10 00:45:362662 EXPECT_EQ(acd->type(), updated_acd->type());
2663 EXPECT_EQ(acd->codecs(), updated_acd->codecs());
2664 EXPECT_EQ(vcd->type(), updated_vcd->type());
2665 EXPECT_EQ(vcd->codecs(), updated_vcd->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:362666
2667 const StreamParamsVec& updated_audio_streams = updated_acd->streams();
2668 ASSERT_EQ(1U, updated_audio_streams.size());
Yves Gerey665174f2018-06-19 13:03:052669 EXPECT_TRUE(audio_streams[0] == updated_audio_streams[0]);
henrike@webrtc.org28e20752013-07-10 00:45:362670
2671 const StreamParamsVec& updated_video_streams = updated_vcd->streams();
2672 ASSERT_EQ(2U, updated_video_streams.size());
2673 EXPECT_EQ(video_streams[0], updated_video_streams[0]);
2674 EXPECT_EQ(kVideoTrack2, updated_video_streams[1].id);
zhihuang8f65cdf2016-05-07 01:40:302675 // All media streams in one PeerConnection share one CNAME.
2676 EXPECT_EQ(updated_video_streams[1].cname, updated_video_streams[0].cname);
henrike@webrtc.org28e20752013-07-10 00:45:362677}
2678
henrike@webrtc.org28e20752013-07-10 00:45:362679// Create an updated offer after creating an answer to the original offer and
2680// verify that the codecs that were part of the original answer are not changed
2681// in the updated offer.
2682TEST_F(MediaSessionDescriptionFactoryTest,
2683 RespondentCreatesOfferAfterCreatingAnswer) {
2684 MediaSessionOptions opts;
Steve Anton4e70a722017-11-28 22:57:102685 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
henrike@webrtc.org28e20752013-07-10 00:45:362686
Philipp Hancke2bf1b992023-09-26 07:04:462687 std::unique_ptr<SessionDescription> offer =
2688 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Steve Anton6fe1fba2018-12-11 18:15:232689 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:462690 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:362691
2692 const AudioContentDescription* acd =
2693 GetFirstAudioContentDescription(answer.get());
Steve Antone38a5a12018-11-22 00:05:152694 EXPECT_THAT(acd->codecs(), ElementsAreArray(kAudioCodecsAnswer));
henrike@webrtc.org28e20752013-07-10 00:45:362695
2696 const VideoContentDescription* vcd =
2697 GetFirstVideoContentDescription(answer.get());
Steve Antone38a5a12018-11-22 00:05:152698 EXPECT_THAT(vcd->codecs(), ElementsAreArray(kVideoCodecsAnswer));
henrike@webrtc.org28e20752013-07-10 00:45:362699
kwiberg31022942016-03-11 22:18:212700 std::unique_ptr<SessionDescription> updated_offer(
Philipp Hancke2bf1b992023-09-26 07:04:462701 f2_.CreateOfferOrError(opts, answer.get()).MoveValue());
henrike@webrtc.org28e20752013-07-10 00:45:362702
2703 // The expected audio codecs are the common audio codecs from the first
Artem Titov880fa812021-07-30 20:30:232704 // offer/answer exchange plus the audio codecs only `f2_` offer, sorted in
henrike@webrtc.org28e20752013-07-10 00:45:362705 // preference order.
Artem Titov880fa812021-07-30 20:30:232706 // TODO(wu): `updated_offer` should not include the codec
Artem Titovcfea2182021-08-09 23:22:312707 // (i.e. `kAudioCodecs2[0]`) the other side doesn't support.
henrike@webrtc.org28e20752013-07-10 00:45:362708 const AudioCodec kUpdatedAudioCodecOffer[] = {
Jonas Olssona4d87372019-07-05 17:08:332709 kAudioCodecsAnswer[0],
2710 kAudioCodecsAnswer[1],
2711 kAudioCodecs2[0],
henrike@webrtc.org28e20752013-07-10 00:45:362712 };
2713
2714 // The expected video codecs are the common video codecs from the first
Artem Titov880fa812021-07-30 20:30:232715 // offer/answer exchange plus the video codecs only `f2_` offer, sorted in
henrike@webrtc.org28e20752013-07-10 00:45:362716 // preference order.
2717 const VideoCodec kUpdatedVideoCodecOffer[] = {
Jonas Olssona4d87372019-07-05 17:08:332718 kVideoCodecsAnswer[0],
2719 kVideoCodecs2[1],
henrike@webrtc.org28e20752013-07-10 00:45:362720 };
2721
2722 const AudioContentDescription* updated_acd =
2723 GetFirstAudioContentDescription(updated_offer.get());
Steve Antone38a5a12018-11-22 00:05:152724 EXPECT_THAT(updated_acd->codecs(), ElementsAreArray(kUpdatedAudioCodecOffer));
henrike@webrtc.org28e20752013-07-10 00:45:362725
2726 const VideoContentDescription* updated_vcd =
2727 GetFirstVideoContentDescription(updated_offer.get());
Steve Antone38a5a12018-11-22 00:05:152728 EXPECT_THAT(updated_vcd->codecs(), ElementsAreArray(kUpdatedVideoCodecOffer));
henrike@webrtc.org28e20752013-07-10 00:45:362729}
2730
Steve Anton5c72e712018-12-10 22:25:302731// Test that a reoffer does not reuse audio codecs from a previous media section
2732// that is being recycled.
2733TEST_F(MediaSessionDescriptionFactoryTest,
2734 ReOfferDoesNotReUseRecycledAudioCodecs) {
Johannes Kron3e983682020-03-29 20:17:002735 f1_.set_video_codecs({}, {});
2736 f2_.set_video_codecs({}, {});
Steve Anton5c72e712018-12-10 22:25:302737
2738 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:582739 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "a0",
2740 RtpTransceiverDirection::kSendRecv, kActive,
2741 &opts);
Philipp Hancke2bf1b992023-09-26 07:04:462742 std::unique_ptr<SessionDescription> offer =
2743 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Steve Anton6fe1fba2018-12-11 18:15:232744 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:462745 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Steve Anton5c72e712018-12-10 22:25:302746
2747 // Recycle the media section by changing its mid.
2748 opts.media_description_options[0].mid = "a1";
Steve Anton6fe1fba2018-12-11 18:15:232749 std::unique_ptr<SessionDescription> reoffer =
Philipp Hancke2bf1b992023-09-26 07:04:462750 f2_.CreateOfferOrError(opts, answer.get()).MoveValue();
Steve Anton5c72e712018-12-10 22:25:302751
2752 // Expect that the results of the first negotiation are ignored. If the m=
2753 // section was not recycled the payload types would match the initial offerer.
2754 const AudioContentDescription* acd =
2755 GetFirstAudioContentDescription(reoffer.get());
2756 EXPECT_THAT(acd->codecs(), ElementsAreArray(kAudioCodecs2));
2757}
2758
2759// Test that a reoffer does not reuse video codecs from a previous media section
2760// that is being recycled.
2761TEST_F(MediaSessionDescriptionFactoryTest,
2762 ReOfferDoesNotReUseRecycledVideoCodecs) {
2763 f1_.set_audio_codecs({}, {});
2764 f2_.set_audio_codecs({}, {});
2765
2766 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:582767 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "v0",
2768 RtpTransceiverDirection::kSendRecv, kActive,
2769 &opts);
Philipp Hancke2bf1b992023-09-26 07:04:462770 std::unique_ptr<SessionDescription> offer =
2771 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
2772 auto answer = f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Steve Anton5c72e712018-12-10 22:25:302773
2774 // Recycle the media section by changing its mid.
2775 opts.media_description_options[0].mid = "v1";
Steve Anton6fe1fba2018-12-11 18:15:232776 std::unique_ptr<SessionDescription> reoffer =
Philipp Hancke2bf1b992023-09-26 07:04:462777 f2_.CreateOfferOrError(opts, answer.get()).MoveValue();
Steve Anton5c72e712018-12-10 22:25:302778
2779 // Expect that the results of the first negotiation are ignored. If the m=
2780 // section was not recycled the payload types would match the initial offerer.
2781 const VideoContentDescription* vcd =
2782 GetFirstVideoContentDescription(reoffer.get());
2783 EXPECT_THAT(vcd->codecs(), ElementsAreArray(kVideoCodecs2));
2784}
2785
2786// Test that a reanswer does not reuse audio codecs from a previous media
2787// section that is being recycled.
2788TEST_F(MediaSessionDescriptionFactoryTest,
2789 ReAnswerDoesNotReUseRecycledAudioCodecs) {
Johannes Kron3e983682020-03-29 20:17:002790 f1_.set_video_codecs({}, {});
2791 f2_.set_video_codecs({}, {});
Steve Anton5c72e712018-12-10 22:25:302792
Artem Titov880fa812021-07-30 20:30:232793 // Perform initial offer/answer in reverse (`f2_` as offerer) so that the
2794 // second offer/answer is forward (`f1_` as offerer).
Steve Anton5c72e712018-12-10 22:25:302795 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:582796 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "a0",
2797 RtpTransceiverDirection::kSendRecv, kActive,
2798 &opts);
Philipp Hancke2bf1b992023-09-26 07:04:462799 std::unique_ptr<SessionDescription> offer =
2800 f2_.CreateOfferOrError(opts, nullptr).MoveValue();
Steve Anton6fe1fba2018-12-11 18:15:232801 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:462802 f1_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Steve Anton5c72e712018-12-10 22:25:302803
2804 // Recycle the media section by changing its mid.
2805 opts.media_description_options[0].mid = "a1";
Steve Anton6fe1fba2018-12-11 18:15:232806 std::unique_ptr<SessionDescription> reoffer =
Philipp Hancke2bf1b992023-09-26 07:04:462807 f1_.CreateOfferOrError(opts, answer.get()).MoveValue();
Steve Anton6fe1fba2018-12-11 18:15:232808 std::unique_ptr<SessionDescription> reanswer =
Philipp Hancke2bf1b992023-09-26 07:04:462809 f2_.CreateAnswerOrError(reoffer.get(), opts, offer.get()).MoveValue();
Steve Anton5c72e712018-12-10 22:25:302810
2811 // Expect that the results of the first negotiation are ignored. If the m=
2812 // section was not recycled the payload types would match the initial offerer.
2813 const AudioContentDescription* acd =
2814 GetFirstAudioContentDescription(reanswer.get());
2815 EXPECT_THAT(acd->codecs(), ElementsAreArray(kAudioCodecsAnswer));
2816}
2817
2818// Test that a reanswer does not reuse video codecs from a previous media
2819// section that is being recycled.
2820TEST_F(MediaSessionDescriptionFactoryTest,
2821 ReAnswerDoesNotReUseRecycledVideoCodecs) {
2822 f1_.set_audio_codecs({}, {});
2823 f2_.set_audio_codecs({}, {});
2824
Artem Titov880fa812021-07-30 20:30:232825 // Perform initial offer/answer in reverse (`f2_` as offerer) so that the
2826 // second offer/answer is forward (`f1_` as offerer).
Steve Anton5c72e712018-12-10 22:25:302827 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:582828 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "v0",
2829 RtpTransceiverDirection::kSendRecv, kActive,
2830 &opts);
Philipp Hancke2bf1b992023-09-26 07:04:462831 std::unique_ptr<SessionDescription> offer =
2832 f2_.CreateOfferOrError(opts, nullptr).MoveValue();
Steve Anton6fe1fba2018-12-11 18:15:232833 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:462834 f1_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Steve Anton5c72e712018-12-10 22:25:302835
2836 // Recycle the media section by changing its mid.
2837 opts.media_description_options[0].mid = "v1";
Steve Anton6fe1fba2018-12-11 18:15:232838 std::unique_ptr<SessionDescription> reoffer =
Philipp Hancke2bf1b992023-09-26 07:04:462839 f1_.CreateOfferOrError(opts, answer.get()).MoveValue();
Steve Anton6fe1fba2018-12-11 18:15:232840 std::unique_ptr<SessionDescription> reanswer =
Philipp Hancke2bf1b992023-09-26 07:04:462841 f2_.CreateAnswerOrError(reoffer.get(), opts, offer.get()).MoveValue();
Steve Anton5c72e712018-12-10 22:25:302842
2843 // Expect that the results of the first negotiation are ignored. If the m=
2844 // section was not recycled the payload types would match the initial offerer.
2845 const VideoContentDescription* vcd =
2846 GetFirstVideoContentDescription(reanswer.get());
2847 EXPECT_THAT(vcd->codecs(), ElementsAreArray(kVideoCodecsAnswer));
2848}
2849
henrike@webrtc.org28e20752013-07-10 00:45:362850// Create an updated offer after creating an answer to the original offer and
2851// verify that the codecs that were part of the original answer are not changed
2852// in the updated offer. In this test Rtx is enabled.
2853TEST_F(MediaSessionDescriptionFactoryTest,
2854 RespondentCreatesOfferAfterCreatingAnswerWithRtx) {
2855 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:582856 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
2857 RtpTransceiverDirection::kRecvOnly, kActive,
2858 &opts);
henrike@webrtc.org28e20752013-07-10 00:45:362859 std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1);
Artem Titov880fa812021-07-30 20:30:232860 // This creates rtx for H264 with the payload type `f1_` uses.
Emil Lundmark7d2e6162023-11-08 15:27:452861 AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs);
Johannes Kron3e983682020-03-29 20:17:002862 f1_.set_video_codecs(f1_codecs, f1_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:362863
2864 std::vector<VideoCodec> f2_codecs = MAKE_VECTOR(kVideoCodecs2);
Artem Titov880fa812021-07-30 20:30:232865 // This creates rtx for H264 with the payload type `f2_` uses.
Emil Lundmark7d2e6162023-11-08 15:27:452866 AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs2[0].id), &f2_codecs);
Johannes Kron3e983682020-03-29 20:17:002867 f2_.set_video_codecs(f2_codecs, f2_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:362868
Philipp Hancke2bf1b992023-09-26 07:04:462869 std::unique_ptr<SessionDescription> offer =
2870 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
2871 ASSERT_TRUE(offer.get());
Steve Anton6fe1fba2018-12-11 18:15:232872 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:462873 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:362874
2875 const VideoContentDescription* vcd =
2876 GetFirstVideoContentDescription(answer.get());
2877
2878 std::vector<VideoCodec> expected_codecs = MAKE_VECTOR(kVideoCodecsAnswer);
Emil Lundmark7d2e6162023-11-08 15:27:452879 AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &expected_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:362880
2881 EXPECT_EQ(expected_codecs, vcd->codecs());
2882
Artem Titov880fa812021-07-30 20:30:232883 // Now, make sure we get same result (except for the order) if `f2_` creates
2884 // an updated offer even though the default payload types between `f1_` and
2885 // `f2_` are different.
kwiberg31022942016-03-11 22:18:212886 std::unique_ptr<SessionDescription> updated_offer(
Philipp Hancke2bf1b992023-09-26 07:04:462887 f2_.CreateOfferOrError(opts, answer.get()).MoveValue());
henrike@webrtc.org28e20752013-07-10 00:45:362888 ASSERT_TRUE(updated_offer);
kwiberg31022942016-03-11 22:18:212889 std::unique_ptr<SessionDescription> updated_answer(
Philipp Hancke2bf1b992023-09-26 07:04:462890 f1_.CreateAnswerOrError(updated_offer.get(), opts, answer.get())
2891 .MoveValue());
henrike@webrtc.org28e20752013-07-10 00:45:362892
2893 const VideoContentDescription* updated_vcd =
2894 GetFirstVideoContentDescription(updated_answer.get());
2895
2896 EXPECT_EQ(expected_codecs, updated_vcd->codecs());
2897}
2898
Taylor Brandstetter1c349742017-10-04 01:25:362899// Regression test for:
2900// https://bugs.chromium.org/p/webrtc/issues/detail?id=8332
2901// Existing codecs should always appear before new codecs in re-offers. But
2902// under a specific set of circumstances, the existing RTX codec was ending up
2903// added to the end of the list.
2904TEST_F(MediaSessionDescriptionFactoryTest,
2905 RespondentCreatesOfferAfterCreatingAnswerWithRemappedRtxPayloadType) {
2906 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:582907 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
2908 RtpTransceiverDirection::kRecvOnly, kActive,
2909 &opts);
Taylor Brandstetter1c349742017-10-04 01:25:362910 // We specifically choose different preferred payload types for VP8 to
2911 // trigger the issue.
Emil Lundmark7d2e6162023-11-08 15:27:452912 VideoCodec vp8_offerer = CreateVideoCodec(100, "VP8");
2913 VideoCodec vp8_offerer_rtx = CreateVideoRtxCodec(101, vp8_offerer.id);
2914 VideoCodec vp8_answerer = CreateVideoCodec(110, "VP8");
2915 VideoCodec vp8_answerer_rtx = CreateVideoRtxCodec(111, vp8_answerer.id);
2916 VideoCodec vp9 = CreateVideoCodec(120, "VP9");
2917 VideoCodec vp9_rtx = CreateVideoRtxCodec(121, vp9.id);
Taylor Brandstetter1c349742017-10-04 01:25:362918
2919 std::vector<VideoCodec> f1_codecs = {vp8_offerer, vp8_offerer_rtx};
2920 // We also specifically cause the answerer to prefer VP9, such that if it
2921 // *doesn't* honor the existing preferred codec (VP8) we'll notice.
2922 std::vector<VideoCodec> f2_codecs = {vp9, vp9_rtx, vp8_answerer,
2923 vp8_answerer_rtx};
2924
Johannes Kron3e983682020-03-29 20:17:002925 f1_.set_video_codecs(f1_codecs, f1_codecs);
2926 f2_.set_video_codecs(f2_codecs, f2_codecs);
Taylor Brandstetter1c349742017-10-04 01:25:362927 std::vector<AudioCodec> audio_codecs;
2928 f1_.set_audio_codecs(audio_codecs, audio_codecs);
2929 f2_.set_audio_codecs(audio_codecs, audio_codecs);
2930
2931 // Offer will be {VP8, RTX for VP8}. Answer will be the same.
Philipp Hancke2bf1b992023-09-26 07:04:462932 std::unique_ptr<SessionDescription> offer =
2933 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
2934 ASSERT_TRUE(offer.get());
Steve Anton6fe1fba2018-12-11 18:15:232935 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:462936 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Taylor Brandstetter1c349742017-10-04 01:25:362937
2938 // Updated offer *should* be {VP8, RTX for VP8, VP9, RTX for VP9}.
2939 // But if the bug is triggered, RTX for VP8 ends up last.
2940 std::unique_ptr<SessionDescription> updated_offer(
Philipp Hancke2bf1b992023-09-26 07:04:462941 f2_.CreateOfferOrError(opts, answer.get()).MoveValue());
Taylor Brandstetter1c349742017-10-04 01:25:362942
2943 const VideoContentDescription* vcd =
2944 GetFirstVideoContentDescription(updated_offer.get());
Emil Lundmark7d2e6162023-11-08 15:27:452945 std::vector<VideoCodec> codecs = vcd->codecs();
Taylor Brandstetter1c349742017-10-04 01:25:362946 ASSERT_EQ(4u, codecs.size());
2947 EXPECT_EQ(vp8_offerer, codecs[0]);
2948 EXPECT_EQ(vp8_offerer_rtx, codecs[1]);
2949 EXPECT_EQ(vp9, codecs[2]);
2950 EXPECT_EQ(vp9_rtx, codecs[3]);
Taylor Brandstetter1c349742017-10-04 01:25:362951}
2952
henrike@webrtc.org28e20752013-07-10 00:45:362953// Create an updated offer that adds video after creating an audio only answer
2954// to the original offer. This test verifies that if a video codec and the RTX
2955// codec have the same default payload type as an audio codec that is already in
2956// use, the added codecs payload types are changed.
2957TEST_F(MediaSessionDescriptionFactoryTest,
2958 RespondentCreatesOfferWithVideoAndRtxAfterCreatingAudioAnswer) {
2959 std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1);
Artem Titov880fa812021-07-30 20:30:232960 // This creates rtx for H264 with the payload type `f1_` uses.
Emil Lundmark7d2e6162023-11-08 15:27:452961 AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs);
Johannes Kron3e983682020-03-29 20:17:002962 f1_.set_video_codecs(f1_codecs, f1_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:362963
2964 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:582965 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
2966 RtpTransceiverDirection::kRecvOnly, kActive,
2967 &opts);
henrike@webrtc.org28e20752013-07-10 00:45:362968
Philipp Hancke2bf1b992023-09-26 07:04:462969 std::unique_ptr<SessionDescription> offer =
2970 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Steve Anton6fe1fba2018-12-11 18:15:232971 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:462972 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:362973
2974 const AudioContentDescription* acd =
2975 GetFirstAudioContentDescription(answer.get());
Steve Antone38a5a12018-11-22 00:05:152976 EXPECT_THAT(acd->codecs(), ElementsAreArray(kAudioCodecsAnswer));
henrike@webrtc.org28e20752013-07-10 00:45:362977
Artem Titov880fa812021-07-30 20:30:232978 // Now - let `f2_` add video with RTX and let the payload type the RTX codec
henrike@webrtc.org28e20752013-07-10 00:45:362979 // reference be the same as an audio codec that was negotiated in the
2980 // first offer/answer exchange.
zhihuang1c378ed2017-08-17 21:10:502981 opts.media_description_options.clear();
Steve Anton4e70a722017-11-28 22:57:102982 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
henrike@webrtc.org28e20752013-07-10 00:45:362983
2984 std::vector<VideoCodec> f2_codecs = MAKE_VECTOR(kVideoCodecs2);
2985 int used_pl_type = acd->codecs()[0].id;
2986 f2_codecs[0].id = used_pl_type; // Set the payload type for H264.
Emil Lundmark7d2e6162023-11-08 15:27:452987 AddRtxCodec(CreateVideoRtxCodec(125, used_pl_type), &f2_codecs);
Johannes Kron3e983682020-03-29 20:17:002988 f2_.set_video_codecs(f2_codecs, f2_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:362989
kwiberg31022942016-03-11 22:18:212990 std::unique_ptr<SessionDescription> updated_offer(
Philipp Hancke2bf1b992023-09-26 07:04:462991 f2_.CreateOfferOrError(opts, answer.get()).MoveValue());
henrike@webrtc.org28e20752013-07-10 00:45:362992 ASSERT_TRUE(updated_offer);
kwiberg31022942016-03-11 22:18:212993 std::unique_ptr<SessionDescription> updated_answer(
Philipp Hancke2bf1b992023-09-26 07:04:462994 f1_.CreateAnswerOrError(updated_offer.get(), opts, answer.get())
2995 .MoveValue());
henrike@webrtc.org28e20752013-07-10 00:45:362996
2997 const AudioContentDescription* updated_acd =
2998 GetFirstAudioContentDescription(answer.get());
Steve Antone38a5a12018-11-22 00:05:152999 EXPECT_THAT(updated_acd->codecs(), ElementsAreArray(kAudioCodecsAnswer));
henrike@webrtc.org28e20752013-07-10 00:45:363000
3001 const VideoContentDescription* updated_vcd =
3002 GetFirstVideoContentDescription(updated_answer.get());
3003
3004 ASSERT_EQ("H264", updated_vcd->codecs()[0].name);
Emil Lundmark7d2e6162023-11-08 15:27:453005 ASSERT_EQ(kRtxCodecName, updated_vcd->codecs()[1].name);
Yves Gerey665174f2018-06-19 13:03:053006 int new_h264_pl_type = updated_vcd->codecs()[0].id;
henrike@webrtc.org28e20752013-07-10 00:45:363007 EXPECT_NE(used_pl_type, new_h264_pl_type);
3008 VideoCodec rtx = updated_vcd->codecs()[1];
Emil Lundmark7d2e6162023-11-08 15:27:453009 int pt_referenced_by_rtx =
3010 rtc::FromString<int>(rtx.params[kCodecParamAssociatedPayloadType]);
henrike@webrtc.org28e20752013-07-10 00:45:363011 EXPECT_EQ(new_h264_pl_type, pt_referenced_by_rtx);
3012}
3013
Taylor Brandstetter6ec641b2016-03-05 00:47:563014// Create an updated offer with RTX after creating an answer to an offer
3015// without RTX, and with different default payload types.
3016// Verify that the added RTX codec references the correct payload type.
3017TEST_F(MediaSessionDescriptionFactoryTest,
3018 RespondentCreatesOfferWithRtxAfterCreatingAnswerWithoutRtx) {
3019 MediaSessionOptions opts;
Steve Anton4e70a722017-11-28 22:57:103020 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
Taylor Brandstetter6ec641b2016-03-05 00:47:563021
3022 std::vector<VideoCodec> f2_codecs = MAKE_VECTOR(kVideoCodecs2);
Artem Titov880fa812021-07-30 20:30:233023 // This creates rtx for H264 with the payload type `f2_` uses.
Emil Lundmark7d2e6162023-11-08 15:27:453024 AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs2[0].id), &f2_codecs);
Johannes Kron3e983682020-03-29 20:17:003025 f2_.set_video_codecs(f2_codecs, f2_codecs);
Taylor Brandstetter6ec641b2016-03-05 00:47:563026
Philipp Hancke2bf1b992023-09-26 07:04:463027 std::unique_ptr<SessionDescription> offer =
3028 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
3029 ASSERT_TRUE(offer.get());
Steve Anton6fe1fba2018-12-11 18:15:233030 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:463031 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Taylor Brandstetter6ec641b2016-03-05 00:47:563032
3033 const VideoContentDescription* vcd =
3034 GetFirstVideoContentDescription(answer.get());
3035
3036 std::vector<VideoCodec> expected_codecs = MAKE_VECTOR(kVideoCodecsAnswer);
3037 EXPECT_EQ(expected_codecs, vcd->codecs());
3038
Artem Titov880fa812021-07-30 20:30:233039 // Now, ensure that the RTX codec is created correctly when `f2_` creates an
Taylor Brandstetter6ec641b2016-03-05 00:47:563040 // updated offer, even though the default payload types are different from
Artem Titov880fa812021-07-30 20:30:233041 // those of `f1_`.
kwiberg31022942016-03-11 22:18:213042 std::unique_ptr<SessionDescription> updated_offer(
Philipp Hancke2bf1b992023-09-26 07:04:463043 f2_.CreateOfferOrError(opts, answer.get()).MoveValue());
Taylor Brandstetter6ec641b2016-03-05 00:47:563044 ASSERT_TRUE(updated_offer);
3045
3046 const VideoContentDescription* updated_vcd =
3047 GetFirstVideoContentDescription(updated_offer.get());
3048
3049 // New offer should attempt to add H263, and RTX for H264.
3050 expected_codecs.push_back(kVideoCodecs2[1]);
Emil Lundmark7d2e6162023-11-08 15:27:453051 AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs1[1].id), &expected_codecs);
Taylor Brandstetter6ec641b2016-03-05 00:47:563052 EXPECT_EQ(expected_codecs, updated_vcd->codecs());
3053}
3054
henrike@webrtc.org28e20752013-07-10 00:45:363055// Test that RTX is ignored when there is no associated payload type parameter.
3056TEST_F(MediaSessionDescriptionFactoryTest, RtxWithoutApt) {
3057 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:583058 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
3059 RtpTransceiverDirection::kRecvOnly, kActive,
3060 &opts);
henrike@webrtc.org28e20752013-07-10 00:45:363061 std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1);
changbin.shao@webrtc.org2d25b442015-03-16 04:14:343062 // This creates RTX without associated payload type parameter.
Emil Lundmark7d2e6162023-11-08 15:27:453063 AddRtxCodec(CreateVideoCodec(126, kRtxCodecName), &f1_codecs);
Johannes Kron3e983682020-03-29 20:17:003064 f1_.set_video_codecs(f1_codecs, f1_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:363065
3066 std::vector<VideoCodec> f2_codecs = MAKE_VECTOR(kVideoCodecs2);
Artem Titov880fa812021-07-30 20:30:233067 // This creates RTX for H264 with the payload type `f2_` uses.
Emil Lundmark7d2e6162023-11-08 15:27:453068 AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs2[0].id), &f2_codecs);
Johannes Kron3e983682020-03-29 20:17:003069 f2_.set_video_codecs(f2_codecs, f2_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:363070
Philipp Hancke2bf1b992023-09-26 07:04:463071 std::unique_ptr<SessionDescription> offer =
3072 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
3073 ASSERT_TRUE(offer.get());
henrike@webrtc.org28e20752013-07-10 00:45:363074 // kCodecParamAssociatedPayloadType will always be added to the offer when RTX
3075 // is selected. Manually remove kCodecParamAssociatedPayloadType so that it
3076 // is possible to test that that RTX is dropped when
3077 // kCodecParamAssociatedPayloadType is missing in the offer.
Steve Antonb1c1de12017-12-21 23:14:303078 MediaContentDescription* media_desc =
Emil Lundmark7d2e6162023-11-08 15:27:453079 offer->GetContentDescriptionByName(CN_VIDEO);
Steve Antonb1c1de12017-12-21 23:14:303080 ASSERT_TRUE(media_desc);
Philipp Hancked0f0f382023-11-23 19:21:053081 std::vector<Codec> codecs = media_desc->codecs();
3082 for (Codec& codec : codecs) {
Emil Lundmark7d2e6162023-11-08 15:27:453083 if (absl::StartsWith(codec.name, kRtxCodecName)) {
Steve Anton3a66edf2018-09-10 19:57:373084 codec.params.clear();
henrike@webrtc.org28e20752013-07-10 00:45:363085 }
3086 }
Philipp Hancked0f0f382023-11-23 19:21:053087 media_desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:363088
Steve Anton6fe1fba2018-12-11 18:15:233089 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:463090 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:363091
Steve Anton64b626b2019-01-29 01:25:263092 EXPECT_THAT(
3093 GetCodecNames(GetFirstVideoContentDescription(answer.get())->codecs()),
Emil Lundmark7d2e6162023-11-08 15:27:453094 Not(Contains(kRtxCodecName)));
changbin.shao@webrtc.org2d25b442015-03-16 04:14:343095}
3096
3097// Test that RTX will be filtered out in the answer if its associated payload
3098// type doesn't match the local value.
3099TEST_F(MediaSessionDescriptionFactoryTest, FilterOutRtxIfAptDoesntMatch) {
3100 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:583101 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
3102 RtpTransceiverDirection::kRecvOnly, kActive,
3103 &opts);
changbin.shao@webrtc.org2d25b442015-03-16 04:14:343104 std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1);
3105 // This creates RTX for H264 in sender.
Emil Lundmark7d2e6162023-11-08 15:27:453106 AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs);
Johannes Kron3e983682020-03-29 20:17:003107 f1_.set_video_codecs(f1_codecs, f1_codecs);
changbin.shao@webrtc.org2d25b442015-03-16 04:14:343108
3109 std::vector<VideoCodec> f2_codecs = MAKE_VECTOR(kVideoCodecs2);
3110 // This creates RTX for H263 in receiver.
Emil Lundmark7d2e6162023-11-08 15:27:453111 AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs2[1].id), &f2_codecs);
Johannes Kron3e983682020-03-29 20:17:003112 f2_.set_video_codecs(f2_codecs, f2_codecs);
changbin.shao@webrtc.org2d25b442015-03-16 04:14:343113
Philipp Hancke2bf1b992023-09-26 07:04:463114 std::unique_ptr<SessionDescription> offer =
3115 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
3116 ASSERT_TRUE(offer.get());
changbin.shao@webrtc.org2d25b442015-03-16 04:14:343117 // Associated payload type doesn't match, therefore, RTX codec is removed in
3118 // the answer.
Steve Anton6fe1fba2018-12-11 18:15:233119 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:463120 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
changbin.shao@webrtc.org2d25b442015-03-16 04:14:343121
Steve Anton64b626b2019-01-29 01:25:263122 EXPECT_THAT(
3123 GetCodecNames(GetFirstVideoContentDescription(answer.get())->codecs()),
Emil Lundmark7d2e6162023-11-08 15:27:453124 Not(Contains(kRtxCodecName)));
changbin.shao@webrtc.org2d25b442015-03-16 04:14:343125}
3126
3127// Test that when multiple RTX codecs are offered, only the matched RTX codec
3128// is added in the answer, and the unsupported RTX codec is filtered out.
3129TEST_F(MediaSessionDescriptionFactoryTest,
3130 FilterOutUnsupportedRtxWhenCreatingAnswer) {
3131 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:583132 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
3133 RtpTransceiverDirection::kRecvOnly, kActive,
3134 &opts);
changbin.shao@webrtc.org2d25b442015-03-16 04:14:343135 std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1);
3136 // This creates RTX for H264-SVC in sender.
Emil Lundmark7d2e6162023-11-08 15:27:453137 AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs1[0].id), &f1_codecs);
Johannes Kron3e983682020-03-29 20:17:003138 f1_.set_video_codecs(f1_codecs, f1_codecs);
changbin.shao@webrtc.org2d25b442015-03-16 04:14:343139
3140 // This creates RTX for H264 in sender.
Emil Lundmark7d2e6162023-11-08 15:27:453141 AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs);
Johannes Kron3e983682020-03-29 20:17:003142 f1_.set_video_codecs(f1_codecs, f1_codecs);
changbin.shao@webrtc.org2d25b442015-03-16 04:14:343143
3144 std::vector<VideoCodec> f2_codecs = MAKE_VECTOR(kVideoCodecs2);
3145 // This creates RTX for H264 in receiver.
Emil Lundmark7d2e6162023-11-08 15:27:453146 AddRtxCodec(CreateVideoRtxCodec(124, kVideoCodecs2[0].id), &f2_codecs);
Johannes Kron3e983682020-03-29 20:17:003147 f2_.set_video_codecs(f2_codecs, f1_codecs);
changbin.shao@webrtc.org2d25b442015-03-16 04:14:343148
3149 // H264-SVC codec is removed in the answer, therefore, associated RTX codec
3150 // for H264-SVC should also be removed.
Philipp Hancke2bf1b992023-09-26 07:04:463151 std::unique_ptr<SessionDescription> offer =
3152 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
3153 ASSERT_TRUE(offer.get());
Steve Anton6fe1fba2018-12-11 18:15:233154 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:463155 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:363156 const VideoContentDescription* vcd =
3157 GetFirstVideoContentDescription(answer.get());
changbin.shao@webrtc.org2d25b442015-03-16 04:14:343158 std::vector<VideoCodec> expected_codecs = MAKE_VECTOR(kVideoCodecsAnswer);
Emil Lundmark7d2e6162023-11-08 15:27:453159 AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &expected_codecs);
henrike@webrtc.org28e20752013-07-10 00:45:363160
changbin.shao@webrtc.org2d25b442015-03-16 04:14:343161 EXPECT_EQ(expected_codecs, vcd->codecs());
henrike@webrtc.org28e20752013-07-10 00:45:363162}
3163
Taylor Brandstetter6ec641b2016-03-05 00:47:563164// Test that after one RTX codec has been negotiated, a new offer can attempt
3165// to add another.
3166TEST_F(MediaSessionDescriptionFactoryTest, AddSecondRtxInNewOffer) {
3167 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:583168 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
3169 RtpTransceiverDirection::kRecvOnly, kActive,
3170 &opts);
Taylor Brandstetter6ec641b2016-03-05 00:47:563171 std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1);
3172 // This creates RTX for H264 for the offerer.
Emil Lundmark7d2e6162023-11-08 15:27:453173 AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs);
Johannes Kron3e983682020-03-29 20:17:003174 f1_.set_video_codecs(f1_codecs, f1_codecs);
Taylor Brandstetter6ec641b2016-03-05 00:47:563175
Philipp Hancke2bf1b992023-09-26 07:04:463176 std::unique_ptr<SessionDescription> offer =
3177 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Taylor Brandstetter6ec641b2016-03-05 00:47:563178 ASSERT_TRUE(offer);
3179 const VideoContentDescription* vcd =
3180 GetFirstVideoContentDescription(offer.get());
3181
3182 std::vector<VideoCodec> expected_codecs = MAKE_VECTOR(kVideoCodecs1);
Emil Lundmark7d2e6162023-11-08 15:27:453183 AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &expected_codecs);
Taylor Brandstetter6ec641b2016-03-05 00:47:563184 EXPECT_EQ(expected_codecs, vcd->codecs());
3185
3186 // Now, attempt to add RTX for H264-SVC.
Emil Lundmark7d2e6162023-11-08 15:27:453187 AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs1[0].id), &f1_codecs);
Johannes Kron3e983682020-03-29 20:17:003188 f1_.set_video_codecs(f1_codecs, f1_codecs);
Taylor Brandstetter6ec641b2016-03-05 00:47:563189
kwiberg31022942016-03-11 22:18:213190 std::unique_ptr<SessionDescription> updated_offer(
Philipp Hancke2bf1b992023-09-26 07:04:463191 f1_.CreateOfferOrError(opts, offer.get()).MoveValue());
Taylor Brandstetter6ec641b2016-03-05 00:47:563192 ASSERT_TRUE(updated_offer);
3193 vcd = GetFirstVideoContentDescription(updated_offer.get());
3194
Emil Lundmark7d2e6162023-11-08 15:27:453195 AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs1[0].id), &expected_codecs);
Taylor Brandstetter6ec641b2016-03-05 00:47:563196 EXPECT_EQ(expected_codecs, vcd->codecs());
3197}
3198
Noah Richards2e7a0982015-05-18 21:02:543199// Test that when RTX is used in conjunction with simulcast, an RTX ssrc is
3200// generated for each simulcast ssrc and correctly grouped.
3201TEST_F(MediaSessionDescriptionFactoryTest, SimSsrcsGenerateMultipleRtxSsrcs) {
3202 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:583203 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
3204 RtpTransceiverDirection::kSendRecv, kActive,
3205 &opts);
Noah Richards2e7a0982015-05-18 21:02:543206 // Add simulcast streams.
Amit Hilbuchc63ddb22019-01-02 18:13:583207 AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, "stream1",
3208 {"stream1label"}, 3, &opts);
Noah Richards2e7a0982015-05-18 21:02:543209
3210 // Use a single real codec, and then add RTX for it.
3211 std::vector<VideoCodec> f1_codecs;
Emil Lundmark7d2e6162023-11-08 15:27:453212 f1_codecs.push_back(CreateVideoCodec(97, "H264"));
3213 AddRtxCodec(CreateVideoRtxCodec(125, 97), &f1_codecs);
Johannes Kron3e983682020-03-29 20:17:003214 f1_.set_video_codecs(f1_codecs, f1_codecs);
Noah Richards2e7a0982015-05-18 21:02:543215
3216 // Ensure that the offer has an RTX ssrc for each regular ssrc, and that there
3217 // is a FID ssrc + grouping for each.
Philipp Hancke2bf1b992023-09-26 07:04:463218 std::unique_ptr<SessionDescription> offer =
3219 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
3220 ASSERT_TRUE(offer.get());
Steve Antonb1c1de12017-12-21 23:14:303221 MediaContentDescription* media_desc =
Emil Lundmark7d2e6162023-11-08 15:27:453222 offer->GetContentDescriptionByName(CN_VIDEO);
Steve Antonb1c1de12017-12-21 23:14:303223 ASSERT_TRUE(media_desc);
Philipp Hancked0f0f382023-11-23 19:21:053224 const StreamParamsVec& streams = media_desc->streams();
Noah Richards2e7a0982015-05-18 21:02:543225 // Single stream.
3226 ASSERT_EQ(1u, streams.size());
3227 // Stream should have 6 ssrcs: 3 for video, 3 for RTX.
3228 EXPECT_EQ(6u, streams[0].ssrcs.size());
3229 // And should have a SIM group for the simulcast.
3230 EXPECT_TRUE(streams[0].has_ssrc_group("SIM"));
3231 // And a FID group for RTX.
3232 EXPECT_TRUE(streams[0].has_ssrc_group("FID"));
Peter Boström0c4e06b2015-10-07 10:23:213233 std::vector<uint32_t> primary_ssrcs;
Noah Richards2e7a0982015-05-18 21:02:543234 streams[0].GetPrimarySsrcs(&primary_ssrcs);
3235 EXPECT_EQ(3u, primary_ssrcs.size());
Peter Boström0c4e06b2015-10-07 10:23:213236 std::vector<uint32_t> fid_ssrcs;
Noah Richards2e7a0982015-05-18 21:02:543237 streams[0].GetFidSsrcs(primary_ssrcs, &fid_ssrcs);
3238 EXPECT_EQ(3u, fid_ssrcs.size());
3239}
3240
brandtr03d5fb12016-11-22 11:37:593241// Test that, when the FlexFEC codec is added, a FlexFEC ssrc is created
Philipp Hanckefedc7ab2020-11-17 20:59:123242// together with a FEC-FR grouping. Guarded by WebRTC-FlexFEC-03 trial.
brandtr03d5fb12016-11-22 11:37:593243TEST_F(MediaSessionDescriptionFactoryTest, GenerateFlexfecSsrc) {
Jonas Orelanded99dae2022-03-09 08:28:103244 webrtc::test::ScopedKeyValueConfig override_field_trials(
3245 field_trials, "WebRTC-FlexFEC-03/Enabled/");
brandtr03d5fb12016-11-22 11:37:593246 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:583247 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
3248 RtpTransceiverDirection::kSendRecv, kActive,
3249 &opts);
brandtr03d5fb12016-11-22 11:37:593250 // Add single stream.
Amit Hilbuchc63ddb22019-01-02 18:13:583251 AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, "stream1",
3252 {"stream1label"}, 1, &opts);
brandtr03d5fb12016-11-22 11:37:593253
3254 // Use a single real codec, and then add FlexFEC for it.
3255 std::vector<VideoCodec> f1_codecs;
Emil Lundmark7d2e6162023-11-08 15:27:453256 f1_codecs.push_back(CreateVideoCodec(97, "H264"));
3257 f1_codecs.push_back(CreateVideoCodec(118, "flexfec-03"));
Johannes Kron3e983682020-03-29 20:17:003258 f1_.set_video_codecs(f1_codecs, f1_codecs);
brandtr03d5fb12016-11-22 11:37:593259
3260 // Ensure that the offer has a single FlexFEC ssrc and that
3261 // there is no FEC-FR ssrc + grouping for each.
Philipp Hancke2bf1b992023-09-26 07:04:463262 std::unique_ptr<SessionDescription> offer =
3263 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
3264 ASSERT_TRUE(offer.get());
Steve Antonb1c1de12017-12-21 23:14:303265 MediaContentDescription* media_desc =
Emil Lundmark7d2e6162023-11-08 15:27:453266 offer->GetContentDescriptionByName(CN_VIDEO);
Steve Antonb1c1de12017-12-21 23:14:303267 ASSERT_TRUE(media_desc);
Philipp Hancked0f0f382023-11-23 19:21:053268 const StreamParamsVec& streams = media_desc->streams();
brandtr03d5fb12016-11-22 11:37:593269 // Single stream.
3270 ASSERT_EQ(1u, streams.size());
3271 // Stream should have 2 ssrcs: 1 for video, 1 for FlexFEC.
3272 EXPECT_EQ(2u, streams[0].ssrcs.size());
3273 // And should have a FEC-FR group for FlexFEC.
3274 EXPECT_TRUE(streams[0].has_ssrc_group("FEC-FR"));
3275 std::vector<uint32_t> primary_ssrcs;
3276 streams[0].GetPrimarySsrcs(&primary_ssrcs);
3277 ASSERT_EQ(1u, primary_ssrcs.size());
3278 uint32_t flexfec_ssrc;
3279 EXPECT_TRUE(streams[0].GetFecFrSsrc(primary_ssrcs[0], &flexfec_ssrc));
3280 EXPECT_NE(flexfec_ssrc, 0u);
3281}
3282
3283// Test that FlexFEC is disabled for simulcast.
3284// TODO(brandtr): Remove this test when we support simulcast, either through
3285// multiple FlexfecSenders, or through multistream protection.
3286TEST_F(MediaSessionDescriptionFactoryTest, SimSsrcsGenerateNoFlexfecSsrcs) {
Jonas Orelanded99dae2022-03-09 08:28:103287 webrtc::test::ScopedKeyValueConfig override_field_trials(
3288 field_trials, "WebRTC-FlexFEC-03/Enabled/");
brandtr03d5fb12016-11-22 11:37:593289 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:583290 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
3291 RtpTransceiverDirection::kSendRecv, kActive,
3292 &opts);
brandtr03d5fb12016-11-22 11:37:593293 // Add simulcast streams.
Amit Hilbuchc63ddb22019-01-02 18:13:583294 AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, "stream1",
3295 {"stream1label"}, 3, &opts);
brandtr03d5fb12016-11-22 11:37:593296
3297 // Use a single real codec, and then add FlexFEC for it.
3298 std::vector<VideoCodec> f1_codecs;
Emil Lundmark7d2e6162023-11-08 15:27:453299 f1_codecs.push_back(CreateVideoCodec(97, "H264"));
3300 f1_codecs.push_back(CreateVideoCodec(118, "flexfec-03"));
Johannes Kron3e983682020-03-29 20:17:003301 f1_.set_video_codecs(f1_codecs, f1_codecs);
brandtr03d5fb12016-11-22 11:37:593302
3303 // Ensure that the offer has no FlexFEC ssrcs for each regular ssrc, and that
3304 // there is no FEC-FR ssrc + grouping for each.
Philipp Hancke2bf1b992023-09-26 07:04:463305 std::unique_ptr<SessionDescription> offer =
3306 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
3307 ASSERT_TRUE(offer.get());
Steve Antonb1c1de12017-12-21 23:14:303308 MediaContentDescription* media_desc =
Emil Lundmark7d2e6162023-11-08 15:27:453309 offer->GetContentDescriptionByName(CN_VIDEO);
Steve Antonb1c1de12017-12-21 23:14:303310 ASSERT_TRUE(media_desc);
Philipp Hancked0f0f382023-11-23 19:21:053311 const StreamParamsVec& streams = media_desc->streams();
brandtr03d5fb12016-11-22 11:37:593312 // Single stream.
3313 ASSERT_EQ(1u, streams.size());
3314 // Stream should have 3 ssrcs: 3 for video, 0 for FlexFEC.
3315 EXPECT_EQ(3u, streams[0].ssrcs.size());
3316 // And should have a SIM group for the simulcast.
3317 EXPECT_TRUE(streams[0].has_ssrc_group("SIM"));
3318 // And not a FEC-FR group for FlexFEC.
3319 EXPECT_FALSE(streams[0].has_ssrc_group("FEC-FR"));
3320 std::vector<uint32_t> primary_ssrcs;
3321 streams[0].GetPrimarySsrcs(&primary_ssrcs);
3322 EXPECT_EQ(3u, primary_ssrcs.size());
3323 for (uint32_t primary_ssrc : primary_ssrcs) {
3324 uint32_t flexfec_ssrc;
3325 EXPECT_FALSE(streams[0].GetFecFrSsrc(primary_ssrc, &flexfec_ssrc));
3326 }
3327}
3328
henrike@webrtc.org28e20752013-07-10 00:45:363329// Create an updated offer after creating an answer to the original offer and
3330// verify that the RTP header extensions that were part of the original answer
3331// are not changed in the updated offer.
3332TEST_F(MediaSessionDescriptionFactoryTest,
3333 RespondentCreatesOfferAfterCreatingAnswerWithRtpExtensions) {
3334 MediaSessionOptions opts;
Steve Anton4e70a722017-11-28 22:57:103335 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
henrike@webrtc.org28e20752013-07-10 00:45:363336
Markus Handell755c65d2020-06-23 23:06:103337 SetAudioVideoRtpHeaderExtensions(MAKE_VECTOR(kAudioRtpExtension1),
3338 MAKE_VECTOR(kVideoRtpExtension1), &opts);
Philipp Hancke2bf1b992023-09-26 07:04:463339 std::unique_ptr<SessionDescription> offer =
3340 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Markus Handell755c65d2020-06-23 23:06:103341 SetAudioVideoRtpHeaderExtensions(MAKE_VECTOR(kAudioRtpExtension2),
3342 MAKE_VECTOR(kVideoRtpExtension2), &opts);
Steve Anton6fe1fba2018-12-11 18:15:233343 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:463344 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
henrike@webrtc.org28e20752013-07-10 00:45:363345
Yves Gerey665174f2018-06-19 13:03:053346 EXPECT_EQ(
3347 MAKE_VECTOR(kAudioRtpExtensionAnswer),
3348 GetFirstAudioContentDescription(answer.get())->rtp_header_extensions());
3349 EXPECT_EQ(
3350 MAKE_VECTOR(kVideoRtpExtensionAnswer),
3351 GetFirstVideoContentDescription(answer.get())->rtp_header_extensions());
henrike@webrtc.org28e20752013-07-10 00:45:363352
kwiberg31022942016-03-11 22:18:213353 std::unique_ptr<SessionDescription> updated_offer(
Philipp Hancke2bf1b992023-09-26 07:04:463354 f2_.CreateOfferOrError(opts, answer.get()).MoveValue());
henrike@webrtc.org28e20752013-07-10 00:45:363355
3356 // The expected RTP header extensions in the new offer are the resulting
3357 // extensions from the first offer/answer exchange plus the extensions only
Artem Titov880fa812021-07-30 20:30:233358 // `f2_` offer.
3359 // Since the default local extension id `f2_` uses has already been used by
3360 // `f1_` for another extensions, it is changed to 13.
isheriff6f8d6862016-05-26 18:24:553361 const RtpExtension kUpdatedAudioRtpExtensions[] = {
Jonas Olssona4d87372019-07-05 17:08:333362 kAudioRtpExtensionAnswer[0],
3363 RtpExtension(kAudioRtpExtension2[1].uri, 13),
isheriff6f8d6862016-05-26 18:24:553364 kAudioRtpExtension2[2],
henrike@webrtc.org28e20752013-07-10 00:45:363365 };
3366
Artem Titov880fa812021-07-30 20:30:233367 // Since the default local extension id `f2_` uses has already been used by
3368 // `f1_` for another extensions, is is changed to 12.
isheriff6f8d6862016-05-26 18:24:553369 const RtpExtension kUpdatedVideoRtpExtensions[] = {
Jonas Olssona4d87372019-07-05 17:08:333370 kVideoRtpExtensionAnswer[0],
3371 RtpExtension(kVideoRtpExtension2[1].uri, 12),
isheriff6f8d6862016-05-26 18:24:553372 kVideoRtpExtension2[2],
henrike@webrtc.org28e20752013-07-10 00:45:363373 };
3374
3375 const AudioContentDescription* updated_acd =
3376 GetFirstAudioContentDescription(updated_offer.get());
3377 EXPECT_EQ(MAKE_VECTOR(kUpdatedAudioRtpExtensions),
3378 updated_acd->rtp_header_extensions());
3379
3380 const VideoContentDescription* updated_vcd =
3381 GetFirstVideoContentDescription(updated_offer.get());
3382 EXPECT_EQ(MAKE_VECTOR(kUpdatedVideoRtpExtensions),
3383 updated_vcd->rtp_header_extensions());
3384}
3385
deadbeefa5b273a2015-08-21 00:30:133386// Verify that if the same RTP extension URI is used for audio and video, the
3387// same ID is used. Also verify that the ID isn't changed when creating an
3388// updated offer (this was previously a bug).
isheriff6f8d6862016-05-26 18:24:553389TEST_F(MediaSessionDescriptionFactoryTest, RtpExtensionIdReused) {
deadbeefa5b273a2015-08-21 00:30:133390 MediaSessionOptions opts;
Steve Anton4e70a722017-11-28 22:57:103391 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
deadbeefa5b273a2015-08-21 00:30:133392
Markus Handell755c65d2020-06-23 23:06:103393 SetAudioVideoRtpHeaderExtensions(MAKE_VECTOR(kAudioRtpExtension3),
3394 MAKE_VECTOR(kVideoRtpExtension3), &opts);
Philipp Hancke2bf1b992023-09-26 07:04:463395 std::unique_ptr<SessionDescription> offer =
3396 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
deadbeefa5b273a2015-08-21 00:30:133397
3398 // Since the audio extensions used ID 3 for "both_audio_and_video", so should
3399 // the video extensions.
isheriff6f8d6862016-05-26 18:24:553400 const RtpExtension kExpectedVideoRtpExtension[] = {
Jonas Olssona4d87372019-07-05 17:08:333401 kVideoRtpExtension3[0],
3402 kAudioRtpExtension3[1],
deadbeefa5b273a2015-08-21 00:30:133403 };
3404
Yves Gerey665174f2018-06-19 13:03:053405 EXPECT_EQ(
3406 MAKE_VECTOR(kAudioRtpExtension3),
3407 GetFirstAudioContentDescription(offer.get())->rtp_header_extensions());
3408 EXPECT_EQ(
3409 MAKE_VECTOR(kExpectedVideoRtpExtension),
3410 GetFirstVideoContentDescription(offer.get())->rtp_header_extensions());
deadbeefa5b273a2015-08-21 00:30:133411
3412 // Nothing should change when creating a new offer
kwiberg31022942016-03-11 22:18:213413 std::unique_ptr<SessionDescription> updated_offer(
Philipp Hancke2bf1b992023-09-26 07:04:463414 f1_.CreateOfferOrError(opts, offer.get()).MoveValue());
deadbeefa5b273a2015-08-21 00:30:133415
3416 EXPECT_EQ(MAKE_VECTOR(kAudioRtpExtension3),
Yves Gerey665174f2018-06-19 13:03:053417 GetFirstAudioContentDescription(updated_offer.get())
3418 ->rtp_header_extensions());
deadbeefa5b273a2015-08-21 00:30:133419 EXPECT_EQ(MAKE_VECTOR(kExpectedVideoRtpExtension),
Yves Gerey665174f2018-06-19 13:03:053420 GetFirstVideoContentDescription(updated_offer.get())
3421 ->rtp_header_extensions());
deadbeefa5b273a2015-08-21 00:30:133422}
3423
jbauch5869f502017-06-29 19:31:363424// Same as "RtpExtensionIdReused" above for encrypted RTP extensions.
3425TEST_F(MediaSessionDescriptionFactoryTest, RtpExtensionIdReusedEncrypted) {
3426 MediaSessionOptions opts;
Steve Anton4e70a722017-11-28 22:57:103427 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
jbauch5869f502017-06-29 19:31:363428
3429 f1_.set_enable_encrypted_rtp_header_extensions(true);
3430 f2_.set_enable_encrypted_rtp_header_extensions(true);
3431
Markus Handell755c65d2020-06-23 23:06:103432 SetAudioVideoRtpHeaderExtensions(
3433 MAKE_VECTOR(kAudioRtpExtension3ForEncryption),
3434 MAKE_VECTOR(kVideoRtpExtension3ForEncryption), &opts);
Philipp Hancke2bf1b992023-09-26 07:04:463435 std::unique_ptr<SessionDescription> offer =
3436 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
jbauch5869f502017-06-29 19:31:363437
Yves Gerey665174f2018-06-19 13:03:053438 EXPECT_EQ(
3439 MAKE_VECTOR(kAudioRtpExtension3ForEncryptionOffer),
3440 GetFirstAudioContentDescription(offer.get())->rtp_header_extensions());
3441 EXPECT_EQ(
Lennart Grahl0d0ed762021-05-17 14:06:373442 MAKE_VECTOR(kVideoRtpExtension3ForEncryptionOffer),
Yves Gerey665174f2018-06-19 13:03:053443 GetFirstVideoContentDescription(offer.get())->rtp_header_extensions());
jbauch5869f502017-06-29 19:31:363444
3445 // Nothing should change when creating a new offer
3446 std::unique_ptr<SessionDescription> updated_offer(
Philipp Hancke2bf1b992023-09-26 07:04:463447 f1_.CreateOfferOrError(opts, offer.get()).MoveValue());
jbauch5869f502017-06-29 19:31:363448
3449 EXPECT_EQ(MAKE_VECTOR(kAudioRtpExtension3ForEncryptionOffer),
Yves Gerey665174f2018-06-19 13:03:053450 GetFirstAudioContentDescription(updated_offer.get())
3451 ->rtp_header_extensions());
Lennart Grahl0d0ed762021-05-17 14:06:373452 EXPECT_EQ(MAKE_VECTOR(kVideoRtpExtension3ForEncryptionOffer),
Yves Gerey665174f2018-06-19 13:03:053453 GetFirstVideoContentDescription(updated_offer.get())
3454 ->rtp_header_extensions());
jbauch5869f502017-06-29 19:31:363455}
3456
henrike@webrtc.org28e20752013-07-10 00:45:363457TEST(MediaSessionDescription, CopySessionDescription) {
3458 SessionDescription source;
Emil Lundmark7d2e6162023-11-08 15:27:453459 ContentGroup group(CN_AUDIO);
henrike@webrtc.org28e20752013-07-10 00:45:363460 source.AddGroup(group);
Harald Alvestrand1716d392019-06-03 18:35:453461 std::unique_ptr<AudioContentDescription> acd =
Mirko Bonadei317a1f02019-09-17 15:06:183462 std::make_unique<AudioContentDescription>();
henrike@webrtc.org28e20752013-07-10 00:45:363463 acd->set_codecs(MAKE_VECTOR(kAudioCodecs1));
3464 acd->AddLegacyStream(1);
Emil Lundmark7d2e6162023-11-08 15:27:453465 source.AddContent(CN_AUDIO, MediaProtocolType::kRtp, acd->Clone());
Harald Alvestrand1716d392019-06-03 18:35:453466 std::unique_ptr<VideoContentDescription> vcd =
Mirko Bonadei317a1f02019-09-17 15:06:183467 std::make_unique<VideoContentDescription>();
henrike@webrtc.org28e20752013-07-10 00:45:363468 vcd->set_codecs(MAKE_VECTOR(kVideoCodecs1));
3469 vcd->AddLegacyStream(2);
Emil Lundmark7d2e6162023-11-08 15:27:453470 source.AddContent(CN_VIDEO, MediaProtocolType::kRtp, vcd->Clone());
henrike@webrtc.org28e20752013-07-10 00:45:363471
Harald Alvestrand4d7160e2019-04-12 05:01:293472 std::unique_ptr<SessionDescription> copy = source.Clone();
Philipp Hancke2bf1b992023-09-26 07:04:463473 ASSERT_TRUE(copy.get());
Emil Lundmark7d2e6162023-11-08 15:27:453474 EXPECT_TRUE(copy->HasGroup(CN_AUDIO));
henrike@webrtc.org28e20752013-07-10 00:45:363475 const ContentInfo* ac = copy->GetContentByName("audio");
3476 const ContentInfo* vc = copy->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:463477 ASSERT_TRUE(ac);
3478 ASSERT_TRUE(vc);
Steve Anton5adfafd2017-12-21 00:34:003479 EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
Philipp Hancked0f0f382023-11-23 19:21:053480 const MediaContentDescription* acd_copy = ac->media_description();
henrike@webrtc.org28e20752013-07-10 00:45:363481 EXPECT_EQ(acd->codecs(), acd_copy->codecs());
3482 EXPECT_EQ(1u, acd->first_ssrc());
3483
Steve Anton5adfafd2017-12-21 00:34:003484 EXPECT_EQ(MediaProtocolType::kRtp, vc->type);
Philipp Hancked0f0f382023-11-23 19:21:053485 const MediaContentDescription* vcd_copy = vc->media_description();
henrike@webrtc.org28e20752013-07-10 00:45:363486 EXPECT_EQ(vcd->codecs(), vcd_copy->codecs());
3487 EXPECT_EQ(2u, vcd->first_ssrc());
3488}
3489
3490// The below TestTransportInfoXXX tests create different offers/answers, and
3491// ensure the TransportInfo in the SessionDescription matches what we expect.
3492TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoOfferAudio) {
3493 MediaSessionOptions options;
Amit Hilbuchc63ddb22019-01-02 18:13:583494 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
3495 RtpTransceiverDirection::kRecvOnly, kActive,
3496 &options);
henrike@webrtc.org28e20752013-07-10 00:45:363497 TestTransportInfo(true, options, false);
3498}
3499
Honghai Zhang4cedf2b2016-08-31 15:18:113500TEST_F(MediaSessionDescriptionFactoryTest,
3501 TestTransportInfoOfferIceRenomination) {
3502 MediaSessionOptions options;
Amit Hilbuchc63ddb22019-01-02 18:13:583503 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
3504 RtpTransceiverDirection::kRecvOnly, kActive,
3505 &options);
zhihuang1c378ed2017-08-17 21:10:503506 options.media_description_options[0]
3507 .transport_options.enable_ice_renomination = true;
Honghai Zhang4cedf2b2016-08-31 15:18:113508 TestTransportInfo(true, options, false);
3509}
3510
henrike@webrtc.org28e20752013-07-10 00:45:363511TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoOfferAudioCurrent) {
3512 MediaSessionOptions options;
Amit Hilbuchc63ddb22019-01-02 18:13:583513 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
3514 RtpTransceiverDirection::kRecvOnly, kActive,
3515 &options);
henrike@webrtc.org28e20752013-07-10 00:45:363516 TestTransportInfo(true, options, true);
3517}
3518
3519TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoOfferMultimedia) {
3520 MediaSessionOptions options;
Steve Anton4e70a722017-11-28 22:57:103521 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &options);
henrike@webrtc.org28e20752013-07-10 00:45:363522 TestTransportInfo(true, options, false);
3523}
3524
3525TEST_F(MediaSessionDescriptionFactoryTest,
Yves Gerey665174f2018-06-19 13:03:053526 TestTransportInfoOfferMultimediaCurrent) {
henrike@webrtc.org28e20752013-07-10 00:45:363527 MediaSessionOptions options;
Steve Anton4e70a722017-11-28 22:57:103528 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &options);
henrike@webrtc.org28e20752013-07-10 00:45:363529 TestTransportInfo(true, options, true);
3530}
3531
3532TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoOfferBundle) {
3533 MediaSessionOptions options;
Steve Anton4e70a722017-11-28 22:57:103534 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &options);
henrike@webrtc.org28e20752013-07-10 00:45:363535 options.bundle_enabled = true;
3536 TestTransportInfo(true, options, false);
3537}
3538
3539TEST_F(MediaSessionDescriptionFactoryTest,
3540 TestTransportInfoOfferBundleCurrent) {
3541 MediaSessionOptions options;
Steve Anton4e70a722017-11-28 22:57:103542 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &options);
henrike@webrtc.org28e20752013-07-10 00:45:363543 options.bundle_enabled = true;
3544 TestTransportInfo(true, options, true);
3545}
3546
3547TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoAnswerAudio) {
3548 MediaSessionOptions options;
Amit Hilbuchc63ddb22019-01-02 18:13:583549 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
3550 RtpTransceiverDirection::kRecvOnly, kActive,
3551 &options);
henrike@webrtc.org28e20752013-07-10 00:45:363552 TestTransportInfo(false, options, false);
3553}
3554
3555TEST_F(MediaSessionDescriptionFactoryTest,
Honghai Zhang4cedf2b2016-08-31 15:18:113556 TestTransportInfoAnswerIceRenomination) {
3557 MediaSessionOptions options;
Amit Hilbuchc63ddb22019-01-02 18:13:583558 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
3559 RtpTransceiverDirection::kRecvOnly, kActive,
3560 &options);
zhihuang1c378ed2017-08-17 21:10:503561 options.media_description_options[0]
3562 .transport_options.enable_ice_renomination = true;
Honghai Zhang4cedf2b2016-08-31 15:18:113563 TestTransportInfo(false, options, false);
3564}
3565
3566TEST_F(MediaSessionDescriptionFactoryTest,
3567 TestTransportInfoAnswerAudioCurrent) {
henrike@webrtc.org28e20752013-07-10 00:45:363568 MediaSessionOptions options;
Amit Hilbuchc63ddb22019-01-02 18:13:583569 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
3570 RtpTransceiverDirection::kRecvOnly, kActive,
3571 &options);
henrike@webrtc.org28e20752013-07-10 00:45:363572 TestTransportInfo(false, options, true);
3573}
3574
3575TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoAnswerMultimedia) {
3576 MediaSessionOptions options;
Steve Anton4e70a722017-11-28 22:57:103577 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &options);
henrike@webrtc.org28e20752013-07-10 00:45:363578 TestTransportInfo(false, options, false);
3579}
3580
3581TEST_F(MediaSessionDescriptionFactoryTest,
Yves Gerey665174f2018-06-19 13:03:053582 TestTransportInfoAnswerMultimediaCurrent) {
henrike@webrtc.org28e20752013-07-10 00:45:363583 MediaSessionOptions options;
Steve Anton4e70a722017-11-28 22:57:103584 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &options);
henrike@webrtc.org28e20752013-07-10 00:45:363585 TestTransportInfo(false, options, true);
3586}
3587
3588TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoAnswerBundle) {
3589 MediaSessionOptions options;
Steve Anton4e70a722017-11-28 22:57:103590 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &options);
henrike@webrtc.org28e20752013-07-10 00:45:363591 options.bundle_enabled = true;
3592 TestTransportInfo(false, options, false);
3593}
3594
3595TEST_F(MediaSessionDescriptionFactoryTest,
Yves Gerey665174f2018-06-19 13:03:053596 TestTransportInfoAnswerBundleCurrent) {
henrike@webrtc.org28e20752013-07-10 00:45:363597 MediaSessionOptions options;
Steve Anton4e70a722017-11-28 22:57:103598 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &options);
henrike@webrtc.org28e20752013-07-10 00:45:363599 options.bundle_enabled = true;
3600 TestTransportInfo(false, options, true);
3601}
3602
jiayl@webrtc.org8dcd43c2014-05-29 22:07:593603// Offers UDP/TLS/RTP/SAVPF and verifies the answer can be created and contains
3604// UDP/TLS/RTP/SAVPF.
3605TEST_F(MediaSessionDescriptionFactoryTest, TestOfferDtlsSavpfCreateAnswer) {
Steve Anton6fe1fba2018-12-11 18:15:233606 std::unique_ptr<SessionDescription> offer =
Philipp Hancke7fbcc8c2023-11-02 09:53:213607 f1_.CreateOfferOrError(CreateAudioMediaSession(), nullptr).MoveValue();
Philipp Hancke2bf1b992023-09-26 07:04:463608 ASSERT_TRUE(offer.get());
jiayl@webrtc.org8dcd43c2014-05-29 22:07:593609 ContentInfo* offer_content = offer->GetContentByName("audio");
Philipp Hancke2bf1b992023-09-26 07:04:463610 ASSERT_TRUE(offer_content);
Philipp Hancked0f0f382023-11-23 19:21:053611 MediaContentDescription* offer_audio_desc =
3612 offer_content->media_description();
Emil Lundmark7d2e6162023-11-08 15:27:453613 offer_audio_desc->set_protocol(kMediaProtocolDtlsSavpf);
jiayl@webrtc.org8dcd43c2014-05-29 22:07:593614
Steve Anton6fe1fba2018-12-11 18:15:233615 std::unique_ptr<SessionDescription> answer =
Philipp Hancke7fbcc8c2023-11-02 09:53:213616 f2_.CreateAnswerOrError(offer.get(), CreateAudioMediaSession(), nullptr)
Philipp Hancke2bf1b992023-09-26 07:04:463617 .MoveValue();
3618 ASSERT_TRUE(answer);
jiayl@webrtc.org8dcd43c2014-05-29 22:07:593619
3620 const ContentInfo* answer_content = answer->GetContentByName("audio");
Philipp Hancke2bf1b992023-09-26 07:04:463621 ASSERT_TRUE(answer_content);
jiayl@webrtc.org8dcd43c2014-05-29 22:07:593622 ASSERT_FALSE(answer_content->rejected);
3623
Philipp Hancked0f0f382023-11-23 19:21:053624 const MediaContentDescription* answer_audio_desc =
3625 answer_content->media_description();
Emil Lundmark7d2e6162023-11-08 15:27:453626 EXPECT_EQ(kMediaProtocolDtlsSavpf, answer_audio_desc->protocol());
jiayl@webrtc.org8dcd43c2014-05-29 22:07:593627}
3628
Harald Alvestrand0d018412021-11-04 13:52:313629
henrike@webrtc.org28e20752013-07-10 00:45:363630// Test that we accept a DTLS offer without SDES and create an appropriate
3631// answer.
3632TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoOfferDtlsButNotSdes) {
Harald Alvestrand974044e2024-02-08 13:15:513633 /* TODO(hta): Figure this one out.
Harald Alvestrand0d018412021-11-04 13:52:313634 f1_.set_secure(SEC_DISABLED);
3635 f2_.set_secure(SEC_ENABLED);
3636 tdf1_.set_secure(SEC_ENABLED);
3637 tdf2_.set_secure(SEC_ENABLED);
Harald Alvestrand974044e2024-02-08 13:15:513638 */
henrike@webrtc.org28e20752013-07-10 00:45:363639 MediaSessionOptions options;
Steve Anton4e70a722017-11-28 22:57:103640 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &options);
henrike@webrtc.org28e20752013-07-10 00:45:363641
Harald Alvestrand974044e2024-02-08 13:15:513642 // Generate an offer with DTLS
Philipp Hancke2bf1b992023-09-26 07:04:463643 std::unique_ptr<SessionDescription> offer =
3644 f1_.CreateOfferOrError(options, nullptr).MoveValue();
3645 ASSERT_TRUE(offer.get());
henrike@webrtc.org28e20752013-07-10 00:45:363646
Emil Lundmark7d2e6162023-11-08 15:27:453647 const TransportDescription* audio_offer_trans_desc =
henrike@webrtc.org28e20752013-07-10 00:45:363648 offer->GetTransportDescriptionByName("audio");
Philipp Hancke2bf1b992023-09-26 07:04:463649 ASSERT_TRUE(audio_offer_trans_desc->identity_fingerprint.get());
Emil Lundmark7d2e6162023-11-08 15:27:453650 const TransportDescription* video_offer_trans_desc =
henrike@webrtc.org28e20752013-07-10 00:45:363651 offer->GetTransportDescriptionByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:463652 ASSERT_TRUE(video_offer_trans_desc->identity_fingerprint.get());
henrike@webrtc.org28e20752013-07-10 00:45:363653
3654 // Generate an answer with DTLS.
Steve Anton6fe1fba2018-12-11 18:15:233655 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:463656 f2_.CreateAnswerOrError(offer.get(), options, nullptr).MoveValue();
3657 ASSERT_TRUE(answer.get());
henrike@webrtc.org28e20752013-07-10 00:45:363658
Emil Lundmark7d2e6162023-11-08 15:27:453659 const TransportDescription* audio_answer_trans_desc =
henrike@webrtc.org28e20752013-07-10 00:45:363660 answer->GetTransportDescriptionByName("audio");
Philipp Hancke2bf1b992023-09-26 07:04:463661 EXPECT_TRUE(audio_answer_trans_desc->identity_fingerprint.get());
Emil Lundmark7d2e6162023-11-08 15:27:453662 const TransportDescription* video_answer_trans_desc =
henrike@webrtc.org28e20752013-07-10 00:45:363663 answer->GetTransportDescriptionByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:463664 EXPECT_TRUE(video_answer_trans_desc->identity_fingerprint.get());
henrike@webrtc.org28e20752013-07-10 00:45:363665}
3666
3667// Verifies if vad_enabled option is set to false, CN codecs are not present in
3668// offer or answer.
3669TEST_F(MediaSessionDescriptionFactoryTest, TestVADEnableOption) {
3670 MediaSessionOptions options;
Steve Anton4e70a722017-11-28 22:57:103671 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &options);
Philipp Hancke2bf1b992023-09-26 07:04:463672 std::unique_ptr<SessionDescription> offer =
3673 f1_.CreateOfferOrError(options, nullptr).MoveValue();
3674 ASSERT_TRUE(offer.get());
henrike@webrtc.org28e20752013-07-10 00:45:363675 const ContentInfo* audio_content = offer->GetContentByName("audio");
3676 EXPECT_FALSE(VerifyNoCNCodecs(audio_content));
3677
3678 options.vad_enabled = false;
Philipp Hancke2bf1b992023-09-26 07:04:463679 offer = f1_.CreateOfferOrError(options, nullptr).MoveValue();
3680 ASSERT_TRUE(offer.get());
henrike@webrtc.org28e20752013-07-10 00:45:363681 audio_content = offer->GetContentByName("audio");
3682 EXPECT_TRUE(VerifyNoCNCodecs(audio_content));
Steve Anton6fe1fba2018-12-11 18:15:233683 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:463684 f1_.CreateAnswerOrError(offer.get(), options, nullptr).MoveValue();
3685 ASSERT_TRUE(answer.get());
henrike@webrtc.org28e20752013-07-10 00:45:363686 audio_content = answer->GetContentByName("audio");
3687 EXPECT_TRUE(VerifyNoCNCodecs(audio_content));
3688}
deadbeef44f08192015-12-16 00:20:093689
zhihuang1c378ed2017-08-17 21:10:503690// Test that the generated MIDs match the existing offer.
3691TEST_F(MediaSessionDescriptionFactoryTest, TestMIDsMatchesExistingOffer) {
deadbeef44f08192015-12-16 00:20:093692 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:583693 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio_modified",
3694 RtpTransceiverDirection::kRecvOnly, kActive,
3695 &opts);
3696 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video_modified",
3697 RtpTransceiverDirection::kRecvOnly, kActive,
3698 &opts);
Amit Hilbuchc63ddb22019-01-02 18:13:583699 AddMediaDescriptionOptions(MEDIA_TYPE_DATA, "data_modified",
3700 RtpTransceiverDirection::kSendRecv, kActive,
3701 &opts);
zhihuang1c378ed2017-08-17 21:10:503702 // Create offer.
Philipp Hancke2bf1b992023-09-26 07:04:463703 std::unique_ptr<SessionDescription> offer =
3704 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
kwiberg31022942016-03-11 22:18:213705 std::unique_ptr<SessionDescription> updated_offer(
Philipp Hancke2bf1b992023-09-26 07:04:463706 f1_.CreateOfferOrError(opts, offer.get()).MoveValue());
zhihuang1c378ed2017-08-17 21:10:503707
deadbeef44f08192015-12-16 00:20:093708 const ContentInfo* audio_content = GetFirstAudioContent(updated_offer.get());
3709 const ContentInfo* video_content = GetFirstVideoContent(updated_offer.get());
3710 const ContentInfo* data_content = GetFirstDataContent(updated_offer.get());
Philipp Hancke2bf1b992023-09-26 07:04:463711 ASSERT_TRUE(audio_content);
3712 ASSERT_TRUE(video_content);
3713 ASSERT_TRUE(data_content);
deadbeef44f08192015-12-16 00:20:093714 EXPECT_EQ("audio_modified", audio_content->name);
3715 EXPECT_EQ("video_modified", video_content->name);
3716 EXPECT_EQ("data_modified", data_content->name);
3717}
zhihuangcf5b37c2016-05-05 18:44:353718
zhihuang1c378ed2017-08-17 21:10:503719// The following tests verify that the unified plan SDP is supported.
3720// Test that we can create an offer with multiple media sections of same media
3721// type.
3722TEST_F(MediaSessionDescriptionFactoryTest,
3723 CreateOfferWithMultipleAVMediaSections) {
3724 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:583725 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio_1",
3726 RtpTransceiverDirection::kSendRecv, kActive,
3727 &opts);
3728 AttachSenderToMediaDescriptionOptions(
3729 "audio_1", MEDIA_TYPE_AUDIO, kAudioTrack1, {kMediaStream1}, 1, &opts);
zhihuang1c378ed2017-08-17 21:10:503730
Amit Hilbuchc63ddb22019-01-02 18:13:583731 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video_1",
3732 RtpTransceiverDirection::kSendRecv, kActive,
3733 &opts);
3734 AttachSenderToMediaDescriptionOptions(
3735 "video_1", MEDIA_TYPE_VIDEO, kVideoTrack1, {kMediaStream1}, 1, &opts);
zhihuang1c378ed2017-08-17 21:10:503736
Amit Hilbuchc63ddb22019-01-02 18:13:583737 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio_2",
3738 RtpTransceiverDirection::kSendRecv, kActive,
3739 &opts);
3740 AttachSenderToMediaDescriptionOptions(
3741 "audio_2", MEDIA_TYPE_AUDIO, kAudioTrack2, {kMediaStream2}, 1, &opts);
zhihuang1c378ed2017-08-17 21:10:503742
Amit Hilbuchc63ddb22019-01-02 18:13:583743 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video_2",
3744 RtpTransceiverDirection::kSendRecv, kActive,
3745 &opts);
3746 AttachSenderToMediaDescriptionOptions(
3747 "video_2", MEDIA_TYPE_VIDEO, kVideoTrack2, {kMediaStream2}, 1, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:463748 std::unique_ptr<SessionDescription> offer =
3749 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
zhihuang1c378ed2017-08-17 21:10:503750 ASSERT_TRUE(offer);
3751
3752 ASSERT_EQ(4u, offer->contents().size());
3753 EXPECT_FALSE(offer->contents()[0].rejected);
Philipp Hancked0f0f382023-11-23 19:21:053754 const MediaContentDescription* acd = offer->contents()[0].media_description();
zhihuang1c378ed2017-08-17 21:10:503755 ASSERT_EQ(1u, acd->streams().size());
3756 EXPECT_EQ(kAudioTrack1, acd->streams()[0].id);
Steve Anton4e70a722017-11-28 22:57:103757 EXPECT_EQ(RtpTransceiverDirection::kSendRecv, acd->direction());
zhihuang1c378ed2017-08-17 21:10:503758
3759 EXPECT_FALSE(offer->contents()[1].rejected);
Philipp Hancked0f0f382023-11-23 19:21:053760 const MediaContentDescription* vcd = offer->contents()[1].media_description();
zhihuang1c378ed2017-08-17 21:10:503761 ASSERT_EQ(1u, vcd->streams().size());
3762 EXPECT_EQ(kVideoTrack1, vcd->streams()[0].id);
Steve Anton4e70a722017-11-28 22:57:103763 EXPECT_EQ(RtpTransceiverDirection::kSendRecv, vcd->direction());
zhihuang1c378ed2017-08-17 21:10:503764
3765 EXPECT_FALSE(offer->contents()[2].rejected);
Philipp Hancked0f0f382023-11-23 19:21:053766 acd = offer->contents()[2].media_description();
zhihuang1c378ed2017-08-17 21:10:503767 ASSERT_EQ(1u, acd->streams().size());
3768 EXPECT_EQ(kAudioTrack2, acd->streams()[0].id);
Steve Anton4e70a722017-11-28 22:57:103769 EXPECT_EQ(RtpTransceiverDirection::kSendRecv, acd->direction());
zhihuang1c378ed2017-08-17 21:10:503770
3771 EXPECT_FALSE(offer->contents()[3].rejected);
Philipp Hancked0f0f382023-11-23 19:21:053772 vcd = offer->contents()[3].media_description();
zhihuang1c378ed2017-08-17 21:10:503773 ASSERT_EQ(1u, vcd->streams().size());
3774 EXPECT_EQ(kVideoTrack2, vcd->streams()[0].id);
Steve Anton4e70a722017-11-28 22:57:103775 EXPECT_EQ(RtpTransceiverDirection::kSendRecv, vcd->direction());
zhihuang1c378ed2017-08-17 21:10:503776}
3777
3778// Test that we can create an answer with multiple media sections of same media
3779// type.
3780TEST_F(MediaSessionDescriptionFactoryTest,
3781 CreateAnswerWithMultipleAVMediaSections) {
3782 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:583783 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio_1",
3784 RtpTransceiverDirection::kSendRecv, kActive,
3785 &opts);
3786 AttachSenderToMediaDescriptionOptions(
3787 "audio_1", MEDIA_TYPE_AUDIO, kAudioTrack1, {kMediaStream1}, 1, &opts);
zhihuang1c378ed2017-08-17 21:10:503788
Amit Hilbuchc63ddb22019-01-02 18:13:583789 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video_1",
3790 RtpTransceiverDirection::kSendRecv, kActive,
3791 &opts);
3792 AttachSenderToMediaDescriptionOptions(
3793 "video_1", MEDIA_TYPE_VIDEO, kVideoTrack1, {kMediaStream1}, 1, &opts);
zhihuang1c378ed2017-08-17 21:10:503794
Amit Hilbuchc63ddb22019-01-02 18:13:583795 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio_2",
3796 RtpTransceiverDirection::kSendRecv, kActive,
3797 &opts);
3798 AttachSenderToMediaDescriptionOptions(
3799 "audio_2", MEDIA_TYPE_AUDIO, kAudioTrack2, {kMediaStream2}, 1, &opts);
zhihuang1c378ed2017-08-17 21:10:503800
Amit Hilbuchc63ddb22019-01-02 18:13:583801 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video_2",
3802 RtpTransceiverDirection::kSendRecv, kActive,
3803 &opts);
3804 AttachSenderToMediaDescriptionOptions(
3805 "video_2", MEDIA_TYPE_VIDEO, kVideoTrack2, {kMediaStream2}, 1, &opts);
zhihuang1c378ed2017-08-17 21:10:503806
Philipp Hancke2bf1b992023-09-26 07:04:463807 std::unique_ptr<SessionDescription> offer =
3808 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
zhihuang1c378ed2017-08-17 21:10:503809 ASSERT_TRUE(offer);
Steve Anton6fe1fba2018-12-11 18:15:233810 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:463811 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
zhihuang1c378ed2017-08-17 21:10:503812
3813 ASSERT_EQ(4u, answer->contents().size());
3814 EXPECT_FALSE(answer->contents()[0].rejected);
Philipp Hancked0f0f382023-11-23 19:21:053815 const MediaContentDescription* acd =
3816 answer->contents()[0].media_description();
zhihuang1c378ed2017-08-17 21:10:503817 ASSERT_EQ(1u, acd->streams().size());
3818 EXPECT_EQ(kAudioTrack1, acd->streams()[0].id);
Steve Anton4e70a722017-11-28 22:57:103819 EXPECT_EQ(RtpTransceiverDirection::kSendRecv, acd->direction());
zhihuang1c378ed2017-08-17 21:10:503820
3821 EXPECT_FALSE(answer->contents()[1].rejected);
Philipp Hancked0f0f382023-11-23 19:21:053822 const MediaContentDescription* vcd =
3823 answer->contents()[1].media_description();
zhihuang1c378ed2017-08-17 21:10:503824 ASSERT_EQ(1u, vcd->streams().size());
3825 EXPECT_EQ(kVideoTrack1, vcd->streams()[0].id);
Steve Anton4e70a722017-11-28 22:57:103826 EXPECT_EQ(RtpTransceiverDirection::kSendRecv, vcd->direction());
zhihuang1c378ed2017-08-17 21:10:503827
3828 EXPECT_FALSE(answer->contents()[2].rejected);
Philipp Hancked0f0f382023-11-23 19:21:053829 acd = answer->contents()[2].media_description();
zhihuang1c378ed2017-08-17 21:10:503830 ASSERT_EQ(1u, acd->streams().size());
3831 EXPECT_EQ(kAudioTrack2, acd->streams()[0].id);
Steve Anton4e70a722017-11-28 22:57:103832 EXPECT_EQ(RtpTransceiverDirection::kSendRecv, acd->direction());
zhihuang1c378ed2017-08-17 21:10:503833
3834 EXPECT_FALSE(answer->contents()[3].rejected);
Philipp Hancked0f0f382023-11-23 19:21:053835 vcd = answer->contents()[3].media_description();
zhihuang1c378ed2017-08-17 21:10:503836 ASSERT_EQ(1u, vcd->streams().size());
3837 EXPECT_EQ(kVideoTrack2, vcd->streams()[0].id);
Steve Anton4e70a722017-11-28 22:57:103838 EXPECT_EQ(RtpTransceiverDirection::kSendRecv, vcd->direction());
zhihuang1c378ed2017-08-17 21:10:503839}
3840
3841// Test that the media section will be rejected in offer if the corresponding
3842// MediaDescriptionOptions is stopped by the offerer.
3843TEST_F(MediaSessionDescriptionFactoryTest,
3844 CreateOfferWithMediaSectionStoppedByOfferer) {
3845 // Create an offer with two audio sections and one of them is stopped.
3846 MediaSessionOptions offer_opts;
Amit Hilbuchc63ddb22019-01-02 18:13:583847 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio1",
3848 RtpTransceiverDirection::kSendRecv, kActive,
3849 &offer_opts);
3850 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio2",
3851 RtpTransceiverDirection::kInactive, kStopped,
3852 &offer_opts);
Steve Anton6fe1fba2018-12-11 18:15:233853 std::unique_ptr<SessionDescription> offer =
Philipp Hancke2bf1b992023-09-26 07:04:463854 f1_.CreateOfferOrError(offer_opts, nullptr).MoveValue();
zhihuang1c378ed2017-08-17 21:10:503855 ASSERT_TRUE(offer);
3856 ASSERT_EQ(2u, offer->contents().size());
3857 EXPECT_FALSE(offer->contents()[0].rejected);
3858 EXPECT_TRUE(offer->contents()[1].rejected);
3859}
3860
3861// Test that the media section will be rejected in answer if the corresponding
3862// MediaDescriptionOptions is stopped by the offerer.
3863TEST_F(MediaSessionDescriptionFactoryTest,
3864 CreateAnswerWithMediaSectionStoppedByOfferer) {
3865 // Create an offer with two audio sections and one of them is stopped.
3866 MediaSessionOptions offer_opts;
Amit Hilbuchc63ddb22019-01-02 18:13:583867 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio1",
3868 RtpTransceiverDirection::kSendRecv, kActive,
3869 &offer_opts);
3870 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio2",
3871 RtpTransceiverDirection::kInactive, kStopped,
3872 &offer_opts);
Steve Anton6fe1fba2018-12-11 18:15:233873 std::unique_ptr<SessionDescription> offer =
Philipp Hancke2bf1b992023-09-26 07:04:463874 f1_.CreateOfferOrError(offer_opts, nullptr).MoveValue();
zhihuang1c378ed2017-08-17 21:10:503875 ASSERT_TRUE(offer);
3876 ASSERT_EQ(2u, offer->contents().size());
3877 EXPECT_FALSE(offer->contents()[0].rejected);
3878 EXPECT_TRUE(offer->contents()[1].rejected);
3879
3880 // Create an answer based on the offer.
3881 MediaSessionOptions answer_opts;
Amit Hilbuchc63ddb22019-01-02 18:13:583882 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio1",
3883 RtpTransceiverDirection::kSendRecv, kActive,
3884 &answer_opts);
3885 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio2",
3886 RtpTransceiverDirection::kSendRecv, kActive,
3887 &answer_opts);
Steve Anton6fe1fba2018-12-11 18:15:233888 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:463889 f2_.CreateAnswerOrError(offer.get(), answer_opts, nullptr).MoveValue();
zhihuang1c378ed2017-08-17 21:10:503890 ASSERT_EQ(2u, answer->contents().size());
3891 EXPECT_FALSE(answer->contents()[0].rejected);
3892 EXPECT_TRUE(answer->contents()[1].rejected);
3893}
3894
3895// Test that the media section will be rejected in answer if the corresponding
3896// MediaDescriptionOptions is stopped by the answerer.
3897TEST_F(MediaSessionDescriptionFactoryTest,
3898 CreateAnswerWithMediaSectionRejectedByAnswerer) {
3899 // Create an offer with two audio sections.
3900 MediaSessionOptions offer_opts;
Amit Hilbuchc63ddb22019-01-02 18:13:583901 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio1",
3902 RtpTransceiverDirection::kSendRecv, kActive,
3903 &offer_opts);
3904 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio2",
3905 RtpTransceiverDirection::kSendRecv, kActive,
3906 &offer_opts);
Steve Anton6fe1fba2018-12-11 18:15:233907 std::unique_ptr<SessionDescription> offer =
Philipp Hancke2bf1b992023-09-26 07:04:463908 f1_.CreateOfferOrError(offer_opts, nullptr).MoveValue();
zhihuang1c378ed2017-08-17 21:10:503909 ASSERT_TRUE(offer);
3910 ASSERT_EQ(2u, offer->contents().size());
3911 ASSERT_FALSE(offer->contents()[0].rejected);
3912 ASSERT_FALSE(offer->contents()[1].rejected);
3913
3914 // The answerer rejects one of the audio sections.
3915 MediaSessionOptions answer_opts;
Amit Hilbuchc63ddb22019-01-02 18:13:583916 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio1",
3917 RtpTransceiverDirection::kSendRecv, kActive,
3918 &answer_opts);
3919 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio2",
3920 RtpTransceiverDirection::kInactive, kStopped,
3921 &answer_opts);
Steve Anton6fe1fba2018-12-11 18:15:233922 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:463923 f2_.CreateAnswerOrError(offer.get(), answer_opts, nullptr).MoveValue();
zhihuang1c378ed2017-08-17 21:10:503924 ASSERT_EQ(2u, answer->contents().size());
3925 EXPECT_FALSE(answer->contents()[0].rejected);
3926 EXPECT_TRUE(answer->contents()[1].rejected);
Zhi Huang3518e7b2018-01-30 21:20:353927
3928 // The TransportInfo of the rejected m= section is expected to be added in the
3929 // answer.
3930 EXPECT_EQ(offer->transport_infos().size(), answer->transport_infos().size());
zhihuang1c378ed2017-08-17 21:10:503931}
3932
3933// Test the generated media sections has the same order of the
3934// corresponding MediaDescriptionOptions.
3935TEST_F(MediaSessionDescriptionFactoryTest,
3936 CreateOfferRespectsMediaDescriptionOptionsOrder) {
3937 MediaSessionOptions opts;
3938 // This tests put video section first because normally audio comes first by
3939 // default.
Amit Hilbuchc63ddb22019-01-02 18:13:583940 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
3941 RtpTransceiverDirection::kSendRecv, kActive,
3942 &opts);
3943 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
3944 RtpTransceiverDirection::kSendRecv, kActive,
3945 &opts);
Philipp Hancke2bf1b992023-09-26 07:04:463946 std::unique_ptr<SessionDescription> offer =
3947 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
zhihuang1c378ed2017-08-17 21:10:503948
3949 ASSERT_TRUE(offer);
3950 ASSERT_EQ(2u, offer->contents().size());
3951 EXPECT_EQ("video", offer->contents()[0].name);
3952 EXPECT_EQ("audio", offer->contents()[1].name);
3953}
3954
3955// Test that different media sections using the same codec have same payload
3956// type.
3957TEST_F(MediaSessionDescriptionFactoryTest,
3958 PayloadTypesSharedByMediaSectionsOfSameType) {
3959 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:583960 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video1",
3961 RtpTransceiverDirection::kSendRecv, kActive,
3962 &opts);
3963 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video2",
3964 RtpTransceiverDirection::kSendRecv, kActive,
3965 &opts);
zhihuang1c378ed2017-08-17 21:10:503966 // Create an offer with two video sections using same codecs.
Philipp Hancke2bf1b992023-09-26 07:04:463967 std::unique_ptr<SessionDescription> offer =
3968 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
zhihuang1c378ed2017-08-17 21:10:503969 ASSERT_TRUE(offer);
3970 ASSERT_EQ(2u, offer->contents().size());
Philipp Hancked0f0f382023-11-23 19:21:053971 const MediaContentDescription* vcd1 =
3972 offer->contents()[0].media_description();
3973 const MediaContentDescription* vcd2 =
3974 offer->contents()[1].media_description();
zhihuang1c378ed2017-08-17 21:10:503975 EXPECT_EQ(vcd1->codecs().size(), vcd2->codecs().size());
3976 ASSERT_EQ(2u, vcd1->codecs().size());
3977 EXPECT_EQ(vcd1->codecs()[0].name, vcd2->codecs()[0].name);
3978 EXPECT_EQ(vcd1->codecs()[0].id, vcd2->codecs()[0].id);
3979 EXPECT_EQ(vcd1->codecs()[1].name, vcd2->codecs()[1].name);
3980 EXPECT_EQ(vcd1->codecs()[1].id, vcd2->codecs()[1].id);
3981
3982 // Create answer and negotiate the codecs.
Steve Anton6fe1fba2018-12-11 18:15:233983 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:463984 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
zhihuang1c378ed2017-08-17 21:10:503985 ASSERT_TRUE(answer);
3986 ASSERT_EQ(2u, answer->contents().size());
Philipp Hancked0f0f382023-11-23 19:21:053987 vcd1 = answer->contents()[0].media_description();
3988 vcd2 = answer->contents()[1].media_description();
zhihuang1c378ed2017-08-17 21:10:503989 EXPECT_EQ(vcd1->codecs().size(), vcd2->codecs().size());
3990 ASSERT_EQ(1u, vcd1->codecs().size());
3991 EXPECT_EQ(vcd1->codecs()[0].name, vcd2->codecs()[0].name);
3992 EXPECT_EQ(vcd1->codecs()[0].id, vcd2->codecs()[0].id);
3993}
3994
Qiu Jianlinb3488d02023-12-07 00:12:123995#ifdef RTC_ENABLE_H265
3996// Test verifying that negotiating codecs with the same tx-mode retains the
3997// tx-mode value.
3998TEST_F(MediaSessionDescriptionFactoryTest, H265TxModeIsEqualRetainIt) {
3999 std::vector f1_codecs = {CreateVideoCodec(96, "H265")};
4000 f1_codecs.back().tx_mode = "mrst";
4001 f1_.set_video_codecs(f1_codecs, f1_codecs);
4002
4003 std::vector f2_codecs = {CreateVideoCodec(96, "H265")};
4004 f2_codecs.back().tx_mode = "mrst";
4005 f2_.set_video_codecs(f2_codecs, f2_codecs);
4006
4007 MediaSessionOptions opts;
4008 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video1",
4009 RtpTransceiverDirection::kSendRecv, kActive,
4010 &opts);
4011
4012 // Create an offer with two video sections using same codecs.
4013 std::unique_ptr<SessionDescription> offer =
4014 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
4015 ASSERT_TRUE(offer);
4016 ASSERT_EQ(1u, offer->contents().size());
4017 const MediaContentDescription* vcd1 =
4018 offer->contents()[0].media_description();
4019 ASSERT_EQ(1u, vcd1->codecs().size());
4020 EXPECT_EQ(vcd1->codecs()[0].tx_mode, "mrst");
4021
4022 // Create answer and negotiate the codecs.
4023 std::unique_ptr<SessionDescription> answer =
4024 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
4025 ASSERT_TRUE(answer);
4026 ASSERT_EQ(1u, answer->contents().size());
4027 vcd1 = answer->contents()[0].media_description();
4028 ASSERT_EQ(1u, vcd1->codecs().size());
4029 EXPECT_EQ(vcd1->codecs()[0].tx_mode, "mrst");
4030}
4031
4032// Test verifying that negotiating codecs with different tx_mode removes
4033// the tx_mode value.
4034TEST_F(MediaSessionDescriptionFactoryTest, H265TxModeIsDifferentDropCodecs) {
4035 std::vector f1_codecs = {CreateVideoCodec(96, "H265")};
4036 f1_codecs.back().tx_mode = "mrst";
4037 f1_.set_video_codecs(f1_codecs, f1_codecs);
4038
4039 std::vector f2_codecs = {CreateVideoCodec(96, "H265")};
4040 f2_codecs.back().tx_mode = "mrmt";
4041 f2_.set_video_codecs(f2_codecs, f2_codecs);
4042
4043 MediaSessionOptions opts;
4044 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video1",
4045 RtpTransceiverDirection::kSendRecv, kActive,
4046 &opts);
4047
4048 // Create an offer with two video sections using same codecs.
4049 std::unique_ptr<SessionDescription> offer =
4050 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
4051 ASSERT_TRUE(offer);
4052 ASSERT_EQ(1u, offer->contents().size());
4053 const VideoContentDescription* vcd1 =
4054 offer->contents()[0].media_description()->as_video();
4055 ASSERT_EQ(1u, vcd1->codecs().size());
4056 EXPECT_EQ(vcd1->codecs()[0].tx_mode, "mrst");
4057
4058 // Create answer and negotiate the codecs.
4059 std::unique_ptr<SessionDescription> answer =
4060 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
4061 ASSERT_TRUE(answer);
4062 ASSERT_EQ(1u, answer->contents().size());
4063 vcd1 = answer->contents()[0].media_description()->as_video();
4064 ASSERT_EQ(1u, vcd1->codecs().size());
4065 EXPECT_EQ(vcd1->codecs()[0].tx_mode, absl::nullopt);
4066}
4067#endif
4068
Florent Castelli811e24a2023-05-31 12:35:474069// Test verifying that negotiating codecs with the same packetization retains
4070// the packetization value.
4071TEST_F(MediaSessionDescriptionFactoryTest, PacketizationIsEqual) {
Emil Lundmark7d2e6162023-11-08 15:27:454072 std::vector f1_codecs = {CreateVideoCodec(96, "H264")};
Florent Castelli811e24a2023-05-31 12:35:474073 f1_codecs.back().packetization = "raw";
4074 f1_.set_video_codecs(f1_codecs, f1_codecs);
4075
Emil Lundmark7d2e6162023-11-08 15:27:454076 std::vector f2_codecs = {CreateVideoCodec(96, "H264")};
Florent Castelli811e24a2023-05-31 12:35:474077 f2_codecs.back().packetization = "raw";
4078 f2_.set_video_codecs(f2_codecs, f2_codecs);
4079
4080 MediaSessionOptions opts;
4081 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video1",
4082 RtpTransceiverDirection::kSendRecv, kActive,
4083 &opts);
4084
4085 // Create an offer with two video sections using same codecs.
Philipp Hancke2bf1b992023-09-26 07:04:464086 std::unique_ptr<SessionDescription> offer =
4087 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Florent Castelli811e24a2023-05-31 12:35:474088 ASSERT_TRUE(offer);
4089 ASSERT_EQ(1u, offer->contents().size());
Philipp Hancked0f0f382023-11-23 19:21:054090 const MediaContentDescription* vcd1 =
4091 offer->contents()[0].media_description();
Florent Castelli811e24a2023-05-31 12:35:474092 ASSERT_EQ(1u, vcd1->codecs().size());
4093 EXPECT_EQ(vcd1->codecs()[0].packetization, "raw");
4094
4095 // Create answer and negotiate the codecs.
4096 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:464097 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Florent Castelli811e24a2023-05-31 12:35:474098 ASSERT_TRUE(answer);
4099 ASSERT_EQ(1u, answer->contents().size());
Philipp Hancked0f0f382023-11-23 19:21:054100 vcd1 = answer->contents()[0].media_description();
Florent Castelli811e24a2023-05-31 12:35:474101 ASSERT_EQ(1u, vcd1->codecs().size());
4102 EXPECT_EQ(vcd1->codecs()[0].packetization, "raw");
4103}
4104
Tony Herrea5c8ee12023-11-30 11:24:094105// Test verifying that negotiating codecs with different packetization removes
4106// the packetization value.
4107TEST_F(MediaSessionDescriptionFactoryTest, PacketizationIsDifferent) {
4108 std::vector f1_codecs = {CreateVideoCodec(96, "H264")};
4109 f1_codecs.back().packetization = "raw";
4110 f1_.set_video_codecs(f1_codecs, f1_codecs);
4111
4112 std::vector f2_codecs = {CreateVideoCodec(96, "H264")};
4113 f2_codecs.back().packetization = "notraw";
4114 f2_.set_video_codecs(f2_codecs, f2_codecs);
4115
4116 MediaSessionOptions opts;
4117 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video1",
4118 RtpTransceiverDirection::kSendRecv, kActive,
4119 &opts);
4120
4121 // Create an offer with two video sections using same codecs.
4122 std::unique_ptr<SessionDescription> offer =
4123 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
4124 ASSERT_TRUE(offer);
4125 ASSERT_EQ(1u, offer->contents().size());
4126 const VideoContentDescription* vcd1 =
4127 offer->contents()[0].media_description()->as_video();
4128 ASSERT_EQ(1u, vcd1->codecs().size());
4129 EXPECT_EQ(vcd1->codecs()[0].packetization, "raw");
4130
4131 // Create answer and negotiate the codecs.
4132 std::unique_ptr<SessionDescription> answer =
4133 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
4134 ASSERT_TRUE(answer);
4135 ASSERT_EQ(1u, answer->contents().size());
4136 vcd1 = answer->contents()[0].media_description()->as_video();
4137 ASSERT_EQ(1u, vcd1->codecs().size());
4138 EXPECT_EQ(vcd1->codecs()[0].packetization, absl::nullopt);
4139}
4140
zhihuang1c378ed2017-08-17 21:10:504141// Test that the codec preference order per media section is respected in
4142// subsequent offer.
4143TEST_F(MediaSessionDescriptionFactoryTest,
4144 CreateOfferRespectsCodecPreferenceOrder) {
4145 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:584146 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video1",
4147 RtpTransceiverDirection::kSendRecv, kActive,
4148 &opts);
4149 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video2",
4150 RtpTransceiverDirection::kSendRecv, kActive,
4151 &opts);
zhihuang1c378ed2017-08-17 21:10:504152 // Create an offer with two video sections using same codecs.
Philipp Hancke2bf1b992023-09-26 07:04:464153 std::unique_ptr<SessionDescription> offer =
4154 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
zhihuang1c378ed2017-08-17 21:10:504155 ASSERT_TRUE(offer);
4156 ASSERT_EQ(2u, offer->contents().size());
Philipp Hancked0f0f382023-11-23 19:21:054157 MediaContentDescription* vcd1 = offer->contents()[0].media_description();
4158 const MediaContentDescription* vcd2 =
4159 offer->contents()[1].media_description();
zhihuang1c378ed2017-08-17 21:10:504160 auto video_codecs = MAKE_VECTOR(kVideoCodecs1);
4161 EXPECT_EQ(video_codecs, vcd1->codecs());
4162 EXPECT_EQ(video_codecs, vcd2->codecs());
4163
4164 // Change the codec preference of the first video section and create a
4165 // follow-up offer.
4166 auto video_codecs_reverse = MAKE_VECTOR(kVideoCodecs1Reverse);
4167 vcd1->set_codecs(video_codecs_reverse);
4168 std::unique_ptr<SessionDescription> updated_offer(
Philipp Hancke2bf1b992023-09-26 07:04:464169 f1_.CreateOfferOrError(opts, offer.get()).MoveValue());
Philipp Hancked0f0f382023-11-23 19:21:054170 vcd1 = updated_offer->contents()[0].media_description();
4171 vcd2 = updated_offer->contents()[1].media_description();
zhihuang1c378ed2017-08-17 21:10:504172 // The video codec preference order should be respected.
4173 EXPECT_EQ(video_codecs_reverse, vcd1->codecs());
4174 EXPECT_EQ(video_codecs, vcd2->codecs());
4175}
4176
4177// Test that the codec preference order per media section is respected in
4178// the answer.
4179TEST_F(MediaSessionDescriptionFactoryTest,
4180 CreateAnswerRespectsCodecPreferenceOrder) {
4181 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:584182 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video1",
4183 RtpTransceiverDirection::kSendRecv, kActive,
4184 &opts);
4185 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video2",
4186 RtpTransceiverDirection::kSendRecv, kActive,
4187 &opts);
zhihuang1c378ed2017-08-17 21:10:504188 // Create an offer with two video sections using same codecs.
Philipp Hancke2bf1b992023-09-26 07:04:464189 std::unique_ptr<SessionDescription> offer =
4190 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
zhihuang1c378ed2017-08-17 21:10:504191 ASSERT_TRUE(offer);
4192 ASSERT_EQ(2u, offer->contents().size());
Philipp Hancked0f0f382023-11-23 19:21:054193 MediaContentDescription* vcd1 = offer->contents()[0].media_description();
4194 const MediaContentDescription* vcd2 =
4195 offer->contents()[1].media_description();
zhihuang1c378ed2017-08-17 21:10:504196 auto video_codecs = MAKE_VECTOR(kVideoCodecs1);
4197 EXPECT_EQ(video_codecs, vcd1->codecs());
4198 EXPECT_EQ(video_codecs, vcd2->codecs());
4199
4200 // Change the codec preference of the first video section and create an
4201 // answer.
4202 auto video_codecs_reverse = MAKE_VECTOR(kVideoCodecs1Reverse);
4203 vcd1->set_codecs(video_codecs_reverse);
Steve Anton6fe1fba2018-12-11 18:15:234204 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:464205 f1_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Philipp Hancked0f0f382023-11-23 19:21:054206 vcd1 = answer->contents()[0].media_description();
4207 vcd2 = answer->contents()[1].media_description();
zhihuang1c378ed2017-08-17 21:10:504208 // The video codec preference order should be respected.
4209 EXPECT_EQ(video_codecs_reverse, vcd1->codecs());
4210 EXPECT_EQ(video_codecs, vcd2->codecs());
4211}
4212
Zhi Huang6f367472017-11-22 21:20:024213// Test that when creating an answer, the codecs use local parameters instead of
4214// the remote ones.
4215TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerWithLocalCodecParams) {
4216 const std::string audio_param_name = "audio_param";
4217 const std::string audio_value1 = "audio_v1";
4218 const std::string audio_value2 = "audio_v2";
4219 const std::string video_param_name = "video_param";
4220 const std::string video_value1 = "video_v1";
4221 const std::string video_value2 = "video_v2";
4222
4223 auto audio_codecs1 = MAKE_VECTOR(kAudioCodecs1);
4224 auto audio_codecs2 = MAKE_VECTOR(kAudioCodecs1);
4225 auto video_codecs1 = MAKE_VECTOR(kVideoCodecs1);
4226 auto video_codecs2 = MAKE_VECTOR(kVideoCodecs1);
4227
4228 // Set the parameters for codecs.
4229 audio_codecs1[0].SetParam(audio_param_name, audio_value1);
4230 video_codecs1[0].SetParam(video_param_name, video_value1);
4231 audio_codecs2[0].SetParam(audio_param_name, audio_value2);
4232 video_codecs2[0].SetParam(video_param_name, video_value2);
4233
4234 f1_.set_audio_codecs(audio_codecs1, audio_codecs1);
Johannes Kron3e983682020-03-29 20:17:004235 f1_.set_video_codecs(video_codecs1, video_codecs1);
Zhi Huang6f367472017-11-22 21:20:024236 f2_.set_audio_codecs(audio_codecs2, audio_codecs2);
Johannes Kron3e983682020-03-29 20:17:004237 f2_.set_video_codecs(video_codecs2, video_codecs2);
Zhi Huang6f367472017-11-22 21:20:024238
4239 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:584240 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
4241 RtpTransceiverDirection::kSendRecv, kActive,
4242 &opts);
4243 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
4244 RtpTransceiverDirection::kSendRecv, kActive,
4245 &opts);
Zhi Huang6f367472017-11-22 21:20:024246
Philipp Hancke2bf1b992023-09-26 07:04:464247 std::unique_ptr<SessionDescription> offer =
4248 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Zhi Huang6f367472017-11-22 21:20:024249 ASSERT_TRUE(offer);
Philipp Hancked0f0f382023-11-23 19:21:054250 auto offer_acd = offer->contents()[0].media_description();
4251 auto offer_vcd = offer->contents()[1].media_description();
Zhi Huang6f367472017-11-22 21:20:024252 std::string value;
4253 EXPECT_TRUE(offer_acd->codecs()[0].GetParam(audio_param_name, &value));
4254 EXPECT_EQ(audio_value1, value);
4255 EXPECT_TRUE(offer_vcd->codecs()[0].GetParam(video_param_name, &value));
4256 EXPECT_EQ(video_value1, value);
4257
Steve Anton6fe1fba2018-12-11 18:15:234258 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:464259 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Zhi Huang6f367472017-11-22 21:20:024260 ASSERT_TRUE(answer);
Philipp Hancked0f0f382023-11-23 19:21:054261 auto answer_acd = answer->contents()[0].media_description();
4262 auto answer_vcd = answer->contents()[1].media_description();
Zhi Huang6f367472017-11-22 21:20:024263 // Use the parameters from the local codecs.
4264 EXPECT_TRUE(answer_acd->codecs()[0].GetParam(audio_param_name, &value));
4265 EXPECT_EQ(audio_value2, value);
4266 EXPECT_TRUE(answer_vcd->codecs()[0].GetParam(video_param_name, &value));
4267 EXPECT_EQ(video_value2, value);
4268}
4269
Steve Anton9c1fb1e2018-02-26 23:09:414270// Test that matching packetization-mode is part of the criteria for matching
4271// H264 codecs (in addition to profile-level-id). Previously, this was not the
4272// case, so the first H264 codec with the same profile-level-id would match and
4273// the payload type in the answer would be incorrect.
4274// This is a regression test for bugs.webrtc.org/8808
4275TEST_F(MediaSessionDescriptionFactoryTest,
4276 H264MatchCriteriaIncludesPacketizationMode) {
4277 // Create two H264 codecs with the same profile level ID and different
4278 // packetization modes.
Emil Lundmark7d2e6162023-11-08 15:27:454279 VideoCodec h264_pm0 = CreateVideoCodec(96, "H264");
4280 h264_pm0.params[kH264FmtpProfileLevelId] = "42c01f";
4281 h264_pm0.params[kH264FmtpPacketizationMode] = "0";
4282 VideoCodec h264_pm1 = CreateVideoCodec(97, "H264");
4283 h264_pm1.params[kH264FmtpProfileLevelId] = "42c01f";
4284 h264_pm1.params[kH264FmtpPacketizationMode] = "1";
Steve Anton9c1fb1e2018-02-26 23:09:414285
4286 // Offerer will send both codecs, answerer should choose the one with matching
4287 // packetization mode (and not the first one it sees).
Johannes Kron3e983682020-03-29 20:17:004288 f1_.set_video_codecs({h264_pm0, h264_pm1}, {h264_pm0, h264_pm1});
4289 f2_.set_video_codecs({h264_pm1}, {h264_pm1});
Steve Anton9c1fb1e2018-02-26 23:09:414290
4291 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:584292 AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
4293 RtpTransceiverDirection::kSendRecv, kActive,
4294 &opts);
Steve Anton9c1fb1e2018-02-26 23:09:414295
Philipp Hancke2bf1b992023-09-26 07:04:464296 std::unique_ptr<SessionDescription> offer =
4297 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
Steve Anton9c1fb1e2018-02-26 23:09:414298 ASSERT_TRUE(offer);
4299
Steve Anton6fe1fba2018-12-11 18:15:234300 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:464301 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
Steve Anton9c1fb1e2018-02-26 23:09:414302 ASSERT_TRUE(answer);
4303
4304 // Answer should have one negotiated codec with packetization-mode=1 using the
4305 // offered payload type.
4306 ASSERT_EQ(1u, answer->contents().size());
Philipp Hancked0f0f382023-11-23 19:21:054307 auto answer_vcd = answer->contents()[0].media_description();
Steve Anton9c1fb1e2018-02-26 23:09:414308 ASSERT_EQ(1u, answer_vcd->codecs().size());
4309 auto answer_codec = answer_vcd->codecs()[0];
4310 EXPECT_EQ(h264_pm1.id, answer_codec.id);
4311}
4312
Emil Lundmark7d2e6162023-11-08 15:27:454313class MediaProtocolTest : public testing::TestWithParam<const char*> {
zhihuangcf5b37c2016-05-05 18:44:354314 public:
Amit Hilbuchbcd39d42019-01-26 01:13:564315 MediaProtocolTest()
Jonas Orelanded99dae2022-03-09 08:28:104316 : tdf1_(field_trials_),
4317 tdf2_(field_trials_),
Philipp Hancke332c56f2023-09-27 08:06:374318 f1_(nullptr, false, &ssrc_generator1, &tdf1_),
4319 f2_(nullptr, false, &ssrc_generator2, &tdf2_) {
ossu075af922016-06-14 10:29:384320 f1_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs1),
4321 MAKE_VECTOR(kAudioCodecs1));
Johannes Kron3e983682020-03-29 20:17:004322 f1_.set_video_codecs(MAKE_VECTOR(kVideoCodecs1),
4323 MAKE_VECTOR(kVideoCodecs1));
ossu075af922016-06-14 10:29:384324 f2_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs2),
4325 MAKE_VECTOR(kAudioCodecs2));
Johannes Kron3e983682020-03-29 20:17:004326 f2_.set_video_codecs(MAKE_VECTOR(kVideoCodecs2),
4327 MAKE_VECTOR(kVideoCodecs2));
zhihuangcf5b37c2016-05-05 18:44:354328 tdf1_.set_certificate(rtc::RTCCertificate::Create(
kwibergfd8be342016-05-15 02:44:114329 std::unique_ptr<rtc::SSLIdentity>(new rtc::FakeSSLIdentity("id1"))));
zhihuangcf5b37c2016-05-05 18:44:354330 tdf2_.set_certificate(rtc::RTCCertificate::Create(
kwibergfd8be342016-05-15 02:44:114331 std::unique_ptr<rtc::SSLIdentity>(new rtc::FakeSSLIdentity("id2"))));
zhihuangcf5b37c2016-05-05 18:44:354332 }
4333
4334 protected:
Jonas Orelanded99dae2022-03-09 08:28:104335 webrtc::test::ScopedKeyValueConfig field_trials_;
zhihuangcf5b37c2016-05-05 18:44:354336 TransportDescriptionFactory tdf1_;
4337 TransportDescriptionFactory tdf2_;
Jonas Orelanded99dae2022-03-09 08:28:104338 MediaSessionDescriptionFactory f1_;
4339 MediaSessionDescriptionFactory f2_;
Amit Hilbuchbcd39d42019-01-26 01:13:564340 UniqueRandomIdGenerator ssrc_generator1;
4341 UniqueRandomIdGenerator ssrc_generator2;
zhihuangcf5b37c2016-05-05 18:44:354342};
4343
4344TEST_P(MediaProtocolTest, TestAudioVideoAcceptance) {
4345 MediaSessionOptions opts;
Steve Anton4e70a722017-11-28 22:57:104346 AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts);
Philipp Hancke2bf1b992023-09-26 07:04:464347 std::unique_ptr<SessionDescription> offer =
4348 f1_.CreateOfferOrError(opts, nullptr).MoveValue();
4349 ASSERT_TRUE(offer.get());
zhihuangcf5b37c2016-05-05 18:44:354350 // Set the protocol for all the contents.
Harald Alvestrand1716d392019-06-03 18:35:454351 for (auto& content : offer.get()->contents()) {
Steve Antonb1c1de12017-12-21 23:14:304352 content.media_description()->set_protocol(GetParam());
zhihuangcf5b37c2016-05-05 18:44:354353 }
Steve Anton6fe1fba2018-12-11 18:15:234354 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:464355 f2_.CreateAnswerOrError(offer.get(), opts, nullptr).MoveValue();
zhihuangcf5b37c2016-05-05 18:44:354356 const ContentInfo* ac = answer->GetContentByName("audio");
4357 const ContentInfo* vc = answer->GetContentByName("video");
Philipp Hancke2bf1b992023-09-26 07:04:464358 ASSERT_TRUE(ac);
4359 ASSERT_TRUE(vc);
zhihuangcf5b37c2016-05-05 18:44:354360 EXPECT_FALSE(ac->rejected); // the offer is accepted
4361 EXPECT_FALSE(vc->rejected);
Philipp Hancked0f0f382023-11-23 19:21:054362 const MediaContentDescription* acd = ac->media_description();
4363 const MediaContentDescription* vcd = vc->media_description();
zhihuangcf5b37c2016-05-05 18:44:354364 EXPECT_EQ(GetParam(), acd->protocol());
4365 EXPECT_EQ(GetParam(), vcd->protocol());
4366}
4367
Mirko Bonadeic84f6612019-01-31 11:20:574368INSTANTIATE_TEST_SUITE_P(MediaProtocolPatternTest,
4369 MediaProtocolTest,
Emil Lundmark7d2e6162023-11-08 15:27:454370 ValuesIn(kMediaProtocols));
Mirko Bonadeic84f6612019-01-31 11:20:574371INSTANTIATE_TEST_SUITE_P(MediaProtocolDtlsPatternTest,
4372 MediaProtocolTest,
Emil Lundmark7d2e6162023-11-08 15:27:454373 ValuesIn(kMediaProtocolsDtls));
ossu075af922016-06-14 10:29:384374
4375TEST_F(MediaSessionDescriptionFactoryTest, TestSetAudioCodecs) {
Jonas Orelanded99dae2022-03-09 08:28:104376 webrtc::test::ScopedKeyValueConfig field_trials;
4377 TransportDescriptionFactory tdf(field_trials);
Harald Alvestrand974044e2024-02-08 13:15:514378 tdf.set_certificate(rtc::RTCCertificate::Create(
4379 std::unique_ptr<rtc::SSLIdentity>(new rtc::FakeSSLIdentity("id"))));
4380
Amit Hilbuchbcd39d42019-01-26 01:13:564381 UniqueRandomIdGenerator ssrc_generator;
Philipp Hancke332c56f2023-09-27 08:06:374382 MediaSessionDescriptionFactory sf(nullptr, false, &ssrc_generator, &tdf);
ossu075af922016-06-14 10:29:384383 std::vector<AudioCodec> send_codecs = MAKE_VECTOR(kAudioCodecs1);
4384 std::vector<AudioCodec> recv_codecs = MAKE_VECTOR(kAudioCodecs2);
4385
4386 // The merged list of codecs should contain any send codecs that are also
Niels Möllerbe74b802022-03-18 13:10:154387 // nominally in the receive codecs list. Payload types should be picked from
ossu075af922016-06-14 10:29:384388 // the send codecs and a number-of-channels of 0 and 1 should be equivalent
4389 // (set to 1). This equals what happens when the send codecs are used in an
4390 // offer and the receive codecs are used in the following answer.
4391 const std::vector<AudioCodec> sendrecv_codecs =
4392 MAKE_VECTOR(kAudioCodecsAnswer);
4393 const std::vector<AudioCodec> no_codecs;
4394
Tomas Lundqvista26d6ed2023-10-27 12:25:574395 RTC_CHECK_EQ(send_codecs[2].name, "iLBC")
ossu075af922016-06-14 10:29:384396 << "Please don't change shared test data!";
4397 RTC_CHECK_EQ(recv_codecs[2].name, "iLBC")
4398 << "Please don't change shared test data!";
4399 // Alter iLBC send codec to have zero channels, to test that that is handled
4400 // properly.
Tomas Lundqvista26d6ed2023-10-27 12:25:574401 send_codecs[2].channels = 0;
ossu075af922016-06-14 10:29:384402
Philipp Hanckeb41316cd2020-05-26 11:45:204403 // Alter iLBC receive codec to be lowercase, to test that case conversions
ossu075af922016-06-14 10:29:384404 // are handled properly.
4405 recv_codecs[2].name = "ilbc";
4406
4407 // Test proper merge
4408 sf.set_audio_codecs(send_codecs, recv_codecs);
zhihuang1c378ed2017-08-17 21:10:504409 EXPECT_EQ(send_codecs, sf.audio_send_codecs());
4410 EXPECT_EQ(recv_codecs, sf.audio_recv_codecs());
4411 EXPECT_EQ(sendrecv_codecs, sf.audio_sendrecv_codecs());
ossu075af922016-06-14 10:29:384412
4413 // Test empty send codecs list
4414 sf.set_audio_codecs(no_codecs, recv_codecs);
zhihuang1c378ed2017-08-17 21:10:504415 EXPECT_EQ(no_codecs, sf.audio_send_codecs());
4416 EXPECT_EQ(recv_codecs, sf.audio_recv_codecs());
4417 EXPECT_EQ(no_codecs, sf.audio_sendrecv_codecs());
ossu075af922016-06-14 10:29:384418
4419 // Test empty recv codecs list
4420 sf.set_audio_codecs(send_codecs, no_codecs);
zhihuang1c378ed2017-08-17 21:10:504421 EXPECT_EQ(send_codecs, sf.audio_send_codecs());
4422 EXPECT_EQ(no_codecs, sf.audio_recv_codecs());
4423 EXPECT_EQ(no_codecs, sf.audio_sendrecv_codecs());
ossu075af922016-06-14 10:29:384424
4425 // Test all empty codec lists
4426 sf.set_audio_codecs(no_codecs, no_codecs);
zhihuang1c378ed2017-08-17 21:10:504427 EXPECT_EQ(no_codecs, sf.audio_send_codecs());
4428 EXPECT_EQ(no_codecs, sf.audio_recv_codecs());
4429 EXPECT_EQ(no_codecs, sf.audio_sendrecv_codecs());
ossu075af922016-06-14 10:29:384430}
4431
zhihuang1c378ed2017-08-17 21:10:504432// Compare the two vectors of codecs ignoring the payload type.
Emil Lundmark7d2e6162023-11-08 15:27:454433bool CodecsMatch(const std::vector<Codec>& codecs1,
Emil Lundmarkf268afd2023-11-08 15:34:114434 const std::vector<Codec>& codecs2) {
zhihuang1c378ed2017-08-17 21:10:504435 if (codecs1.size() != codecs2.size()) {
4436 return false;
4437 }
4438
4439 for (size_t i = 0; i < codecs1.size(); ++i) {
Emil Lundmarkf268afd2023-11-08 15:34:114440 if (!codecs1[i].Matches(codecs2[i])) {
zhihuang1c378ed2017-08-17 21:10:504441 return false;
4442 }
4443 }
4444 return true;
4445}
4446
Steve Anton4e70a722017-11-28 22:57:104447void TestAudioCodecsOffer(RtpTransceiverDirection direction) {
Jonas Orelanded99dae2022-03-09 08:28:104448 webrtc::test::ScopedKeyValueConfig field_trials;
4449 TransportDescriptionFactory tdf(field_trials);
Harald Alvestrand974044e2024-02-08 13:15:514450 tdf.set_certificate(rtc::RTCCertificate::Create(
4451 std::unique_ptr<rtc::SSLIdentity>(new rtc::FakeSSLIdentity("id"))));
4452
Amit Hilbuchbcd39d42019-01-26 01:13:564453 UniqueRandomIdGenerator ssrc_generator;
Philipp Hancke332c56f2023-09-27 08:06:374454 MediaSessionDescriptionFactory sf(nullptr, false, &ssrc_generator, &tdf);
ossu075af922016-06-14 10:29:384455 const std::vector<AudioCodec> send_codecs = MAKE_VECTOR(kAudioCodecs1);
4456 const std::vector<AudioCodec> recv_codecs = MAKE_VECTOR(kAudioCodecs2);
4457 const std::vector<AudioCodec> sendrecv_codecs =
4458 MAKE_VECTOR(kAudioCodecsAnswer);
4459 sf.set_audio_codecs(send_codecs, recv_codecs);
ossu075af922016-06-14 10:29:384460
4461 MediaSessionOptions opts;
Amit Hilbuchc63ddb22019-01-02 18:13:584462 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", direction, kActive,
4463 &opts);
zhihuang1c378ed2017-08-17 21:10:504464
Steve Anton4e70a722017-11-28 22:57:104465 if (direction == RtpTransceiverDirection::kSendRecv ||
4466 direction == RtpTransceiverDirection::kSendOnly) {
Amit Hilbuchc63ddb22019-01-02 18:13:584467 AttachSenderToMediaDescriptionOptions(
4468 "audio", MEDIA_TYPE_AUDIO, kAudioTrack1, {kMediaStream1}, 1, &opts);
zhihuang1c378ed2017-08-17 21:10:504469 }
ossu075af922016-06-14 10:29:384470
Philipp Hancke2bf1b992023-09-26 07:04:464471 std::unique_ptr<SessionDescription> offer =
4472 sf.CreateOfferOrError(opts, nullptr).MoveValue();
4473 ASSERT_TRUE(offer.get());
Steve Antonb1c1de12017-12-21 23:14:304474 ContentInfo* ac = offer->GetContentByName("audio");
ossu075af922016-06-14 10:29:384475
4476 // If the factory didn't add any audio content to the offer, we cannot check
zhihuang1c378ed2017-08-17 21:10:504477 // that the codecs put in are right. This happens when we neither want to
4478 // send nor receive audio. The checks are still in place if at some point
4479 // we'd instead create an inactive stream.
ossu075af922016-06-14 10:29:384480 if (ac) {
Philipp Hancked0f0f382023-11-23 19:21:054481 MediaContentDescription* acd = ac->media_description();
zhihuang1c378ed2017-08-17 21:10:504482 // sendrecv and inactive should both present lists as if the channel was
4483 // to be used for sending and receiving. Inactive essentially means it
4484 // might eventually be used anything, but we don't know more at this
4485 // moment.
Steve Anton4e70a722017-11-28 22:57:104486 if (acd->direction() == RtpTransceiverDirection::kSendOnly) {
Emil Lundmarkf268afd2023-11-08 15:34:114487 EXPECT_TRUE(CodecsMatch(send_codecs, acd->codecs()));
Steve Anton4e70a722017-11-28 22:57:104488 } else if (acd->direction() == RtpTransceiverDirection::kRecvOnly) {
Emil Lundmarkf268afd2023-11-08 15:34:114489 EXPECT_TRUE(CodecsMatch(recv_codecs, acd->codecs()));
ossu075af922016-06-14 10:29:384490 } else {
Emil Lundmarkf268afd2023-11-08 15:34:114491 EXPECT_TRUE(CodecsMatch(sendrecv_codecs, acd->codecs()));
ossu075af922016-06-14 10:29:384492 }
4493 }
4494}
4495
Emil Lundmark7d2e6162023-11-08 15:27:454496const AudioCodec kOfferAnswerCodecs[] = {
4497 CreateAudioCodec(0, "codec0", 16000, 1),
4498 CreateAudioCodec(1, "codec1", 8000, 1),
4499 CreateAudioCodec(2, "codec2", 8000, 1),
4500 CreateAudioCodec(3, "codec3", 8000, 1),
4501 CreateAudioCodec(4, "codec4", 8000, 2),
4502 CreateAudioCodec(5, "codec5", 32000, 1),
4503 CreateAudioCodec(6, "codec6", 48000, 1)};
ossu075af922016-06-14 10:29:384504
zhihuang1c378ed2017-08-17 21:10:504505/* The codecs groups below are chosen as per the matrix below. The objective
4506 * is to have different sets of codecs in the inputs, to get unique sets of
4507 * codecs after negotiation, depending on offer and answer communication
4508 * directions. One-way directions in the offer should either result in the
4509 * opposite direction in the answer, or an inactive answer. Regardless, the
4510 * choice of codecs should be as if the answer contained the opposite
4511 * direction. Inactive offers should be treated as sendrecv/sendrecv.
ossu075af922016-06-14 10:29:384512 *
4513 * | Offer | Answer | Result
4514 * codec|send recv sr | send recv sr | s/r r/s sr/s sr/r sr/sr
4515 * 0 | x - - | - x - | x - - - -
4516 * 1 | x x x | - x - | x - - x -
4517 * 2 | - x - | x - - | - x - - -
4518 * 3 | x x x | x - - | - x x - -
4519 * 4 | - x - | x x x | - x - - -
4520 * 5 | x - - | x x x | x - - - -
4521 * 6 | x x x | x x x | x x x x x
4522 */
4523// Codecs used by offerer in the AudioCodecsAnswerTest
Emil Lundmark7d2e6162023-11-08 15:27:454524const int kOfferSendCodecs[] = {0, 1, 3, 5, 6};
4525const int kOfferRecvCodecs[] = {1, 2, 3, 4, 6};
ossu075af922016-06-14 10:29:384526// Codecs used in the answerer in the AudioCodecsAnswerTest. The order is
4527// jumbled to catch the answer not following the order in the offer.
Emil Lundmark7d2e6162023-11-08 15:27:454528const int kAnswerSendCodecs[] = {6, 5, 2, 3, 4};
4529const int kAnswerRecvCodecs[] = {6, 5, 4, 1, 0};
ossu075af922016-06-14 10:29:384530// The resulting sets of codecs in the answer in the AudioCodecsAnswerTest
Emil Lundmark7d2e6162023-11-08 15:27:454531const int kResultSend_RecvCodecs[] = {0, 1, 5, 6};
4532const int kResultRecv_SendCodecs[] = {2, 3, 4, 6};
4533const int kResultSendrecv_SendCodecs[] = {3, 6};
4534const int kResultSendrecv_RecvCodecs[] = {1, 6};
4535const int kResultSendrecv_SendrecvCodecs[] = {6};
ossu075af922016-06-14 10:29:384536
4537template <typename T, int IDXS>
4538std::vector<T> VectorFromIndices(const T* array, const int (&indices)[IDXS]) {
4539 std::vector<T> out;
4540 out.reserve(IDXS);
4541 for (int idx : indices)
4542 out.push_back(array[idx]);
4543
4544 return out;
4545}
4546
Steve Anton4e70a722017-11-28 22:57:104547void TestAudioCodecsAnswer(RtpTransceiverDirection offer_direction,
4548 RtpTransceiverDirection answer_direction,
ossu075af922016-06-14 10:29:384549 bool add_legacy_stream) {
Jonas Orelanded99dae2022-03-09 08:28:104550 webrtc::test::ScopedKeyValueConfig field_trials;
4551 TransportDescriptionFactory offer_tdf(field_trials);
4552 TransportDescriptionFactory answer_tdf(field_trials);
Harald Alvestrand974044e2024-02-08 13:15:514553 offer_tdf.set_certificate(rtc::RTCCertificate::Create(
4554 std::unique_ptr<rtc::SSLIdentity>(new rtc::FakeSSLIdentity("offer_id"))));
4555 answer_tdf.set_certificate(
4556 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
4557 new rtc::FakeSSLIdentity("answer_id"))));
Amit Hilbuchbcd39d42019-01-26 01:13:564558 UniqueRandomIdGenerator ssrc_generator1, ssrc_generator2;
Philipp Hancke332c56f2023-09-27 08:06:374559 MediaSessionDescriptionFactory offer_factory(nullptr, false, &ssrc_generator1,
4560 &offer_tdf);
4561 MediaSessionDescriptionFactory answer_factory(nullptr, false,
4562 &ssrc_generator2, &answer_tdf);
Jonas Orelanded99dae2022-03-09 08:28:104563
ossu075af922016-06-14 10:29:384564 offer_factory.set_audio_codecs(
4565 VectorFromIndices(kOfferAnswerCodecs, kOfferSendCodecs),
4566 VectorFromIndices(kOfferAnswerCodecs, kOfferRecvCodecs));
4567 answer_factory.set_audio_codecs(
4568 VectorFromIndices(kOfferAnswerCodecs, kAnswerSendCodecs),
4569 VectorFromIndices(kOfferAnswerCodecs, kAnswerRecvCodecs));
4570
ossu075af922016-06-14 10:29:384571 MediaSessionOptions offer_opts;
Amit Hilbuchc63ddb22019-01-02 18:13:584572 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", offer_direction,
4573 kActive, &offer_opts);
zhihuang1c378ed2017-08-17 21:10:504574
Steve Anton4e70a722017-11-28 22:57:104575 if (webrtc::RtpTransceiverDirectionHasSend(offer_direction)) {
Amit Hilbuchc63ddb22019-01-02 18:13:584576 AttachSenderToMediaDescriptionOptions("audio", MEDIA_TYPE_AUDIO,
4577 kAudioTrack1, {kMediaStream1}, 1,
4578 &offer_opts);
ossu075af922016-06-14 10:29:384579 }
4580
Steve Anton6fe1fba2018-12-11 18:15:234581 std::unique_ptr<SessionDescription> offer =
Philipp Hancke2bf1b992023-09-26 07:04:464582 offer_factory.CreateOfferOrError(offer_opts, nullptr).MoveValue();
4583 ASSERT_TRUE(offer.get());
ossu075af922016-06-14 10:29:384584
4585 MediaSessionOptions answer_opts;
Amit Hilbuchc63ddb22019-01-02 18:13:584586 AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", answer_direction,
4587 kActive, &answer_opts);
zhihuang1c378ed2017-08-17 21:10:504588
Steve Anton4e70a722017-11-28 22:57:104589 if (webrtc::RtpTransceiverDirectionHasSend(answer_direction)) {
Amit Hilbuchc63ddb22019-01-02 18:13:584590 AttachSenderToMediaDescriptionOptions("audio", MEDIA_TYPE_AUDIO,
4591 kAudioTrack1, {kMediaStream1}, 1,
4592 &answer_opts);
ossu075af922016-06-14 10:29:384593 }
Steve Anton6fe1fba2018-12-11 18:15:234594 std::unique_ptr<SessionDescription> answer =
Philipp Hancke2bf1b992023-09-26 07:04:464595 answer_factory.CreateAnswerOrError(offer.get(), answer_opts, nullptr)
4596 .MoveValue();
ossu075af922016-06-14 10:29:384597 const ContentInfo* ac = answer->GetContentByName("audio");
4598
zhihuang1c378ed2017-08-17 21:10:504599 // If the factory didn't add any audio content to the answer, we cannot
4600 // check that the codecs put in are right. This happens when we neither want
4601 // to send nor receive audio. The checks are still in place if at some point
4602 // we'd instead create an inactive stream.
ossu075af922016-06-14 10:29:384603 if (ac) {
Steve Antonb1c1de12017-12-21 23:14:304604 ASSERT_EQ(MEDIA_TYPE_AUDIO, ac->media_description()->type());
Philipp Hancked0f0f382023-11-23 19:21:054605 const MediaContentDescription* acd = ac->media_description();
ossu075af922016-06-14 10:29:384606
ossu075af922016-06-14 10:29:384607 std::vector<AudioCodec> target_codecs;
4608 // For offers with sendrecv or inactive, we should never reply with more
4609 // codecs than offered, with these codec sets.
4610 switch (offer_direction) {
Steve Anton4e70a722017-11-28 22:57:104611 case RtpTransceiverDirection::kInactive:
ossu075af922016-06-14 10:29:384612 target_codecs = VectorFromIndices(kOfferAnswerCodecs,
4613 kResultSendrecv_SendrecvCodecs);
4614 break;
Steve Anton4e70a722017-11-28 22:57:104615 case RtpTransceiverDirection::kSendOnly:
zhihuang1c378ed2017-08-17 21:10:504616 target_codecs =
4617 VectorFromIndices(kOfferAnswerCodecs, kResultSend_RecvCodecs);
ossu075af922016-06-14 10:29:384618 break;
Steve Anton4e70a722017-11-28 22:57:104619 case RtpTransceiverDirection::kRecvOnly:
zhihuang1c378ed2017-08-17 21:10:504620 target_codecs =
4621 VectorFromIndices(kOfferAnswerCodecs, kResultRecv_SendCodecs);
ossu075af922016-06-14 10:29:384622 break;
Steve Anton4e70a722017-11-28 22:57:104623 case RtpTransceiverDirection::kSendRecv:
4624 if (acd->direction() == RtpTransceiverDirection::kSendOnly) {
zhihuang1c378ed2017-08-17 21:10:504625 target_codecs =
4626 VectorFromIndices(kOfferAnswerCodecs, kResultSendrecv_SendCodecs);
Steve Anton4e70a722017-11-28 22:57:104627 } else if (acd->direction() == RtpTransceiverDirection::kRecvOnly) {
zhihuang1c378ed2017-08-17 21:10:504628 target_codecs =
4629 VectorFromIndices(kOfferAnswerCodecs, kResultSendrecv_RecvCodecs);
ossu075af922016-06-14 10:29:384630 } else {
4631 target_codecs = VectorFromIndices(kOfferAnswerCodecs,
4632 kResultSendrecv_SendrecvCodecs);
4633 }
4634 break;
Harald Alvestrand6060df52020-08-11 07:54:024635 case RtpTransceiverDirection::kStopped:
4636 // This does not happen in any current test.
Artem Titovd3251962021-11-15 15:57:074637 RTC_DCHECK_NOTREACHED();
ossu075af922016-06-14 10:29:384638 }
4639
zhihuang1c378ed2017-08-17 21:10:504640 auto format_codecs = [](const std::vector<AudioCodec>& codecs) {
Jonas Olsson366a50c2018-09-06 11:41:304641 rtc::StringBuilder os;
ossu075af922016-06-14 10:29:384642 bool first = true;
4643 os << "{";
4644 for (const auto& c : codecs) {
4645 os << (first ? " " : ", ") << c.id;
4646 first = false;
4647 }
4648 os << " }";
Jonas Olsson84df1c72018-09-14 14:59:324649 return os.Release();
ossu075af922016-06-14 10:29:384650 };
4651
4652 EXPECT_TRUE(acd->codecs() == target_codecs)
4653 << "Expected: " << format_codecs(target_codecs)
Steve Anton4e70a722017-11-28 22:57:104654 << ", got: " << format_codecs(acd->codecs()) << "; Offered: "
4655 << webrtc::RtpTransceiverDirectionToString(offer_direction)
ossu075af922016-06-14 10:29:384656 << ", answerer wants: "
Steve Anton4e70a722017-11-28 22:57:104657 << webrtc::RtpTransceiverDirectionToString(answer_direction)
4658 << "; got: "
4659 << webrtc::RtpTransceiverDirectionToString(acd->direction());
ossu075af922016-06-14 10:29:384660 } else {
Steve Anton4e70a722017-11-28 22:57:104661 EXPECT_EQ(offer_direction, RtpTransceiverDirection::kInactive)
zhihuang1c378ed2017-08-17 21:10:504662 << "Only inactive offers are allowed to not generate any audio "
4663 "content";
ossu075af922016-06-14 10:29:384664 }
4665}
brandtr03d5fb12016-11-22 11:37:594666
Emil Lundmark7d2e6162023-11-08 15:27:454667using AudioCodecsOfferTest = testing::TestWithParam<RtpTransceiverDirection>;
ossu075af922016-06-14 10:29:384668
4669TEST_P(AudioCodecsOfferTest, TestCodecsInOffer) {
zhihuang1c378ed2017-08-17 21:10:504670 TestAudioCodecsOffer(GetParam());
ossu075af922016-06-14 10:29:384671}
4672
Mirko Bonadeic84f6612019-01-31 11:20:574673INSTANTIATE_TEST_SUITE_P(MediaSessionDescriptionFactoryTest,
4674 AudioCodecsOfferTest,
Emil Lundmark7d2e6162023-11-08 15:27:454675 Values(RtpTransceiverDirection::kSendOnly,
4676 RtpTransceiverDirection::kRecvOnly,
4677 RtpTransceiverDirection::kSendRecv,
4678 RtpTransceiverDirection::kInactive));
ossu075af922016-06-14 10:29:384679
Emil Lundmark7d2e6162023-11-08 15:27:454680using AudioCodecsAnswerTest = testing::TestWithParam<
4681 std::tuple<RtpTransceiverDirection, RtpTransceiverDirection, bool>>;
ossu075af922016-06-14 10:29:384682
4683TEST_P(AudioCodecsAnswerTest, TestCodecsInAnswer) {
Emil Lundmark7d2e6162023-11-08 15:27:454684 TestAudioCodecsAnswer(std::get<0>(GetParam()), std::get<1>(GetParam()),
4685 std::get<2>(GetParam()));
ossu075af922016-06-14 10:29:384686}
4687
Emil Lundmark7d2e6162023-11-08 15:27:454688INSTANTIATE_TEST_SUITE_P(MediaSessionDescriptionFactoryTest,
4689 AudioCodecsAnswerTest,
4690 Combine(Values(RtpTransceiverDirection::kSendOnly,
4691 RtpTransceiverDirection::kRecvOnly,
4692 RtpTransceiverDirection::kSendRecv,
4693 RtpTransceiverDirection::kInactive),
4694 Values(RtpTransceiverDirection::kSendOnly,
4695 RtpTransceiverDirection::kRecvOnly,
4696 RtpTransceiverDirection::kSendRecv,
4697 RtpTransceiverDirection::kInactive),
4698 Bool()));
4699
4700} // namespace
4701} // namespace cricket