/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #define GTEST_HAS_RTTI 0 #include "gtest/gtest.h" #include "nss.h" #include "ssl.h" #include "sdp/RsdparsaSdpParser.h" #include "sdp/SipccSdpParser.h" #include "sdp/SdpMediaSection.h" #include "sdp/SdpAttribute.h" #include "sdp/ParsingResultComparer.h" extern "C" { #include "sipcc_sdp.h" #include "sdp_private.h" } #ifdef CRLF # undef CRLF #endif #define CRLF "\r\n" #define SKIP_TEST_WITH_RUST_PARSER \ if (!::testing::get<1>(GetParam())) { \ return; \ } #define SKIP_TEST_WITH_SIPCC_PARSER \ if (ResultsAreFromSipcc()) { \ return; \ } // If you want to see the SDP as it is parsed // #define DEBUG_DISPLAY_SDP using namespace mozilla; namespace test { class SdpTest : public ::testing::Test { public: SdpTest() : final_level_(0), sdp_ptr_(nullptr) {} ~SdpTest() { sdp_free_description(sdp_ptr_); } static void SetUpTestCase() { NSS_NoDB_Init(nullptr); NSS_SetDomesticPolicy(); } void SetUp() { final_level_ = 0; sdp_ptr_ = nullptr; } static void TearDownTestCase() {} void ResetSdp() { if (!sdp_ptr_) { sdp_free_description(sdp_ptr_); } sdp_media_e supported_media[] = { SDP_MEDIA_AUDIO, SDP_MEDIA_VIDEO, SDP_MEDIA_APPLICATION, SDP_MEDIA_DATA, SDP_MEDIA_CONTROL, SDP_MEDIA_NAS_RADIUS, SDP_MEDIA_NAS_TACACS, SDP_MEDIA_NAS_DIAMETER, SDP_MEDIA_NAS_L2TP, SDP_MEDIA_NAS_LOGIN, SDP_MEDIA_NAS_NONE, SDP_MEDIA_IMAGE, }; sdp_conf_options_t* config_p = sdp_init_config(); unsigned int i; for (i = 0; i < sizeof(supported_media) / sizeof(sdp_media_e); i++) { sdp_media_supported(config_p, supported_media[i], true); } sdp_nettype_supported(config_p, SDP_NT_INTERNET, true); sdp_addrtype_supported(config_p, SDP_AT_IP4, true); sdp_addrtype_supported(config_p, SDP_AT_IP6, true); sdp_transport_supported(config_p, SDP_TRANSPORT_RTPSAVPF, true); sdp_transport_supported(config_p, SDP_TRANSPORT_UDPTL, true); sdp_require_session_name(config_p, false); sdp_ptr_ = sdp_init_description(config_p); if (!sdp_ptr_) { sdp_free_config(config_p); } } void ParseSdp(const std::string& sdp_str) { const char* buf = sdp_str.data(); ResetSdp(); ASSERT_EQ(sdp_parse(sdp_ptr_, buf, sdp_str.size()), SDP_SUCCESS); } void InitLocalSdp() { ResetSdp(); ASSERT_EQ(sdp_set_version(sdp_ptr_, 0), SDP_SUCCESS); ASSERT_EQ(sdp_set_owner_username(sdp_ptr_, "-"), SDP_SUCCESS); ASSERT_EQ(sdp_set_owner_sessionid(sdp_ptr_, "132954853"), SDP_SUCCESS); ASSERT_EQ(sdp_set_owner_version(sdp_ptr_, "0"), SDP_SUCCESS); ASSERT_EQ(sdp_set_owner_network_type(sdp_ptr_, SDP_NT_INTERNET), SDP_SUCCESS); ASSERT_EQ(sdp_set_owner_address_type(sdp_ptr_, SDP_AT_IP4), SDP_SUCCESS); ASSERT_EQ(sdp_set_owner_address(sdp_ptr_, "198.51.100.7"), SDP_SUCCESS); ASSERT_EQ(sdp_set_session_name(sdp_ptr_, "SDP Unit Test"), SDP_SUCCESS); ASSERT_EQ(sdp_set_time_start(sdp_ptr_, "0"), SDP_SUCCESS); ASSERT_EQ(sdp_set_time_stop(sdp_ptr_, "0"), SDP_SUCCESS); } std::string SerializeSdp() { flex_string fs; flex_string_init(&fs); EXPECT_EQ(sdp_build(sdp_ptr_, &fs), SDP_SUCCESS); std::string body(fs.buffer); flex_string_free(&fs); return body; } // Returns "level" for new media section int AddNewMedia(sdp_media_e type) { final_level_++; EXPECT_EQ(sdp_insert_media_line(sdp_ptr_, final_level_), SDP_SUCCESS); EXPECT_EQ(sdp_set_conn_nettype(sdp_ptr_, final_level_, SDP_NT_INTERNET), SDP_SUCCESS); EXPECT_EQ(sdp_set_conn_addrtype(sdp_ptr_, final_level_, SDP_AT_IP4), SDP_SUCCESS); EXPECT_EQ(sdp_set_conn_address(sdp_ptr_, final_level_, "198.51.100.7"), SDP_SUCCESS); EXPECT_EQ(sdp_set_media_type(sdp_ptr_, final_level_, SDP_MEDIA_VIDEO), SDP_SUCCESS); EXPECT_EQ( sdp_set_media_transport(sdp_ptr_, final_level_, SDP_TRANSPORT_RTPAVP), SDP_SUCCESS); EXPECT_EQ(sdp_set_media_portnum(sdp_ptr_, final_level_, 12345, 0), SDP_SUCCESS); EXPECT_EQ(sdp_add_media_payload_type(sdp_ptr_, final_level_, 120, SDP_PAYLOAD_NUMERIC), SDP_SUCCESS); return final_level_; } uint16_t AddNewRtcpFbAck(int level, sdp_rtcp_fb_ack_type_e type, uint16_t payload = SDP_ALL_PAYLOADS) { uint16_t inst_num = 0; EXPECT_EQ(sdp_add_new_attr(sdp_ptr_, level, 0, SDP_ATTR_RTCP_FB, &inst_num), SDP_SUCCESS); EXPECT_EQ( sdp_attr_set_rtcp_fb_ack(sdp_ptr_, level, payload, inst_num, type), SDP_SUCCESS); return inst_num; } uint16_t AddNewRtcpFbNack(int level, sdp_rtcp_fb_nack_type_e type, uint16_t payload = SDP_ALL_PAYLOADS) { uint16_t inst_num = 0; EXPECT_EQ(sdp_add_new_attr(sdp_ptr_, level, 0, SDP_ATTR_RTCP_FB, &inst_num), SDP_SUCCESS); EXPECT_EQ( sdp_attr_set_rtcp_fb_nack(sdp_ptr_, level, payload, inst_num, type), SDP_SUCCESS); return inst_num; } uint16_t AddNewRtcpFbTrrInt(int level, uint32_t interval, uint16_t payload = SDP_ALL_PAYLOADS) { uint16_t inst_num = 0; EXPECT_EQ(sdp_add_new_attr(sdp_ptr_, level, 0, SDP_ATTR_RTCP_FB, &inst_num), SDP_SUCCESS); EXPECT_EQ(sdp_attr_set_rtcp_fb_trr_int(sdp_ptr_, level, payload, inst_num, interval), SDP_SUCCESS); return inst_num; } uint16_t AddNewRtcpFbRemb(int level, uint16_t payload = SDP_ALL_PAYLOADS) { uint16_t inst_num = 0; EXPECT_EQ(sdp_add_new_attr(sdp_ptr_, level, 0, SDP_ATTR_RTCP_FB, &inst_num), SDP_SUCCESS); EXPECT_EQ(sdp_attr_set_rtcp_fb_remb(sdp_ptr_, level, payload, inst_num), SDP_SUCCESS); return inst_num; } uint16_t AddNewRtcpFbCcm(int level, sdp_rtcp_fb_ccm_type_e type, uint16_t payload = SDP_ALL_PAYLOADS) { uint16_t inst_num = 0; EXPECT_EQ(sdp_add_new_attr(sdp_ptr_, level, 0, SDP_ATTR_RTCP_FB, &inst_num), SDP_SUCCESS); EXPECT_EQ( sdp_attr_set_rtcp_fb_ccm(sdp_ptr_, level, payload, inst_num, type), SDP_SUCCESS); return inst_num; } uint16_t AddNewExtMap(int level, const char* uri) { uint16_t inst_num = 0; EXPECT_EQ(sdp_add_new_attr(sdp_ptr_, level, 0, SDP_ATTR_EXTMAP, &inst_num), SDP_SUCCESS); EXPECT_EQ(sdp_attr_set_extmap(sdp_ptr_, level, inst_num, uri, inst_num), SDP_SUCCESS); return inst_num; } uint16_t AddNewFmtpMaxFs(int level, uint32_t max_fs) { uint16_t inst_num = 0; EXPECT_EQ(sdp_add_new_attr(sdp_ptr_, level, 0, SDP_ATTR_FMTP, &inst_num), SDP_SUCCESS); EXPECT_EQ(sdp_attr_set_fmtp_payload_type(sdp_ptr_, level, 0, inst_num, 120), SDP_SUCCESS); EXPECT_EQ(sdp_attr_set_fmtp_max_fs(sdp_ptr_, level, 0, inst_num, max_fs), SDP_SUCCESS); return inst_num; } uint16_t AddNewFmtpMaxFr(int level, uint32_t max_fr) { uint16_t inst_num = 0; EXPECT_EQ(sdp_add_new_attr(sdp_ptr_, level, 0, SDP_ATTR_FMTP, &inst_num), SDP_SUCCESS); EXPECT_EQ(sdp_attr_set_fmtp_payload_type(sdp_ptr_, level, 0, inst_num, 120), SDP_SUCCESS); EXPECT_EQ(sdp_attr_set_fmtp_max_fr(sdp_ptr_, level, 0, inst_num, max_fr), SDP_SUCCESS); return inst_num; } uint16_t AddNewFmtpMaxFsFr(int level, uint32_t max_fs, uint32_t max_fr) { uint16_t inst_num = 0; EXPECT_EQ(sdp_add_new_attr(sdp_ptr_, level, 0, SDP_ATTR_FMTP, &inst_num), SDP_SUCCESS); EXPECT_EQ(sdp_attr_set_fmtp_payload_type(sdp_ptr_, level, 0, inst_num, 120), SDP_SUCCESS); EXPECT_EQ(sdp_attr_set_fmtp_max_fs(sdp_ptr_, level, 0, inst_num, max_fs), SDP_SUCCESS); EXPECT_EQ(sdp_attr_set_fmtp_max_fr(sdp_ptr_, level, 0, inst_num, max_fr), SDP_SUCCESS); return inst_num; } protected: int final_level_; sdp_t* sdp_ptr_; }; static const std::string kVideoSdp = "v=0\r\n" "o=- 4294967296 2 IN IP4 127.0.0.1\r\n" "s=SIP Call\r\n" "c=IN IP4 198.51.100.7\r\n" "t=0 0\r\n" "m=video 56436 RTP/SAVPF 120\r\n" "a=rtpmap:120 VP8/90000\r\n"; TEST_F(SdpTest, parseRtcpFbAckRpsi) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 ack rpsi\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_ack(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_ACK_RPSI); } TEST_F(SdpTest, parseRtcpFbAckApp) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 ack app\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_ack(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_ACK_APP); } TEST_F(SdpTest, parseRtcpFbAckAppFoo) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 ack app foo\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_ack(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_ACK_APP); } TEST_F(SdpTest, parseRtcpFbAckFooBar) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 ack foo bar\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_ack(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_ACK_UNKNOWN); } TEST_F(SdpTest, parseRtcpFbAckFooBarBaz) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 ack foo bar baz\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_ack(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_ACK_UNKNOWN); } TEST_F(SdpTest, parseRtcpFbNack) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 nack\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_nack(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_NACK_BASIC); } TEST_F(SdpTest, parseRtcpFbNackPli) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 nack pli\r\n"); } TEST_F(SdpTest, parseRtcpFbNackSli) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 nack sli\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_nack(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_NACK_SLI); } TEST_F(SdpTest, parseRtcpFbNackRpsi) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 nack rpsi\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_nack(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_NACK_RPSI); } TEST_F(SdpTest, parseRtcpFbNackApp) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 nack app\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_nack(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_NACK_APP); } TEST_F(SdpTest, parseRtcpFbNackAppFoo) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 nack app foo\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_nack(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_NACK_APP); } TEST_F(SdpTest, parseRtcpFbNackAppFooBar) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 nack app foo bar\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_nack(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_NACK_APP); } TEST_F(SdpTest, parseRtcpFbNackFooBarBaz) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 nack foo bar baz\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_nack(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_NACK_UNKNOWN); } TEST_F(SdpTest, parseRtcpFbRemb) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 goog-remb\r\n"); ASSERT_EQ((bool)sdp_attr_get_rtcp_fb_remb_enabled(sdp_ptr_, 1, 120), true); } TEST_F(SdpTest, parseRtcpRbRembAllPt) { ParseSdp(kVideoSdp + "a=rtcp-fb:* goog-remb\r\n"); ASSERT_EQ( (bool)sdp_attr_get_rtcp_fb_remb_enabled(sdp_ptr_, 1, SDP_ALL_PAYLOADS), true); } TEST_F(SdpTest, parseRtcpFbTrrInt0) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 trr-int 0\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_trr_int(sdp_ptr_, 1, 120, 1), 0U); } TEST_F(SdpTest, parseRtcpFbTrrInt123) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 trr-int 123\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_trr_int(sdp_ptr_, 1, 120, 1), 123U); } TEST_F(SdpTest, parseRtcpFbCcmFir) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 ccm fir\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_ccm(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_CCM_FIR); } TEST_F(SdpTest, parseRtcpFbCcmTmmbr) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 ccm tmmbr\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_ccm(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_CCM_TMMBR); } TEST_F(SdpTest, parseRtcpFbCcmTmmbrSmaxpr) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 ccm tmmbr smaxpr=456\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_ccm(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_CCM_TMMBR); } TEST_F(SdpTest, parseRtcpFbCcmTstr) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 ccm tstr\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_ccm(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_CCM_TSTR); } TEST_F(SdpTest, parseRtcpFbCcmVbcm) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 ccm vbcm 123 456 789\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_ccm(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_CCM_VBCM); // We don't currently parse out VBCM submessage types, since we don't have // any use for them. } TEST_F(SdpTest, parseRtcpFbCcmFoo) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 ccm foo\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_ccm(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_CCM_UNKNOWN); } TEST_F(SdpTest, parseRtcpFbCcmFooBarBaz) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 ccm foo bar baz\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_ccm(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_CCM_UNKNOWN); } TEST_F(SdpTest, parseRtcpFbFoo) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 foo\r\n"); } TEST_F(SdpTest, parseRtcpFbFooBar) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 foo bar\r\n"); } TEST_F(SdpTest, parseRtcpFbFooBarBaz) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 foo bar baz\r\n"); } static const std::string kVideoSdpWithUnknonwBrokenFtmp = "v=0\r\n" "o=- 4294967296 2 IN IP4 127.0.0.1\r\n" "s=SIP Call\r\n" "c=IN IP4 198.51.100.7\r\n" "t=0 0\r\n" "m=video 56436 RTP/SAVPF 120\r\n" "a=rtpmap:120 VP8/90000\r\n" "a=fmtp:122 unknown=10\n" "a=rtpmap:122 red/90000\r\n"; TEST_F(SdpTest, parseUnknownBrokenFtmp) { ParseSdp(kVideoSdpWithUnknonwBrokenFtmp); } TEST_F(SdpTest, parseRtcpFbKitchenSink) { ParseSdp(kVideoSdp + "a=rtcp-fb:120 ack rpsi\r\n" "a=rtcp-fb:120 ack app\r\n" "a=rtcp-fb:120 ack app foo\r\n" "a=rtcp-fb:120 ack foo bar\r\n" "a=rtcp-fb:120 ack foo bar baz\r\n" "a=rtcp-fb:120 nack\r\n" "a=rtcp-fb:120 nack pli\r\n" "a=rtcp-fb:120 nack sli\r\n" "a=rtcp-fb:120 nack rpsi\r\n" "a=rtcp-fb:120 nack app\r\n" "a=rtcp-fb:120 nack app foo\r\n" "a=rtcp-fb:120 nack app foo bar\r\n" "a=rtcp-fb:120 nack foo bar baz\r\n" "a=rtcp-fb:120 trr-int 0\r\n" "a=rtcp-fb:120 trr-int 123\r\n" "a=rtcp-fb:120 goog-remb\r\n" "a=rtcp-fb:120 ccm fir\r\n" "a=rtcp-fb:120 ccm tmmbr\r\n" "a=rtcp-fb:120 ccm tmmbr smaxpr=456\r\n" "a=rtcp-fb:120 ccm tstr\r\n" "a=rtcp-fb:120 ccm vbcm 123 456 789\r\n" "a=rtcp-fb:120 ccm foo\r\n" "a=rtcp-fb:120 ccm foo bar baz\r\n" "a=rtcp-fb:120 foo\r\n" "a=rtcp-fb:120 foo bar\r\n" "a=rtcp-fb:120 foo bar baz\r\n"); ASSERT_EQ(sdp_attr_get_rtcp_fb_ack(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_ACK_RPSI); ASSERT_EQ(sdp_attr_get_rtcp_fb_ack(sdp_ptr_, 1, 120, 2), SDP_RTCP_FB_ACK_APP); ASSERT_EQ(sdp_attr_get_rtcp_fb_ack(sdp_ptr_, 1, 120, 3), SDP_RTCP_FB_ACK_APP); ASSERT_EQ(sdp_attr_get_rtcp_fb_ack(sdp_ptr_, 1, 120, 4), SDP_RTCP_FB_ACK_UNKNOWN); ASSERT_EQ(sdp_attr_get_rtcp_fb_ack(sdp_ptr_, 1, 120, 5), SDP_RTCP_FB_ACK_UNKNOWN); ASSERT_EQ(sdp_attr_get_rtcp_fb_ack(sdp_ptr_, 1, 120, 6), SDP_RTCP_FB_ACK_NOT_FOUND); ASSERT_EQ(sdp_attr_get_rtcp_fb_nack(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_NACK_BASIC); ASSERT_EQ(sdp_attr_get_rtcp_fb_nack(sdp_ptr_, 1, 120, 2), SDP_RTCP_FB_NACK_PLI); ASSERT_EQ(sdp_attr_get_rtcp_fb_nack(sdp_ptr_, 1, 120, 3), SDP_RTCP_FB_NACK_SLI); ASSERT_EQ(sdp_attr_get_rtcp_fb_nack(sdp_ptr_, 1, 120, 4), SDP_RTCP_FB_NACK_RPSI); ASSERT_EQ(sdp_attr_get_rtcp_fb_nack(sdp_ptr_, 1, 120, 5), SDP_RTCP_FB_NACK_APP); ASSERT_EQ(sdp_attr_get_rtcp_fb_nack(sdp_ptr_, 1, 120, 6), SDP_RTCP_FB_NACK_APP); ASSERT_EQ(sdp_attr_get_rtcp_fb_nack(sdp_ptr_, 1, 120, 7), SDP_RTCP_FB_NACK_APP); ASSERT_EQ(sdp_attr_get_rtcp_fb_nack(sdp_ptr_, 1, 120, 8), SDP_RTCP_FB_NACK_UNKNOWN); ASSERT_EQ(sdp_attr_get_rtcp_fb_nack(sdp_ptr_, 1, 120, 9), SDP_RTCP_FB_NACK_NOT_FOUND); ASSERT_EQ(sdp_attr_get_rtcp_fb_trr_int(sdp_ptr_, 1, 120, 1), 0U); ASSERT_EQ(sdp_attr_get_rtcp_fb_trr_int(sdp_ptr_, 1, 120, 2), 123U); ASSERT_EQ(sdp_attr_get_rtcp_fb_trr_int(sdp_ptr_, 1, 120, 3), 0xFFFFFFFF); ASSERT_EQ((bool)sdp_attr_get_rtcp_fb_remb_enabled(sdp_ptr_, 1, 120), true); ASSERT_EQ((bool)sdp_attr_get_rtcp_fb_remb_enabled(sdp_ptr_, 2, 120), false); ASSERT_EQ(sdp_attr_get_rtcp_fb_ccm(sdp_ptr_, 1, 120, 1), SDP_RTCP_FB_CCM_FIR); ASSERT_EQ(sdp_attr_get_rtcp_fb_ccm(sdp_ptr_, 1, 120, 2), SDP_RTCP_FB_CCM_TMMBR); ASSERT_EQ(sdp_attr_get_rtcp_fb_ccm(sdp_ptr_, 1, 120, 3), SDP_RTCP_FB_CCM_TMMBR); ASSERT_EQ(sdp_attr_get_rtcp_fb_ccm(sdp_ptr_, 1, 120, 4), SDP_RTCP_FB_CCM_TSTR); ASSERT_EQ(sdp_attr_get_rtcp_fb_ccm(sdp_ptr_, 1, 120, 5), SDP_RTCP_FB_CCM_VBCM); // We don't currently parse out VBCM submessage types, since we don't have // any use for them. ASSERT_EQ(sdp_attr_get_rtcp_fb_ccm(sdp_ptr_, 1, 120, 6), SDP_RTCP_FB_CCM_UNKNOWN); ASSERT_EQ(sdp_attr_get_rtcp_fb_ccm(sdp_ptr_, 1, 120, 7), SDP_RTCP_FB_CCM_UNKNOWN); ASSERT_EQ(sdp_attr_get_rtcp_fb_ccm(sdp_ptr_, 1, 120, 8), SDP_RTCP_FB_CCM_NOT_FOUND); } TEST_F(SdpTest, addRtcpFbAckRpsi) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbAck(level, SDP_RTCP_FB_ACK_RPSI, 120); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:120 ack rpsi\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbAckRpsiAllPt) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbAck(level, SDP_RTCP_FB_ACK_RPSI); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:* ack rpsi\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbAckApp) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbAck(level, SDP_RTCP_FB_ACK_APP, 120); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:120 ack app\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbAckAppAllPt) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbAck(level, SDP_RTCP_FB_ACK_APP); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:* ack app\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNack) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbNack(level, SDP_RTCP_FB_NACK_BASIC, 120); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:120 nack\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNackAllPt) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbNack(level, SDP_RTCP_FB_NACK_BASIC); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:* nack\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNackSli) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbNack(level, SDP_RTCP_FB_NACK_SLI, 120); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:120 nack sli\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNackSliAllPt) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbNack(level, SDP_RTCP_FB_NACK_SLI); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:* nack sli\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNackPli) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbNack(level, SDP_RTCP_FB_NACK_PLI, 120); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:120 nack pli\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNackPliAllPt) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbNack(level, SDP_RTCP_FB_NACK_PLI); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:* nack pli\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNackRpsi) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbNack(level, SDP_RTCP_FB_NACK_RPSI, 120); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:120 nack rpsi\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNackRpsiAllPt) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbNack(level, SDP_RTCP_FB_NACK_RPSI); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:* nack rpsi\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNackApp) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbNack(level, SDP_RTCP_FB_NACK_APP, 120); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:120 nack app\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNackAppAllPt) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbNack(level, SDP_RTCP_FB_NACK_APP); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:* nack app\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNackRai) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbNack(level, SDP_RTCP_FB_NACK_RAI, 120); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:120 nack rai\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNackRaiAllPt) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbNack(level, SDP_RTCP_FB_NACK_RAI); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:* nack rai\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNackTllei) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbNack(level, SDP_RTCP_FB_NACK_TLLEI, 120); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:120 nack tllei\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNackTlleiAllPt) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbNack(level, SDP_RTCP_FB_NACK_TLLEI); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:* nack tllei\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNackPslei) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbNack(level, SDP_RTCP_FB_NACK_PSLEI, 120); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:120 nack pslei\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNackPsleiAllPt) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbNack(level, SDP_RTCP_FB_NACK_PSLEI); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:* nack pslei\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNackEcn) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbNack(level, SDP_RTCP_FB_NACK_ECN, 120); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:120 nack ecn\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNackEcnAllPt) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbNack(level, SDP_RTCP_FB_NACK_ECN); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:* nack ecn\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbRemb) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbRemb(level, 120); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:120 goog-remb\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbRembAllPt) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbRemb(level); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:* goog-remb\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbTrrInt) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbTrrInt(level, 12345, 120); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:120 trr-int 12345\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbNackTrrIntAllPt) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbTrrInt(level, 0); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:* trr-int 0\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbCcmFir) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbCcm(level, SDP_RTCP_FB_CCM_FIR, 120); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:120 ccm fir\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbCcmFirAllPt) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbCcm(level, SDP_RTCP_FB_CCM_FIR); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:* ccm fir\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbCcmTmmbr) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbCcm(level, SDP_RTCP_FB_CCM_TMMBR, 120); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:120 ccm tmmbr\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbCcmTmmbrAllPt) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbCcm(level, SDP_RTCP_FB_CCM_TMMBR); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:* ccm tmmbr\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbCcmTstr) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbCcm(level, SDP_RTCP_FB_CCM_TSTR, 120); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:120 ccm tstr\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbCcmTstrAllPt) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbCcm(level, SDP_RTCP_FB_CCM_TSTR); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:* ccm tstr\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbCcmVbcm) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbCcm(level, SDP_RTCP_FB_CCM_VBCM, 120); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:120 ccm vbcm\r\n"), std::string::npos); } TEST_F(SdpTest, addRtcpFbCcmVbcmAllPt) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewRtcpFbCcm(level, SDP_RTCP_FB_CCM_VBCM); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=rtcp-fb:* ccm vbcm\r\n"), std::string::npos); } TEST_F(SdpTest, parseRtcpFbAllPayloads) { ParseSdp(kVideoSdp + "a=rtcp-fb:* ack rpsi\r\n"); for (int i = 0; i < 128; i++) { ASSERT_EQ(sdp_attr_get_rtcp_fb_ack(sdp_ptr_, 1, i, 1), SDP_RTCP_FB_ACK_RPSI); } } TEST_F(SdpTest, addExtMap) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewExtMap(level, SDP_EXTMAP_AUDIO_LEVEL); std::string body = SerializeSdp(); ASSERT_NE( body.find("a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"), std::string::npos); } TEST_F(SdpTest, parseExtMap) { ParseSdp(kVideoSdp + "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"); ASSERT_STREQ(sdp_attr_get_extmap_uri(sdp_ptr_, 1, 1), SDP_EXTMAP_AUDIO_LEVEL); ASSERT_EQ(sdp_attr_get_extmap_id(sdp_ptr_, 1, 1), 1); } TEST_F(SdpTest, parseFmtpBitrate) { ParseSdp(kVideoSdp + "a=fmtp:120 bitrate=400\r\n"); ASSERT_EQ(400, sdp_attr_get_fmtp_bitrate_type(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpBitrateWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 bitrate=0\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_bitrate_type(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpBitrateWith32001) { ParseSdp(kVideoSdp + "a=fmtp:120 bitrate=32001\r\n"); ASSERT_EQ(32001, sdp_attr_get_fmtp_bitrate_type(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpBitrateWith4294967296) { ParseSdp(kVideoSdp + "a=fmtp:120 bitrate=4294967296\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_bitrate_type(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpMode) { ParseSdp(kVideoSdp + "a=fmtp:120 mode=200\r\n"); ASSERT_EQ(200U, sdp_attr_get_fmtp_mode_for_payload_type(sdp_ptr_, 1, 0, 120)); } TEST_F(SdpTest, parseFmtpModeWith4294967295) { ParseSdp(kVideoSdp + "a=fmtp:120 mode=4294967295\r\n"); ASSERT_EQ(4294967295, sdp_attr_get_fmtp_mode_for_payload_type(sdp_ptr_, 1, 0, 120)); } TEST_F(SdpTest, parseFmtpModeWith4294967296) { ParseSdp(kVideoSdp + "a=fmtp:120 mode=4294967296\r\n"); // returns 0 if not found ASSERT_EQ(0U, sdp_attr_get_fmtp_mode_for_payload_type(sdp_ptr_, 1, 0, 120)); } TEST_F(SdpTest, parseFmtpQcif) { ParseSdp(kVideoSdp + "a=fmtp:120 qcif=20\r\n"); ASSERT_EQ(20, sdp_attr_get_fmtp_qcif(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpQcifWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 qcif=0\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_qcif(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpQcifWith33) { ParseSdp(kVideoSdp + "a=fmtp:120 qcif=33\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_qcif(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpCif) { ParseSdp(kVideoSdp + "a=fmtp:120 cif=11\r\n"); ASSERT_EQ(11, sdp_attr_get_fmtp_cif(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpCifWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 cif=0\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_cif(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpCifWith33) { ParseSdp(kVideoSdp + "a=fmtp:120 cif=33\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_cif(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpMaxbr) { ParseSdp(kVideoSdp + "a=fmtp:120 maxbr=21\r\n"); ASSERT_EQ(21, sdp_attr_get_fmtp_maxbr(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpMaxbrWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 maxbr=0\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_maxbr(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpMaxbrWith65536) { ParseSdp(kVideoSdp + "a=fmtp:120 maxbr=65536\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_maxbr(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpSqcif) { ParseSdp(kVideoSdp + "a=fmtp:120 sqcif=6\r\n"); ASSERT_EQ(6, sdp_attr_get_fmtp_sqcif(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpSqcifWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 sqcif=0\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_sqcif(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpSqcifWith33) { ParseSdp(kVideoSdp + "a=fmtp:120 sqcif=33\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_sqcif(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpCif4) { ParseSdp(kVideoSdp + "a=fmtp:120 cif4=11\r\n"); ASSERT_EQ(11, sdp_attr_get_fmtp_cif4(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpCif4With0) { ParseSdp(kVideoSdp + "a=fmtp:120 cif4=0\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_cif4(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpCif4With33) { ParseSdp(kVideoSdp + "a=fmtp:120 cif4=33\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_cif4(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpCif16) { ParseSdp(kVideoSdp + "a=fmtp:120 cif16=11\r\n"); ASSERT_EQ(11, sdp_attr_get_fmtp_cif16(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpCif16With0) { ParseSdp(kVideoSdp + "a=fmtp:120 cif16=0\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_cif16(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpCif16With33) { ParseSdp(kVideoSdp + "a=fmtp:120 cif16=33\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_cif16(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpBpp) { ParseSdp(kVideoSdp + "a=fmtp:120 bpp=7\r\n"); ASSERT_EQ(7, sdp_attr_get_fmtp_bpp(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpBppWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 bpp=0\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_bpp(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpBppWith65536) { ParseSdp(kVideoSdp + "a=fmtp:120 bpp=65536\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_bpp(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpHrd) { ParseSdp(kVideoSdp + "a=fmtp:120 hrd=800\r\n"); ASSERT_EQ(800, sdp_attr_get_fmtp_hrd(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpHrdWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 hrd=0\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_hrd(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpHrdWith65536) { ParseSdp(kVideoSdp + "a=fmtp:120 hrd=65536\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_hrd(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpProfile) { ParseSdp(kVideoSdp + "a=fmtp:120 profile=4\r\n"); ASSERT_EQ(4, sdp_attr_get_fmtp_profile(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpProfileWith11) { ParseSdp(kVideoSdp + "a=fmtp:120 profile=11\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_profile(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpLevel) { ParseSdp(kVideoSdp + "a=fmtp:120 level=56\r\n"); ASSERT_EQ(56, sdp_attr_get_fmtp_level(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpLevelWith101) { ParseSdp(kVideoSdp + "a=fmtp:120 level=101\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_level(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpPacketizationMode) { ParseSdp(kVideoSdp + "a=fmtp:120 packetization-mode=1\r\n"); uint16_t packetizationMode; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_pack_mode(sdp_ptr_, 1, 0, 1, &packetizationMode)); ASSERT_EQ(1, packetizationMode); } TEST_F(SdpTest, parseFmtpPacketizationModeWith3) { ParseSdp(kVideoSdp + "a=fmtp:120 packetization-mode=3\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_pack_mode(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpInterleavingDepth) { ParseSdp(kVideoSdp + "a=fmtp:120 sprop-interleaving-depth=566\r\n"); uint16_t interleavingDepth; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_interleaving_depth( sdp_ptr_, 1, 0, 1, &interleavingDepth)); ASSERT_EQ(566, interleavingDepth); } TEST_F(SdpTest, parseFmtpInterleavingDepthWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 sprop-interleaving-depth=0\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_interleaving_depth(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpInterleavingDepthWith65536) { ParseSdp(kVideoSdp + "a=fmtp:120 sprop-interleaving-depth=65536\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_interleaving_depth(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpDeintBuf) { ParseSdp(kVideoSdp + "a=fmtp:120 sprop-deint-buf-req=4294967295\r\n"); uint32_t deintBuf; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_deint_buf_req(sdp_ptr_, 1, 0, 1, &deintBuf)); ASSERT_EQ(4294967295, deintBuf); } TEST_F(SdpTest, parseFmtpDeintBufWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 sprop-deint-buf-req=0\r\n"); uint32_t deintBuf; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_deint_buf_req(sdp_ptr_, 1, 0, 1, &deintBuf)); ASSERT_EQ(0U, deintBuf); } TEST_F(SdpTest, parseFmtpDeintBufWith4294967296) { ParseSdp(kVideoSdp + "a=fmtp:120 sprop-deint-buf-req=4294967296\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_deint_buf_req(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpMaxDonDiff) { ParseSdp(kVideoSdp + "a=fmtp:120 sprop-max-don-diff=5678\r\n"); uint32_t maxDonDiff; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_max_don_diff(sdp_ptr_, 1, 0, 1, &maxDonDiff)); ASSERT_EQ(5678U, maxDonDiff); } TEST_F(SdpTest, parseFmtpMaxDonDiffWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 sprop-max-don-diff=0\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_max_don_diff(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpMaxDonDiffWith4294967296) { ParseSdp(kVideoSdp + "a=fmtp:120 sprop-max-don-diff=4294967296\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_max_don_diff(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpInitBufTime) { ParseSdp(kVideoSdp + "a=fmtp:120 sprop-init-buf-time=4294967295\r\n"); uint32_t initBufTime; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_init_buf_time(sdp_ptr_, 1, 0, 1, &initBufTime)); ASSERT_EQ(4294967295, initBufTime); } TEST_F(SdpTest, parseFmtpInitBufTimeWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 sprop-init-buf-time=0\r\n"); uint32_t initBufTime; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_init_buf_time(sdp_ptr_, 1, 0, 1, &initBufTime)); ASSERT_EQ(0U, initBufTime); } TEST_F(SdpTest, parseFmtpInitBufTimeWith4294967296) { ParseSdp(kVideoSdp + "a=fmtp:120 sprop-init-buf-time=4294967296\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_init_buf_time(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpMaxMbps) { ParseSdp(kVideoSdp + "a=fmtp:120 max-mbps=46789\r\n"); uint32_t maxMpbs; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_max_mbps(sdp_ptr_, 1, 0, 1, &maxMpbs)); ASSERT_EQ(46789U, maxMpbs); } TEST_F(SdpTest, parseFmtpMaxMbpsWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 max-mbps=0\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_max_mbps(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpMaxMbpsWith4294967296) { ParseSdp(kVideoSdp + "a=fmtp:120 max-mbps=4294967296\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_max_mbps(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpMaxCpb) { ParseSdp(kVideoSdp + "a=fmtp:120 max-cpb=47891\r\n"); uint32_t maxCpb; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_max_cpb(sdp_ptr_, 1, 0, 1, &maxCpb)); ASSERT_EQ(47891U, maxCpb); } TEST_F(SdpTest, parseFmtpMaxCpbWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 max-cpb=0\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_max_cpb(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpMaxCpbWith4294967296) { ParseSdp(kVideoSdp + "a=fmtp:120 max-cpb=4294967296\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_max_cpb(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpMaxDpb) { ParseSdp(kVideoSdp + "a=fmtp:120 max-dpb=47892\r\n"); uint32_t maxDpb; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_max_dpb(sdp_ptr_, 1, 0, 1, &maxDpb)); ASSERT_EQ(47892U, maxDpb); } TEST_F(SdpTest, parseFmtpMaxDpbWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 max-dpb=0\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_max_dpb(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpMaxDpbWith4294967296) { ParseSdp(kVideoSdp + "a=fmtp:120 max-dpb=4294967296\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_max_dpb(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpMaxBr) { ParseSdp(kVideoSdp + "a=fmtp:120 max-br=47893\r\n"); uint32_t maxBr; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_max_br(sdp_ptr_, 1, 0, 1, &maxBr)); ASSERT_EQ(47893U, maxBr); } TEST_F(SdpTest, parseFmtpMaxBrWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 max-br=0\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_max_br(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpMaxBrWith4294967296) { ParseSdp(kVideoSdp + "a=fmtp:120 max-br=4294967296\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_max_br(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpRedundantPicCap) { ParseSdp(kVideoSdp + "a=fmtp:120 redundant-pic-cap=1\r\n"); ASSERT_EQ(1, sdp_attr_fmtp_is_redundant_pic_cap(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpRedundantPicCapWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 redundant-pic-cap=0\r\n"); ASSERT_EQ(0, sdp_attr_fmtp_is_redundant_pic_cap(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpRedundantPicCapWith2) { ParseSdp(kVideoSdp + "a=fmtp:120 redundant-pic-cap=2\r\n"); ASSERT_EQ(0, sdp_attr_fmtp_is_redundant_pic_cap(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpDeintBufCap) { ParseSdp(kVideoSdp + "a=fmtp:120 deint-buf-cap=4294967295\r\n"); uint32_t deintBufCap; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_deint_buf_cap(sdp_ptr_, 1, 0, 1, &deintBufCap)); ASSERT_EQ(4294967295, deintBufCap); } TEST_F(SdpTest, parseFmtpDeintBufCapWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 deint-buf-cap=0\r\n"); uint32_t deintBufCap; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_deint_buf_cap(sdp_ptr_, 1, 0, 1, &deintBufCap)); ASSERT_EQ(0U, deintBufCap); } TEST_F(SdpTest, parseFmtpDeintBufCapWith4294967296) { ParseSdp(kVideoSdp + "a=fmtp:120 deint-buf-cap=4294967296\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_deint_buf_cap(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpMaxRcmdNaluSize) { ParseSdp(kVideoSdp + "a=fmtp:120 max-rcmd-nalu-size=4294967295\r\n"); uint32_t maxRcmdNaluSize; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_max_rcmd_nalu_size( sdp_ptr_, 1, 0, 1, &maxRcmdNaluSize)); ASSERT_EQ(4294967295, maxRcmdNaluSize); } TEST_F(SdpTest, parseFmtpMaxRcmdNaluSizeWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 max-rcmd-nalu-size=0\r\n"); uint32_t maxRcmdNaluSize; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_max_rcmd_nalu_size( sdp_ptr_, 1, 0, 1, &maxRcmdNaluSize)); ASSERT_EQ(0U, maxRcmdNaluSize); } TEST_F(SdpTest, parseFmtpMaxRcmdNaluSizeWith4294967296) { ParseSdp(kVideoSdp + "a=fmtp:120 max-rcmd-nalu-size=4294967296\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_max_rcmd_nalu_size(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpParameterAdd) { ParseSdp(kVideoSdp + "a=fmtp:120 parameter-add=1\r\n"); ASSERT_EQ(1, sdp_attr_fmtp_is_parameter_add(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpParameterAddWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 parameter-add=0\r\n"); ASSERT_EQ(0, sdp_attr_fmtp_is_parameter_add(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpParameterAddWith2) { ParseSdp(kVideoSdp + "a=fmtp:120 parameter-add=2\r\n"); ASSERT_EQ(0, sdp_attr_fmtp_is_parameter_add(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpAnnexK) { ParseSdp(kVideoSdp + "a=fmtp:120 K=566\r\n"); ASSERT_EQ(566, sdp_attr_get_fmtp_annex_k_val(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpAnnexKWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 K=0\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_annex_k_val(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpAnnexKWith65536) { ParseSdp(kVideoSdp + "a=fmtp:120 K=65536\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_annex_k_val(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpAnnexN) { ParseSdp(kVideoSdp + "a=fmtp:120 N=4567\r\n"); ASSERT_EQ(4567, sdp_attr_get_fmtp_annex_n_val(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpAnnexNWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 N=0\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_annex_n_val(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpAnnexNWith65536) { ParseSdp(kVideoSdp + "a=fmtp:120 N=65536\r\n"); ASSERT_EQ(SDP_INVALID_VALUE, sdp_attr_get_fmtp_annex_n_val(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpAnnexP) { ParseSdp(kVideoSdp + "a=fmtp:120 P=5678,2\r\n"); ASSERT_EQ(5678, sdp_attr_get_fmtp_annex_p_picture_resize(sdp_ptr_, 1, 0, 1)); ASSERT_EQ(2, sdp_attr_get_fmtp_annex_p_warp(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpAnnexPWithResize0) { ParseSdp(kVideoSdp + "a=fmtp:120 P=0,3\r\n"); ASSERT_EQ(0, sdp_attr_get_fmtp_annex_p_picture_resize(sdp_ptr_, 1, 0, 1)); ASSERT_EQ(3, sdp_attr_get_fmtp_annex_p_warp(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpAnnexPWithResize65536) { ParseSdp(kVideoSdp + "a=fmtp:120 P=65536,4\r\n"); ASSERT_EQ(0, sdp_attr_get_fmtp_annex_p_picture_resize(sdp_ptr_, 1, 0, 1)); // if the first fails, the second will too. Both default to 0 on failure. ASSERT_EQ(0, sdp_attr_get_fmtp_annex_p_warp(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpAnnexPWithWarp65536) { ParseSdp(kVideoSdp + "a=fmtp:120 P=346,65536\r\n"); ASSERT_EQ(346, sdp_attr_get_fmtp_annex_p_picture_resize(sdp_ptr_, 1, 0, 1)); ASSERT_EQ(0, sdp_attr_get_fmtp_annex_p_warp(sdp_ptr_, 1, 0, 1)); } TEST_F(SdpTest, parseFmtpLevelAsymmetryAllowed) { ParseSdp(kVideoSdp + "a=fmtp:120 level-asymmetry-allowed=1\r\n"); uint16_t levelAsymmetryAllowed; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_level_asymmetry_allowed( sdp_ptr_, 1, 0, 1, &levelAsymmetryAllowed)); ASSERT_EQ(1U, levelAsymmetryAllowed); } TEST_F(SdpTest, parseFmtpLevelAsymmetryAllowedWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 level-asymmetry-allowed=0\r\n"); uint16_t levelAsymmetryAllowed; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_level_asymmetry_allowed( sdp_ptr_, 1, 0, 1, &levelAsymmetryAllowed)); ASSERT_EQ(0U, levelAsymmetryAllowed); } TEST_F(SdpTest, parseFmtpLevelAsymmetryAllowedWith2) { ParseSdp(kVideoSdp + "a=fmtp:120 level-asymmetry-allowed=2\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_level_asymmetry_allowed( sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpMaxAverageBitrate) { ParseSdp(kVideoSdp + "a=fmtp:120 maxaveragebitrate=47893\r\n"); uint32_t maxAverageBitrate; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_max_average_bitrate( sdp_ptr_, 1, 0, 1, &maxAverageBitrate)); ASSERT_EQ(47893U, maxAverageBitrate); } TEST_F(SdpTest, parseFmtpMaxAverageBitrateWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 maxaveragebitrate=0\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_max_average_bitrate(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpMaxAverageBitrateWith4294967296) { ParseSdp(kVideoSdp + "a=fmtp:120 maxaveragebitrate=4294967296\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_max_average_bitrate(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpUsedTx) { ParseSdp(kVideoSdp + "a=fmtp:120 usedtx=1\r\n"); tinybool usedTx; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_usedtx(sdp_ptr_, 1, 0, 1, &usedTx)); ASSERT_EQ(1, usedTx); } TEST_F(SdpTest, parseFmtpUsedTxWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 usedtx=0\r\n"); tinybool usedTx; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_usedtx(sdp_ptr_, 1, 0, 1, &usedTx)); ASSERT_EQ(0, usedTx); } TEST_F(SdpTest, parseFmtpUsedTxWith2) { ParseSdp(kVideoSdp + "a=fmtp:120 usedtx=2\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_usedtx(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpStereo) { ParseSdp(kVideoSdp + "a=fmtp:120 stereo=1\r\n"); tinybool stereo; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_stereo(sdp_ptr_, 1, 0, 1, &stereo)); ASSERT_EQ(1, stereo); } TEST_F(SdpTest, parseFmtpStereoWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 stereo=0\r\n"); tinybool stereo; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_stereo(sdp_ptr_, 1, 0, 1, &stereo)); ASSERT_EQ(0, stereo); } TEST_F(SdpTest, parseFmtpStereoWith2) { ParseSdp(kVideoSdp + "a=fmtp:120 stereo=2\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_stereo(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpUseInBandFec) { ParseSdp(kVideoSdp + "a=fmtp:120 useinbandfec=1\r\n"); tinybool useInbandFec; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_useinbandfec(sdp_ptr_, 1, 0, 1, &useInbandFec)); ASSERT_EQ(1, useInbandFec); } TEST_F(SdpTest, parseFmtpUseInBandWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 useinbandfec=0\r\n"); tinybool useInbandFec; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_useinbandfec(sdp_ptr_, 1, 0, 1, &useInbandFec)); ASSERT_EQ(0, useInbandFec); } TEST_F(SdpTest, parseFmtpUseInBandWith2) { ParseSdp(kVideoSdp + "a=fmtp:120 useinbandfec=2\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_useinbandfec(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpMaxCodedAudioBandwidth) { ParseSdp(kVideoSdp + "a=fmtp:120 maxcodedaudiobandwidth=abcdefg\r\n"); char* maxCodedAudioBandwith = sdp_attr_get_fmtp_maxcodedaudiobandwidth(sdp_ptr_, 1, 0, 1); ASSERT_EQ(0, strcmp("abcdefg", maxCodedAudioBandwith)); } TEST_F(SdpTest, parseFmtpMaxCodedAudioBandwidthBad) { ParseSdp(kVideoSdp + "a=fmtp:120 maxcodedaudiobandwidth=\r\n"); char* maxCodedAudioBandwith = sdp_attr_get_fmtp_maxcodedaudiobandwidth(sdp_ptr_, 1, 0, 1); ASSERT_EQ(0, *maxCodedAudioBandwith); } TEST_F(SdpTest, parseFmtpCbr) { ParseSdp(kVideoSdp + "a=fmtp:120 cbr=1\r\n"); tinybool cbr; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_cbr(sdp_ptr_, 1, 0, 1, &cbr)); ASSERT_EQ(1, cbr); } TEST_F(SdpTest, parseFmtpCbrWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 cbr=0\r\n"); tinybool cbr; ASSERT_EQ(SDP_SUCCESS, sdp_attr_get_fmtp_cbr(sdp_ptr_, 1, 0, 1, &cbr)); ASSERT_EQ(0, cbr); } TEST_F(SdpTest, parseFmtpCbrWith2) { ParseSdp(kVideoSdp + "a=fmtp:120 cbr=2\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_cbr(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpMaxPlaybackRate) { ParseSdp(kVideoSdp + "a=fmtp:120 maxplaybackrate=47900\r\n"); sdp_attr_t* attr_p = sdp_find_attr(sdp_ptr_, 1, 0, SDP_ATTR_FMTP, 1); ASSERT_NE(nullptr, attr_p); ASSERT_EQ(47900U, attr_p->attr.fmtp.maxplaybackrate); } TEST_F(SdpTest, parseFmtpMaxPlaybackRateWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 maxplaybackrate=0\r\n"); sdp_attr_t* attr_p = sdp_find_attr(sdp_ptr_, 1, 0, SDP_ATTR_FMTP, 1); ASSERT_EQ(NULL, attr_p); } TEST_F(SdpTest, parseFmtpMaxPlaybackRateWith4294967296) { ParseSdp(kVideoSdp + "a=fmtp:120 maxplaybackrate=4294967296\r\n"); sdp_attr_t* attr_p = sdp_find_attr(sdp_ptr_, 1, 0, SDP_ATTR_FMTP, 1); ASSERT_EQ(NULL, attr_p); } TEST_F(SdpTest, parseFmtpMaxFs) { uint32_t val = 0; ParseSdp(kVideoSdp + "a=fmtp:120 max-fs=300;max-fr=30\r\n"); ASSERT_EQ(sdp_attr_get_fmtp_max_fs(sdp_ptr_, 1, 0, 1, &val), SDP_SUCCESS); ASSERT_EQ(val, 300U); } TEST_F(SdpTest, parseFmtpMaxFsWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 max-fs=0\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_max_fs(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpMaxFsWith4294967296) { ParseSdp(kVideoSdp + "a=fmtp:120 max-fs=4294967296\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_max_fs(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpMaxFr) { uint32_t val = 0; ParseSdp(kVideoSdp + "a=fmtp:120 max-fs=300;max-fr=30\r\n"); ASSERT_EQ(sdp_attr_get_fmtp_max_fr(sdp_ptr_, 1, 0, 1, &val), SDP_SUCCESS); ASSERT_EQ(val, 30U); } TEST_F(SdpTest, parseFmtpMaxFrWith0) { ParseSdp(kVideoSdp + "a=fmtp:120 max-fr=0\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_max_fr(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, parseFmtpMaxFrWith4294967296) { ParseSdp(kVideoSdp + "a=fmtp:120 max-fr=4294967296\r\n"); ASSERT_EQ(SDP_INVALID_PARAMETER, sdp_attr_get_fmtp_max_fr(sdp_ptr_, 1, 0, 1, nullptr)); } TEST_F(SdpTest, addFmtpMaxFs) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewFmtpMaxFs(level, 300); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=fmtp:120 max-fs=300\r\n"), std::string::npos); } TEST_F(SdpTest, addFmtpMaxFr) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewFmtpMaxFr(level, 30); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=fmtp:120 max-fr=30\r\n"), std::string::npos); } TEST_F(SdpTest, addFmtpMaxFsFr) { InitLocalSdp(); int level = AddNewMedia(SDP_MEDIA_VIDEO); AddNewFmtpMaxFsFr(level, 300, 30); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=fmtp:120 max-fs=300;max-fr=30\r\n"), std::string::npos); } static const std::string kBrokenFmtp = "v=0\r\n" "o=- 4294967296 2 IN IP4 127.0.0.1\r\n" "s=SIP Call\r\n" "t=0 0\r\n" "m=video 56436 RTP/SAVPF 120\r\n" "c=IN IP4 198.51.100.7\r\n" "a=rtpmap:120 VP8/90000\r\n" /* Note: the \0 in this string triggered bz://1089207 */ "a=fmtp:120 max-fs=300;max\0fr=30"; TEST_F(SdpTest, parseBrokenFmtp) { uint32_t val = 0; const char* buf = kBrokenFmtp.data(); ResetSdp(); /* We need to manually invoke the parser here to be able to specify the length * of the string beyond the \0 in last line of the string. */ ASSERT_EQ(sdp_parse(sdp_ptr_, buf, 165), SDP_SUCCESS); ASSERT_EQ(sdp_attr_get_fmtp_max_fs(sdp_ptr_, 1, 0, 1, &val), SDP_INVALID_PARAMETER); } TEST_F(SdpTest, addIceLite) { InitLocalSdp(); uint16_t inst_num = 0; EXPECT_EQ(sdp_add_new_attr(sdp_ptr_, SDP_SESSION_LEVEL, 0, SDP_ATTR_ICE_LITE, &inst_num), SDP_SUCCESS); std::string body = SerializeSdp(); ASSERT_NE(body.find("a=ice-lite\r\n"), std::string::npos); } TEST_F(SdpTest, parseIceLite) { std::string sdp = "v=0\r\n" "o=- 4294967296 2 IN IP4 127.0.0.1\r\n" "s=SIP Call\r\n" "t=0 0\r\n" "a=ice-lite\r\n"; ParseSdp(sdp); ASSERT_TRUE( sdp_attr_is_present(sdp_ptr_, SDP_ATTR_ICE_LITE, SDP_SESSION_LEVEL, 0)); } class NewSdpTest : public ::testing::Test, public ::testing::WithParamInterface< ::testing::tuple > { public: NewSdpTest() = default; void ParseSdp(const std::string& sdp, bool expectSuccess = true, bool expectEqual = true) { UniquePtr firstParser(new RsdparsaSdpParser()); UniquePtr secondParser(new SipccSdpParser()); if (::testing::get<1>(GetParam())) { firstParser.swap(secondParser); } mResults = firstParser->Parse(sdp); #ifdef DEBUG_DISPLAY_SDP std::cout << firstParser->Name() << " Parsing SDP:" << std::endl; std::stringstream sdpStream(sdp); std::string line; size_t lineNumber = 0; while (std::getline(sdpStream, line, '\n')) { if (line.length() && line.back() == '\r') { line.pop_back(); } lineNumber++; std::cout << std::setw(4) << lineNumber << " " << line << std::endl; } #endif // Are we configured to do a parse and serialize before actually // running the test? if (::testing::get<0>(GetParam())) { if (expectSuccess) { ASSERT_TRUE(!!mResults->Sdp()) << "Parse failed on first pass: " << SerializeParseErrors(); } if (mResults->Sdp()) { std::stringstream os, os2; // Serialize and re-parse mResults->Sdp()->Serialize(os); const auto secondResults = secondParser->Parse(os.str()); // Whether we expected the parse to work or not, it should // succeed the second time if it succeeded the first. ASSERT_TRUE(!!Sdp()) << "Parse failed on second pass, SDP was: " << std::endl << os.str() << std::endl << "Errors were: " << IntSerializeParseErrors(secondResults); // Serialize again and compare secondResults->Sdp()->Serialize(os2); if (expectEqual) { ASSERT_EQ(os.str(), os2.str()) << "FIRST IS " << mResults->ParserName() << ", SECOND IS " << secondResults->ParserName(); } else { ASSERT_NE(os.str(), os2.str()) << "FIRST IS " << mResults->ParserName() << ", SECOND IS " << secondResults->ParserName(); } } } if (expectSuccess) { ASSERT_TRUE(!!mResults->Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(0U, ParseErrorCount()) << "Got unexpected parse errors/warnings: " << SerializeParseErrors(); } } const UniquePtr& Sdp() const { static const UniquePtr NO_SDP(nullptr); return mResults ? mResults->Sdp() : NO_SDP; } size_t ParseErrorCount() const { return mResults->Errors().size(); } size_t ParseWarningCount() const { return mResults->Warnings().size(); } std::string IntSerializeParseErrors( const UniquePtr& aResults) const { std::stringstream output; for (const auto& e : aResults->Errors()) { output << e.first << ": " << e.second << std::endl; } return output.str(); } // For streaming parse errors std::string SerializeParseErrors() const { return IntSerializeParseErrors(mResults); } std::string IntSerializeParseWarnings( const UniquePtr& aResults) const { std::stringstream output; for (const auto& e : aResults->Warnings()) { output << e.first << ": " << e.second << std::endl; } return output.str(); } std::string SerializeParseWarnings() const { return IntSerializeParseWarnings(mResults); } void CheckRtpmap(const std::string& expected_pt, SdpRtpmapAttributeList::CodecType codec, const std::string& name, uint32_t clock, uint16_t channels, const std::string& search_pt, const SdpRtpmapAttributeList& rtpmaps) const { ASSERT_TRUE(rtpmaps.HasEntry(search_pt)); auto attr = rtpmaps.GetEntry(search_pt); ASSERT_EQ(expected_pt, attr.pt); ASSERT_EQ(codec, attr.codec); std::cout << "Codec = " << name << std::endl; ASSERT_EQ(name, attr.name); ASSERT_EQ(clock, attr.clock); ASSERT_EQ(channels, attr.channels); } void CheckSctpmap(const std::string& expected_pt, const std::string& name, uint16_t streams, const std::string& search_pt, const SdpSctpmapAttributeList& sctpmaps) const { ASSERT_TRUE(sctpmaps.HasEntry(search_pt)); auto attr = sctpmaps.GetFirstEntry(); ASSERT_EQ(expected_pt, search_pt); ASSERT_EQ(expected_pt, attr.pt); ASSERT_EQ(name, attr.name); ASSERT_EQ(streams, attr.streams); } void CheckRtcpFb(const SdpRtcpFbAttributeList::Feedback& feedback, const std::string& pt, SdpRtcpFbAttributeList::Type type, const std::string& first_parameter, const std::string& extra = "") const { ASSERT_EQ(pt, feedback.pt); ASSERT_EQ(type, feedback.type); ASSERT_EQ(first_parameter, feedback.parameter); ASSERT_EQ(extra, feedback.extra); } void CheckDtmfFmtp(const std::string& expectedDtmfTones) const { ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kFmtpAttribute)); auto audio_format_params = Sdp()->GetMediaSection(0).GetAttributeList().GetFmtp().mFmtps; ASSERT_EQ(2U, audio_format_params.size()); ASSERT_EQ("101", audio_format_params[1].format); ASSERT_TRUE(!!audio_format_params[1].parameters); const SdpFmtpAttributeList::TelephoneEventParameters* te_parameters = static_cast( audio_format_params[1].parameters.get()); ASSERT_NE(0U, te_parameters->dtmfTones.size()); ASSERT_EQ(expectedDtmfTones, te_parameters->dtmfTones); } void CheckSerialize(const std::string& expected, const SdpAttribute& attr) const { std::stringstream str; attr.Serialize(str); ASSERT_EQ(expected, str.str()); } bool ResultsAreFromSipcc() const { return mResults && SipccSdpParser::IsNamed(mResults->ParserName()); } mozilla::UniquePtr mResults; }; // class NewSdpTest TEST_P(NewSdpTest, CreateDestroy) {} TEST_P(NewSdpTest, ParseEmpty) { ParseSdp("", false); ASSERT_FALSE(Sdp()); ASSERT_NE(0U, ParseErrorCount()) << "Expected at least one parse error."; } const std::string kBadSdp = "This is SDPARTA!!!!"; TEST_P(NewSdpTest, ParseGarbage) { ParseSdp(kBadSdp, false); ASSERT_FALSE(Sdp()); ASSERT_NE(0U, ParseErrorCount()) << "Expected at least one parse error."; } TEST_P(NewSdpTest, ParseGarbageTwice) { ParseSdp(kBadSdp, false); ASSERT_FALSE(Sdp()); size_t errorCount = ParseErrorCount(); ASSERT_NE(0U, errorCount) << "Expected at least one parse error."; ParseSdp(kBadSdp, false); ASSERT_FALSE(Sdp()); ASSERT_EQ(errorCount, ParseErrorCount()) << "Expected same error count for same SDP."; } TEST_P(NewSdpTest, ParseMinimal) { ParseSdp(kVideoSdp); ASSERT_EQ(0U, ParseErrorCount()) << "Got parse errors: " << SerializeParseErrors(); } TEST_P(NewSdpTest, CheckOriginGetUsername) { ParseSdp(kVideoSdp); ASSERT_EQ("-", Sdp()->GetOrigin().GetUsername()) << "Wrong username in origin"; } TEST_P(NewSdpTest, CheckOriginGetSessionId) { ParseSdp(kVideoSdp); ASSERT_EQ(4294967296U, Sdp()->GetOrigin().GetSessionId()) << "Wrong session id in origin"; } TEST_P(NewSdpTest, CheckOriginGetSessionVersion) { ParseSdp(kVideoSdp); ASSERT_EQ(2U, Sdp()->GetOrigin().GetSessionVersion()) << "Wrong version in origin"; } TEST_P(NewSdpTest, CheckOriginGetAddrType) { ParseSdp(kVideoSdp); ASSERT_EQ(sdp::kIPv4, Sdp()->GetOrigin().GetAddrType()) << "Wrong address type in origin"; } TEST_P(NewSdpTest, CheckOriginGetAddress) { ParseSdp(kVideoSdp); ASSERT_EQ("127.0.0.1", Sdp()->GetOrigin().GetAddress()) << "Wrong address in origin"; } TEST_P(NewSdpTest, CheckGetMissingBandwidth) { ParseSdp(kVideoSdp); ASSERT_EQ(0U, Sdp()->GetBandwidth("CT")) << "Wrong bandwidth in session"; } TEST_P(NewSdpTest, CheckGetBandwidth) { ParseSdp("v=0" CRLF "o=- 4294967296 2 IN IP4 127.0.0.1" CRLF "s=SIP Call" CRLF "c=IN IP4 198.51.100.7" CRLF "b=CT:5000" CRLF "b=FOOBAR:10" CRLF "b=AS:4" CRLF "t=0 0" CRLF "m=video 56436 RTP/SAVPF 120" CRLF "a=rtpmap:120 VP8/90000" CRLF, true, ::testing::get<1>(GetParam())); ASSERT_EQ(5000U, Sdp()->GetBandwidth("CT")) << "Wrong CT bandwidth in session"; ASSERT_EQ(0U, Sdp()->GetBandwidth("FOOBAR")) << "Wrong FOOBAR bandwidth in session"; ASSERT_EQ(4U, Sdp()->GetBandwidth("AS")) << "Wrong AS bandwidth in session"; } TEST_P(NewSdpTest, CheckGetMediaSectionsCount) { ParseSdp(kVideoSdp); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; } TEST_P(NewSdpTest, CheckMediaSectionGetMediaType) { ParseSdp(kVideoSdp); ASSERT_EQ(SdpMediaSection::kVideo, Sdp()->GetMediaSection(0).GetMediaType()) << "Wrong type for first media section"; } TEST_P(NewSdpTest, CheckMediaSectionGetProtocol) { ParseSdp(kVideoSdp); ASSERT_EQ(SdpMediaSection::kRtpSavpf, Sdp()->GetMediaSection(0).GetProtocol()) << "Wrong protocol for video"; } TEST_P(NewSdpTest, CheckMediaSectionGetFormats) { ParseSdp(kVideoSdp); auto video_formats = Sdp()->GetMediaSection(0).GetFormats(); ASSERT_EQ(1U, video_formats.size()) << "Wrong number of formats for video"; ASSERT_EQ("120", video_formats[0]); } TEST_P(NewSdpTest, CheckMediaSectionGetPort) { ParseSdp(kVideoSdp); ASSERT_EQ(56436U, Sdp()->GetMediaSection(0).GetPort()) << "Wrong port number in media section"; } TEST_P(NewSdpTest, CheckMediaSectionGetMissingPortCount) { ParseSdp(kVideoSdp); ASSERT_EQ(0U, Sdp()->GetMediaSection(0).GetPortCount()) << "Wrong port count in media section"; } TEST_P(NewSdpTest, CheckMediaSectionGetPortCount) { ParseSdp(kVideoSdp + "m=audio 12345/2 RTP/SAVPF 0" CRLF "a=rtpmap:0 PCMU/8000" CRLF); ASSERT_EQ(2U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_EQ(2U, Sdp()->GetMediaSection(1).GetPortCount()) << "Wrong port count in media section"; } TEST_P(NewSdpTest, CheckMediaSectionGetMissingBandwidth) { ParseSdp(kVideoSdp); ASSERT_EQ(0U, Sdp()->GetMediaSection(0).GetBandwidth("CT")) << "Wrong bandwidth in media section"; } TEST_P(NewSdpTest, CheckMediaSectionGetBandwidth) { ParseSdp( "v=0\r\n" "o=- 4294967296 2 IN IP4 127.0.0.1\r\n" "s=SIP Call\r\n" "c=IN IP4 198.51.100.7\r\n" "t=0 0\r\n" "m=video 56436 RTP/SAVPF 120\r\n" "b=CT:1000\r\n" "a=rtpmap:120 VP8/90000\r\n"); ASSERT_EQ(1000U, Sdp()->GetMediaSection(0).GetBandwidth("CT")) << "Wrong bandwidth in media section"; } // Define a string that is 258 characters long. We use a long string here so // that we can test that we are able to parse and handle a string longer than // the default maximum length of 256 in sipcc. #define ID_A "1234567890abcdef" #define ID_B ID_A ID_A ID_A ID_A #define LONG_IDENTITY ID_B ID_B ID_B ID_B "xx" #define BASE64_DTLS_HELLO \ "FgEAAAAAAAAAAAAAagEAAF4AAAAAAAAAXgEARI11KHx3QB6Ky" \ "CKgoBj/kwjKrApkL8kiZLwIqBaJGT8AAAA2ADkAOAA1ABYAEwAKADMAMgAvAAcAZgAFAAQAYw" \ "BiAGEAFQASAAkAZQBkAGAAFAARAAgABgADAQA=" // SDP from a basic A/V apprtc call FFX/FFX const std::string kBasicAudioVideoOffer = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=dtls-message:client " BASE64_DTLS_HELLO CRLF "a=ice-ufrag:4a799b2e" CRLF "a=ice-pwd:e4cc12a910f106a0a744719425510e17" CRLF "a=ice-lite" CRLF "a=ice-options:trickle foo" CRLF "a=msid-semantic:WMS stream streama" CRLF "a=msid-semantic:foo stream" CRLF "a=fingerprint:sha-256 " "DF:2E:AC:8A:FD:0A:8E:99:BF:5D:E8:3C:E7:FA:FB:08:3B:3C:54:1D:D7:D4:05:77:" "A0:72:9B:14:08:6D:0F:4C" CRLF "a=identity:" LONG_IDENTITY CRLF "a=group:BUNDLE first second" CRLF "a=group:BUNDLE third" CRLF "a=group:LS first third" CRLF "m=audio 9 RTP/SAVPF 109 9 0 8 101" CRLF "c=IN IP4 0.0.0.0" CRLF "a=mid:first" CRLF "a=rtpmap:109 opus/48000/2" CRLF "a=fmtp:109 maxplaybackrate=32000;stereo=1" CRLF "a=ptime:20" CRLF "a=maxptime:20" CRLF "a=rtpmap:9 G722/8000" CRLF "a=rtpmap:0 PCMU/8000" CRLF "a=rtpmap:8 PCMA/8000" CRLF "a=rtpmap:101 telephone-event/8000" CRLF "a=fmtp:101 0-15,66,32-34,67" CRLF "a=ice-ufrag:00000000" CRLF "a=ice-pwd:0000000000000000000000000000000" CRLF "a=sendonly" CRLF "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level" CRLF "a=setup:actpass" CRLF "a=rtcp-mux" CRLF "a=msid:stream track" CRLF "a=candidate:0 1 UDP 2130379007 10.0.0.36 62453 typ host" CRLF "a=candidate:2 1 UDP 1694236671 24.6.134.204 62453 typ srflx raddr " "10.0.0.36 rport 62453" CRLF "a=candidate:3 1 UDP 100401151 162.222.183.171 49761 typ relay raddr " "162.222.183.171 rport 49761" CRLF "a=candidate:6 1 UDP 16515071 162.222.183.171 51858 typ relay raddr " "162.222.183.171 rport 51858" CRLF "a=candidate:3 2 UDP 100401150 162.222.183.171 62454 typ relay raddr " "162.222.183.171 rport 62454" CRLF "a=candidate:2 2 UDP 1694236670 24.6.134.204 55428 typ srflx raddr " "10.0.0.36 rport 55428" CRLF "a=candidate:6 2 UDP 16515070 162.222.183.171 50340 typ relay raddr " "162.222.183.171 rport 50340" CRLF "a=candidate:0 2 UDP 2130379006 10.0.0.36 55428 typ host" CRLF "a=rtcp:62454 IN IP4 162.222.183.171" CRLF "a=end-of-candidates" CRLF "a=ssrc:5150" CRLF "m=video 9 RTP/SAVPF 120 121 122 123" CRLF "c=IN IP6 ::1" CRLF "a=fingerprint:sha-1 " "DF:FA:FB:08:3B:3C:54:1D:D7:D4:05:77:A0:72:9B:14:08:6D:0F:4C" CRLF "a=mid:second" CRLF "a=rtpmap:120 VP8/90000" CRLF "a=fmtp:120 max-fs=3600;max-fr=30" CRLF "a=rtpmap:121 VP9/90000" CRLF "a=fmtp:121 max-fs=3600;max-fr=30" CRLF "a=rtpmap:122 red/90000" CRLF "a=rtpmap:123 ulpfec/90000" CRLF "a=recvonly" CRLF "a=rtcp-fb:120 nack" CRLF "a=rtcp-fb:120 nack pli" CRLF "a=rtcp-fb:120 ccm fir" CRLF "a=rtcp-fb:121 nack" CRLF "a=rtcp-fb:121 nack pli" CRLF "a=rtcp-fb:121 ccm fir" CRLF "a=setup:active" CRLF "a=rtcp-mux" CRLF "a=msid:streama tracka" CRLF "a=msid:streamb trackb" CRLF "a=candidate:0 1 UDP 2130379007 10.0.0.36 59530 typ host" CRLF "a=candidate:0 2 UDP 2130379006 10.0.0.36 64378 typ host" CRLF "a=candidate:2 2 UDP 1694236670 24.6.134.204 64378 typ srflx raddr " "10.0.0.36 rport 64378" CRLF "a=candidate:6 2 UDP 16515070 162.222.183.171 64941 typ relay raddr " "162.222.183.171 rport 64941" CRLF "a=candidate:6 1 UDP 16515071 162.222.183.171 64800 typ relay raddr " "162.222.183.171 rport 64800" CRLF "a=candidate:2 1 UDP 1694236671 24.6.134.204 59530 typ srflx raddr " "10.0.0.36 rport 59530" CRLF "a=candidate:3 1 UDP 100401151 162.222.183.171 62935 typ relay raddr " "162.222.183.171 rport 62935" CRLF "a=candidate:3 2 UDP 100401150 162.222.183.171 61026 typ relay raddr " "162.222.183.171 rport 61026" CRLF "a=rtcp:61026" CRLF "a=end-of-candidates" CRLF "a=ssrc:1111 foo" CRLF "a=ssrc:1111 foo:bar" CRLF "a=ssrc:1111 msid:1d0cdb4e-5934-4f0f-9f88-40392cb60d31 " "315b086a-5cb6-4221-89de-caf0b038c79d" CRLF "a=imageattr:120 send * recv *" CRLF "a=imageattr:121 send [x=640,y=480] recv [x=640,y=480]" CRLF "a=rid:bar recv pt=120;max-width=800;max-height=600" CRLF "a=rid:bar123 recv max-width=1920;max-height=1080" CRLF "a=simulcast:recv bar;bar123" CRLF "m=audio 9 RTP/SAVPF 0" CRLF "a=mid:third" CRLF "a=rtpmap:0 PCMU/8000" CRLF "a=ice-options:foo bar" CRLF "a=msid:noappdata" CRLF "a=bundle-only" CRLF; TEST_P(NewSdpTest, BasicAudioVideoSdpParse) { ParseSdp(kBasicAudioVideoOffer); } TEST_P(NewSdpTest, CheckRemoveFmtp) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; SdpAttributeList& audioAttrList = Sdp()->GetMediaSection(0).GetAttributeList(); ASSERT_TRUE(audioAttrList.HasAttribute(SdpAttribute::kFmtpAttribute)); ASSERT_EQ(2U, audioAttrList.GetFmtp().mFmtps.size()); ASSERT_TRUE(Sdp()->GetMediaSection(0).FindFmtp("109")); ASSERT_TRUE(Sdp()->GetMediaSection(0).FindFmtp("101")); Sdp()->GetMediaSection(0).RemoveFmtp("101"); ASSERT_TRUE(audioAttrList.HasAttribute(SdpAttribute::kFmtpAttribute)); ASSERT_EQ(1U, audioAttrList.GetFmtp().mFmtps.size()); ASSERT_TRUE(Sdp()->GetMediaSection(0).FindFmtp("109")); ASSERT_FALSE(Sdp()->GetMediaSection(0).FindFmtp("101")); Sdp()->GetMediaSection(0).RemoveFmtp("109"); ASSERT_TRUE(audioAttrList.HasAttribute(SdpAttribute::kFmtpAttribute)); ASSERT_EQ(0U, audioAttrList.GetFmtp().mFmtps.size()); ASSERT_FALSE(Sdp()->GetMediaSection(0).FindFmtp("109")); ASSERT_FALSE(Sdp()->GetMediaSection(0).FindFmtp("101")); // make sure we haven't disturbed the video fmtps SdpAttributeList& videoAttrList = Sdp()->GetMediaSection(1).GetAttributeList(); ASSERT_TRUE(videoAttrList.HasAttribute(SdpAttribute::kFmtpAttribute)); ASSERT_EQ(2U, videoAttrList.GetFmtp().mFmtps.size()); ASSERT_TRUE(Sdp()->GetMediaSection(1).FindFmtp("120")); ASSERT_TRUE(Sdp()->GetMediaSection(1).FindFmtp("121")); } TEST_P(NewSdpTest, CheckIceUfrag) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_TRUE( Sdp()->GetAttributeList().HasAttribute(SdpAttribute::kIceUfragAttribute)); auto ice_ufrag = Sdp()->GetAttributeList().GetIceUfrag(); ASSERT_EQ("4a799b2e", ice_ufrag) << "Wrong ice-ufrag value"; ice_ufrag = Sdp()->GetMediaSection(0).GetAttributeList().GetIceUfrag(); ASSERT_EQ("00000000", ice_ufrag) << "ice-ufrag isn't overridden"; ice_ufrag = Sdp()->GetMediaSection(1).GetAttributeList().GetIceUfrag(); ASSERT_EQ("4a799b2e", ice_ufrag) << "ice-ufrag isn't carried to m-section"; } TEST_P(NewSdpTest, CheckIcePwd) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_TRUE( Sdp()->GetAttributeList().HasAttribute(SdpAttribute::kIcePwdAttribute)); auto ice_pwd = Sdp()->GetAttributeList().GetIcePwd(); ASSERT_EQ("e4cc12a910f106a0a744719425510e17", ice_pwd) << "Wrong ice-pwd value"; ice_pwd = Sdp()->GetMediaSection(0).GetAttributeList().GetIcePwd(); ASSERT_EQ("0000000000000000000000000000000", ice_pwd) << "ice-pwd isn't overridden"; ice_pwd = Sdp()->GetMediaSection(1).GetAttributeList().GetIcePwd(); ASSERT_EQ("e4cc12a910f106a0a744719425510e17", ice_pwd) << "ice-pwd isn't carried to m-section"; } TEST_P(NewSdpTest, CheckIceOptions) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_TRUE(Sdp()->GetAttributeList().HasAttribute( SdpAttribute::kIceOptionsAttribute)); auto ice_options = Sdp()->GetAttributeList().GetIceOptions(); ASSERT_EQ(2U, ice_options.mValues.size()) << "Wrong ice-options size"; ASSERT_EQ("trickle", ice_options.mValues[0]) << "Wrong ice-options value"; ASSERT_EQ("foo", ice_options.mValues[1]) << "Wrong ice-options value"; ASSERT_TRUE(Sdp()->GetMediaSection(2).GetAttributeList().HasAttribute( SdpAttribute::kIceOptionsAttribute)); auto ice_options_media_level = Sdp()->GetMediaSection(2).GetAttributeList().GetIceOptions(); ASSERT_EQ(2U, ice_options_media_level.mValues.size()) << "Wrong ice-options size"; ASSERT_EQ("foo", ice_options_media_level.mValues[0]) << "Wrong ice-options value"; ASSERT_EQ("bar", ice_options_media_level.mValues[1]) << "Wrong ice-options value"; } TEST_P(NewSdpTest, CheckFingerprint) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_TRUE(Sdp()->GetAttributeList().HasAttribute( SdpAttribute::kFingerprintAttribute)); auto fingerprints = Sdp()->GetAttributeList().GetFingerprint(); ASSERT_EQ(1U, fingerprints.mFingerprints.size()); ASSERT_EQ(SdpFingerprintAttributeList::kSha256, fingerprints.mFingerprints[0].hashFunc) << "Wrong hash function"; ASSERT_EQ( "DF:2E:AC:8A:FD:0A:8E:99:BF:5D:E8:3C:E7:FA:FB:08:" "3B:3C:54:1D:D7:D4:05:77:A0:72:9B:14:08:6D:0F:4C", SdpFingerprintAttributeList::FormatFingerprint( fingerprints.mFingerprints[0].fingerprint)) << "Wrong fingerprint"; ASSERT_EQ(0xdfU, fingerprints.mFingerprints[0].fingerprint[0]) << "first fingerprint element is iffy"; ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()); // Fallback to session level ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kFingerprintAttribute)); fingerprints = Sdp()->GetMediaSection(0).GetAttributeList().GetFingerprint(); ASSERT_EQ(1U, fingerprints.mFingerprints.size()); ASSERT_EQ(SdpFingerprintAttributeList::kSha256, fingerprints.mFingerprints[0].hashFunc) << "Wrong hash function"; ASSERT_EQ( "DF:2E:AC:8A:FD:0A:8E:99:BF:5D:E8:3C:E7:FA:FB:08:" "3B:3C:54:1D:D7:D4:05:77:A0:72:9B:14:08:6D:0F:4C", SdpFingerprintAttributeList::FormatFingerprint( fingerprints.mFingerprints[0].fingerprint)) << "Wrong fingerprint"; ASSERT_EQ(0xdfU, fingerprints.mFingerprints[0].fingerprint[0]) << "first fingerprint element is iffy"; // Media level ASSERT_TRUE(Sdp()->GetMediaSection(1).GetAttributeList().HasAttribute( SdpAttribute::kFingerprintAttribute)); fingerprints = Sdp()->GetMediaSection(1).GetAttributeList().GetFingerprint(); ASSERT_EQ(1U, fingerprints.mFingerprints.size()); ASSERT_EQ(SdpFingerprintAttributeList::kSha1, fingerprints.mFingerprints[0].hashFunc) << "Wrong hash function"; ASSERT_EQ( "DF:FA:FB:08:3B:3C:54:1D:D7:D4:05:77:A0:72:9B:14:" "08:6D:0F:4C", SdpFingerprintAttributeList::FormatFingerprint( fingerprints.mFingerprints[0].fingerprint)) << "Wrong fingerprint"; ASSERT_EQ(0xdfU, fingerprints.mFingerprints[0].fingerprint[0]) << "first fingerprint element is iffy"; } TEST_P(NewSdpTest, CheckIdentity) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_TRUE( Sdp()->GetAttributeList().HasAttribute(SdpAttribute::kIdentityAttribute)); auto identity = Sdp()->GetAttributeList().GetIdentity(); ASSERT_EQ(LONG_IDENTITY, identity) << "Wrong identity assertion"; } TEST_P(NewSdpTest, CheckDtlsMessage) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_TRUE(Sdp()->GetAttributeList().HasAttribute( SdpAttribute::kDtlsMessageAttribute)); auto dtls_message = Sdp()->GetAttributeList().GetDtlsMessage(); ASSERT_EQ(SdpDtlsMessageAttribute::kClient, dtls_message.mRole) << "Wrong dtls-message role"; ASSERT_EQ(BASE64_DTLS_HELLO, dtls_message.mValue) << "Wrong dtls-message value"; } TEST_P(NewSdpTest, CheckNumberOfMediaSections) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; } TEST_P(NewSdpTest, CheckMlines) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_EQ(SdpMediaSection::kAudio, Sdp()->GetMediaSection(0).GetMediaType()) << "Wrong type for first media section"; ASSERT_EQ(SdpMediaSection::kRtpSavpf, Sdp()->GetMediaSection(0).GetProtocol()) << "Wrong protocol for audio"; auto audio_formats = Sdp()->GetMediaSection(0).GetFormats(); ASSERT_EQ(5U, audio_formats.size()) << "Wrong number of formats for audio"; ASSERT_EQ("109", audio_formats[0]); ASSERT_EQ("9", audio_formats[1]); ASSERT_EQ("0", audio_formats[2]); ASSERT_EQ("8", audio_formats[3]); ASSERT_EQ("101", audio_formats[4]); ASSERT_EQ(SdpMediaSection::kVideo, Sdp()->GetMediaSection(1).GetMediaType()) << "Wrong type for second media section"; ASSERT_EQ(SdpMediaSection::kRtpSavpf, Sdp()->GetMediaSection(1).GetProtocol()) << "Wrong protocol for video"; auto video_formats = Sdp()->GetMediaSection(1).GetFormats(); ASSERT_EQ(4U, video_formats.size()) << "Wrong number of formats for video"; ASSERT_EQ("120", video_formats[0]); ASSERT_EQ("121", video_formats[1]); ASSERT_EQ("122", video_formats[2]); ASSERT_EQ("123", video_formats[3]); ASSERT_EQ(SdpMediaSection::kAudio, Sdp()->GetMediaSection(2).GetMediaType()) << "Wrong type for third media section"; } TEST_P(NewSdpTest, CheckSetup) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kSetupAttribute)); ASSERT_EQ(SdpSetupAttribute::kActpass, Sdp()->GetMediaSection(0).GetAttributeList().GetSetup().mRole); ASSERT_TRUE(Sdp()->GetMediaSection(1).GetAttributeList().HasAttribute( SdpAttribute::kSetupAttribute)); ASSERT_EQ(SdpSetupAttribute::kActive, Sdp()->GetMediaSection(1).GetAttributeList().GetSetup().mRole); ASSERT_FALSE(Sdp()->GetMediaSection(2).GetAttributeList().HasAttribute( SdpAttribute::kSetupAttribute)); } TEST_P(NewSdpTest, CheckSsrc) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kSsrcAttribute)); auto ssrcs = Sdp()->GetMediaSection(0).GetAttributeList().GetSsrc().mSsrcs; ASSERT_EQ(1U, ssrcs.size()); ASSERT_EQ(5150U, ssrcs[0].ssrc); ASSERT_EQ("", ssrcs[0].attribute); ASSERT_TRUE(Sdp()->GetMediaSection(1).GetAttributeList().HasAttribute( SdpAttribute::kSsrcAttribute)); ssrcs = Sdp()->GetMediaSection(1).GetAttributeList().GetSsrc().mSsrcs; ASSERT_EQ(3U, ssrcs.size()); ASSERT_EQ(1111U, ssrcs[0].ssrc); ASSERT_EQ("foo", ssrcs[0].attribute); ASSERT_EQ(1111U, ssrcs[1].ssrc); ASSERT_EQ("foo:bar", ssrcs[1].attribute); ASSERT_EQ(1111U, ssrcs[2].ssrc); ASSERT_EQ( "msid:1d0cdb4e-5934-4f0f-9f88-40392cb60d31 " "315b086a-5cb6-4221-89de-caf0b038c79d", ssrcs[2].attribute); } TEST_P(NewSdpTest, CheckRtpmap) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; const SdpMediaSection& audiosec = Sdp()->GetMediaSection(0); const SdpRtpmapAttributeList& rtpmap = audiosec.GetAttributeList().GetRtpmap(); ASSERT_EQ(5U, rtpmap.mRtpmaps.size()) << "Wrong number of rtpmap attributes for audio"; // Need to know name of type CheckRtpmap("109", SdpRtpmapAttributeList::kOpus, "opus", 48000, 2, audiosec.GetFormats()[0], rtpmap); CheckRtpmap("9", SdpRtpmapAttributeList::kG722, "G722", 8000, 1, audiosec.GetFormats()[1], rtpmap); CheckRtpmap("0", SdpRtpmapAttributeList::kPCMU, "PCMU", 8000, 1, audiosec.GetFormats()[2], rtpmap); CheckRtpmap("8", SdpRtpmapAttributeList::kPCMA, "PCMA", 8000, 1, audiosec.GetFormats()[3], rtpmap); CheckRtpmap("101", SdpRtpmapAttributeList::kTelephoneEvent, "telephone-event", 8000, 1, audiosec.GetFormats()[4], rtpmap); const SdpMediaSection& videosec = Sdp()->GetMediaSection(1); const SdpRtpmapAttributeList videoRtpmap = videosec.GetAttributeList().GetRtpmap(); ASSERT_EQ(4U, videoRtpmap.mRtpmaps.size()) << "Wrong number of rtpmap attributes for video"; CheckRtpmap("120", SdpRtpmapAttributeList::kVP8, "VP8", 90000, 0, videosec.GetFormats()[0], videoRtpmap); CheckRtpmap("121", SdpRtpmapAttributeList::kVP9, "VP9", 90000, 0, videosec.GetFormats()[1], videoRtpmap); CheckRtpmap("122", SdpRtpmapAttributeList::kRed, "red", 90000, 0, videosec.GetFormats()[2], videoRtpmap); CheckRtpmap("123", SdpRtpmapAttributeList::kUlpfec, "ulpfec", 90000, 0, videosec.GetFormats()[3], videoRtpmap); } static const std::string kAudioWithTelephoneEvent = "v=0" CRLF "o=- 4294967296 2 IN IP4 127.0.0.1" CRLF "s=SIP Call" CRLF "c=IN IP4 198.51.100.7" CRLF "t=0 0" CRLF "m=audio 9 RTP/SAVPF 109 9 0 8 101" CRLF "c=IN IP4 0.0.0.0" CRLF "a=mid:first" CRLF "a=rtpmap:109 opus/48000/2" CRLF "a=fmtp:109 maxplaybackrate=32000;stereo=1" CRLF "a=ptime:20" CRLF "a=maxptime:20" CRLF "a=rtpmap:9 G722/8000" CRLF "a=rtpmap:0 PCMU/8000" CRLF "a=rtpmap:8 PCMA/8000" CRLF "a=rtpmap:101 telephone-event/8000" CRLF; TEST_P(NewSdpTest, CheckTelephoneEventNoFmtp) { ParseSdp(kAudioWithTelephoneEvent); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kFmtpAttribute)); auto audio_format_params = Sdp()->GetMediaSection(0).GetAttributeList().GetFmtp().mFmtps; ASSERT_EQ(1U, audio_format_params.size()); // make sure we don't get a fmtp for codec 101 for (size_t i = 0; i < audio_format_params.size(); ++i) { ASSERT_NE("101", audio_format_params[i].format); } } TEST_P(NewSdpTest, CheckTelephoneEventWithDefaultEvents) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 0-15" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; CheckDtmfFmtp("0-15"); } TEST_P(NewSdpTest, CheckTelephoneEventWithBadCharacter) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 0-5." CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; CheckDtmfFmtp("0-15"); } TEST_P(NewSdpTest, CheckTelephoneEventIncludingCommas) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 0-15,66,67" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; CheckDtmfFmtp("0-15,66,67"); } TEST_P(NewSdpTest, CheckTelephoneEventComplexEvents) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 0,1,2-4,5-15,66,67" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; CheckDtmfFmtp("0,1,2-4,5-15,66,67"); } TEST_P(NewSdpTest, CheckTelephoneEventNoHyphen) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 5,6,7" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; CheckDtmfFmtp("5,6,7"); } TEST_P(NewSdpTest, CheckTelephoneEventOnlyZero) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 0" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; CheckDtmfFmtp("0"); } TEST_P(NewSdpTest, CheckTelephoneEventOnlyOne) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 1" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; CheckDtmfFmtp("1"); } TEST_P(NewSdpTest, CheckTelephoneEventBadThreeDigit) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 123" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; // check for the default dtmf tones CheckDtmfFmtp("0-15"); } TEST_P(NewSdpTest, CheckTelephoneEventBadThreeDigitWithHyphen) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 0-123" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; // check for the default dtmf tones CheckDtmfFmtp("0-15"); } TEST_P(NewSdpTest, CheckTelephoneEventBadLeadingHyphen) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 -12" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; // check for the default dtmf tones CheckDtmfFmtp("0-15"); } TEST_P(NewSdpTest, CheckTelephoneEventBadTrailingHyphen) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 12-" CRLF, false); } TEST_P(NewSdpTest, CheckTelephoneEventBadTrailingHyphenInMiddle) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 1,12-,4" CRLF, false); } TEST_P(NewSdpTest, CheckTelephoneEventBadLeadingComma) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 ,2,3" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; // check for the default dtmf tones CheckDtmfFmtp("0-15"); } TEST_P(NewSdpTest, CheckTelephoneEventBadMultipleLeadingComma) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 ,,,2,3" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; // check for the default dtmf tones CheckDtmfFmtp("0-15"); } TEST_P(NewSdpTest, CheckTelephoneEventBadConsecutiveCommas) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 1,,,,,,,,3" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; // check for the default dtmf tones CheckDtmfFmtp("0-15"); } TEST_P(NewSdpTest, CheckTelephoneEventBadTrailingComma) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 1,2,3," CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; // check for the default dtmf tones CheckDtmfFmtp("0-15"); } TEST_P(NewSdpTest, CheckTelephoneEventBadTwoHyphens) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 1-2-3" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; // check for the default dtmf tones CheckDtmfFmtp("0-15"); } TEST_P(NewSdpTest, CheckTelephoneEventBadSixDigit) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 112233" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; // check for the default dtmf tones CheckDtmfFmtp("0-15"); } TEST_P(NewSdpTest, CheckTelephoneEventBadRangeReversed) { ParseSdp(kAudioWithTelephoneEvent + "a=fmtp:101 33-2" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; // check for the default dtmf tones CheckDtmfFmtp("0-15"); } static const std::string kVideoWithRedAndUlpfecSdp = "v=0" CRLF "o=- 4294967296 2 IN IP4 127.0.0.1" CRLF "s=SIP Call" CRLF "c=IN IP4 198.51.100.7" CRLF "t=0 0" CRLF "m=video 9 RTP/SAVPF 97 120 121 122 123" CRLF "c=IN IP6 ::1" CRLF "a=fingerprint:sha-1 " "DF:FA:FB:08:3B:3C:54:1D:D7:D4:05:77:A0:72:9B:14:08:6D:0F:4C" CRLF "a=rtpmap:97 H264/90000" CRLF "a=fmtp:97 profile-level-id=42a01e" CRLF "a=rtpmap:120 VP8/90000" CRLF "a=fmtp:120 max-fs=3600;max-fr=30" CRLF "a=rtpmap:121 VP9/90000" CRLF "a=fmtp:121 max-fs=3600;max-fr=30" CRLF "a=rtpmap:122 red/90000" CRLF "a=rtpmap:123 ulpfec/90000" CRLF; TEST_P(NewSdpTest, CheckRedNoFmtp) { ParseSdp(kVideoWithRedAndUlpfecSdp); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kFmtpAttribute)); auto video_format_params = Sdp()->GetMediaSection(0).GetAttributeList().GetFmtp().mFmtps; ASSERT_EQ(3U, video_format_params.size()); // make sure we don't get a fmtp for codec 122 for (size_t i = 0; i < video_format_params.size(); ++i) { ASSERT_NE("122", video_format_params[i].format); } } TEST_P(NewSdpTest, CheckRedEmptyFmtp) { // if serializing and re-parsing, we expect errors if (::testing::get<0>(GetParam())) { ParseSdp(kVideoWithRedAndUlpfecSdp + "a=fmtp:122" CRLF, false); } else { ParseSdp(kVideoWithRedAndUlpfecSdp + "a=fmtp:122" CRLF, false); if (ResultsAreFromSipcc()) { ASSERT_NE(0U, ParseErrorCount()); } else { // This is the branch for the rust parser to check for warnings, as the // rust parser will give a warning at that point instead of an error. ASSERT_NE(0U, ParseWarningCount()); } } ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kFmtpAttribute)); auto video_format_params = Sdp()->GetMediaSection(0).GetAttributeList().GetFmtp().mFmtps; ASSERT_EQ(3U, video_format_params.size()); // make sure we don't get a fmtp for codec 122 for (size_t i = 0; i < video_format_params.size(); ++i) { ASSERT_NE("122", video_format_params[i].format); } } TEST_P(NewSdpTest, CheckRedFmtpWith2Codecs) { ParseSdp(kVideoWithRedAndUlpfecSdp + "a=fmtp:122 120/121" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kFmtpAttribute)); auto video_format_params = Sdp()->GetMediaSection(0).GetAttributeList().GetFmtp().mFmtps; ASSERT_EQ(4U, video_format_params.size()); ASSERT_EQ("122", video_format_params[3].format); ASSERT_TRUE(!!video_format_params[3].parameters); ASSERT_EQ(SdpRtpmapAttributeList::kRed, video_format_params[3].parameters->codec_type); const SdpFmtpAttributeList::RedParameters* red_parameters( static_cast( video_format_params[3].parameters.get())); ASSERT_EQ(2U, red_parameters->encodings.size()); ASSERT_EQ(120U, red_parameters->encodings[0]); ASSERT_EQ(121U, red_parameters->encodings[1]); } TEST_P(NewSdpTest, CheckRedFmtpWith3Codecs) { ParseSdp(kVideoWithRedAndUlpfecSdp + "a=fmtp:122 120/121/123" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kFmtpAttribute)); auto video_format_params = Sdp()->GetMediaSection(0).GetAttributeList().GetFmtp().mFmtps; ASSERT_EQ(4U, video_format_params.size()); ASSERT_EQ("122", video_format_params[3].format); ASSERT_TRUE(!!video_format_params[3].parameters); ASSERT_EQ(SdpRtpmapAttributeList::kRed, video_format_params[3].parameters->codec_type); const SdpFmtpAttributeList::RedParameters* red_parameters( static_cast( video_format_params[3].parameters.get())); ASSERT_EQ(3U, red_parameters->encodings.size()); ASSERT_EQ(120U, red_parameters->encodings[0]); ASSERT_EQ(121U, red_parameters->encodings[1]); ASSERT_EQ(123U, red_parameters->encodings[2]); } const std::string kH264AudioVideoOffer = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=ice-ufrag:4a799b2e" CRLF "a=ice-pwd:e4cc12a910f106a0a744719425510e17" CRLF "a=ice-lite" CRLF "a=msid-semantic:WMS stream streama" CRLF "a=fingerprint:sha-256 " "DF:2E:AC:8A:FD:0A:8E:99:BF:5D:E8:3C:E7:FA:FB:08:3B:3C:54:1D:D7:D4:05:77:" "A0:72:9B:14:08:6D:0F:4C" CRLF "a=group:BUNDLE first second" CRLF "a=group:BUNDLE third" CRLF "a=group:LS first third" CRLF "m=audio 9 RTP/SAVPF 109 9 0 8 101" CRLF "c=IN IP4 0.0.0.0" CRLF "a=mid:first" CRLF "a=rtpmap:109 opus/48000/2" CRLF "a=ptime:20" CRLF "a=maxptime:20" CRLF "a=rtpmap:9 G722/8000" CRLF "a=rtpmap:0 PCMU/8000" CRLF "a=rtpmap:8 PCMA/8000" CRLF "a=rtpmap:101 telephone-event/8000" CRLF "a=fmtp:109 maxplaybackrate=32000;stereo=1;useinbandfec=1" CRLF "a=fmtp:101 0-15,66,32-34,67" CRLF "a=ice-ufrag:00000000" CRLF "a=ice-pwd:0000000000000000000000000000000" CRLF "a=sendonly" CRLF "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level" CRLF "a=setup:actpass" CRLF "a=rtcp-mux" CRLF "a=msid:stream track" CRLF "a=candidate:0 1 UDP 2130379007 10.0.0.36 62453 typ host" CRLF "a=candidate:2 1 UDP 1694236671 24.6.134.204 62453 typ srflx raddr " "10.0.0.36 rport 62453" CRLF "a=candidate:3 1 UDP 100401151 162.222.183.171 49761 typ relay raddr " "162.222.183.171 rport 49761" CRLF "a=candidate:6 1 UDP 16515071 162.222.183.171 51858 typ relay raddr " "162.222.183.171 rport 51858" CRLF "a=candidate:3 2 UDP 100401150 162.222.183.171 62454 typ relay raddr " "162.222.183.171 rport 62454" CRLF "a=candidate:2 2 UDP 1694236670 24.6.134.204 55428 typ srflx raddr " "10.0.0.36 rport 55428" CRLF "a=candidate:6 2 UDP 16515070 162.222.183.171 50340 typ relay raddr " "162.222.183.171 rport 50340" CRLF "a=candidate:0 2 UDP 2130379006 10.0.0.36 55428 typ host" CRLF "m=video 9 RTP/SAVPF 97 98 120" CRLF "c=IN IP6 ::1" CRLF "a=mid:second" CRLF "a=rtpmap:97 H264/90000" CRLF "a=fmtp:97 profile-level-id=42a01e" CRLF "a=rtpmap:98 H264/90000" CRLF "a=fmtp:98 " "PROFILE=0;LEVEL=0;parameter-add=1;profile-level-id=42a00d;packetization-" "mode=1;level-asymmetry-allowed=1;max-mbps=42000;max-fs=1400;max-cpb=1000;" "max-dpb=1000;max-br=180000;usedtx=0;stereo=0;useinbandfec=0;cbr=0" CRLF "a=rtpmap:120 VP8/90000" CRLF "a=fmtp:120 max-fs=3601;max-fr=31" CRLF "a=recvonly" CRLF "a=setup:active" CRLF "a=rtcp-mux" CRLF "a=msid:streama tracka" CRLF "a=msid:streamb trackb" CRLF "a=candidate:0 1 UDP 2130379007 10.0.0.36 59530 typ host" CRLF "a=candidate:0 2 UDP 2130379006 10.0.0.36 64378 typ host" CRLF "a=candidate:2 2 UDP 1694236670 24.6.134.204 64378 typ srflx raddr " "10.0.0.36 rport 64378" CRLF "a=candidate:6 2 UDP 16515070 162.222.183.171 64941 typ relay raddr " "162.222.183.171 rport 64941" CRLF "a=candidate:6 1 UDP 16515071 162.222.183.171 64800 typ relay raddr " "162.222.183.171 rport 64800" CRLF "a=candidate:2 1 UDP 1694236671 24.6.134.204 59530 typ srflx raddr " "10.0.0.36 rport 59530" CRLF "a=candidate:3 1 UDP 100401151 162.222.183.171 62935 typ relay raddr " "162.222.183.171 rport 62935" CRLF "a=candidate:3 2 UDP 100401150 162.222.183.171 61026 typ relay raddr " "162.222.183.171 rport 61026" CRLF "m=audio 9 RTP/SAVPF 0" CRLF "a=mid:third" CRLF "a=rtpmap:0 PCMU/8000" CRLF "a=msid:noappdata" CRLF; TEST_P(NewSdpTest, CheckFormatParameters) { ParseSdp(kH264AudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kFmtpAttribute)); auto audio_format_params = Sdp()->GetMediaSection(0).GetAttributeList().GetFmtp().mFmtps; ASSERT_EQ(2U, audio_format_params.size()); ASSERT_EQ("109", audio_format_params[0].format); ASSERT_TRUE(!!audio_format_params[0].parameters); const SdpFmtpAttributeList::OpusParameters* opus_parameters = static_cast( audio_format_params[0].parameters.get()); ASSERT_EQ(32000U, opus_parameters->maxplaybackrate); ASSERT_EQ(1U, opus_parameters->stereo); ASSERT_EQ(1U, opus_parameters->useInBandFec); ASSERT_EQ(0U, opus_parameters->maxAverageBitrate); ASSERT_EQ(0U, opus_parameters->useDTX); ASSERT_EQ(0U, opus_parameters->useCbr); ASSERT_EQ(0U, opus_parameters->frameSizeMs); ASSERT_EQ(0U, opus_parameters->minFrameSizeMs); ASSERT_EQ(0U, opus_parameters->maxFrameSizeMs); ASSERT_EQ("101", audio_format_params[1].format); ASSERT_TRUE(!!audio_format_params[1].parameters); const SdpFmtpAttributeList::TelephoneEventParameters* te_parameters = static_cast( audio_format_params[1].parameters.get()); ASSERT_NE(0U, te_parameters->dtmfTones.size()); ASSERT_EQ("0-15,66,32-34,67", te_parameters->dtmfTones); ASSERT_TRUE(Sdp()->GetMediaSection(1).GetAttributeList().HasAttribute( SdpAttribute::kFmtpAttribute)); auto video_format_params = Sdp()->GetMediaSection(1).GetAttributeList().GetFmtp().mFmtps; ASSERT_EQ(3U, video_format_params.size()); ASSERT_EQ("97", video_format_params[0].format); ASSERT_TRUE(!!video_format_params[0].parameters); ASSERT_EQ(SdpRtpmapAttributeList::kH264, video_format_params[0].parameters->codec_type); const SdpFmtpAttributeList::H264Parameters* h264_parameters( static_cast( video_format_params[0].parameters.get())); ASSERT_EQ((uint32_t)0x42a01e, h264_parameters->profile_level_id); ASSERT_EQ(0U, h264_parameters->packetization_mode); ASSERT_FALSE(static_cast(h264_parameters->level_asymmetry_allowed)); ASSERT_EQ(0U, h264_parameters->max_mbps); ASSERT_EQ(0U, h264_parameters->max_fs); ASSERT_EQ(0U, h264_parameters->max_cpb); ASSERT_EQ(0U, h264_parameters->max_dpb); ASSERT_EQ(0U, h264_parameters->max_br); ASSERT_EQ("98", video_format_params[1].format); ASSERT_TRUE(!!video_format_params[1].parameters); ASSERT_EQ(SdpRtpmapAttributeList::kH264, video_format_params[1].parameters->codec_type); h264_parameters = static_cast( video_format_params[1].parameters.get()); ASSERT_EQ((uint32_t)0x42a00d, h264_parameters->profile_level_id); ASSERT_EQ(1U, h264_parameters->packetization_mode); ASSERT_TRUE(static_cast(h264_parameters->level_asymmetry_allowed)); ASSERT_EQ(42000U, h264_parameters->max_mbps); ASSERT_EQ(1400U, h264_parameters->max_fs); ASSERT_EQ(1000U, h264_parameters->max_cpb); ASSERT_EQ(1000U, h264_parameters->max_dpb); ASSERT_EQ(180000U, h264_parameters->max_br); ASSERT_EQ("120", video_format_params[2].format); ASSERT_TRUE(!!video_format_params[2].parameters); ASSERT_EQ(SdpRtpmapAttributeList::kVP8, video_format_params[2].parameters->codec_type); const SdpFmtpAttributeList::VP8Parameters* vp8_parameters = static_cast( video_format_params[2].parameters.get()); ASSERT_EQ(3601U, vp8_parameters->max_fs); ASSERT_EQ(31U, vp8_parameters->max_fr); ASSERT_FALSE(Sdp()->GetMediaSection(2).GetAttributeList().HasAttribute( SdpAttribute::kFmtpAttribute)); } TEST_P(NewSdpTest, CheckPtime) { ParseSdp(kBasicAudioVideoOffer); ASSERT_EQ(20U, Sdp()->GetMediaSection(0).GetAttributeList().GetPtime()); ASSERT_FALSE(Sdp()->GetMediaSection(1).GetAttributeList().HasAttribute( SdpAttribute::kPtimeAttribute)); } TEST_P(NewSdpTest, CheckFlags) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE( Sdp()->GetAttributeList().HasAttribute(SdpAttribute::kIceLiteAttribute)); ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kIceLiteAttribute)); ASSERT_FALSE(Sdp()->GetMediaSection(1).GetAttributeList().HasAttribute( SdpAttribute::kIceLiteAttribute)); ASSERT_FALSE(Sdp()->GetMediaSection(2).GetAttributeList().HasAttribute( SdpAttribute::kIceLiteAttribute)); ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kRtcpMuxAttribute)); ASSERT_FALSE(Sdp()->GetMediaSection(2).GetAttributeList().HasAttribute( SdpAttribute::kRtcpMuxAttribute)); ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kBundleOnlyAttribute)); ASSERT_TRUE(Sdp()->GetMediaSection(2).GetAttributeList().HasAttribute( SdpAttribute::kBundleOnlyAttribute)); ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kEndOfCandidatesAttribute)); ASSERT_TRUE(Sdp()->GetMediaSection(1).GetAttributeList().HasAttribute( SdpAttribute::kEndOfCandidatesAttribute)); ASSERT_FALSE(Sdp()->GetMediaSection(2).GetAttributeList().HasAttribute( SdpAttribute::kEndOfCandidatesAttribute)); } TEST_P(NewSdpTest, CheckConnectionLines) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; const SdpConnection& conn1 = Sdp()->GetMediaSection(0).GetConnection(); ASSERT_EQ(sdp::kIPv4, conn1.GetAddrType()); ASSERT_EQ("0.0.0.0", conn1.GetAddress()); ASSERT_EQ(0U, conn1.GetTtl()); ASSERT_EQ(0U, conn1.GetCount()); const SdpConnection& conn2 = Sdp()->GetMediaSection(1).GetConnection(); ASSERT_EQ(sdp::kIPv6, conn2.GetAddrType()); ASSERT_EQ("::1", conn2.GetAddress()); ASSERT_EQ(0U, conn2.GetTtl()); ASSERT_EQ(0U, conn2.GetCount()); // tests that we can fall through to session level as appropriate const SdpConnection& conn3 = Sdp()->GetMediaSection(2).GetConnection(); ASSERT_EQ(sdp::kIPv4, conn3.GetAddrType()); ASSERT_EQ("224.0.0.1", conn3.GetAddress()); ASSERT_EQ(100U, conn3.GetTtl()); ASSERT_EQ(12U, conn3.GetCount()); } TEST_P(NewSdpTest, CheckDirections) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(SdpDirectionAttribute::kSendonly, Sdp()->GetMediaSection(0).GetAttributeList().GetDirection()); ASSERT_EQ(SdpDirectionAttribute::kRecvonly, Sdp()->GetMediaSection(1).GetAttributeList().GetDirection()); ASSERT_EQ(SdpDirectionAttribute::kSendrecv, Sdp()->GetMediaSection(2).GetAttributeList().GetDirection()); } TEST_P(NewSdpTest, CheckCandidates) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kCandidateAttribute)); auto audio_candidates = Sdp()->GetMediaSection(0).GetAttributeList().GetCandidate(); ASSERT_EQ(8U, audio_candidates.size()); ASSERT_EQ("0 1 UDP 2130379007 10.0.0.36 62453 typ host", audio_candidates[0]); ASSERT_EQ( "2 1 UDP 1694236671 24.6.134.204 62453 typ srflx raddr 10.0.0.36 rport " "62453", audio_candidates[1]); ASSERT_EQ( "3 1 UDP 100401151 162.222.183.171 49761 typ relay raddr 162.222.183.171 " "rport 49761", audio_candidates[2]); ASSERT_EQ( "6 1 UDP 16515071 162.222.183.171 51858 typ relay raddr 162.222.183.171 " "rport 51858", audio_candidates[3]); ASSERT_EQ( "3 2 UDP 100401150 162.222.183.171 62454 typ relay raddr 162.222.183.171 " "rport 62454", audio_candidates[4]); ASSERT_EQ( "2 2 UDP 1694236670 24.6.134.204 55428 typ srflx raddr 10.0.0.36 rport " "55428", audio_candidates[5]); ASSERT_EQ( "6 2 UDP 16515070 162.222.183.171 50340 typ relay raddr 162.222.183.171 " "rport 50340", audio_candidates[6]); ASSERT_EQ("0 2 UDP 2130379006 10.0.0.36 55428 typ host", audio_candidates[7]); ASSERT_TRUE(Sdp()->GetMediaSection(1).GetAttributeList().HasAttribute( SdpAttribute::kCandidateAttribute)); auto video_candidates = Sdp()->GetMediaSection(1).GetAttributeList().GetCandidate(); ASSERT_EQ(8U, video_candidates.size()); ASSERT_EQ("0 1 UDP 2130379007 10.0.0.36 59530 typ host", video_candidates[0]); ASSERT_EQ("0 2 UDP 2130379006 10.0.0.36 64378 typ host", video_candidates[1]); ASSERT_EQ( "2 2 UDP 1694236670 24.6.134.204 64378 typ srflx raddr 10.0.0.36 rport " "64378", video_candidates[2]); ASSERT_EQ( "6 2 UDP 16515070 162.222.183.171 64941 typ relay raddr 162.222.183.171 " "rport 64941", video_candidates[3]); ASSERT_EQ( "6 1 UDP 16515071 162.222.183.171 64800 typ relay raddr 162.222.183.171 " "rport 64800", video_candidates[4]); ASSERT_EQ( "2 1 UDP 1694236671 24.6.134.204 59530 typ srflx raddr 10.0.0.36 rport " "59530", video_candidates[5]); ASSERT_EQ( "3 1 UDP 100401151 162.222.183.171 62935 typ relay raddr 162.222.183.171 " "rport 62935", video_candidates[6]); ASSERT_EQ( "3 2 UDP 100401150 162.222.183.171 61026 typ relay raddr 162.222.183.171 " "rport 61026", video_candidates[7]); ASSERT_FALSE(Sdp()->GetMediaSection(2).GetAttributeList().HasAttribute( SdpAttribute::kCandidateAttribute)); } TEST_P(NewSdpTest, CheckMid) { ParseSdp(kBasicAudioVideoOffer); ASSERT_EQ("first", Sdp()->GetMediaSection(0).GetAttributeList().GetMid()); ASSERT_EQ("second", Sdp()->GetMediaSection(1).GetAttributeList().GetMid()); ASSERT_EQ("third", Sdp()->GetMediaSection(2).GetAttributeList().GetMid()); } TEST_P(NewSdpTest, CheckMsid) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(Sdp()->GetAttributeList().HasAttribute( SdpAttribute::kMsidSemanticAttribute)); auto semantics = Sdp()->GetAttributeList().GetMsidSemantic().mMsidSemantics; ASSERT_EQ(2U, semantics.size()); ASSERT_EQ("WMS", semantics[0].semantic); ASSERT_EQ(2U, semantics[0].msids.size()); ASSERT_EQ("stream", semantics[0].msids[0]); ASSERT_EQ("streama", semantics[0].msids[1]); ASSERT_EQ("foo", semantics[1].semantic); ASSERT_EQ(1U, semantics[1].msids.size()); ASSERT_EQ("stream", semantics[1].msids[0]); const SdpMsidAttributeList& msids1 = Sdp()->GetMediaSection(0).GetAttributeList().GetMsid(); ASSERT_EQ(1U, msids1.mMsids.size()); ASSERT_EQ("stream", msids1.mMsids[0].identifier); ASSERT_EQ("track", msids1.mMsids[0].appdata); const SdpMsidAttributeList& msids2 = Sdp()->GetMediaSection(1).GetAttributeList().GetMsid(); ASSERT_EQ(2U, msids2.mMsids.size()); ASSERT_EQ("streama", msids2.mMsids[0].identifier); ASSERT_EQ("tracka", msids2.mMsids[0].appdata); ASSERT_EQ("streamb", msids2.mMsids[1].identifier); ASSERT_EQ("trackb", msids2.mMsids[1].appdata); const SdpMsidAttributeList& msids3 = Sdp()->GetMediaSection(2).GetAttributeList().GetMsid(); ASSERT_EQ(1U, msids3.mMsids.size()); ASSERT_EQ("noappdata", msids3.mMsids[0].identifier); ASSERT_EQ("", msids3.mMsids[0].appdata); } TEST_P(NewSdpTest, CheckManyMsidSemantics) { if (::testing::get<0>(GetParam())) { return; } std::ostringstream msid; msid << "a=msid-semantic: foo"; for (size_t i = 0; i < SDP_MAX_MEDIA_STREAMS + 1; ++i) { msid << " " << i; } msid << CRLF; std::string offer = "v=0" CRLF "o=- 4294967296 2 IN IP4 127.0.0.1" CRLF "s=SIP Call" CRLF "c=IN IP4 198.51.100.7" CRLF "t=0 0" CRLF; offer += msid.str(); offer += "m=video 56436 RTP/SAVPF 120" CRLF "b=CT:1000" CRLF "a=rtpmap:120 VP8/90000" CRLF; ParseSdp(offer); auto semantics = Sdp()->GetAttributeList().GetMsidSemantic().mMsidSemantics; ASSERT_EQ(1U, semantics.size()); // Only sipcc is limited by SDP_MAX_MEDIA_STREAMS, the Rust parser // will parse all of the msids. ASSERT_GE(semantics[0].msids.size(), static_cast(SDP_MAX_MEDIA_STREAMS)); } TEST_P(NewSdpTest, CheckRid) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_FALSE( Sdp()->GetAttributeList().HasAttribute(SdpAttribute::kRidAttribute)); ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kRidAttribute)); ASSERT_TRUE(Sdp()->GetMediaSection(1).GetAttributeList().HasAttribute( SdpAttribute::kRidAttribute)); ASSERT_FALSE(Sdp()->GetMediaSection(2).GetAttributeList().HasAttribute( SdpAttribute::kRidAttribute)); const SdpRidAttributeList& rids = Sdp()->GetMediaSection(1).GetAttributeList().GetRid(); ASSERT_EQ(2U, rids.mRids.size()); ASSERT_EQ("bar", rids.mRids[0].id); ASSERT_EQ(sdp::kRecv, rids.mRids[0].direction); ASSERT_EQ(1U, rids.mRids[0].formats.size()); ASSERT_EQ(120U, rids.mRids[0].formats[0]); ASSERT_EQ(800U, rids.mRids[0].constraints.maxWidth); ASSERT_EQ(600U, rids.mRids[0].constraints.maxHeight); ASSERT_EQ("bar123", rids.mRids[1].id); ASSERT_EQ(sdp::kRecv, rids.mRids[1].direction); ASSERT_EQ(0U, rids.mRids[1].formats.size()); ASSERT_EQ(1920U, rids.mRids[1].constraints.maxWidth); ASSERT_EQ(1080U, rids.mRids[1].constraints.maxHeight); } TEST_P(NewSdpTest, CheckMediaLevelIceUfrag) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kIceUfragAttribute, true)); ASSERT_EQ("00000000", Sdp()->GetMediaSection(0).GetAttributeList().GetIceUfrag()); ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kIceUfragAttribute, false)); ASSERT_TRUE(Sdp()->GetMediaSection(1).GetAttributeList().HasAttribute( SdpAttribute::kIceUfragAttribute, true)); ASSERT_EQ("4a799b2e", Sdp()->GetMediaSection(1).GetAttributeList().GetIceUfrag()); } TEST_P(NewSdpTest, CheckMediaLevelIcePwd) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kIcePwdAttribute)); ASSERT_EQ("0000000000000000000000000000000", Sdp()->GetMediaSection(0).GetAttributeList().GetIcePwd()); ASSERT_TRUE(Sdp()->GetMediaSection(1).GetAttributeList().HasAttribute( SdpAttribute::kIcePwdAttribute)); ASSERT_EQ("e4cc12a910f106a0a744719425510e17", Sdp()->GetMediaSection(1).GetAttributeList().GetIcePwd()); } TEST_P(NewSdpTest, CheckGroups) { ParseSdp(kBasicAudioVideoOffer); const SdpGroupAttributeList& group = Sdp()->GetAttributeList().GetGroup(); const SdpGroupAttributeList::Group& group1 = group.mGroups[0]; ASSERT_EQ(SdpGroupAttributeList::kBundle, group1.semantics); ASSERT_EQ(2U, group1.tags.size()); ASSERT_EQ("first", group1.tags[0]); ASSERT_EQ("second", group1.tags[1]); const SdpGroupAttributeList::Group& group2 = group.mGroups[1]; ASSERT_EQ(SdpGroupAttributeList::kBundle, group2.semantics); ASSERT_EQ(1U, group2.tags.size()); ASSERT_EQ("third", group2.tags[0]); const SdpGroupAttributeList::Group& group3 = group.mGroups[2]; ASSERT_EQ(SdpGroupAttributeList::kLs, group3.semantics); ASSERT_EQ(2U, group3.tags.size()); ASSERT_EQ("first", group3.tags[0]); ASSERT_EQ("third", group3.tags[1]); } TEST_P(NewSdpTest, CheckManyGroups) { std::ostringstream bundle; bundle << "a=group:BUNDLE"; for (size_t i = 0; i < SDP_MAX_MEDIA_STREAMS + 1; ++i) { bundle << " " << i; } bundle << CRLF; std::string offer = "v=0" CRLF "o=- 4294967296 2 IN IP4 127.0.0.1" CRLF "s=SIP Call" CRLF "c=IN IP4 198.51.100.7" CRLF "t=0 0" CRLF; offer += bundle.str(); offer += "m=video 56436 RTP/SAVPF 120" CRLF "b=CT:1000" CRLF "a=rtpmap:120 VP8/90000" CRLF; // Rust will reserialize what Sipcc serializes to, but otherwise they will // differ. ParseSdp(offer, true, ::testing::get<0>(GetParam()) && ::testing::get<1>(GetParam())); const SdpGroupAttributeList& group = Sdp()->GetAttributeList().GetGroup(); const SdpGroupAttributeList::Group& group1 = group.mGroups[0]; ASSERT_EQ(SdpGroupAttributeList::kBundle, group1.semantics); // Only sipcc is limited by SDP_MAX_MEDIA_STREAMS, the Rust parser // will parse all of the groups. ASSERT_GE(group1.tags.size(), static_cast(SDP_MAX_MEDIA_STREAMS)); } // SDP from a basic A/V call with data channel FFX/FFX const std::string kBasicAudioVideoDataOffer = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 27987 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "t=0 0" CRLF "a=ice-ufrag:8a39d2ae" CRLF "a=ice-pwd:601d53aba51a318351b3ecf5ee00048f" CRLF "a=fingerprint:sha-256 " "30:FF:8E:2B:AC:9D:ED:70:18:10:67:C8:AE:9E:68:F3:86:53:51:B0:AC:31:B7:BE:" "6D:CF:A4:2E:D3:6E:B4:28" CRLF "m=audio 9 RTP/SAVPF 109 9 0 8 101" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:109 opus/48000/2" CRLF "a=ptime:20" CRLF "a=rtpmap:9 G722/8000" CRLF "a=rtpmap:0 PCMU/8000" CRLF "a=rtpmap:8 PCMA/8000" CRLF "a=rtpmap:101 telephone-event/8000" CRLF "a=fmtp:101 0-15" CRLF "a=sendrecv" CRLF "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level" CRLF "a=extmap:2/sendonly some_extension" CRLF "a=extmap:3 some_other_extension some_params some more params" CRLF "a=setup:actpass" CRLF "a=rtcp-mux" CRLF "m=video 9 RTP/SAVPF 120 126 97" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:120 VP8/90000" CRLF "a=rtpmap:126 H264/90000" CRLF "a=fmtp:126 profile-level-id=42e01f;packetization-mode=1" CRLF "a=rtpmap:97 H264/90000" CRLF "a=fmtp:97 profile-level-id=42e01f" CRLF "a=sendrecv" CRLF // sipcc barfs on this, despite that it is valid syntax // Do we care about fixing? //"a=rtcp-fb:120 ack" CRLF // Should be ignored by sipcc "a=rtcp-fb:120 ack rpsi" CRLF "a=rtcp-fb:120 ack app foo" CRLF "a=rtcp-fb:120 ack foo" CRLF // Should be ignored "a=rtcp-fb:120 nack" CRLF "a=rtcp-fb:120 nack sli" CRLF "a=rtcp-fb:120 nack pli" CRLF "a=rtcp-fb:120 nack rpsi" CRLF "a=rtcp-fb:120 nack app foo" CRLF "a=rtcp-fb:120 nack foo" CRLF // Should be ignored "a=rtcp-fb:120 ccm fir" CRLF "a=rtcp-fb:120 ccm tmmbr" CRLF "a=rtcp-fb:120 ccm tstr" CRLF "a=rtcp-fb:120 ccm vbcm" CRLF "a=rtcp-fb:120 ccm foo" CRLF // Should be ignored "a=rtcp-fb:120 trr-int 10" CRLF "a=rtcp-fb:120 goog-remb" CRLF "a=rtcp-fb:120 foo" CRLF // Should be ignored "a=rtcp-fb:126 nack" CRLF "a=rtcp-fb:126 nack pli" CRLF "a=rtcp-fb:126 ccm fir" CRLF "a=rtcp-fb:97 nack" CRLF "a=rtcp-fb:97 nack pli" CRLF "a=rtcp-fb:97 ccm fir" CRLF "a=rtcp-fb:* ccm tmmbr" CRLF "a=rtcp-fb:120 transport-cc" CRLF "a=setup:actpass" CRLF "a=rtcp-mux" CRLF "m=application 9 DTLS/SCTP 5000" CRLF "c=IN IP4 0.0.0.0" CRLF "a=sctpmap:5000 webrtc-datachannel 16" CRLF "a=setup:actpass" CRLF; TEST_P(NewSdpTest, BasicAudioVideoDataSdpParse) { ParseSdp(kBasicAudioVideoDataOffer, true); ASSERT_EQ(0U, ParseErrorCount()) << "Got parse errors: " << SerializeParseErrors(); } TEST_P(NewSdpTest, CheckApplicationParameters) { ParseSdp(kBasicAudioVideoDataOffer, true); ASSERT_TRUE(!!Sdp()); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_EQ(SdpMediaSection::kAudio, Sdp()->GetMediaSection(0).GetMediaType()) << "Wrong type for first media section"; ASSERT_EQ(SdpMediaSection::kVideo, Sdp()->GetMediaSection(1).GetMediaType()) << "Wrong type for second media section"; ASSERT_EQ(SdpMediaSection::kApplication, Sdp()->GetMediaSection(2).GetMediaType()) << "Wrong type for third media section"; ASSERT_EQ(SdpMediaSection::kDtlsSctp, Sdp()->GetMediaSection(2).GetProtocol()) << "Wrong protocol for application"; auto app_formats = Sdp()->GetMediaSection(2).GetFormats(); ASSERT_EQ(1U, app_formats.size()) << "Wrong number of formats for audio"; ASSERT_EQ("5000", app_formats[0]); const SdpConnection& conn3 = Sdp()->GetMediaSection(2).GetConnection(); ASSERT_EQ(sdp::kIPv4, conn3.GetAddrType()); ASSERT_EQ("0.0.0.0", conn3.GetAddress()); ASSERT_EQ(0U, conn3.GetTtl()); ASSERT_EQ(0U, conn3.GetCount()); ASSERT_TRUE(Sdp()->GetMediaSection(2).GetAttributeList().HasAttribute( SdpAttribute::kSetupAttribute)); ASSERT_EQ(SdpSetupAttribute::kActpass, Sdp()->GetMediaSection(2).GetAttributeList().GetSetup().mRole); } TEST_P(NewSdpTest, CheckExtmap) { ParseSdp(kBasicAudioVideoDataOffer, true); ASSERT_TRUE(!!Sdp()); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kExtmapAttribute)); auto extmaps = Sdp()->GetMediaSection(0).GetAttributeList().GetExtmap().mExtmaps; ASSERT_EQ(3U, extmaps.size()); ASSERT_EQ(1U, extmaps[0].entry); ASSERT_FALSE(extmaps[0].direction_specified); ASSERT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level", extmaps[0].extensionname); ASSERT_EQ("", extmaps[0].extensionattributes); ASSERT_EQ(2U, extmaps[1].entry); ASSERT_TRUE(extmaps[1].direction_specified); ASSERT_EQ(SdpDirectionAttribute::kSendonly, extmaps[1].direction); ASSERT_EQ("some_extension", extmaps[1].extensionname); ASSERT_EQ("", extmaps[1].extensionattributes); ASSERT_EQ(3U, extmaps[2].entry); ASSERT_FALSE(extmaps[2].direction_specified); ASSERT_EQ("some_other_extension", extmaps[2].extensionname); ASSERT_EQ("some_params some more params", extmaps[2].extensionattributes); } TEST_P(NewSdpTest, CheckRtcpFb) { ParseSdp(kBasicAudioVideoDataOffer, true); ASSERT_TRUE(!!Sdp()); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; auto& video_attrs = Sdp()->GetMediaSection(1).GetAttributeList(); ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kRtcpFbAttribute)); auto& rtcpfbs = video_attrs.GetRtcpFb().mFeedbacks; if (ResultsAreFromSipcc()) { ASSERT_EQ(21U, rtcpfbs.size()); } else { ASSERT_EQ("WEBRTCSDP", mResults->ParserName()); std::stringstream os; Sdp()->Serialize(os); // Reserialized results should be the same if (::testing::get<1>(GetParam())) { ASSERT_EQ(21U, rtcpfbs.size()); } else { ASSERT_EQ(21U, rtcpfbs.size()); } } CheckRtcpFb(rtcpfbs[0], "120", SdpRtcpFbAttributeList::kAck, "rpsi"); CheckRtcpFb(rtcpfbs[1], "120", SdpRtcpFbAttributeList::kAck, "app", "foo"); CheckRtcpFb(rtcpfbs[2], "120", SdpRtcpFbAttributeList::kNack, ""); CheckRtcpFb(rtcpfbs[3], "120", SdpRtcpFbAttributeList::kNack, "sli"); CheckRtcpFb(rtcpfbs[4], "120", SdpRtcpFbAttributeList::kNack, "pli"); CheckRtcpFb(rtcpfbs[5], "120", SdpRtcpFbAttributeList::kNack, "rpsi"); CheckRtcpFb(rtcpfbs[6], "120", SdpRtcpFbAttributeList::kNack, "app", "foo"); CheckRtcpFb(rtcpfbs[7], "120", SdpRtcpFbAttributeList::kCcm, "fir"); CheckRtcpFb(rtcpfbs[8], "120", SdpRtcpFbAttributeList::kCcm, "tmmbr"); CheckRtcpFb(rtcpfbs[9], "120", SdpRtcpFbAttributeList::kCcm, "tstr"); CheckRtcpFb(rtcpfbs[10], "120", SdpRtcpFbAttributeList::kCcm, "vbcm"); CheckRtcpFb(rtcpfbs[11], "120", SdpRtcpFbAttributeList::kTrrInt, "10"); CheckRtcpFb(rtcpfbs[12], "120", SdpRtcpFbAttributeList::kRemb, ""); CheckRtcpFb(rtcpfbs[13], "126", SdpRtcpFbAttributeList::kNack, ""); CheckRtcpFb(rtcpfbs[14], "126", SdpRtcpFbAttributeList::kNack, "pli"); CheckRtcpFb(rtcpfbs[15], "126", SdpRtcpFbAttributeList::kCcm, "fir"); CheckRtcpFb(rtcpfbs[16], "97", SdpRtcpFbAttributeList::kNack, ""); CheckRtcpFb(rtcpfbs[17], "97", SdpRtcpFbAttributeList::kNack, "pli"); CheckRtcpFb(rtcpfbs[18], "97", SdpRtcpFbAttributeList::kCcm, "fir"); CheckRtcpFb(rtcpfbs[19], "*", SdpRtcpFbAttributeList::kCcm, "tmmbr"); CheckRtcpFb(rtcpfbs[20], "120", SdpRtcpFbAttributeList::kTransportCC, ""); } TEST_P(NewSdpTest, CheckRtcp) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_FALSE( Sdp()->GetAttributeList().HasAttribute(SdpAttribute::kRtcpAttribute)); ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kRtcpAttribute)); ASSERT_TRUE(Sdp()->GetMediaSection(1).GetAttributeList().HasAttribute( SdpAttribute::kRtcpAttribute)); ASSERT_FALSE(Sdp()->GetMediaSection(2).GetAttributeList().HasAttribute( SdpAttribute::kRtcpAttribute)); auto& rtcpAttr_0 = Sdp()->GetMediaSection(0).GetAttributeList().GetRtcp(); ASSERT_EQ(62454U, rtcpAttr_0.mPort); ASSERT_EQ(sdp::kInternet, rtcpAttr_0.mNetType); ASSERT_EQ(sdp::kIPv4, rtcpAttr_0.mAddrType); ASSERT_EQ("162.222.183.171", rtcpAttr_0.mAddress); auto& rtcpAttr_1 = Sdp()->GetMediaSection(1).GetAttributeList().GetRtcp(); ASSERT_EQ(61026U, rtcpAttr_1.mPort); ASSERT_EQ("", rtcpAttr_1.mAddress); } TEST_P(NewSdpTest, CheckImageattr) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_FALSE(Sdp()->GetAttributeList().HasAttribute( SdpAttribute::kImageattrAttribute)); ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kImageattrAttribute)); ASSERT_TRUE(Sdp()->GetMediaSection(1).GetAttributeList().HasAttribute( SdpAttribute::kImageattrAttribute)); ASSERT_FALSE(Sdp()->GetMediaSection(2).GetAttributeList().HasAttribute( SdpAttribute::kImageattrAttribute)); const SdpImageattrAttributeList& imageattrs = Sdp()->GetMediaSection(1).GetAttributeList().GetImageattr(); ASSERT_EQ(2U, imageattrs.mImageattrs.size()); const SdpImageattrAttributeList::Imageattr& imageattr_0( imageattrs.mImageattrs[0]); ASSERT_TRUE(imageattr_0.pt.isSome()); ASSERT_EQ(120U, *imageattr_0.pt); ASSERT_TRUE(imageattr_0.sendAll); ASSERT_TRUE(imageattr_0.recvAll); const SdpImageattrAttributeList::Imageattr& imageattr_1( imageattrs.mImageattrs[1]); ASSERT_TRUE(imageattr_1.pt.isSome()); ASSERT_EQ(121U, *imageattr_1.pt); ASSERT_FALSE(imageattr_1.sendAll); ASSERT_FALSE(imageattr_1.recvAll); ASSERT_EQ(1U, imageattr_1.sendSets.size()); ASSERT_EQ(1U, imageattr_1.sendSets[0].xRange.discreteValues.size()); ASSERT_EQ(640U, imageattr_1.sendSets[0].xRange.discreteValues.front()); ASSERT_EQ(1U, imageattr_1.sendSets[0].yRange.discreteValues.size()); ASSERT_EQ(480U, imageattr_1.sendSets[0].yRange.discreteValues.front()); ASSERT_EQ(1U, imageattr_1.recvSets.size()); ASSERT_EQ(1U, imageattr_1.recvSets[0].xRange.discreteValues.size()); ASSERT_EQ(640U, imageattr_1.recvSets[0].xRange.discreteValues.front()); ASSERT_EQ(1U, imageattr_1.recvSets[0].yRange.discreteValues.size()); ASSERT_EQ(480U, imageattr_1.recvSets[0].yRange.discreteValues.front()); } TEST_P(NewSdpTest, CheckSimulcast) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_FALSE(Sdp()->GetAttributeList().HasAttribute( SdpAttribute::kSimulcastAttribute)); ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kSimulcastAttribute)); ASSERT_TRUE(Sdp()->GetMediaSection(1).GetAttributeList().HasAttribute( SdpAttribute::kSimulcastAttribute)); ASSERT_FALSE(Sdp()->GetMediaSection(2).GetAttributeList().HasAttribute( SdpAttribute::kSimulcastAttribute)); const SdpSimulcastAttribute& simulcast = Sdp()->GetMediaSection(1).GetAttributeList().GetSimulcast(); ASSERT_EQ(2U, simulcast.recvVersions.size()); ASSERT_EQ(0U, simulcast.sendVersions.size()); ASSERT_EQ(1U, simulcast.recvVersions[0].choices.size()); ASSERT_EQ("bar", simulcast.recvVersions[0].choices[0].rid); ASSERT_EQ(1U, simulcast.recvVersions[1].choices.size()); ASSERT_EQ("bar123", simulcast.recvVersions[1].choices[0].rid); } TEST_P(NewSdpTest, CheckSctpmap) { ParseSdp(kBasicAudioVideoDataOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; const SdpMediaSection& appsec = Sdp()->GetMediaSection(2); ASSERT_TRUE( appsec.GetAttributeList().HasAttribute(SdpAttribute::kSctpmapAttribute)); const SdpSctpmapAttributeList& sctpmap = appsec.GetAttributeList().GetSctpmap(); ASSERT_EQ(1U, sctpmap.mSctpmaps.size()) << "Wrong number of sctpmap attributes"; ASSERT_EQ(1U, appsec.GetFormats().size()); // Need to know name of type CheckSctpmap("5000", "webrtc-datachannel", 16, appsec.GetFormats()[0], sctpmap); } TEST_P(NewSdpTest, CheckMaxPtime) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kMaxptimeAttribute)); ASSERT_EQ(Sdp()->GetMediaSection(0).GetAttributeList().GetMaxptime(), 20U); } const std::string kNewSctpportOfferDraft21 = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 27987 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "t=0 0" CRLF "a=ice-ufrag:8a39d2ae" CRLF "a=ice-pwd:601d53aba51a318351b3ecf5ee00048f" CRLF "a=fingerprint:sha-256 " "30:FF:8E:2B:AC:9D:ED:70:18:10:67:C8:AE:9E:68:F3:86:53:51:B0:AC:31:B7:BE:" "6D:CF:A4:2E:D3:6E:B4:28" CRLF "m=application 9 UDP/DTLS/SCTP webrtc-datachannel" CRLF "c=IN IP4 0.0.0.0" CRLF "a=sctp-port:5000" CRLF "a=max-message-size:10000" CRLF "a=setup:actpass" CRLF; TEST_P(NewSdpTest, NewSctpportSdpParse) { ParseSdp(kNewSctpportOfferDraft21, false); } INSTANTIATE_TEST_SUITE_P(RoundTripSerialize, NewSdpTest, ::testing::Combine(::testing::Bool(), ::testing::Bool())); const std::string kCandidateInSessionSDP = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=candidate:0 1 UDP 2130379007 10.0.0.36 62453 typ host" CRLF "m=audio 9 RTP/SAVPF 109 9 0 8 101" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:109 opus/48000/2" CRLF; // This may or may not parse, but if it does, the errant candidate attribute // should be ignored. TEST_P(NewSdpTest, CheckCandidateInSessionLevel) { ParseSdp(kCandidateInSessionSDP, false); if (Sdp()) { ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kCandidateAttribute)); ASSERT_FALSE(Sdp()->GetAttributeList().HasAttribute( SdpAttribute::kCandidateAttribute)); } } const std::string kBundleOnlyInSessionSDP = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=bundle-only" CRLF "m=audio 9 RTP/SAVPF 109 9 0 8 101" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:109 opus/48000/2" CRLF; // This may or may not parse, but if it does, the errant attribute // should be ignored. TEST_P(NewSdpTest, CheckBundleOnlyInSessionLevel) { ParseSdp(kBundleOnlyInSessionSDP, false); if (Sdp()) { ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kBundleOnlyAttribute)); ASSERT_FALSE(Sdp()->GetAttributeList().HasAttribute( SdpAttribute::kBundleOnlyAttribute)); } } const std::string kFmtpInSessionSDP = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=fmtp:109 0-15" CRLF "m=audio 9 RTP/SAVPF 109 9 0 8 101" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:109 opus/48000/2" CRLF; // This may or may not parse, but if it does, the errant attribute // should be ignored. TEST_P(NewSdpTest, CheckFmtpInSessionLevel) { ParseSdp(kFmtpInSessionSDP, false); if (Sdp()) { ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kFmtpAttribute)); ASSERT_FALSE( Sdp()->GetAttributeList().HasAttribute(SdpAttribute::kFmtpAttribute)); } } const std::string kIceMismatchInSessionSDP = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=ice-mismatch" CRLF "m=audio 9 RTP/SAVPF 109 9 0 8 101" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:109 opus/48000/2" CRLF; // This may or may not parse, but if it does, the errant attribute // should be ignored. TEST_P(NewSdpTest, CheckIceMismatchInSessionLevel) { ParseSdp(kIceMismatchInSessionSDP, false); if (Sdp()) { ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kIceMismatchAttribute)); ASSERT_FALSE(Sdp()->GetAttributeList().HasAttribute( SdpAttribute::kIceMismatchAttribute)); } } const std::string kImageattrInSessionSDP = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=imageattr:120 send * recv *" CRLF "m=video 9 RTP/SAVPF 120" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:120 VP8/90000" CRLF; // This may or may not parse, but if it does, the errant attribute // should be ignored. TEST_P(NewSdpTest, CheckImageattrInSessionLevel) { ParseSdp(kImageattrInSessionSDP, false); if (Sdp()) { ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kImageattrAttribute)); ASSERT_FALSE(Sdp()->GetAttributeList().HasAttribute( SdpAttribute::kImageattrAttribute)); } } const std::string kLabelInSessionSDP = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=label:foobar" CRLF "m=video 9 RTP/SAVPF 120" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:120 VP8/90000" CRLF; // This may or may not parse, but if it does, the errant attribute // should be ignored. TEST_P(NewSdpTest, CheckLabelInSessionLevel) { ParseSdp(kLabelInSessionSDP, false); if (Sdp()) { ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kLabelAttribute)); ASSERT_FALSE( Sdp()->GetAttributeList().HasAttribute(SdpAttribute::kLabelAttribute)); } } const std::string kMaxptimeInSessionSDP = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=maxptime:100" CRLF "m=video 9 RTP/SAVPF 120" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:120 VP8/90000" CRLF; // This may or may not parse, but if it does, the errant attribute // should be ignored. TEST_P(NewSdpTest, CheckMaxptimeInSessionLevel) { ParseSdp(kMaxptimeInSessionSDP, false); if (Sdp()) { ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kMaxptimeAttribute)); ASSERT_FALSE(Sdp()->GetAttributeList().HasAttribute( SdpAttribute::kMaxptimeAttribute)); } } const std::string kMidInSessionSDP = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=mid:foobar" CRLF "m=video 9 RTP/SAVPF 120" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:120 VP8/90000" CRLF; // This may or may not parse, but if it does, the errant attribute // should be ignored. TEST_P(NewSdpTest, CheckMidInSessionLevel) { ParseSdp(kMidInSessionSDP, false); if (Sdp()) { ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kMidAttribute)); ASSERT_FALSE( Sdp()->GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute)); } } const std::string kMsidInSessionSDP = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=msid:foobar" CRLF "m=video 9 RTP/SAVPF 120" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:120 VP8/90000" CRLF; // This may or may not parse, but if it does, the errant attribute // should be ignored. TEST_P(NewSdpTest, CheckMsidInSessionLevel) { ParseSdp(kMsidInSessionSDP, false); if (Sdp()) { ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kMsidAttribute)); ASSERT_FALSE( Sdp()->GetAttributeList().HasAttribute(SdpAttribute::kMsidAttribute)); } } const std::string kPtimeInSessionSDP = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=ptime:50" CRLF "m=video 9 RTP/SAVPF 120" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:120 VP8/90000" CRLF; // This may or may not parse, but if it does, the errant attribute // should be ignored. TEST_P(NewSdpTest, CheckPtimeInSessionLevel) { ParseSdp(kPtimeInSessionSDP, false); if (Sdp()) { ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kPtimeAttribute)); ASSERT_FALSE( Sdp()->GetAttributeList().HasAttribute(SdpAttribute::kPtimeAttribute)); } } const std::string kRemoteCandidatesInSessionSDP = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=remote-candidates:0 10.0.0.1 5555" CRLF "m=video 9 RTP/SAVPF 120" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:120 VP8/90000" CRLF; // This may or may not parse, but if it does, the errant attribute // should be ignored. TEST_P(NewSdpTest, CheckRemoteCandidatesInSessionLevel) { ParseSdp(kRemoteCandidatesInSessionSDP, false); if (Sdp()) { ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kRemoteCandidatesAttribute)); ASSERT_FALSE(Sdp()->GetAttributeList().HasAttribute( SdpAttribute::kRemoteCandidatesAttribute)); } } const std::string kRtcpInSessionSDP = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=rtcp:5555" CRLF "m=video 9 RTP/SAVPF 120" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:120 VP8/90000" CRLF; // This may or may not parse, but if it does, the errant attribute // should be ignored. TEST_P(NewSdpTest, CheckRtcpInSessionLevel) { ParseSdp(kRtcpInSessionSDP, false); if (Sdp()) { ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kRtcpAttribute)); ASSERT_FALSE( Sdp()->GetAttributeList().HasAttribute(SdpAttribute::kRtcpAttribute)); } } const std::string kRtcpFbInSessionSDP = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=rtcp-fb:120 nack" CRLF "m=video 9 RTP/SAVPF 120" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:120 VP8/90000" CRLF; // This may or may not parse, but if it does, the errant attribute // should be ignored. TEST_P(NewSdpTest, CheckRtcpFbInSessionLevel) { ParseSdp(kRtcpFbInSessionSDP, false, true); if (Sdp()) { ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kRtcpFbAttribute)); ASSERT_FALSE( Sdp()->GetAttributeList().HasAttribute(SdpAttribute::kRtcpFbAttribute)); } } const std::string kRtcpMuxInSessionSDP = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=rtcp-mux" CRLF "m=video 9 RTP/SAVPF 120" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:120 VP8/90000" CRLF; // This may or may not parse, but if it does, the errant attribute // should be ignored. TEST_P(NewSdpTest, CheckRtcpMuxInSessionLevel) { ParseSdp(kRtcpMuxInSessionSDP, false); if (Sdp()) { ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kRtcpMuxAttribute)); ASSERT_FALSE(Sdp()->GetAttributeList().HasAttribute( SdpAttribute::kRtcpMuxAttribute)); } } const std::string kRtcpRsizeInSessionSDP = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=rtcp-rsize" CRLF "m=video 9 RTP/SAVPF 120" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:120 VP8/90000" CRLF; // This may or may not parse, but if it does, the errant attribute // should be ignored. TEST_P(NewSdpTest, CheckRtcpRsizeInSessionLevel) { ParseSdp(kRtcpRsizeInSessionSDP, false); if (Sdp()) { ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kRtcpRsizeAttribute)); ASSERT_FALSE(Sdp()->GetAttributeList().HasAttribute( SdpAttribute::kRtcpRsizeAttribute)); } } const std::string kRtpmapInSessionSDP = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=rtpmap:120 VP8/90000" CRLF "m=video 9 RTP/SAVPF 120" CRLF "c=IN IP4 0.0.0.0" CRLF; // This may or may not parse, but if it does, the errant attribute // should be ignored. TEST_P(NewSdpTest, CheckRtpmapInSessionLevel) { ParseSdp(kRtpmapInSessionSDP, false); if (Sdp()) { ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kRtpmapAttribute)); ASSERT_FALSE( Sdp()->GetAttributeList().HasAttribute(SdpAttribute::kRtpmapAttribute)); } } const std::string kSctpmapInSessionSDP = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=sctpmap:5000" CRLF "m=video 9 RTP/SAVPF 120" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:120 VP8/90000" CRLF; // This may or may not parse, but if it does, the errant attribute // should be ignored. TEST_P(NewSdpTest, CheckSctpmapInSessionLevel) { ParseSdp(kSctpmapInSessionSDP, false); if (Sdp()) { ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kSctpmapAttribute)); ASSERT_FALSE(Sdp()->GetAttributeList().HasAttribute( SdpAttribute::kSctpmapAttribute)); } } const std::string kSsrcInSessionSDP = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "a=ssrc:5000" CRLF "m=video 9 RTP/SAVPF 120" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:120 VP8/90000" CRLF; // This may or may not parse, but if it does, the errant attribute // should be ignored. TEST_P(NewSdpTest, CheckSsrcInSessionLevel) { ParseSdp(kSsrcInSessionSDP, false); if (Sdp()) { ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kSsrcAttribute)); ASSERT_FALSE( Sdp()->GetAttributeList().HasAttribute(SdpAttribute::kSsrcAttribute)); } } const std::string kMalformedImageattr = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "m=video 9 RTP/SAVPF 120" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:120 VP8/90000" CRLF "a=imageattr:flob" CRLF; TEST_P(NewSdpTest, CheckMalformedImageattr) { if (::testing::get<0>(GetParam())) { // Don't do a parse/serialize before running this test return; } ParseSdp(kMalformedImageattr, false); ASSERT_NE(0U, ParseErrorCount()); } TEST_P(NewSdpTest, ParseInvalidSimulcastNoSuchSendRid) { ParseSdp("v=0" CRLF "o=- 4294967296 2 IN IP4 127.0.0.1" CRLF "s=SIP Call" CRLF "c=IN IP4 198.51.100.7" CRLF "b=CT:5000" CRLF "t=0 0" CRLF "m=video 56436 RTP/SAVPF 120" CRLF "a=rtpmap:120 VP8/90000" CRLF "a=sendrecv" CRLF "a=simulcast:send 9" CRLF "a=rid:9 recv max-width=800;max-height=600" CRLF, false); ASSERT_NE(0U, ParseErrorCount()); } TEST_P(NewSdpTest, ParseInvalidSimulcastNoSuchRecvRid) { ParseSdp("v=0" CRLF "o=- 4294967296 2 IN IP4 127.0.0.1" CRLF "s=SIP Call" CRLF "c=IN IP4 198.51.100.7" CRLF "b=CT:5000" CRLF "t=0 0" CRLF "m=video 56436 RTP/SAVPF 120" CRLF "a=rtpmap:120 VP8/90000" CRLF "a=sendrecv" CRLF "a=simulcast:recv 9" CRLF "a=rid:9 send max-width=800;max-height=600" CRLF, false); ASSERT_NE(0U, ParseErrorCount()); } TEST_P(NewSdpTest, ParseSimulcastNotSending) { ParseSdp("v=0" CRLF "o=- 4294967296 2 IN IP4 127.0.0.1" CRLF "s=SIP Call" CRLF "c=IN IP4 198.51.100.7" CRLF "b=CT:5000" CRLF "t=0 0" CRLF "m=video 56436 RTP/SAVPF 120" CRLF "a=rtpmap:120 VP8/90000" CRLF "a=recvonly" CRLF "a=simulcast:send 120" CRLF "a=rid:120 send" CRLF, false); ASSERT_EQ(0U, ParseErrorCount()); } TEST_P(NewSdpTest, ParseSimulcastNotReceiving) { ParseSdp("v=0" CRLF "o=- 4294967296 2 IN IP4 127.0.0.1" CRLF "s=SIP Call" CRLF "c=IN IP4 198.51.100.7" CRLF "b=CT:5000" CRLF "t=0 0" CRLF "m=video 56436 RTP/SAVPF 120" CRLF "a=rtpmap:120 VP8/90000" CRLF "a=sendonly" CRLF "a=simulcast:recv 120" CRLF "a=rid:120 recv" CRLF, false); ASSERT_EQ(0U, ParseErrorCount()); } TEST_P(NewSdpTest, ParseInvalidRidNoSuchPt) { SKIP_TEST_WITH_SIPCC_PARSER ParseSdp("v=0" CRLF "o=- 4294967296 2 IN IP4 127.0.0.1" CRLF "s=SIP Call" CRLF "c=IN IP4 198.51.100.7" CRLF "b=CT:5000" CRLF "t=0 0" CRLF "m=video 56436 RTP/SAVPF 120" CRLF "a=rtpmap:120 VP8/90000" CRLF "a=sendrecv" CRLF "a=simulcast:recv rid=9" CRLF "a=rid:9 recv pt=101;max-width=800;max-height=600" CRLF, false); ASSERT_NE(0U, ParseErrorCount()); } const std::string kNoAttributes = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "m=video 9 RTP/SAVPF 120" CRLF "c=IN IP4 0.0.0.0" CRLF "a=rtpmap:120 VP8/90000" CRLF; TEST_P(NewSdpTest, CheckNoAttributes) { ParseSdp(kNoAttributes); for (auto a = static_cast(SdpAttribute::kFirstAttribute); a <= static_cast(SdpAttribute::kLastAttribute); ++a) { SdpAttribute::AttributeType type = static_cast(a); // rtpmap is a special case right now, we throw parse errors if it is // missing, and then insert one. // direction is another special case that gets a default if not present if (type != SdpAttribute::kRtpmapAttribute && type != SdpAttribute::kDirectionAttribute) { ASSERT_FALSE( Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute(type)) << "Attribute " << a << " should not have been present at media level"; ASSERT_FALSE(Sdp()->GetAttributeList().HasAttribute(type)) << "Attribute " << a << " should not have been present at session level"; } } ASSERT_FALSE( Sdp()->GetAttributeList().HasAttribute(SdpAttribute::kRtpmapAttribute)); ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kDirectionAttribute)); ASSERT_EQ(SdpDirectionAttribute::kSendrecv, Sdp()->GetMediaSection(0).GetAttributeList().GetDirection()); ASSERT_TRUE(Sdp()->GetAttributeList().HasAttribute( SdpAttribute::kDirectionAttribute)); ASSERT_EQ(SdpDirectionAttribute::kSendrecv, Sdp()->GetAttributeList().GetDirection()); } const std::string kMediaLevelDtlsMessage = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "m=video 9 RTP/SAVPF 120" CRLF "c=IN IP4 0.0.0.0" CRLF "a=dtls-message:client " BASE64_DTLS_HELLO CRLF "a=rtpmap:120 VP8/90000" CRLF; TEST_P(NewSdpTest, CheckMediaLevelDtlsMessage) { ParseSdp(kMediaLevelDtlsMessage); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); // dtls-message is not defined for use at the media level; we don't // parse it ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kDtlsMessageAttribute)); } TEST_P(NewSdpTest, CheckSetPort) { // Parse any valid sdp with a media section ParseSdp("v=0" CRLF "o=- 4294967296 2 IN IP4 127.0.0.1" CRLF "s=SIP Call" CRLF "c=IN IP4 198.51.100.7" CRLF "b=CT:5000" CRLF "t=0 0" CRLF "m=video 56436 RTP/SAVPF 120" CRLF "a=rtpmap:120 VP8/90000" CRLF "a=sendonly" CRLF, false); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); constexpr unsigned int expectedParesPort = 56436; unsigned int currentPort = Sdp()->GetMediaSection(0).GetPort(); ASSERT_EQ(expectedParesPort, currentPort); Sdp()->GetMediaSection(0).SetPort(currentPort + 1); ASSERT_EQ(currentPort + 1, Sdp()->GetMediaSection(0).GetPort()); } TEST_P(NewSdpTest, CheckAddCodec) { // Parse any valid sdp with a media section ParseSdp("v=0" CRLF "o=- 4294967296 2 IN IP4 127.0.0.1" CRLF "s=SIP Call" CRLF "c=IN IP4 198.51.100.7" CRLF "b=CT:5000" CRLF "t=0 0" CRLF "m=video 56436 RTP/SAVPF 120" CRLF "a=rtpmap:120 VP8/90000" CRLF "a=sendonly" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()); ASSERT_EQ(1U, Sdp()->GetMediaSection(0).GetFormats().size()); ASSERT_EQ( 1U, Sdp()->GetMediaSection(0).GetAttributeList().GetRtpmap().mRtpmaps.size()); Sdp()->GetMediaSection(0).AddCodec("110", "opus", 48000, 2); ASSERT_EQ(2U, Sdp()->GetMediaSection(0).GetFormats().size()); const auto& rtpmaps = Sdp()->GetMediaSection(0).GetAttributeList().GetRtpmap(); ASSERT_EQ(2U, rtpmaps.mRtpmaps.size()); ASSERT_TRUE(rtpmaps.HasEntry("120")); ASSERT_TRUE(rtpmaps.HasEntry("110")); const auto aRtpmap = rtpmaps.GetEntry("110"); ASSERT_EQ(aRtpmap.pt, "110"); ASSERT_EQ(aRtpmap.codec, SdpRtpmapAttributeList::kOpus); ASSERT_EQ(aRtpmap.name, "opus"); ASSERT_EQ(aRtpmap.clock, 48000U); ASSERT_EQ(aRtpmap.channels, 2U); } TEST_P(NewSdpTest, CheckClearCodecs) { // Parse any valid sdp with a media section ParseSdp("v=0" CRLF "o=- 4294967296 2 IN IP4 127.0.0.1" CRLF "s=SIP Call" CRLF "c=IN IP4 198.51.100.7" CRLF "b=CT:5000" CRLF "t=0 0" CRLF "m=video 56436 RTP/SAVPF 120 110" CRLF "a=rtpmap:120 VP8/90000" CRLF "a=sendonly" CRLF "a=rtpmap:110 opus/48000" CRLF); // SIPCC strips the channels on opus so the /2 was omitted ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()); ASSERT_EQ(2U, Sdp()->GetMediaSection(0).GetFormats().size()); ASSERT_EQ( 2U, Sdp()->GetMediaSection(0).GetAttributeList().GetRtpmap().mRtpmaps.size()); Sdp()->GetMediaSection(0).ClearCodecs(); ASSERT_EQ(0U, Sdp()->GetMediaSection(0).GetFormats().size()); ASSERT_FALSE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kRtpmapAttribute)); } TEST_P(NewSdpTest, CheckAddMediaSection) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(3U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections"; Sdp()->AddMediaSection( SdpMediaSection::kVideo, SdpDirectionAttribute::Direction::kSendrecv, 58000, SdpMediaSection::kUdpDtlsSctp, sdp::kIPv4, "127.0.0.1"); ASSERT_EQ(4U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections after adding media section"; const SdpMediaSection& newMediaSection = Sdp()->GetMediaSection(3); ASSERT_EQ(SdpMediaSection::kVideo, newMediaSection.GetMediaType()); ASSERT_EQ(SdpDirectionAttribute::Direction::kSendrecv, newMediaSection.GetDirectionAttribute().mValue); ASSERT_EQ(58000U, newMediaSection.GetPort()); ASSERT_EQ(SdpMediaSection::kUdpDtlsSctp, newMediaSection.GetProtocol()); ASSERT_EQ(sdp::kIPv4, newMediaSection.GetConnection().GetAddrType()); ASSERT_EQ("127.0.0.1", newMediaSection.GetConnection().GetAddress()); Sdp()->AddMediaSection(SdpMediaSection::kAudio, SdpDirectionAttribute::Direction::kSendonly, 14006, SdpMediaSection::kTcpDtlsRtpSavpf, sdp::kIPv6, "2607:f8b0:4004:801::2013"); ASSERT_EQ(5U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections after adding media section"; const SdpMediaSection& nextNewMediaSection = Sdp()->GetMediaSection(4); ASSERT_EQ(SdpMediaSection::kAudio, nextNewMediaSection.GetMediaType()); ASSERT_EQ(SdpDirectionAttribute::Direction::kSendonly, nextNewMediaSection.GetDirectionAttribute().mValue); ASSERT_EQ(14006U, nextNewMediaSection.GetPort()); ASSERT_EQ(SdpMediaSection::kTcpDtlsRtpSavpf, nextNewMediaSection.GetProtocol()); ASSERT_EQ(sdp::kIPv6, nextNewMediaSection.GetConnection().GetAddrType()); ASSERT_EQ("2607:f8b0:4004:801::2013", nextNewMediaSection.GetConnection().GetAddress()); if (!ResultsAreFromSipcc()) { // All following AddMediaSection calls are expected to fail // SdpMediaSection::kDccpRtpAvp is expected to cause a failure Sdp()->AddMediaSection(SdpMediaSection::kAudio, SdpDirectionAttribute::Direction::kSendonly, 14006, SdpMediaSection::kDccpRtpAvp, sdp::kIPv6, "2607:f8b0:4004:801::2013"); ASSERT_EQ(5U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections after adding media section"; // sdp::kAddrTypeNone is expected to cause a failure Sdp()->AddMediaSection(SdpMediaSection::kAudio, SdpDirectionAttribute::Direction::kSendonly, 14006, SdpMediaSection::kDtlsSctp, sdp::kAddrTypeNone, "2607:f8b0:4004:801::2013"); ASSERT_EQ(5U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections after adding media section"; // "NOT:AN.IP.ADDRESS" is expected to cause a failure Sdp()->AddMediaSection(SdpMediaSection::kAudio, SdpDirectionAttribute::Direction::kSendonly, 14006, SdpMediaSection::kTcpDtlsRtpSavpf, sdp::kIPv6, "NOT:AN.IP.ADDRESS"); ASSERT_EQ(5U, Sdp()->GetMediaSectionCount()) << "Wrong number of media sections after adding media section"; } } TEST_P(NewSdpTest, CheckAddDataChannel_Draft05) { // Parse any valid sdp with a media section ParseSdp("v=0" CRLF "o=- 4294967296 2 IN IP4 127.0.0.1" CRLF "s=SIP Call" CRLF "c=IN IP4 198.51.100.7" CRLF "b=CT:5000" CRLF "t=0 0" CRLF "m=application 56436 DTLS/SCTP 5000" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()); auto& mediaSection = Sdp()->GetMediaSection(0); mediaSection.AddDataChannel("webrtc-datachannel", 6000, 16, 0); ASSERT_FALSE(mediaSection.GetAttributeList().HasAttribute( SdpAttribute::kMaxMessageSizeAttribute)); ASSERT_TRUE(mediaSection.GetAttributeList().HasAttribute( SdpAttribute::kSctpmapAttribute)); ASSERT_TRUE(mediaSection.GetAttributeList().GetSctpmap().HasEntry("6000")); ASSERT_EQ( 16U, mediaSection.GetAttributeList().GetSctpmap().GetFirstEntry().streams); ASSERT_EQ("webrtc-datachannel", mediaSection.GetAttributeList().GetSctpmap().GetFirstEntry().name); mediaSection.AddDataChannel("webrtc-datachannel", 15000, 8, 1800); ASSERT_TRUE(mediaSection.GetAttributeList().HasAttribute( SdpAttribute::kMaxMessageSizeAttribute)); ASSERT_EQ(1800U, mediaSection.GetAttributeList().GetMaxMessageSize()); ASSERT_TRUE(mediaSection.GetAttributeList().HasAttribute( SdpAttribute::kSctpmapAttribute)); ASSERT_TRUE(mediaSection.GetAttributeList().GetSctpmap().HasEntry("15000")); ASSERT_EQ( 8U, mediaSection.GetAttributeList().GetSctpmap().GetFirstEntry().streams); } TEST_P(NewSdpTest, CheckAddDataChannel) { ParseSdp("v=0" CRLF "o=- 4294967296 2 IN IP4 127.0.0.1" CRLF "s=SIP Call" CRLF "c=IN IP4 198.51.100.7" CRLF "b=CT:5000" CRLF "t=0 0" CRLF "m=application 56436 UDP/DTLS/SCTP webrtc-datachannel" CRLF); ASSERT_TRUE(!!Sdp()) << "Parse failed: " << SerializeParseErrors(); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()); auto& mediaSection = Sdp()->GetMediaSection(0); mediaSection.AddDataChannel("webrtc-datachannel", 6000, 16, 0); ASSERT_FALSE(mediaSection.GetAttributeList().HasAttribute( SdpAttribute::kMaxMessageSizeAttribute)); ASSERT_TRUE(mediaSection.GetAttributeList().HasAttribute( SdpAttribute::kSctpPortAttribute)); ASSERT_EQ(6000U, mediaSection.GetAttributeList().GetSctpPort()); mediaSection.AddDataChannel("webrtc-datachannel", 15000, 8, 1800); ASSERT_TRUE(mediaSection.GetAttributeList().HasAttribute( SdpAttribute::kMaxMessageSizeAttribute)); ASSERT_EQ(1800U, mediaSection.GetAttributeList().GetMaxMessageSize()); ASSERT_TRUE(mediaSection.GetAttributeList().HasAttribute( SdpAttribute::kSctpPortAttribute)); ASSERT_EQ(15000U, mediaSection.GetAttributeList().GetSctpPort()); } TEST(NewSdpTestNoFixture, CheckParsingResultComparer) { auto check_comparison = [](const std::string sdp_string) { SipccSdpParser sipccParser; RsdparsaSdpParser rustParser; auto print_errors = [](const mozilla::UniquePtr& results, const char* name) { for (const auto& e : results->Errors()) { std::cerr << name << " Line " << e.first << ": " << e.second; } }; auto sipccResults = sipccParser.Parse(sdp_string); auto sipccSdp = std::move(sipccResults->Sdp()); print_errors(sipccResults, "sipcc"); auto rustResults = rustParser.Parse(sdp_string); auto rustSdp = std::move(rustResults->Sdp()); print_errors(rustResults, "webrtc-sdp"); ParsingResultComparer comparer; return sipccSdp && rustSdp ? comparer.Compare(*rustSdp, *sipccSdp, sdp_string) : false; }; ASSERT_TRUE(check_comparison(kBasicAudioVideoOffer)); ASSERT_TRUE(check_comparison(kH264AudioVideoOffer)); // Check the Fmtp comprison const std::string kBasicOpusFmtp1 = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "m=video 9 RTP/SAVPF 120" CRLF "a=rtpmap:120 opus/48000/1" CRLF "a=fmtp:120 stereo=1;useinbandfec=1" CRLF; ASSERT_TRUE(check_comparison(kBasicOpusFmtp1)); const std::string kBasicOpusFmtp2 = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "m=video 9 RTP/SAVPF 120" CRLF "a=rtpmap:120 opus/48000" CRLF "a=fmtp:120 useinbandfec=1;stereo=1;maxplaybackrate=32000" CRLF; ASSERT_TRUE(check_comparison(kBasicOpusFmtp2)); const std::string kBasicVP8Fmtp1 = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "m=video 9 RTP/SAVPF 120" CRLF "a=rtpmap:120 VP8/90000" CRLF "a=fmtp:120 max-fs=3600;max-fr=60" CRLF; ASSERT_TRUE(check_comparison(kBasicVP8Fmtp1)); // const std::string kBasicVP8Fmtp2 = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "m=video 9 RTP/SAVPF 120" CRLF "a=rtpmap:120 VP8/90000" CRLF "a=fmtp:120 max-fr=60;max-fs=3600" CRLF; ASSERT_TRUE(check_comparison(kBasicVP8Fmtp2)); const std::string kBasicH264Fmtp1 = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "m=video 9 RTP/SAVPF 120" CRLF "a=rtpmap:120 H264/90000" CRLF "a=fmtp:120 profile-level-id=42a01e;level_asymmetry_allowed=1" CRLF; ASSERT_TRUE(check_comparison(kBasicH264Fmtp1)); const std::string kBasicH264Fmtp2 = "v=0" CRLF "o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF "s=SIP Call" CRLF "c=IN IP4 224.0.0.1/100/12" CRLF "t=0 0" CRLF "m=video 9 RTP/SAVPF 120" CRLF "a=rtpmap:120 H264/90000" CRLF "a=fmtp:120 " "level_asymmetry_allowed=1;profile-level-id=42a01e;max_fs=3600" CRLF; ASSERT_TRUE(check_comparison(kBasicH264Fmtp2)); } TEST(NewSdpTestNoFixture, CheckAttributeTypeSerialize) { for (auto a = static_cast(SdpAttribute::kFirstAttribute); a <= static_cast(SdpAttribute::kLastAttribute); ++a) { SdpAttribute::AttributeType type = static_cast(a); // Direction attributes are handled a little differently if (type != SdpAttribute::kDirectionAttribute) { std::ostringstream os; os << type; ASSERT_NE("", os.str()); } } } static SdpImageattrAttributeList::XYRange ParseXYRange( const std::string& input) { std::istringstream is(input + ","); std::string error; SdpImageattrAttributeList::XYRange range; EXPECT_TRUE(range.Parse(is, &error)) << error; EXPECT_EQ(',', is.get()); EXPECT_EQ(EOF, is.get()); return range; } TEST(NewSdpTestNoFixture, CheckImageattrXYRangeParseValid) { { SdpImageattrAttributeList::XYRange range(ParseXYRange("640")); ASSERT_EQ(1U, range.discreteValues.size()); ASSERT_EQ(640U, range.discreteValues[0]); } { SdpImageattrAttributeList::XYRange range(ParseXYRange("[320,640]")); ASSERT_EQ(2U, range.discreteValues.size()); ASSERT_EQ(320U, range.discreteValues[0]); ASSERT_EQ(640U, range.discreteValues[1]); } { SdpImageattrAttributeList::XYRange range(ParseXYRange("[320,640,1024]")); ASSERT_EQ(3U, range.discreteValues.size()); ASSERT_EQ(320U, range.discreteValues[0]); ASSERT_EQ(640U, range.discreteValues[1]); ASSERT_EQ(1024U, range.discreteValues[2]); } { SdpImageattrAttributeList::XYRange range(ParseXYRange("[320:640]")); ASSERT_EQ(0U, range.discreteValues.size()); ASSERT_EQ(320U, range.min); ASSERT_EQ(1U, range.step); ASSERT_EQ(640U, range.max); } { SdpImageattrAttributeList::XYRange range(ParseXYRange("[320:16:640]")); ASSERT_EQ(0U, range.discreteValues.size()); ASSERT_EQ(320U, range.min); ASSERT_EQ(16U, range.step); ASSERT_EQ(640U, range.max); } } template void ParseInvalid(const std::string& input, size_t last) { std::istringstream is(input); T parsed; std::string error; ASSERT_FALSE(parsed.Parse(is, &error)) << "\'" << input << "\' should not have parsed successfully"; is.clear(); ASSERT_EQ(last, static_cast(is.tellg())) << "Parse failed at unexpected location:" << std::endl << input << std::endl << std::string(is.tellg(), ' ') << "^" << std::endl; // For a human to eyeball to make sure the error strings look sane std::cout << "\"" << input << "\" - " << error << std::endl; } TEST(NewSdpTestNoFixture, CheckImageattrXYRangeParseInvalid) { ParseInvalid("[-1", 1); ParseInvalid("[-", 1); ParseInvalid("[-v", 1); ParseInvalid("[640:-1", 5); ParseInvalid("[640:16:-1", 8); ParseInvalid("[640,-1", 5); ParseInvalid("[640,-]", 5); ParseInvalid("-v", 0); ParseInvalid("-1", 0); ParseInvalid("", 0); ParseInvalid("[", 1); ParseInvalid("[v", 1); ParseInvalid("[", 1); ParseInvalid("[ 640", 1); // It looks like the overflow detection only happens once the whole number // is scanned... ParseInvalid("[99999999999999999:", 18); ParseInvalid("[640", 4); ParseInvalid("[640:", 5); ParseInvalid("[640:v", 5); ParseInvalid("[640:16", 7); ParseInvalid("[640:16:", 8); ParseInvalid("[640:16:v", 8); ParseInvalid("[640:16:320]", 11); ParseInvalid("[640:16:320", 11); ParseInvalid("[640:16:320v", 11); ParseInvalid("[640:1024", 9); ParseInvalid("[640:320]", 8); ParseInvalid("[640:1024v", 9); ParseInvalid("[640,", 5); ParseInvalid("[640,v", 5); ParseInvalid("[640]", 4); ParseInvalid("[640x", 4); ParseInvalid("[640,]", 5); ParseInvalid(" ", 0); ParseInvalid("v", 0); } static SdpImageattrAttributeList::SRange ParseSRange(const std::string& input) { std::istringstream is(input + ","); std::string error; SdpImageattrAttributeList::SRange range; EXPECT_TRUE(range.Parse(is, &error)) << error; EXPECT_EQ(',', is.get()); EXPECT_EQ(EOF, is.get()); return range; } TEST(NewSdpTestNoFixture, CheckImageattrSRangeParseValid) { { SdpImageattrAttributeList::SRange range(ParseSRange("0.1")); ASSERT_EQ(1U, range.discreteValues.size()); ASSERT_FLOAT_EQ(0.1f, range.discreteValues[0]); } { SdpImageattrAttributeList::SRange range(ParseSRange("[0.1,0.2]")); ASSERT_EQ(2U, range.discreteValues.size()); ASSERT_FLOAT_EQ(0.1f, range.discreteValues[0]); ASSERT_FLOAT_EQ(0.2f, range.discreteValues[1]); } { SdpImageattrAttributeList::SRange range(ParseSRange("[0.1,0.2,0.3]")); ASSERT_EQ(3U, range.discreteValues.size()); ASSERT_FLOAT_EQ(0.1f, range.discreteValues[0]); ASSERT_FLOAT_EQ(0.2f, range.discreteValues[1]); ASSERT_FLOAT_EQ(0.3f, range.discreteValues[2]); } { SdpImageattrAttributeList::SRange range(ParseSRange("[0.1-0.2]")); ASSERT_EQ(0U, range.discreteValues.size()); ASSERT_FLOAT_EQ(0.1f, range.min); ASSERT_FLOAT_EQ(0.2f, range.max); } } TEST(NewSdpTestNoFixture, CheckImageattrSRangeParseInvalid) { ParseInvalid("", 0); ParseInvalid("[", 1); ParseInvalid("[v", 1); ParseInvalid("[-1", 1); ParseInvalid("[", 1); ParseInvalid("[-", 1); ParseInvalid("[v", 1); ParseInvalid("[ 0.2", 1); ParseInvalid("[10.1-", 5); ParseInvalid("[0.08-", 5); ParseInvalid("[0.2", 4); ParseInvalid("[0.2-", 5); ParseInvalid("[0.2-v", 5); ParseInvalid("[0.2--1", 5); ParseInvalid("[0.2-0.3", 8); ParseInvalid("[0.2-0.1]", 8); ParseInvalid("[0.2-0.3v", 8); ParseInvalid("[0.2,", 5); ParseInvalid("[0.2,v", 5); ParseInvalid("[0.2,-1", 5); ParseInvalid("[0.2]", 4); ParseInvalid("[0.2v", 4); ParseInvalid("[0.2,]", 5); ParseInvalid("[0.2,-]", 5); ParseInvalid(" ", 0); ParseInvalid("v", 0); ParseInvalid("-v", 0); ParseInvalid("-1", 0); } static SdpImageattrAttributeList::PRange ParsePRange(const std::string& input) { std::istringstream is(input + ","); std::string error; SdpImageattrAttributeList::PRange range; EXPECT_TRUE(range.Parse(is, &error)) << error; EXPECT_EQ(',', is.get()); EXPECT_EQ(EOF, is.get()); return range; } TEST(NewSdpTestNoFixture, CheckImageattrPRangeParseValid) { SdpImageattrAttributeList::PRange range(ParsePRange("[0.1000-9.9999]")); ASSERT_FLOAT_EQ(0.1f, range.min); ASSERT_FLOAT_EQ(9.9999f, range.max); } TEST(NewSdpTestNoFixture, CheckImageattrPRangeParseInvalid) { ParseInvalid("", 0); ParseInvalid("[", 1); ParseInvalid("[v", 1); ParseInvalid("[-1", 1); ParseInvalid("[", 1); ParseInvalid("[-", 1); ParseInvalid("[v", 1); ParseInvalid("[ 0.2", 1); ParseInvalid("[10.1-", 5); ParseInvalid("[0.08-", 5); ParseInvalid("[0.2", 4); ParseInvalid("[0.2-", 5); ParseInvalid("[0.2-v", 5); ParseInvalid("[0.2--1", 5); ParseInvalid("[0.2-0.3", 8); ParseInvalid("[0.2-0.1]", 8); ParseInvalid("[0.2-0.3v", 8); ParseInvalid("[0.2,", 4); ParseInvalid("[0.2:", 4); ParseInvalid("[0.2]", 4); ParseInvalid("[0.2v", 4); ParseInvalid(" ", 0); ParseInvalid("v", 0); ParseInvalid("-x", 0); ParseInvalid("-1", 0); } static SdpImageattrAttributeList::Set ParseSet(const std::string& input) { std::istringstream is(input + " "); std::string error; SdpImageattrAttributeList::Set set; EXPECT_TRUE(set.Parse(is, &error)) << error; EXPECT_EQ(' ', is.get()); EXPECT_EQ(EOF, is.get()); return set; } TEST(NewSdpTestNoFixture, CheckImageattrSetParseValid) { { SdpImageattrAttributeList::Set set(ParseSet("[x=320,y=240]")); ASSERT_EQ(1U, set.xRange.discreteValues.size()); ASSERT_EQ(320U, set.xRange.discreteValues[0]); ASSERT_EQ(1U, set.yRange.discreteValues.size()); ASSERT_EQ(240U, set.yRange.discreteValues[0]); ASSERT_FALSE(set.sRange.IsSet()); ASSERT_FALSE(set.pRange.IsSet()); ASSERT_FLOAT_EQ(0.5f, set.qValue); } { SdpImageattrAttributeList::Set set(ParseSet("[X=320,Y=240]")); ASSERT_EQ(1U, set.xRange.discreteValues.size()); ASSERT_EQ(320U, set.xRange.discreteValues[0]); ASSERT_EQ(1U, set.yRange.discreteValues.size()); ASSERT_EQ(240U, set.yRange.discreteValues[0]); ASSERT_FALSE(set.sRange.IsSet()); ASSERT_FALSE(set.pRange.IsSet()); ASSERT_FLOAT_EQ(0.5f, set.qValue); } { SdpImageattrAttributeList::Set set(ParseSet("[x=320,y=240,par=[0.1-0.2]]")); ASSERT_EQ(1U, set.xRange.discreteValues.size()); ASSERT_EQ(320U, set.xRange.discreteValues[0]); ASSERT_EQ(1U, set.yRange.discreteValues.size()); ASSERT_EQ(240U, set.yRange.discreteValues[0]); ASSERT_FALSE(set.sRange.IsSet()); ASSERT_TRUE(set.pRange.IsSet()); ASSERT_FLOAT_EQ(0.1f, set.pRange.min); ASSERT_FLOAT_EQ(0.2f, set.pRange.max); ASSERT_FLOAT_EQ(0.5f, set.qValue); } { SdpImageattrAttributeList::Set set(ParseSet("[x=320,y=240,sar=[0.1-0.2]]")); ASSERT_EQ(1U, set.xRange.discreteValues.size()); ASSERT_EQ(320U, set.xRange.discreteValues[0]); ASSERT_EQ(1U, set.yRange.discreteValues.size()); ASSERT_EQ(240U, set.yRange.discreteValues[0]); ASSERT_TRUE(set.sRange.IsSet()); ASSERT_FLOAT_EQ(0.1f, set.sRange.min); ASSERT_FLOAT_EQ(0.2f, set.sRange.max); ASSERT_FALSE(set.pRange.IsSet()); ASSERT_FLOAT_EQ(0.5f, set.qValue); } { SdpImageattrAttributeList::Set set(ParseSet("[x=320,y=240,q=0.1]")); ASSERT_EQ(1U, set.xRange.discreteValues.size()); ASSERT_EQ(320U, set.xRange.discreteValues[0]); ASSERT_EQ(1U, set.yRange.discreteValues.size()); ASSERT_EQ(240U, set.yRange.discreteValues[0]); ASSERT_FALSE(set.sRange.IsSet()); ASSERT_FALSE(set.pRange.IsSet()); ASSERT_FLOAT_EQ(0.1f, set.qValue); } { SdpImageattrAttributeList::Set set( ParseSet("[x=320,y=240,par=[0.1-0.2],sar=[0.3-0.4],q=0.6]")); ASSERT_EQ(1U, set.xRange.discreteValues.size()); ASSERT_EQ(320U, set.xRange.discreteValues[0]); ASSERT_EQ(1U, set.yRange.discreteValues.size()); ASSERT_EQ(240U, set.yRange.discreteValues[0]); ASSERT_TRUE(set.sRange.IsSet()); ASSERT_FLOAT_EQ(0.3f, set.sRange.min); ASSERT_FLOAT_EQ(0.4f, set.sRange.max); ASSERT_TRUE(set.pRange.IsSet()); ASSERT_FLOAT_EQ(0.1f, set.pRange.min); ASSERT_FLOAT_EQ(0.2f, set.pRange.max); ASSERT_FLOAT_EQ(0.6f, set.qValue); } { SdpImageattrAttributeList::Set set(ParseSet("[x=320,y=240,foo=bar,q=0.1]")); ASSERT_EQ(1U, set.xRange.discreteValues.size()); ASSERT_EQ(320U, set.xRange.discreteValues[0]); ASSERT_EQ(1U, set.yRange.discreteValues.size()); ASSERT_EQ(240U, set.yRange.discreteValues[0]); ASSERT_FALSE(set.sRange.IsSet()); ASSERT_FALSE(set.pRange.IsSet()); ASSERT_FLOAT_EQ(0.1f, set.qValue); } { SdpImageattrAttributeList::Set set( ParseSet("[x=320,y=240,foo=bar,q=0.1,bar=baz]")); ASSERT_EQ(1U, set.xRange.discreteValues.size()); ASSERT_EQ(320U, set.xRange.discreteValues[0]); ASSERT_EQ(1U, set.yRange.discreteValues.size()); ASSERT_EQ(240U, set.yRange.discreteValues[0]); ASSERT_FALSE(set.sRange.IsSet()); ASSERT_FALSE(set.pRange.IsSet()); ASSERT_FLOAT_EQ(0.1f, set.qValue); } { SdpImageattrAttributeList::Set set( ParseSet("[x=320,y=240,foo=[bar],q=0.1,bar=[baz]]")); ASSERT_EQ(1U, set.xRange.discreteValues.size()); ASSERT_EQ(320U, set.xRange.discreteValues[0]); ASSERT_EQ(1U, set.yRange.discreteValues.size()); ASSERT_EQ(240U, set.yRange.discreteValues[0]); ASSERT_FALSE(set.sRange.IsSet()); ASSERT_FALSE(set.pRange.IsSet()); ASSERT_FLOAT_EQ(0.1f, set.qValue); } { SdpImageattrAttributeList::Set set( ParseSet("[x=320,y=240,foo=[par=foo,sar=bar],q=0.1,bar=[baz]]")); ASSERT_EQ(1U, set.xRange.discreteValues.size()); ASSERT_EQ(320U, set.xRange.discreteValues[0]); ASSERT_EQ(1U, set.yRange.discreteValues.size()); ASSERT_EQ(240U, set.yRange.discreteValues[0]); ASSERT_FALSE(set.sRange.IsSet()); ASSERT_FALSE(set.pRange.IsSet()); ASSERT_FLOAT_EQ(0.1f, set.qValue); } } TEST(NewSdpTestNoFixture, CheckImageattrSetParseInvalid) { ParseInvalid("", 0); ParseInvalid("x", 0); ParseInvalid("[", 1); ParseInvalid("[=", 2); ParseInvalid("[x", 2); ParseInvalid("[y=", 3); ParseInvalid("[x=[", 4); ParseInvalid("[x=320", 6); ParseInvalid("[x=320v", 6); ParseInvalid("[x=320,", 7); ParseInvalid("[x=320,=", 8); ParseInvalid("[x=320,x", 8); ParseInvalid("[x=320,x=", 9); ParseInvalid("[x=320,y=[", 10); ParseInvalid("[x=320,y=240", 12); ParseInvalid("[x=320,y=240x", 12); ParseInvalid("[x=320,y=240,", 13); ParseInvalid("[x=320,y=240,q=", 15); ParseInvalid("[x=320,y=240,q=v", 15); ParseInvalid("[x=320,y=240,q=0.5", 18); ParseInvalid("[x=320,y=240,q=0.5,", 19); ParseInvalid("[x=320,y=240,q=0.5,]", 20); ParseInvalid("[x=320,y=240,q=0.5,=]", 20); ParseInvalid("[x=320,y=240,q=0.5,sar=v]", 23); ParseInvalid("[x=320,y=240,q=0.5,q=0.4", 21); ParseInvalid("[x=320,y=240,sar=", 17); ParseInvalid("[x=320,y=240,sar=v", 17); ParseInvalid( "[x=320,y=240,sar=[0.5-0.6],sar=[0.7-0.8]", 31); ParseInvalid("[x=320,y=240,par=", 17); ParseInvalid("[x=320,y=240,par=x", 17); ParseInvalid( "[x=320,y=240,par=[0.5-0.6],par=[0.7-0.8]", 31); ParseInvalid("[x=320,y=240,foo=", 17); ParseInvalid("[x=320,y=240,foo=x", 18); } static SdpImageattrAttributeList::Imageattr ParseImageattr( const std::string& input) { std::istringstream is(input); std::string error; SdpImageattrAttributeList::Imageattr imageattr; EXPECT_TRUE(imageattr.Parse(is, &error)) << error; EXPECT_TRUE(is.eof()); return imageattr; } TEST(NewSdpTestNoFixture, CheckImageattrParseValid) { { SdpImageattrAttributeList::Imageattr imageattr(ParseImageattr("* send *")); ASSERT_FALSE(imageattr.pt.isSome()); ASSERT_TRUE(imageattr.sendAll); ASSERT_TRUE(imageattr.sendSets.empty()); ASSERT_FALSE(imageattr.recvAll); ASSERT_TRUE(imageattr.recvSets.empty()); } { SdpImageattrAttributeList::Imageattr imageattr(ParseImageattr("* SEND *")); ASSERT_FALSE(imageattr.pt.isSome()); ASSERT_TRUE(imageattr.sendAll); ASSERT_TRUE(imageattr.sendSets.empty()); ASSERT_FALSE(imageattr.recvAll); ASSERT_TRUE(imageattr.recvSets.empty()); } { SdpImageattrAttributeList::Imageattr imageattr(ParseImageattr("* recv *")); ASSERT_FALSE(imageattr.pt.isSome()); ASSERT_FALSE(imageattr.sendAll); ASSERT_TRUE(imageattr.sendSets.empty()); ASSERT_TRUE(imageattr.recvAll); ASSERT_TRUE(imageattr.recvSets.empty()); } { SdpImageattrAttributeList::Imageattr imageattr(ParseImageattr("* RECV *")); ASSERT_FALSE(imageattr.pt.isSome()); ASSERT_FALSE(imageattr.sendAll); ASSERT_TRUE(imageattr.sendSets.empty()); ASSERT_TRUE(imageattr.recvAll); ASSERT_TRUE(imageattr.recvSets.empty()); } { SdpImageattrAttributeList::Imageattr imageattr( ParseImageattr("* recv * send *")); ASSERT_FALSE(imageattr.pt.isSome()); ASSERT_TRUE(imageattr.sendAll); ASSERT_TRUE(imageattr.sendSets.empty()); ASSERT_TRUE(imageattr.recvAll); ASSERT_TRUE(imageattr.recvSets.empty()); } { SdpImageattrAttributeList::Imageattr imageattr( ParseImageattr("* send * recv *")); ASSERT_FALSE(imageattr.pt.isSome()); ASSERT_TRUE(imageattr.sendAll); ASSERT_TRUE(imageattr.sendSets.empty()); ASSERT_TRUE(imageattr.recvAll); ASSERT_TRUE(imageattr.recvSets.empty()); } { SdpImageattrAttributeList::Imageattr imageattr( ParseImageattr("8 send * recv *")); ASSERT_EQ(8U, *imageattr.pt); ASSERT_TRUE(imageattr.sendAll); ASSERT_TRUE(imageattr.sendSets.empty()); ASSERT_TRUE(imageattr.recvAll); ASSERT_TRUE(imageattr.recvSets.empty()); } { SdpImageattrAttributeList::Imageattr imageattr( ParseImageattr("8 send [x=320,y=240] recv *")); ASSERT_EQ(8U, *imageattr.pt); ASSERT_FALSE(imageattr.sendAll); ASSERT_EQ(1U, imageattr.sendSets.size()); ASSERT_EQ(1U, imageattr.sendSets[0].xRange.discreteValues.size()); ASSERT_EQ(320U, imageattr.sendSets[0].xRange.discreteValues[0]); ASSERT_EQ(1U, imageattr.sendSets[0].yRange.discreteValues.size()); ASSERT_EQ(240U, imageattr.sendSets[0].yRange.discreteValues[0]); ASSERT_TRUE(imageattr.recvAll); ASSERT_TRUE(imageattr.recvSets.empty()); } { SdpImageattrAttributeList::Imageattr imageattr( ParseImageattr("8 send [x=320,y=240] [x=640,y=480] recv *")); ASSERT_EQ(8U, *imageattr.pt); ASSERT_FALSE(imageattr.sendAll); ASSERT_EQ(2U, imageattr.sendSets.size()); ASSERT_EQ(1U, imageattr.sendSets[0].xRange.discreteValues.size()); ASSERT_EQ(320U, imageattr.sendSets[0].xRange.discreteValues[0]); ASSERT_EQ(1U, imageattr.sendSets[0].yRange.discreteValues.size()); ASSERT_EQ(240U, imageattr.sendSets[0].yRange.discreteValues[0]); ASSERT_EQ(1U, imageattr.sendSets[1].xRange.discreteValues.size()); ASSERT_EQ(640U, imageattr.sendSets[1].xRange.discreteValues[0]); ASSERT_EQ(1U, imageattr.sendSets[1].yRange.discreteValues.size()); ASSERT_EQ(480U, imageattr.sendSets[1].yRange.discreteValues[0]); ASSERT_TRUE(imageattr.recvAll); ASSERT_TRUE(imageattr.recvSets.empty()); } { SdpImageattrAttributeList::Imageattr imageattr( ParseImageattr("8 send * recv [x=320,y=240]")); ASSERT_EQ(8U, *imageattr.pt); ASSERT_FALSE(imageattr.recvAll); ASSERT_EQ(1U, imageattr.recvSets.size()); ASSERT_EQ(1U, imageattr.recvSets[0].xRange.discreteValues.size()); ASSERT_EQ(320U, imageattr.recvSets[0].xRange.discreteValues[0]); ASSERT_EQ(1U, imageattr.recvSets[0].yRange.discreteValues.size()); ASSERT_EQ(240U, imageattr.recvSets[0].yRange.discreteValues[0]); ASSERT_TRUE(imageattr.sendAll); ASSERT_TRUE(imageattr.sendSets.empty()); } { SdpImageattrAttributeList::Imageattr imageattr( ParseImageattr("8 send * recv [x=320,y=240] [x=640,y=480]")); ASSERT_EQ(8U, *imageattr.pt); ASSERT_FALSE(imageattr.recvAll); ASSERT_EQ(2U, imageattr.recvSets.size()); ASSERT_EQ(1U, imageattr.recvSets[0].xRange.discreteValues.size()); ASSERT_EQ(320U, imageattr.recvSets[0].xRange.discreteValues[0]); ASSERT_EQ(1U, imageattr.recvSets[0].yRange.discreteValues.size()); ASSERT_EQ(240U, imageattr.recvSets[0].yRange.discreteValues[0]); ASSERT_EQ(1U, imageattr.recvSets[1].xRange.discreteValues.size()); ASSERT_EQ(640U, imageattr.recvSets[1].xRange.discreteValues[0]); ASSERT_EQ(1U, imageattr.recvSets[1].yRange.discreteValues.size()); ASSERT_EQ(480U, imageattr.recvSets[1].yRange.discreteValues[0]); ASSERT_TRUE(imageattr.sendAll); ASSERT_TRUE(imageattr.sendSets.empty()); } } TEST(NewSdpTestNoFixture, CheckImageattrParseInvalid) { ParseInvalid("", 0); ParseInvalid(" ", 0); ParseInvalid("-1", 0); ParseInvalid("99999 ", 5); ParseInvalid("*", 1); ParseInvalid("* sen", 5); ParseInvalid("* vcer *", 6); ParseInvalid("* send x", 7); ParseInvalid("* send [x=640,y=480] [", 22); ParseInvalid("* send * sen", 12); ParseInvalid("* send * vcer *", 13); ParseInvalid("* send * send *", 13); ParseInvalid("* recv * recv *", 13); ParseInvalid("* send * recv x", 14); ParseInvalid( "* send * recv [x=640,y=480] [", 29); ParseInvalid( "* send * recv [x=640,y=480] *", 28); ParseInvalid( "* send * recv [x=640,y=480] foobajooba", 28); } TEST(NewSdpTestNoFixture, CheckImageattrXYRangeSerialization) { SdpImageattrAttributeList::XYRange range; std::stringstream os; range.min = 320; range.max = 640; range.Serialize(os); ASSERT_EQ("[320:640]", os.str()); os.str(""); // clear range.step = 16; range.Serialize(os); ASSERT_EQ("[320:16:640]", os.str()); os.str(""); // clear range.min = 0; range.max = 0; range.discreteValues.push_back(320); range.Serialize(os); ASSERT_EQ("320", os.str()); os.str(""); range.discreteValues.push_back(640); range.Serialize(os); ASSERT_EQ("[320,640]", os.str()); } TEST(NewSdpTestNoFixture, CheckImageattrSRangeSerialization) { SdpImageattrAttributeList::SRange range; std::ostringstream os; range.min = 0.1f; range.max = 0.9999f; range.Serialize(os); ASSERT_EQ("[0.1000-0.9999]", os.str()); os.str(""); range.min = 0.0f; range.max = 0.0f; range.discreteValues.push_back(0.1f); range.Serialize(os); ASSERT_EQ("0.1000", os.str()); os.str(""); range.discreteValues.push_back(0.5f); range.Serialize(os); ASSERT_EQ("[0.1000,0.5000]", os.str()); } TEST(NewSdpTestNoFixture, CheckImageattrPRangeSerialization) { SdpImageattrAttributeList::PRange range; std::ostringstream os; range.min = 0.1f; range.max = 0.9999f; range.Serialize(os); ASSERT_EQ("[0.1000-0.9999]", os.str()); } TEST(NewSdpTestNoFixture, CheckImageattrSetSerialization) { SdpImageattrAttributeList::Set set; std::ostringstream os; set.xRange.discreteValues.push_back(640); set.yRange.discreteValues.push_back(480); set.Serialize(os); ASSERT_EQ("[x=640,y=480]", os.str()); os.str(""); set.qValue = 0.00f; set.Serialize(os); ASSERT_EQ("[x=640,y=480,q=0.00]", os.str()); os.str(""); set.qValue = 0.10f; set.Serialize(os); ASSERT_EQ("[x=640,y=480,q=0.10]", os.str()); os.str(""); set.qValue = 1.00f; set.Serialize(os); ASSERT_EQ("[x=640,y=480,q=1.00]", os.str()); os.str(""); set.sRange.discreteValues.push_back(1.1f); set.Serialize(os); ASSERT_EQ("[x=640,y=480,sar=1.1000,q=1.00]", os.str()); os.str(""); set.pRange.min = 0.9f; set.pRange.max = 1.1f; set.Serialize(os); ASSERT_EQ("[x=640,y=480,sar=1.1000,par=[0.9000-1.1000],q=1.00]", os.str()); os.str(""); } TEST(NewSdpTestNoFixture, CheckImageattrSerialization) { SdpImageattrAttributeList::Imageattr imageattr; std::ostringstream os; imageattr.sendAll = true; imageattr.pt = Some(8U); imageattr.Serialize(os); ASSERT_EQ("8 send *", os.str()); os.str(""); imageattr.pt.reset(); ; imageattr.Serialize(os); ASSERT_EQ("* send *", os.str()); os.str(""); imageattr.sendAll = false; imageattr.recvAll = true; imageattr.Serialize(os); ASSERT_EQ("* recv *", os.str()); os.str(""); imageattr.sendAll = true; imageattr.Serialize(os); ASSERT_EQ("* send * recv *", os.str()); os.str(""); imageattr.sendAll = false; imageattr.sendSets.push_back(SdpImageattrAttributeList::Set()); imageattr.sendSets.back().xRange.discreteValues.push_back(320); imageattr.sendSets.back().yRange.discreteValues.push_back(240); imageattr.Serialize(os); ASSERT_EQ("* send [x=320,y=240] recv *", os.str()); os.str(""); imageattr.sendSets.push_back(SdpImageattrAttributeList::Set()); imageattr.sendSets.back().xRange.discreteValues.push_back(640); imageattr.sendSets.back().yRange.discreteValues.push_back(480); imageattr.Serialize(os); ASSERT_EQ("* send [x=320,y=240] [x=640,y=480] recv *", os.str()); os.str(""); imageattr.recvAll = false; imageattr.recvSets.push_back(SdpImageattrAttributeList::Set()); imageattr.recvSets.back().xRange.discreteValues.push_back(320); imageattr.recvSets.back().yRange.discreteValues.push_back(240); imageattr.Serialize(os); ASSERT_EQ("* send [x=320,y=240] [x=640,y=480] recv [x=320,y=240]", os.str()); os.str(""); imageattr.recvSets.push_back(SdpImageattrAttributeList::Set()); imageattr.recvSets.back().xRange.discreteValues.push_back(640); imageattr.recvSets.back().yRange.discreteValues.push_back(480); imageattr.Serialize(os); ASSERT_EQ( "* send [x=320,y=240] [x=640,y=480] recv [x=320,y=240] [x=640,y=480]", os.str()); os.str(""); } TEST(NewSdpTestNoFixture, CheckSimulcastVersionSerialize) { std::ostringstream os; SdpSimulcastAttribute::Version version; version.choices.push_back(SdpSimulcastAttribute::Encoding("8", false)); version.Serialize(os); ASSERT_EQ("8", os.str()); os.str(""); version.choices.push_back(SdpSimulcastAttribute::Encoding("9", true)); version.Serialize(os); ASSERT_EQ("8,~9", os.str()); os.str(""); version.choices.push_back(SdpSimulcastAttribute::Encoding("0", false)); version.Serialize(os); ASSERT_EQ("8,~9,0", os.str()); os.str(""); } static SdpSimulcastAttribute::Version ParseSimulcastVersion( const std::string& input) { std::istringstream is(input + ";"); std::string error; SdpSimulcastAttribute::Version version; EXPECT_TRUE(version.Parse(is, &error)) << error; EXPECT_EQ(';', is.get()); EXPECT_EQ(EOF, is.get()); return version; } TEST(NewSdpTestNoFixture, CheckSimulcastVersionValidParse) { { SdpSimulcastAttribute::Version version(ParseSimulcastVersion("1")); ASSERT_EQ(1U, version.choices.size()); ASSERT_EQ("1", version.choices[0].rid); ASSERT_FALSE(version.choices[0].paused); } { SdpSimulcastAttribute::Version version(ParseSimulcastVersion("1,~2")); ASSERT_EQ(2U, version.choices.size()); ASSERT_EQ("1", version.choices[0].rid); ASSERT_EQ("2", version.choices[1].rid); ASSERT_FALSE(version.choices[0].paused); ASSERT_TRUE(version.choices[1].paused); } } TEST(NewSdpTestNoFixture, CheckSimulcastVersionInvalidParse) { ParseInvalid("", 0); ParseInvalid(",", 0); ParseInvalid(";", 0); ParseInvalid(" ", 0); ParseInvalid("8,", 2); ParseInvalid("8, ", 2); ParseInvalid("8,,", 2); ParseInvalid("8,;", 2); } TEST(NewSdpTestNoFixture, CheckSimulcastVersionsSerialize) { std::ostringstream os; SdpSimulcastAttribute::Versions versions; versions.push_back(SdpSimulcastAttribute::Version()); versions.back().choices.push_back( SdpSimulcastAttribute::Encoding("8", false)); versions.Serialize(os); ASSERT_EQ("8", os.str()); os.str(""); versions.push_back(SdpSimulcastAttribute::Version()); versions.Serialize(os); ASSERT_EQ("8", os.str()); os.str(""); versions.back().choices.push_back(SdpSimulcastAttribute::Encoding("9", true)); versions.Serialize(os); ASSERT_EQ("8;~9", os.str()); os.str(""); versions.push_back(SdpSimulcastAttribute::Version()); versions.back().choices.push_back( SdpSimulcastAttribute::Encoding("0", false)); versions.Serialize(os); ASSERT_EQ("8;~9;0", os.str()); os.str(""); } static SdpSimulcastAttribute::Versions ParseSimulcastVersions( const std::string& input) { std::istringstream is(input + " "); std::string error; SdpSimulcastAttribute::Versions list; EXPECT_TRUE(list.Parse(is, &error)) << error; EXPECT_EQ(' ', is.get()); EXPECT_EQ(EOF, is.get()); return list; } TEST(NewSdpTestNoFixture, CheckSimulcastVersionsValidParse) { { SdpSimulcastAttribute::Versions versions(ParseSimulcastVersions("8")); ASSERT_EQ(1U, versions.size()); ASSERT_EQ(1U, versions[0].choices.size()); ASSERT_EQ("8", versions[0].choices[0].rid); ASSERT_FALSE(versions[0].choices[0].paused); } { SdpSimulcastAttribute::Versions versions(ParseSimulcastVersions("~8,9")); ASSERT_EQ(1U, versions.size()); ASSERT_EQ(2U, versions[0].choices.size()); ASSERT_EQ("8", versions[0].choices[0].rid); ASSERT_EQ("9", versions[0].choices[1].rid); ASSERT_TRUE(versions[0].choices[0].paused); ASSERT_FALSE(versions[0].choices[1].paused); } { SdpSimulcastAttribute::Versions versions(ParseSimulcastVersions("8,9;~10")); ASSERT_EQ(2U, versions.size()); ASSERT_EQ(2U, versions[0].choices.size()); ASSERT_EQ("8", versions[0].choices[0].rid); ASSERT_EQ("9", versions[0].choices[1].rid); ASSERT_FALSE(versions[0].choices[0].paused); ASSERT_FALSE(versions[0].choices[1].paused); ASSERT_EQ(1U, versions[1].choices.size()); ASSERT_EQ("10", versions[1].choices[0].rid); ASSERT_TRUE(versions[1].choices[0].paused); } } TEST(NewSdpTestNoFixture, CheckSimulcastVersionsInvalidParse) { ParseInvalid("", 0); ParseInvalid(";", 0); ParseInvalid("8;", 2); ParseInvalid("8;;", 2); } TEST(NewSdpTestNoFixture, CheckSimulcastSerialize) { std::ostringstream os; SdpSimulcastAttribute simulcast; simulcast.recvVersions.push_back(SdpSimulcastAttribute::Version()); simulcast.recvVersions.back().choices.push_back( SdpSimulcastAttribute::Encoding("8", false)); simulcast.Serialize(os); ASSERT_EQ("a=simulcast:recv 8" CRLF, os.str()); os.str(""); simulcast.sendVersions.push_back(SdpSimulcastAttribute::Version()); simulcast.sendVersions.back().choices.push_back( SdpSimulcastAttribute::Encoding("9", true)); simulcast.Serialize(os); ASSERT_EQ("a=simulcast:send ~9 recv 8" CRLF, os.str()); } static SdpSimulcastAttribute ParseSimulcast(const std::string& input) { std::istringstream is(input); std::string error; SdpSimulcastAttribute simulcast; EXPECT_TRUE(simulcast.Parse(is, &error)) << error; EXPECT_TRUE(is.eof()); return simulcast; } TEST(NewSdpTestNoFixture, CheckSimulcastValidParse) { { SdpSimulcastAttribute simulcast(ParseSimulcast("send 8")); ASSERT_EQ(1U, simulcast.sendVersions.size()); ASSERT_EQ(1U, simulcast.sendVersions[0].choices.size()); ASSERT_EQ("8", simulcast.sendVersions[0].choices[0].rid); ASSERT_FALSE(simulcast.sendVersions[0].choices[0].paused); ASSERT_EQ(0U, simulcast.recvVersions.size()); } { SdpSimulcastAttribute simulcast(ParseSimulcast(" SEND 8")); ASSERT_EQ(1U, simulcast.sendVersions.size()); ASSERT_EQ(1U, simulcast.sendVersions[0].choices.size()); ASSERT_EQ("8", simulcast.sendVersions[0].choices[0].rid); ASSERT_FALSE(simulcast.sendVersions[0].choices[0].paused); ASSERT_EQ(0U, simulcast.recvVersions.size()); } { SdpSimulcastAttribute simulcast(ParseSimulcast("recv 8")); ASSERT_EQ(1U, simulcast.recvVersions.size()); ASSERT_EQ(1U, simulcast.recvVersions[0].choices.size()); ASSERT_EQ("8", simulcast.recvVersions[0].choices[0].rid); ASSERT_FALSE(simulcast.recvVersions[0].choices[0].paused); ASSERT_EQ(0U, simulcast.sendVersions.size()); } { SdpSimulcastAttribute simulcast( ParseSimulcast("send 8,9;~101;97,~98 recv 101,120;97")); ASSERT_EQ(3U, simulcast.sendVersions.size()); ASSERT_EQ(2U, simulcast.sendVersions[0].choices.size()); ASSERT_EQ("8", simulcast.sendVersions[0].choices[0].rid); ASSERT_EQ("9", simulcast.sendVersions[0].choices[1].rid); ASSERT_FALSE(simulcast.sendVersions[0].choices[0].paused); ASSERT_FALSE(simulcast.sendVersions[0].choices[1].paused); ASSERT_EQ(1U, simulcast.sendVersions[1].choices.size()); ASSERT_EQ("101", simulcast.sendVersions[1].choices[0].rid); ASSERT_TRUE(simulcast.sendVersions[1].choices[0].paused); ASSERT_EQ(2U, simulcast.sendVersions[2].choices.size()); ASSERT_EQ("97", simulcast.sendVersions[2].choices[0].rid); ASSERT_EQ("98", simulcast.sendVersions[2].choices[1].rid); ASSERT_FALSE(simulcast.sendVersions[2].choices[0].paused); ASSERT_TRUE(simulcast.sendVersions[2].choices[1].paused); ASSERT_EQ(2U, simulcast.recvVersions.size()); ASSERT_EQ(2U, simulcast.recvVersions[0].choices.size()); ASSERT_EQ("101", simulcast.recvVersions[0].choices[0].rid); ASSERT_EQ("120", simulcast.recvVersions[0].choices[1].rid); ASSERT_EQ(1U, simulcast.recvVersions[1].choices.size()); ASSERT_EQ("97", simulcast.recvVersions[1].choices[0].rid); } } TEST(NewSdpTestNoFixture, CheckSimulcastInvalidParse) { ParseInvalid("", 0); ParseInvalid(" ", 1); ParseInvalid("vcer ", 4); ParseInvalid(" send 8 send ", 12); ParseInvalid(" recv 8 recv ", 12); } static SdpRidAttributeList::Rid ParseRid(const std::string& input) { std::istringstream is(input); std::string error; SdpRidAttributeList::Rid rid; EXPECT_TRUE(rid.Parse(is, &error)) << error; EXPECT_TRUE(is.eof()); return rid; } TEST(NewSdpTestNoFixture, CheckRidValidParse) { { SdpRidAttributeList::Rid rid(ParseRid("1 send")); ASSERT_EQ("1", rid.id); ASSERT_EQ(sdp::kSend, rid.direction); ASSERT_EQ(0U, rid.formats.size()); ASSERT_EQ(0U, rid.constraints.maxWidth); ASSERT_EQ(0U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(0U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(0U, rid.dependIds.size()); } { SdpRidAttributeList::Rid rid(ParseRid("1 send pt=96;max-width=800")); ASSERT_EQ("1", rid.id); ASSERT_EQ(sdp::kSend, rid.direction); ASSERT_EQ(1U, rid.formats.size()); ASSERT_EQ(96U, rid.formats[0]); ASSERT_EQ(800U, rid.constraints.maxWidth); ASSERT_EQ(0U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(0U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(0U, rid.dependIds.size()); } { SdpRidAttributeList::Rid rid(ParseRid("1 send pt=96,97,98;max-width=800")); ASSERT_EQ("1", rid.id); ASSERT_EQ(sdp::kSend, rid.direction); ASSERT_EQ(3U, rid.formats.size()); ASSERT_EQ(96U, rid.formats[0]); ASSERT_EQ(97U, rid.formats[1]); ASSERT_EQ(98U, rid.formats[2]); ASSERT_EQ(800U, rid.constraints.maxWidth); ASSERT_EQ(0U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(0U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(0U, rid.dependIds.size()); } { SdpRidAttributeList::Rid rid(ParseRid("foo send")); ASSERT_EQ(0U, rid.formats.size()); ASSERT_EQ(0U, rid.constraints.maxWidth); ASSERT_EQ(0U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(0U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(0U, rid.dependIds.size()); } { SdpRidAttributeList::Rid rid(ParseRid("foo send pt=96")); ASSERT_EQ(1U, rid.formats.size()); ASSERT_EQ(96U, rid.formats[0]); ASSERT_EQ(0U, rid.constraints.maxWidth); ASSERT_EQ(0U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(0U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(0U, rid.dependIds.size()); } // This is not technically permitted by the BNF, but the parse code is simpler // if we allow it. If we decide to stop allowing this, this will need to be // converted to an invalid parse test-case. { SdpRidAttributeList::Rid rid(ParseRid("foo send max-br=30000;pt=96")); ASSERT_EQ(1U, rid.formats.size()); ASSERT_EQ(96U, rid.formats[0]); ASSERT_EQ(0U, rid.constraints.maxWidth); ASSERT_EQ(0U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(30000U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(0U, rid.dependIds.size()); } { SdpRidAttributeList::Rid rid(ParseRid("foo send pt=96,97,98")); ASSERT_EQ(3U, rid.formats.size()); ASSERT_EQ(96U, rid.formats[0]); ASSERT_EQ(97U, rid.formats[1]); ASSERT_EQ(98U, rid.formats[2]); ASSERT_EQ(0U, rid.constraints.maxWidth); ASSERT_EQ(0U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(0U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(0U, rid.dependIds.size()); } { SdpRidAttributeList::Rid rid(ParseRid("foo send max-width=800")); ASSERT_EQ(0U, rid.formats.size()); ASSERT_EQ(800U, rid.constraints.maxWidth); ASSERT_EQ(0U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(0U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(0U, rid.dependIds.size()); } { SdpRidAttributeList::Rid rid(ParseRid("foo send max-height=640")); ASSERT_EQ(0U, rid.formats.size()); ASSERT_EQ(0U, rid.constraints.maxWidth); ASSERT_EQ(640U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(0U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(0U, rid.dependIds.size()); } { SdpRidAttributeList::Rid rid(ParseRid("foo send max-fps=30")); ASSERT_EQ(0U, rid.formats.size()); ASSERT_EQ(0U, rid.constraints.maxWidth); ASSERT_EQ(0U, rid.constraints.maxHeight); ASSERT_EQ(30.0, *rid.constraints.maxFps); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(0U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(0U, rid.dependIds.size()); } { SdpRidAttributeList::Rid rid(ParseRid("foo send max-fs=3600")); ASSERT_EQ(0U, rid.formats.size()); ASSERT_EQ(0U, rid.constraints.maxWidth); ASSERT_EQ(0U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(3600U, rid.constraints.maxFs); ASSERT_EQ(0U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(0U, rid.dependIds.size()); } { SdpRidAttributeList::Rid rid(ParseRid("foo send max-br=30000")); ASSERT_EQ(0U, rid.formats.size()); ASSERT_EQ(0U, rid.constraints.maxWidth); ASSERT_EQ(0U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(30000U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(0U, rid.dependIds.size()); } { SdpRidAttributeList::Rid rid(ParseRid("foo send max-pps=9216000")); ASSERT_EQ(0U, rid.formats.size()); ASSERT_EQ(0U, rid.constraints.maxWidth); ASSERT_EQ(0U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(0U, rid.constraints.maxBr); ASSERT_EQ(9216000U, rid.constraints.maxPps); ASSERT_EQ(0U, rid.dependIds.size()); } { SdpRidAttributeList::Rid rid(ParseRid("foo send depend=foo")); ASSERT_EQ(0U, rid.formats.size()); ASSERT_EQ(0U, rid.constraints.maxWidth); ASSERT_EQ(0U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(0U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(1U, rid.dependIds.size()); ASSERT_EQ("foo", rid.dependIds[0]); } { SdpRidAttributeList::Rid rid(ParseRid("foo send max-foo=20")); ASSERT_EQ(0U, rid.formats.size()); ASSERT_EQ(0U, rid.constraints.maxWidth); ASSERT_EQ(0U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(0U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(0U, rid.dependIds.size()); } { SdpRidAttributeList::Rid rid(ParseRid("foo send depend=foo,bar")); ASSERT_EQ(0U, rid.formats.size()); ASSERT_EQ(0U, rid.constraints.maxWidth); ASSERT_EQ(0U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(0U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(2U, rid.dependIds.size()); ASSERT_EQ("foo", rid.dependIds[0]); ASSERT_EQ("bar", rid.dependIds[1]); } { SdpRidAttributeList::Rid rid( ParseRid("foo send max-width=800;max-height=600")); ASSERT_EQ(0U, rid.formats.size()); ASSERT_EQ(800U, rid.constraints.maxWidth); ASSERT_EQ(600U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(0U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(0U, rid.dependIds.size()); } { SdpRidAttributeList::Rid rid( ParseRid("foo send pt=96,97;max-width=800;max-height=600")); ASSERT_EQ(2U, rid.formats.size()); ASSERT_EQ(96U, rid.formats[0]); ASSERT_EQ(97U, rid.formats[1]); ASSERT_EQ(800U, rid.constraints.maxWidth); ASSERT_EQ(600U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(0U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(0U, rid.dependIds.size()); } { SdpRidAttributeList::Rid rid( ParseRid("foo send depend=foo,bar;max-width=800;max-height=600")); ASSERT_EQ(0U, rid.formats.size()); ASSERT_EQ(800U, rid.constraints.maxWidth); ASSERT_EQ(600U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(0U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(2U, rid.dependIds.size()); ASSERT_EQ("foo", rid.dependIds[0]); ASSERT_EQ("bar", rid.dependIds[1]); } { SdpRidAttributeList::Rid rid( ParseRid("foo send max-foo=20;max-width=800;max-height=600")); ASSERT_EQ(0U, rid.formats.size()); ASSERT_EQ(800U, rid.constraints.maxWidth); ASSERT_EQ(600U, rid.constraints.maxHeight); ASSERT_FALSE(rid.constraints.maxFps.isSome()); ASSERT_EQ(0U, rid.constraints.maxFs); ASSERT_EQ(0U, rid.constraints.maxBr); ASSERT_EQ(0U, rid.constraints.maxPps); ASSERT_EQ(0U, rid.dependIds.size()); } } TEST(NewSdpTestNoFixture, CheckRidInvalidParse) { ParseInvalid("", 0); ParseInvalid(" ", 0); ParseInvalid("foo", 3); ParseInvalid("foo ", 4); ParseInvalid("foo ", 5); ParseInvalid("foo bar", 7); ParseInvalid("foo recv ", 9); ParseInvalid("foo recv pt=", 12); ParseInvalid(" ", 0); ParseInvalid("foo send pt", 11); ParseInvalid("foo send pt=", 12); ParseInvalid("foo send pt=x", 12); ParseInvalid("foo send pt=-1", 12); ParseInvalid("foo send pt=96,", 15); ParseInvalid("foo send pt=196", 15); ParseInvalid("foo send max-width", 18); ParseInvalid("foo send max-width=", 19); ParseInvalid("foo send max-width=x", 19); ParseInvalid("foo send max-width=-1", 19); ParseInvalid("foo send max-width=800;", 23); ParseInvalid("foo send max-width=800; ", 24); ParseInvalid("foo send depend=", 16); ParseInvalid("foo send depend=,", 16); ParseInvalid("foo send depend=1,", 18); ParseInvalid("0123456789az-_", 14); } TEST(NewSdpTestNoFixture, CheckRidSerialize) { { SdpRidAttributeList::Rid rid; rid.id = "foo"; rid.direction = sdp::kSend; std::ostringstream os; rid.Serialize(os); ASSERT_EQ("foo send", os.str()); } { SdpRidAttributeList::Rid rid; rid.id = "foo"; rid.direction = sdp::kSend; std::ostringstream os; rid.Serialize(os); ASSERT_EQ("foo send", os.str()); } { SdpRidAttributeList::Rid rid; rid.id = "foo"; rid.direction = sdp::kSend; rid.formats.push_back(96); std::ostringstream os; rid.Serialize(os); ASSERT_EQ("foo send pt=96", os.str()); } { SdpRidAttributeList::Rid rid; rid.id = "foo"; rid.direction = sdp::kSend; rid.formats.push_back(96); rid.formats.push_back(97); std::ostringstream os; rid.Serialize(os); ASSERT_EQ("foo send pt=96,97", os.str()); } { SdpRidAttributeList::Rid rid; rid.id = "foo"; rid.direction = sdp::kSend; rid.constraints.maxWidth = 800; std::ostringstream os; rid.Serialize(os); ASSERT_EQ("foo send max-width=800", os.str()); } { SdpRidAttributeList::Rid rid; rid.id = "foo"; rid.direction = sdp::kSend; rid.constraints.maxHeight = 600; std::ostringstream os; rid.Serialize(os); ASSERT_EQ("foo send max-height=600", os.str()); } { SdpRidAttributeList::Rid rid; rid.id = "foo"; rid.direction = sdp::kSend; rid.constraints.maxFps = Some(30); std::ostringstream os; rid.Serialize(os); ASSERT_EQ("foo send max-fps=30", os.str()); } { SdpRidAttributeList::Rid rid; rid.id = "foo"; rid.direction = sdp::kSend; rid.constraints.maxFs = 3600; std::ostringstream os; rid.Serialize(os); ASSERT_EQ("foo send max-fs=3600", os.str()); } { SdpRidAttributeList::Rid rid; rid.id = "foo"; rid.direction = sdp::kSend; rid.constraints.maxBr = 30000; std::ostringstream os; rid.Serialize(os); ASSERT_EQ("foo send max-br=30000", os.str()); } { SdpRidAttributeList::Rid rid; rid.id = "foo"; rid.direction = sdp::kSend; rid.constraints.maxPps = 9216000; std::ostringstream os; rid.Serialize(os); ASSERT_EQ("foo send max-pps=9216000", os.str()); } { SdpRidAttributeList::Rid rid; rid.id = "foo"; rid.direction = sdp::kSend; rid.dependIds.push_back("foo"); std::ostringstream os; rid.Serialize(os); ASSERT_EQ("foo send depend=foo", os.str()); } { SdpRidAttributeList::Rid rid; rid.id = "foo"; rid.direction = sdp::kSend; rid.dependIds.push_back("foo"); rid.dependIds.push_back("bar"); std::ostringstream os; rid.Serialize(os); ASSERT_EQ("foo send depend=foo,bar", os.str()); } { SdpRidAttributeList::Rid rid; rid.id = "foo"; rid.direction = sdp::kSend; rid.formats.push_back(96); rid.constraints.maxBr = 30000; std::ostringstream os; rid.Serialize(os); ASSERT_EQ("foo send pt=96;max-br=30000", os.str()); } } TEST_F(SdpTest, hugeSdp) { std::string offer = "v=0\r\n" "o=- 1109973417102828257 2 IN IP4 127.0.0.1\r\n" "s=-\r\n" "t=0 0\r\n" "a=group:BUNDLE audio video\r\n" "a=msid-semantic: WMS 1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIP\r\n" "m=audio 32952 UDP/TLS/RTP/SAVPF 111 103 104 0 8 107 106 105 13 126\r\n" "c=IN IP4 128.64.32.16\r\n" "a=rtcp:32952 IN IP4 128.64.32.16\r\n" "a=candidate:77142221 1 udp 2113937151 192.168.137.1 54081 typ host " "generation 0\r\n" "a=candidate:77142221 2 udp 2113937151 192.168.137.1 54081 typ host " "generation 0\r\n" "a=candidate:983072742 1 udp 2113937151 172.22.0.56 54082 typ host " "generation 0\r\n" "a=candidate:983072742 2 udp 2113937151 172.22.0.56 54082 typ host " "generation 0\r\n" "a=candidate:2245074553 1 udp 1845501695 32.64.128.1 62397 typ srflx " "raddr 192.168.137.1 rport 54081 generation 0\r\n" "a=candidate:2245074553 2 udp 1845501695 32.64.128.1 62397 typ srflx " "raddr 192.168.137.1 rport 54081 generation 0\r\n" "a=candidate:2479353907 1 udp 1845501695 32.64.128.1 54082 typ srflx " "raddr 172.22.0.56 rport 54082 generation 0\r\n" "a=candidate:2479353907 2 udp 1845501695 32.64.128.1 54082 typ srflx " "raddr 172.22.0.56 rport 54082 generation 0\r\n" "a=candidate:1243276349 1 tcp 1509957375 192.168.137.1 0 typ host " "generation 0\r\n" "a=candidate:1243276349 2 tcp 1509957375 192.168.137.1 0 typ host " "generation 0\r\n" "a=candidate:1947960086 1 tcp 1509957375 172.22.0.56 0 typ host " "generation 0\r\n" "a=candidate:1947960086 2 tcp 1509957375 172.22.0.56 0 typ host " "generation 0\r\n" "a=candidate:1808221584 1 udp 33562367 128.64.32.16 32952 typ relay " "raddr 32.64.128.1 rport 62398 generation 0\r\n" "a=candidate:1808221584 2 udp 33562367 128.64.32.16 32952 typ relay " "raddr 32.64.128.1 rport 62398 generation 0\r\n" "a=candidate:507872740 1 udp 33562367 128.64.32.16 40975 typ relay raddr " "32.64.128.1 rport 54085 generation 0\r\n" "a=candidate:507872740 2 udp 33562367 128.64.32.16 40975 typ relay raddr " "32.64.128.1 rport 54085 generation 0\r\n" "a=ice-ufrag:xQuJwjX3V3eMA81k\r\n" "a=ice-pwd:ZUiRmjS2GDhG140p73dAsSVP\r\n" "a=ice-options:google-ice\r\n" "a=fingerprint:sha-256 " "59:4A:8B:73:A7:73:53:71:88:D7:4D:58:28:0C:79:72:31:29:9B:05:37:DD:58:43:" "C2:D4:85:A2:B3:66:38:7A\r\n" "a=setup:active\r\n" "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n" "a=sendrecv\r\n" "a=mid:audio\r\n" "a=rtcp-mux\r\n" "a=crypto:1 AES_CM_128_HMAC_SHA1_80 " "inline:/U44g3ULdtapeiSg+T3n6dDLBKIjpOhb/NXAL/2b\r\n" "a=rtpmap:111 opus/48000/2\r\n" "a=fmtp:111 minptime=10\r\n" "a=rtpmap:103 ISAC/16000\r\n" "a=rtpmap:104 ISAC/32000\r\n" "a=rtpmap:0 PCMU/8000\r\n" "a=rtpmap:8 PCMA/8000\r\n" "a=rtpmap:107 CN/48000\r\n" "a=rtpmap:106 CN/32000\r\n" "a=rtpmap:105 CN/16000\r\n" "a=rtpmap:13 CN/8000\r\n" "a=rtpmap:126 telephone-event/8000\r\n" "a=maxptime:60\r\n" "a=ssrc:2271517329 cname:mKDNt7SQf6pwDlIn\r\n" "a=ssrc:2271517329 msid:1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIP " "1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIPa0\r\n" "a=ssrc:2271517329 mslabel:1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIP\r\n" "a=ssrc:2271517329 label:1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIPa0\r\n" "m=video 32952 UDP/TLS/RTP/SAVPF 100 116 117\r\n" "c=IN IP4 128.64.32.16\r\n" "a=rtcp:32952 IN IP4 128.64.32.16\r\n" "a=candidate:77142221 1 udp 2113937151 192.168.137.1 54081 typ host " "generation 0\r\n" "a=candidate:77142221 2 udp 2113937151 192.168.137.1 54081 typ host " "generation 0\r\n" "a=candidate:983072742 1 udp 2113937151 172.22.0.56 54082 typ host " "generation 0\r\n" "a=candidate:983072742 2 udp 2113937151 172.22.0.56 54082 typ host " "generation 0\r\n" "a=candidate:2245074553 1 udp 1845501695 32.64.128.1 62397 typ srflx " "raddr 192.168.137.1 rport 54081 generation 0\r\n" "a=candidate:2245074553 2 udp 1845501695 32.64.128.1 62397 typ srflx " "raddr 192.168.137.1 rport 54081 generation 0\r\n" "a=candidate:2479353907 1 udp 1845501695 32.64.128.1 54082 typ srflx " "raddr 172.22.0.56 rport 54082 generation 0\r\n" "a=candidate:2479353907 2 udp 1845501695 32.64.128.1 54082 typ srflx " "raddr 172.22.0.56 rport 54082 generation 0\r\n" "a=candidate:1243276349 1 tcp 1509957375 192.168.137.1 0 typ host " "generation 0\r\n" "a=candidate:1243276349 2 tcp 1509957375 192.168.137.1 0 typ host " "generation 0\r\n" "a=candidate:1947960086 1 tcp 1509957375 172.22.0.56 0 typ host " "generation 0\r\n" "a=candidate:1947960086 2 tcp 1509957375 172.22.0.56 0 typ host " "generation 0\r\n" "a=candidate:1808221584 1 udp 33562367 128.64.32.16 32952 typ relay " "raddr 32.64.128.1 rport 62398 generation 0\r\n" "a=candidate:1808221584 2 udp 33562367 128.64.32.16 32952 typ relay " "raddr 32.64.128.1 rport 62398 generation 0\r\n" "a=candidate:507872740 1 udp 33562367 128.64.32.16 40975 typ relay raddr " "32.64.128.1 rport 54085 generation 0\r\n" "a=candidate:507872740 2 udp 33562367 128.64.32.16 40975 typ relay raddr " "32.64.128.1 rport 54085 generation 0\r\n" "a=ice-ufrag:xQuJwjX3V3eMA81k\r\n" "a=ice-pwd:ZUiRmjS2GDhG140p73dAsSVP\r\n" "a=ice-options:google-ice\r\n" "a=fingerprint:sha-256 " "59:4A:8B:73:A7:73:53:71:88:D7:4D:58:28:0C:79:72:31:29:9B:05:37:DD:58:43:" "C2:D4:85:A2:B3:66:38:7A\r\n" "a=setup:active\r\n" "a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n" "a=extmap:3 " "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n" "a=extmap:6 " "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\n" "a=sendrecv\r\n" "a=mid:video\r\n" "a=rtcp-mux\r\n" "a=crypto:1 AES_CM_128_HMAC_SHA1_80 " "inline:/U44g3ULdtapeiSg+T3n6dDLBKIjpOhb/NXAL/2b\r\n" "a=rtpmap:100 VP8/90000\r\n" "a=rtcp-fb:100 ccm fir\r\n" "a=rtcp-fb:100 nack\r\n" "a=rtcp-fb:100 goog-remb\r\n" "a=rtpmap:116 red/90000\r\n" "a=rtpmap:117 ulpfec/90000\r\n" "a=ssrc:54724160 cname:mKDNt7SQf6pwDlIn\r\n" "a=ssrc:54724160 msid:1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIP " "1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIPv0\r\n" "a=ssrc:54724160 mslabel:1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIP\r\n" "a=ssrc:54724160 label:1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIPv0\r\n"; ParseSdp(offer); } TEST_P(NewSdpTest, CheckSsrcGroup) { ParseSdp(kVideoSdp + "a=ssrc-group:\r\n", false); ParseSdp(kVideoSdp + "a=ssrc-group:BLAH\r\n", false); ParseSdp(kVideoSdp + "a=ssrc-group:FID\r\n", false); ParseSdp(kVideoSdp + "a=ssrc-group:FID 1 2 3 four\r\n", false); ParseSdp(kVideoSdp + "a=ssrc-group:FID 1\r\n"); std::array gigantic = {{0}}; gigantic.fill('A'); gigantic[gigantic.size() - 1] = 0; ParseSdp(kVideoSdp + "a=ssrc-group:" + gigantic.data() + "\r\n", false); ParseSdp(kVideoSdp + "a=ssrc-group:fid 1\r\n"); ParseSdp(kVideoSdp + "a=ssrc-group:FID \r\n", false); ParseSdp(kVideoSdp + "a=ssrc-group:FID 0\r\n"); ParseSdp(kVideoSdp + "a=ssrc-group:FID 9999999999\r\n", false); ParseSdp(kVideoSdp + "a=ssrc-group:FID 99999999999999999999\r\n", false); ParseSdp(kVideoSdp + "a=ssrc-group:FID 1twothree\r\n", false); ParseSdp(kVideoSdp + "a=ssrc-group:FID -1\r\n", false); ParseSdp(kVideoSdp + "a=ssrc-group:FID 3156517279 2673335628\r\n"); ASSERT_EQ(1U, Sdp()->GetMediaSectionCount()); const SdpSsrcGroupAttributeList& group = Sdp()->GetMediaSection(0).GetAttributeList().GetSsrcGroup(); ASSERT_EQ(1U, group.mSsrcGroups.size()); ASSERT_EQ(SdpSsrcGroupAttributeList::Semantics::kFid, group.mSsrcGroups[0].semantics); ASSERT_EQ(2U, group.mSsrcGroups[0].ssrcs.size()); ASSERT_EQ(3156517279U, group.mSsrcGroups[0].ssrcs[0]); ASSERT_EQ(2673335628U, group.mSsrcGroups[0].ssrcs[1]); } TEST_P(NewSdpTest, CheckSsrcGroupSerialization) { std::vector ssrcs = {3156517279U, 2673335628U}; { SdpSsrcGroupAttributeList list; list.PushEntry(SdpSsrcGroupAttributeList::Semantics::kFec, ssrcs); CheckSerialize("a=ssrc-group:FEC 3156517279 2673335628\r\n", list); } { SdpSsrcGroupAttributeList list; list.PushEntry(SdpSsrcGroupAttributeList::Semantics::kFid, ssrcs); CheckSerialize("a=ssrc-group:FID 3156517279 2673335628\r\n", list); } { SdpSsrcGroupAttributeList list; list.PushEntry(SdpSsrcGroupAttributeList::Semantics::kFecFr, ssrcs); CheckSerialize("a=ssrc-group:FEC-FR 3156517279 2673335628\r\n", list); } { SdpSsrcGroupAttributeList list; list.PushEntry(SdpSsrcGroupAttributeList::Semantics::kDup, ssrcs); CheckSerialize("a=ssrc-group:DUP 3156517279 2673335628\r\n", list); } { SdpSsrcGroupAttributeList list; list.PushEntry(SdpSsrcGroupAttributeList::Semantics::kSim, ssrcs); CheckSerialize("a=ssrc-group:SIM 3156517279 2673335628\r\n", list); } } TEST_P(NewSdpTest, CheckRtxApt) { ParseSdp(kVideoSdp + "a=rtpmap:96 rtx/90000\r\na=fmtp:96", false); ParseSdp(kVideoSdp + "a=rtpmap:96 rtx/90000\r\na=fmtp:96 apt=120; apt=124\r\n"); ParseSdp(kVideoSdp + "a=rtpmap:96 rtx/90000\r\na=fmtp:96 apt=", false); ParseSdp(kVideoSdp + "a=rtpmap:96 rtx/90000\r\na=fmtp:96 apt=blah", false); ParseSdp(kVideoSdp + "a=rtpmap:96 rtx/90000\r\na=fmtp:96 " "apt=999999999999999999999999999999", false); ParseSdp(kVideoSdp + "a=rtpmap:96 rtx/90000\r\na=fmtp:96 apt=-120\r\n", false); ParseSdp(kVideoSdp + "a=rtpmap:96 rtx/90000\r\na=fmtp:96 apt=1twenty\r\n", false); ParseSdp(kVideoSdp + "a=rtpmap:96 rtx/90000\r\na=fmtp:96 APT=120\r\n"); ParseSdp(kVideoSdp + "a=rtpmap:96 rtx/90000\r\na=fmtp:96 apt=120\r\n"); const SdpMediaSection& msec = Sdp()->GetMediaSection(0); ASSERT_EQ(1U, msec.GetFormats().size()); ASSERT_EQ(2U, msec.GetAttributeList().GetRtpmap().mRtpmaps.size()); const SdpRtpmapAttributeList::Rtpmap& rtpmap = msec.GetAttributeList().GetRtpmap().GetEntry("96"); ASSERT_EQ(rtpmap.pt, "96"); ASSERT_EQ(rtpmap.codec, SdpRtpmapAttributeList::CodecType::kRtx); ASSERT_EQ(rtpmap.name, "rtx"); ASSERT_EQ(rtpmap.clock, 90000U); ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kFmtpAttribute)); const auto& format_params = msec.GetAttributeList().GetFmtp().mFmtps; ASSERT_EQ(1U, format_params.size()); ASSERT_EQ("96", format_params[0].format); ASSERT_EQ(SdpRtpmapAttributeList::CodecType::kRtx, format_params[0].parameters->codec_type); ASSERT_EQ(static_cast(120), static_cast( format_params[0].parameters.get()) ->apt); ASSERT_EQ(Nothing(), static_cast( format_params[0].parameters.get()) ->rtx_time); } TEST_P(NewSdpTest, CheckRtxAptRtxTime) { ParseSdp(kVideoSdp + "a=rtpmap:96 rtx/90000\r\na=fmtp:96 apt=120;rtx-time=", false); ParseSdp( kVideoSdp + "a=rtpmap:96 rtx/90000\r\na=fmtp:96 apt=120;rtx-time=blah", false); ParseSdp(kVideoSdp + "a=rtpmap:96 rtx/90000\r\na=fmtp:96 " "apt=120;rtx-time=9999999999999999999999999999999", false); ParseSdp( kVideoSdp + "a=rtpmap:96 rtx/90000\r\na=fmtp:96 apt=120;rtx-time=-3000", false); ParseSdp(kVideoSdp + "a=rtpmap:96 rtx/90000\r\na=fmtp:96 apt=120;rtx-time=3thousand", false); { ParseSdp(kVideoSdp + "a=rtpmap:96 rtx/90000\r\na=fmtp:96 apt=120;RTX-TIME=3000\r\n"); const SdpMediaSection& msec = Sdp()->GetMediaSection(0); ASSERT_EQ(1U, msec.GetFormats().size()); ASSERT_EQ(2U, msec.GetAttributeList().GetRtpmap().mRtpmaps.size()); const SdpRtpmapAttributeList::Rtpmap& rtpmap = msec.GetAttributeList().GetRtpmap().GetEntry("96"); ASSERT_EQ(rtpmap.pt, "96"); ASSERT_EQ(rtpmap.codec, SdpRtpmapAttributeList::CodecType::kRtx); ASSERT_EQ(rtpmap.name, "rtx"); ASSERT_EQ(rtpmap.clock, 90000U); ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kFmtpAttribute)); const auto& format_params = msec.GetAttributeList().GetFmtp().mFmtps; ASSERT_EQ(1U, format_params.size()); ASSERT_EQ("96", format_params[0].format); ASSERT_EQ(SdpRtpmapAttributeList::CodecType::kRtx, format_params[0].parameters->codec_type); ASSERT_EQ(static_cast(120), static_cast( format_params[0].parameters.get()) ->apt); ASSERT_EQ(Some(3000U), static_cast( format_params[0].parameters.get()) ->rtx_time); } { ParseSdp(kVideoSdp + "a=rtpmap:96 rtx/90000\r\na=fmtp:96 " "apt=120;rtx-time=3000;rtx-time=3300\r\n"); const SdpMediaSection& msec = Sdp()->GetMediaSection(0); ASSERT_EQ(1U, msec.GetFormats().size()); ASSERT_EQ(2U, msec.GetAttributeList().GetRtpmap().mRtpmaps.size()); const SdpRtpmapAttributeList::Rtpmap& rtpmap = msec.GetAttributeList().GetRtpmap().GetEntry("96"); ASSERT_EQ(rtpmap.pt, "96"); ASSERT_EQ(rtpmap.codec, SdpRtpmapAttributeList::CodecType::kRtx); ASSERT_EQ(rtpmap.name, "rtx"); ASSERT_EQ(rtpmap.clock, 90000U); ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kFmtpAttribute)); const auto& format_params = msec.GetAttributeList().GetFmtp().mFmtps; ASSERT_EQ(1U, format_params.size()); ASSERT_EQ("96", format_params[0].format); ASSERT_EQ(SdpRtpmapAttributeList::CodecType::kRtx, format_params[0].parameters->codec_type); ASSERT_EQ(static_cast(120), static_cast( format_params[0].parameters.get()) ->apt); // Last rtx-time wins. This parameter is unused by our implementation // anyway. ASSERT_EQ(Some(3300U), static_cast( format_params[0].parameters.get()) ->rtx_time); } { ParseSdp(kVideoSdp + "a=rtpmap:96 rtx/90000\r\na=fmtp:96 apt=120;rtx-time=3000\r\n"); const SdpMediaSection& msec = Sdp()->GetMediaSection(0); ASSERT_EQ(1U, msec.GetFormats().size()); ASSERT_EQ(2U, msec.GetAttributeList().GetRtpmap().mRtpmaps.size()); const SdpRtpmapAttributeList::Rtpmap& rtpmap = msec.GetAttributeList().GetRtpmap().GetEntry("96"); ASSERT_EQ(rtpmap.pt, "96"); ASSERT_EQ(rtpmap.codec, SdpRtpmapAttributeList::CodecType::kRtx); ASSERT_EQ(rtpmap.name, "rtx"); ASSERT_EQ(rtpmap.clock, 90000U); ASSERT_TRUE(Sdp()->GetMediaSection(0).GetAttributeList().HasAttribute( SdpAttribute::kFmtpAttribute)); const auto& format_params = msec.GetAttributeList().GetFmtp().mFmtps; ASSERT_EQ(1U, format_params.size()); ASSERT_EQ("96", format_params[0].format); ASSERT_EQ(SdpRtpmapAttributeList::CodecType::kRtx, format_params[0].parameters->codec_type); ASSERT_EQ(static_cast(120), static_cast( format_params[0].parameters.get()) ->apt); ASSERT_EQ(Some(3000U), static_cast( format_params[0].parameters.get()) ->rtx_time); } } } // End namespace test.