From 13e570fcb09da2c453ab13cd7b7467c4f82360cd Mon Sep 17 00:00:00 2001 From: "Piotr (Peter) Slatala" Date: Wed, 27 Feb 2019 11:34:26 -0800 Subject: [PATCH] Add a parser for the x-mt line. This change adds a parser for the x-mt line to the WebRTC SDP, and adds a collection in the SessionDescription for media transport. x-mt line contains information about media transport settings. This is an experimental line. This change will be followed up by two other changes: * Setting the offer with x-mt * Using the x-mt line to pass it to the media transport Bug: webrtc:9719 Change-Id: I46568610d4092a69ada7b7c1d3987d698ddba0be Reviewed-on: https://webrtc-review.googlesource.com/c/124601 Reviewed-by: Steve Anton Reviewed-by: Bjorn Mellem Commit-Queue: Peter Slatala Cr-Commit-Position: refs/heads/master@{#26881} --- pc/session_description.h | 30 +++++++++++++ pc/webrtc_sdp.cc | 48 ++++++++++++++++++++ pc/webrtc_sdp_unittest.cc | 94 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+) diff --git a/pc/session_description.h b/pc/session_description.h index 430cbdb04c..4156683030 100644 --- a/pc/session_description.h +++ b/pc/session_description.h @@ -423,6 +423,8 @@ class SessionDescription { SessionDescription* Copy() const; + struct MediaTransportSetting; + // Content accessors. const ContentInfos& contents() const { return contents_; } ContentInfos& contents() { return contents_; } @@ -510,6 +512,32 @@ class SessionDescription { } bool extmap_allow_mixed() const { return extmap_allow_mixed_; } + // Adds the media transport setting. + // Media transport name uniquely identifies the type of media transport. + // The name cannot be empty, or repeated in the previously added transport + // settings. + void AddMediaTransportSetting(const std::string& media_transport_name, + const std::string& media_transport_setting) { + RTC_DCHECK(!media_transport_name.empty()); + for (const auto& setting : media_transport_settings_) { + RTC_DCHECK(media_transport_name != setting.transport_name) + << "MediaTransportSetting was already registered, transport_name=" + << setting.transport_name; + } + media_transport_settings_.push_back( + {media_transport_name, media_transport_setting}); + } + + // Gets the media transport settings, in order of preference. + const std::vector& MediaTransportSettings() const { + return media_transport_settings_; + } + + struct MediaTransportSetting { + std::string transport_name; + std::string transport_setting; + }; + private: SessionDescription(const SessionDescription&); @@ -526,6 +554,8 @@ class SessionDescription { // correctly. If it's included in offer to us we will respond that we support // it. bool extmap_allow_mixed_ = false; + + std::vector media_transport_settings_; }; // Indicates whether a session description was sent by the local client or diff --git a/pc/webrtc_sdp.cc b/pc/webrtc_sdp.cc index f7a26af32a..7d4b464937 100644 --- a/pc/webrtc_sdp.cc +++ b/pc/webrtc_sdp.cc @@ -46,6 +46,7 @@ #include "rtc_base/logging.h" #include "rtc_base/message_digest.h" #include "rtc_base/strings/string_builder.h" +#include "rtc_base/third_party/base64/base64.h" using cricket::AudioContentDescription; using cricket::Candidate; @@ -235,6 +236,13 @@ static const char kApplicationSpecificMaximum[] = "AS"; static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel"; +// This is a non-standardized media transport settings. +// This setting is going to be set in the offer. There may be one or more +// a=x-mt: settings, and they are in the priority order (the most preferred on +// top). x-mt setting format depends on the media transport, and is generated by +// |MediaTransportInterface::GetTransportParametersOffer|. +static const char kMediaTransportSettingLine[] = "x-mt"; + // RTP payload type is in the 0-127 range. Use -1 to indicate "all" payload // types. const int kWildcardPayloadType = -1; @@ -2049,6 +2057,28 @@ bool ParseConnectionData(const std::string& line, return true; } +bool ParseMediaTransportLine(const std::string& line, + std::string* transport_name, + std::string* transport_setting, + SdpParseError* error) { + std::string value; + if (!GetValue(line, kMediaTransportSettingLine, &value, error)) { + return false; + } + std::string media_transport_settings_base64; + if (!rtc::tokenize_first(value, kSdpDelimiterColonChar, transport_name, + &media_transport_settings_base64)) { + return ParseFailedGetValue(line, kMediaTransportSettingLine, error); + } + if (!rtc::Base64::Decode(media_transport_settings_base64, + rtc::Base64::DO_STRICT, transport_setting, + nullptr)) { + return ParseFailedGetValue(line, kMediaTransportSettingLine, error); + } + + return true; +} + bool ParseSessionDescription(const std::string& message, size_t* pos, std::string* session_id, @@ -2206,6 +2236,24 @@ bool ParseSessionDescription(const std::string& message, return false; } session_extmaps->push_back(extmap); + } else if (HasAttribute(line, kMediaTransportSettingLine)) { + std::string transport_name; + std::string transport_setting; + if (!ParseMediaTransportLine(line, &transport_name, &transport_setting, + error)) { + return false; + } + + for (const auto& setting : desc->MediaTransportSettings()) { + if (setting.transport_name == transport_name) { + // Ignore repeated transport names rather than failing to parse so + // that in the future the same transport could have multiple configs. + RTC_LOG(INFO) << "x-mt line with repeated transport, transport_name=" + << transport_name; + return true; + } + } + desc->AddMediaTransportSetting(transport_name, transport_setting); } } diff --git a/pc/webrtc_sdp_unittest.cc b/pc/webrtc_sdp_unittest.cc index 193a3ef1d6..117269a80a 100644 --- a/pc/webrtc_sdp_unittest.cc +++ b/pc/webrtc_sdp_unittest.cc @@ -4324,3 +4324,97 @@ TEST_F(WebRtcSdpTest, ParseNoMid) { ElementsAre(Field("name", &cricket::ContentInfo::name, ""), Field("name", &cricket::ContentInfo::name, ""))); } + +// Test that the media transport name and base64-decoded setting is parsed from +// an a=x-mt line. +TEST_F(WebRtcSdpTest, ParseMediaTransport) { + JsepSessionDescription output(kDummyType); + std::string sdp = kSdpSessionString; + sdp += "a=x-mt:rtp:dGVzdDY0\r\n"; + SdpParseError error; + + ASSERT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error)) + << error.description; + const auto& settings = output.description()->MediaTransportSettings(); + ASSERT_EQ(1u, settings.size()); + EXPECT_EQ("rtp", settings[0].transport_name); + EXPECT_EQ("test64", settings[0].transport_setting); +} + +// Test that an a=x-mt line fails to parse if its setting is invalid base 64. +TEST_F(WebRtcSdpTest, ParseMediaTransportInvalidBase64) { + JsepSessionDescription output(kDummyType); + std::string sdp = kSdpSessionString; + sdp += "a=x-mt:rtp:ThisIsInvalidBase64\r\n"; + SdpParseError error; + + ASSERT_FALSE(webrtc::SdpDeserialize(sdp, &output, &error)); +} + +// Test that multiple a=x-mt lines are parsed in the order of preference (the +// order of the lines in the SDP). +TEST_F(WebRtcSdpTest, ParseMediaTransportMultipleLines) { + JsepSessionDescription output(kDummyType); + std::string sdp = kSdpSessionString; + sdp += + "a=x-mt:rtp:dGVzdDY0\r\n" + "a=x-mt:generic:Z2VuZXJpY3NldHRpbmc=\r\n"; + SdpParseError error; + + ASSERT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error)) + << error.description; + const auto& settings = output.description()->MediaTransportSettings(); + ASSERT_EQ(2u, settings.size()); + EXPECT_EQ("rtp", settings[0].transport_name); + EXPECT_EQ("test64", settings[0].transport_setting); + EXPECT_EQ("generic", settings[1].transport_name); + EXPECT_EQ("genericsetting", settings[1].transport_setting); +} + +// Test that only the first a=x-mt line associated with a transport name is +// parsed and the rest ignored. +TEST_F(WebRtcSdpTest, ParseMediaTransportSkipRepeatedTransport) { + JsepSessionDescription output(kDummyType); + std::string sdp = kSdpSessionString; + sdp += + "a=x-mt:rtp:dGVzdDY0\r\n" + "a=x-mt:rtp:Z2VuZXJpY3NldHRpbmc=\r\n"; + SdpParseError error; + + // Repeated 'rtp' transport setting. We still parse the SDP successfully, + // but ignore the repeated transport. + ASSERT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error)); + const auto& settings = output.description()->MediaTransportSettings(); + EXPECT_EQ("test64", settings[0].transport_setting); +} + +// Test that an a=x-mt line fails to parse if it is missing a setting. +TEST_F(WebRtcSdpTest, ParseMediaTransportMalformedLine) { + JsepSessionDescription output(kDummyType); + std::string sdp = kSdpSessionString; + sdp += "a=x-mt:rtp\r\n"; + SdpParseError error; + + ASSERT_FALSE(webrtc::SdpDeserialize(sdp, &output, &error)); +} + +// Test that an a=x-mt line fails to parse if its missing a name and setting. +TEST_F(WebRtcSdpTest, ParseMediaTransportMalformedLine2) { + JsepSessionDescription output(kDummyType); + std::string sdp = kSdpSessionString; + sdp += "a=x-mt\r\n"; + SdpParseError error; + + ASSERT_FALSE(webrtc::SdpDeserialize(sdp, &output, &error)); +} + +TEST_F(WebRtcSdpTest, ParseMediaTransportIgnoreNonsenseAttributeLines) { + JsepSessionDescription output(kDummyType); + std::string sdp = kSdpSessionString; + sdp += "a=x-nonsense:rtp:dGVzdDY0\r\n"; + SdpParseError error; + + ASSERT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error)) + << error.description; + EXPECT_TRUE(output.description()->MediaTransportSettings().empty()); +}