From 9d3584c47ef1c73475dfa88e4fef802e3314815b Mon Sep 17 00:00:00 2001 From: deadbeef Date: Tue, 16 Feb 2016 17:54:10 -0800 Subject: [PATCH] Implementing unified plan encoding of msid. Meaning "a=msid:...", instead of "a=ssrc:X msid:...". An additional option to SdpSerialize determines if the "a=msid" attribute is used. Review URL: https://codereview.webrtc.org/1688383002 Cr-Commit-Position: refs/heads/master@{#11644} --- webrtc/api/jsepicecandidate.h | 7 + webrtc/api/jsepsessiondescription.cc | 5 +- webrtc/api/webrtcsdp.cc | 136 +++++-- webrtc/api/webrtcsdp.h | 4 +- webrtc/api/webrtcsdp_unittest.cc | 586 +++++++++++++++++++-------- 5 files changed, 538 insertions(+), 200 deletions(-) diff --git a/webrtc/api/jsepicecandidate.h b/webrtc/api/jsepicecandidate.h index 65a7446af6..529b2a7756 100644 --- a/webrtc/api/jsepicecandidate.h +++ b/webrtc/api/jsepicecandidate.h @@ -53,6 +53,11 @@ class JsepIceCandidate : public IceCandidateInterface { // This implementation stores JsepIceCandidates. class JsepCandidateCollection : public IceCandidateCollection { public: + JsepCandidateCollection() {} + // Move constructor is defined so that a vector of JsepCandidateCollections + // can be resized. + JsepCandidateCollection(JsepCandidateCollection&& o) + : candidates_(std::move(o.candidates_)) {} ~JsepCandidateCollection(); virtual size_t count() const { return candidates_.size(); @@ -68,6 +73,8 @@ class JsepCandidateCollection : public IceCandidateCollection { private: std::vector candidates_; + + RTC_DISALLOW_COPY_AND_ASSIGN(JsepCandidateCollection); }; } // namespace webrtc diff --git a/webrtc/api/jsepsessiondescription.cc b/webrtc/api/jsepsessiondescription.cc index b76b167193..b47114b9bc 100644 --- a/webrtc/api/jsepsessiondescription.cc +++ b/webrtc/api/jsepsessiondescription.cc @@ -151,9 +151,10 @@ const IceCandidateCollection* JsepSessionDescription::candidates( } bool JsepSessionDescription::ToString(std::string* out) const { - if (!description_ || !out) + if (!description_ || !out) { return false; - *out = SdpSerialize(*this); + } + *out = SdpSerialize(*this, false); return !out->empty(); } diff --git a/webrtc/api/webrtcsdp.cc b/webrtc/api/webrtcsdp.cc index a308a08414..a6edf7ce18 100644 --- a/webrtc/api/webrtcsdp.cc +++ b/webrtc/api/webrtcsdp.cc @@ -84,7 +84,7 @@ namespace webrtc { // the form: // = // where MUST be exactly one case-significant character. -static const int kLinePrefixLength = 2; // Lenght of = +static const int kLinePrefixLength = 2; // Length of = static const char kLineTypeVersion = 'v'; static const char kLineTypeOrigin = 'o'; static const char kLineTypeSessionName = 's'; @@ -104,6 +104,7 @@ static const char kLineTypeAttributes = 'a'; // Attributes static const char kAttributeGroup[] = "group"; static const char kAttributeMid[] = "mid"; +static const char kAttributeMsid[] = "msid"; static const char kAttributeRtcpMux[] = "rtcp-mux"; static const char kAttributeRtcpReducedSize[] = "rtcp-rsize"; static const char kAttributeSsrc[] = "ssrc"; @@ -212,15 +213,14 @@ const int kWildcardPayloadType = -1; struct SsrcInfo { SsrcInfo() - : msid_identifier(kDefaultMsid), - // TODO(ronghuawu): What should we do if the appdata doesn't appear? + : stream_id(kDefaultMsid), + // TODO(ronghuawu): What should we do if the track id doesn't appear? // Create random string (which will be used as track label later)? - msid_appdata(rtc::CreateRandomString(8)) { - } + track_id(rtc::CreateRandomString(8)) {} uint32_t ssrc_id; std::string cname; - std::string msid_identifier; - std::string msid_appdata; + std::string stream_id; + std::string track_id; // For backward compatibility. // TODO(ronghuawu): Remove below 2 fields once all the clients support msid. @@ -236,12 +236,13 @@ static void BuildMediaDescription(const ContentInfo* content_info, const TransportInfo* transport_info, const MediaType media_type, const std::vector& candidates, + bool unified_plan_sdp, std::string* message); static void BuildSctpContentAttributes(std::string* message, int sctp_port); -static void BuildRtpContentAttributes( - const MediaContentDescription* media_desc, - const MediaType media_type, - std::string* message); +static void BuildRtpContentAttributes(const MediaContentDescription* media_desc, + const MediaType media_type, + bool unified_plan_sdp, + std::string* message); static void BuildRtpMap(const MediaContentDescription* media_desc, const MediaType media_type, std::string* message); @@ -318,6 +319,10 @@ static bool ParseFingerprintAttribute(const std::string& line, static bool ParseDtlsSetup(const std::string& line, cricket::ConnectionRole* role, SdpParseError* error); +static bool ParseMsidAttribute(const std::string& line, + std::string* stream_id, + std::string* track_id, + SdpParseError* error); // Helper functions @@ -579,18 +584,15 @@ void CreateTracksFromSsrcInfos(const SsrcInfoVec& ssrc_infos, std::string sync_label; std::string track_id; - if (ssrc_info->msid_identifier == kDefaultMsid && - !ssrc_info->mslabel.empty()) { + if (ssrc_info->stream_id == kDefaultMsid && !ssrc_info->mslabel.empty()) { // If there's no msid and there's mslabel, we consider this is a sdp from // a older version of client that doesn't support msid. // In that case, we use the mslabel and label to construct the track. sync_label = ssrc_info->mslabel; track_id = ssrc_info->label; } else { - sync_label = ssrc_info->msid_identifier; - // The appdata consists of the "id" attribute of a MediaStreamTrack, which - // is corresponding to the "id" attribute of StreamParams. - track_id = ssrc_info->msid_appdata; + sync_label = ssrc_info->stream_id; + track_id = ssrc_info->track_id; } if (sync_label.empty() || track_id.empty()) { ASSERT(false); @@ -776,7 +778,8 @@ static void GetCandidatesByMindex(const SessionDescriptionInterface& desci, } } -std::string SdpSerialize(const JsepSessionDescription& jdesc) { +std::string SdpSerialize(const JsepSessionDescription& jdesc, + bool unified_plan_sdp) { const cricket::SessionDescription* desc = jdesc.description(); if (!desc) { return ""; @@ -847,10 +850,8 @@ std::string SdpSerialize(const JsepSessionDescription& jdesc) { static_cast(it->description); std::vector candidates; GetCandidatesByMindex(jdesc, ++mline_index, &candidates); - BuildMediaDescription(&*it, - desc->GetTransportInfoByName(it->name), - mdesc->type(), - candidates, + BuildMediaDescription(&*it, desc->GetTransportInfoByName(it->name), + mdesc->type(), candidates, unified_plan_sdp, &message); } return message; @@ -1162,6 +1163,7 @@ void BuildMediaDescription(const ContentInfo* content_info, const TransportInfo* transport_info, const MediaType media_type, const std::vector& candidates, + bool unified_plan_sdp, std::string* message) { ASSERT(message != NULL); if (content_info == NULL || message == NULL) { @@ -1337,7 +1339,8 @@ void BuildMediaDescription(const ContentInfo* content_info, if (IsDtlsSctp(media_desc->protocol())) { BuildSctpContentAttributes(message, sctp_port); } else if (IsRtp(media_desc->protocol())) { - BuildRtpContentAttributes(media_desc, media_type, message); + BuildRtpContentAttributes(media_desc, media_type, unified_plan_sdp, + message); } } @@ -1354,10 +1357,11 @@ void BuildSctpContentAttributes(std::string* message, int sctp_port) { AddLine(os.str(), message); } -void BuildRtpContentAttributes( - const MediaContentDescription* media_desc, - const MediaType media_type, - std::string* message) { +// If unified_plan_sdp is true, will use "a=msid". +void BuildRtpContentAttributes(const MediaContentDescription* media_desc, + const MediaType media_type, + bool unified_plan_sdp, + std::string* message) { std::ostringstream os; // RFC 5285 // a=extmap:["/"] @@ -1389,6 +1393,22 @@ void BuildRtpContentAttributes( } AddLine(os.str(), message); + // draft-ietf-mmusic-msid-11 + // a=msid: + if (unified_plan_sdp && !media_desc->streams().empty()) { + if (media_desc->streams().size() > 1u) { + LOG(LS_WARNING) << "Trying to serialize unified plan SDP with more than " + << "one track in a media section. Omitting 'a=msid'."; + } else { + auto track = media_desc->streams().begin(); + const std::string& stream_id = track->sync_label; + std::ostringstream os; + InitAttrLine(kAttributeMsid, &os); + os << kSdpDelimiterColon << stream_id << kSdpDelimiterSpace << track->id; + AddLine(os.str(), message); + } + } + // RFC 5761 // a=rtcp-mux if (media_desc->rtcp_mux()) { @@ -1457,17 +1477,18 @@ void BuildRtpContentAttributes( // draft-alvestrand-mmusic-msid-00 // a=ssrc: msid:identifier [appdata] - // The appdata consists of the "id" attribute of a MediaStreamTrack, which - // is corresponding to the "name" attribute of StreamParams. - std::string appdata = track->id; + // The appdata consists of the "id" attribute of a MediaStreamTrack, + // which corresponds to the "id" attribute of StreamParams. + const std::string& stream_id = track->sync_label; std::ostringstream os; InitAttrLine(kAttributeSsrc, &os); os << kSdpDelimiterColon << ssrc << kSdpDelimiterSpace - << kSsrcAttributeMsid << kSdpDelimiterColon << track->sync_label - << kSdpDelimiterSpace << appdata; + << kSsrcAttributeMsid << kSdpDelimiterColon << stream_id + << kSdpDelimiterSpace << track->id; AddLine(os.str(), message); - // TODO(ronghuawu): Remove below code which is for backward compatibility. + // TODO(ronghuawu): Remove below code which is for backward + // compatibility. // draft-alvestrand-rtcweb-mid-01 // a=ssrc: mslabel: // The label isn't yet defined. @@ -2032,6 +2053,29 @@ static bool ParseDtlsSetup(const std::string& line, return true; } +static bool ParseMsidAttribute(const std::string& line, + std::string* stream_id, + std::string* track_id, + SdpParseError* error) { + // draft-ietf-mmusic-msid-11 + // a=msid: + // msid-value = msid-id [ SP msid-appdata ] + // msid-id = 1*64token-char ; see RFC 4566 + // msid-appdata = 1*64token-char ; see RFC 4566 + std::string field1; + if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace, + &field1, track_id)) { + const size_t expected_fields = 2; + return ParseFailedExpectFieldNum(line, expected_fields, error); + } + + // msid: + if (!GetValue(field1, kAttributeMsid, stream_id, error)) { + return false; + } + return true; +} + // RFC 3551 // PT encoding media type clock rate channels // name (Hz) @@ -2456,6 +2500,8 @@ bool ParseContent(const std::string& message, SsrcGroupVec ssrc_groups; std::string maxptime_as_string; std::string ptime_as_string; + std::string stream_id; + std::string track_id; // Loop until the next m line while (!IsLineType(message, kLineTypeMedia, *pos)) { @@ -2615,6 +2661,10 @@ bool ParseContent(const std::string& message, } if (flag_value.compare(kValueConference) == 0) media_desc->set_conference_mode(true); + } else if (HasAttribute(line, kAttributeMsid)) { + if (!ParseMsidAttribute(line, &stream_id, &track_id, error)) { + return false; + } } } else { // Only parse lines that we are interested of. @@ -2623,6 +2673,17 @@ bool ParseContent(const std::string& message, } } + // Found an msid attribute. + // Setting the stream_id/track_id will cause only one StreamParams + // to be created in CreateTracksFromSsrcInfos, containing all the SSRCs from + // the m= section. + if (!stream_id.empty() && !track_id.empty()) { + for (SsrcInfo& ssrc_info : ssrc_infos) { + ssrc_info.stream_id = stream_id; + ssrc_info.track_id = track_id; + } + } + // Create tracks from the |ssrc_infos|. CreateTracksFromSsrcInfos(ssrc_infos, &tracks); @@ -2642,9 +2703,8 @@ bool ParseContent(const std::string& message, } // Add the new tracks to the |media_desc|. - for (StreamParamsVec::iterator track = tracks.begin(); - track != tracks.end(); ++track) { - media_desc->AddStream(*track); + for (StreamParams& track : tracks) { + media_desc->AddStream(track); } if (media_type == cricket::MEDIA_TYPE_AUDIO) { @@ -2749,9 +2809,9 @@ bool ParseSsrcAttribute(const std::string& line, SsrcInfoVec* ssrc_infos, "Expected format \"msid:[ ]\".", error); } - ssrc_info->msid_identifier = fields[0]; + ssrc_info->stream_id = fields[0]; if (fields.size() == 2) { - ssrc_info->msid_appdata = fields[1]; + ssrc_info->track_id = fields[1]; } } else if (attribute == kSsrcAttributeMslabel) { // draft-alvestrand-rtcweb-mid-01 diff --git a/webrtc/api/webrtcsdp.h b/webrtc/api/webrtcsdp.h index 5997fa18d0..2b22b6249f 100644 --- a/webrtc/api/webrtcsdp.h +++ b/webrtc/api/webrtcsdp.h @@ -33,8 +33,10 @@ struct SdpParseError; // Serialize SessionDescription including candidates if // JsepSessionDescription has candidates. // jdesc - The JsepSessionDescription object to be serialized. +// unified_plan_sdp - If set to true, include "a=msid" lines where appropriate. // return - SDP string serialized from the arguments. -std::string SdpSerialize(const JsepSessionDescription& jdesc); +std::string SdpSerialize(const JsepSessionDescription& jdesc, + bool unified_plan_sdp); // Serializes the passed in IceCandidateInterface to a SDP string. // candidate - The candidate to be serialized. diff --git a/webrtc/api/webrtcsdp_unittest.cc b/webrtc/api/webrtcsdp_unittest.cc index 7f54a9dab3..f68010902b 100644 --- a/webrtc/api/webrtcsdp_unittest.cc +++ b/webrtc/api/webrtcsdp_unittest.cc @@ -64,14 +64,8 @@ typedef std::vector Candidates; static const uint32_t kDefaultSctpPort = 5000; static const char kSessionTime[] = "t=0 0\r\n"; static const uint32_t kCandidatePriority = 2130706432U; // pref = 1.0 -static const char kCandidateUfragVoice[] = "ufrag_voice"; -static const char kCandidatePwdVoice[] = "pwd_voice"; static const char kAttributeIceUfragVoice[] = "a=ice-ufrag:ufrag_voice\r\n"; static const char kAttributeIcePwdVoice[] = "a=ice-pwd:pwd_voice\r\n"; -static const char kCandidateUfragVideo[] = "ufrag_video"; -static const char kCandidatePwdVideo[] = "pwd_video"; -static const char kCandidateUfragData[] = "ufrag_data"; -static const char kCandidatePwdData[] = "pwd_data"; static const char kAttributeIceUfragVideo[] = "a=ice-ufrag:ufrag_video\r\n"; static const char kAttributeIcePwdVideo[] = "a=ice-pwd:pwd_video\r\n"; static const uint32_t kCandidateGeneration = 2; @@ -113,13 +107,17 @@ struct CodecParams { int maxaveragebitrate; }; +// TODO(deadbeef): In these reference strings, use "a=fingerprint" by default +// instead of "a=crypto", and have an explicit test for adding "a=crypto". +// Currently it's the other way around. + // Reference sdp string static const char kSdpFullString[] = "v=0\r\n" "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n" "s=-\r\n" "t=0 0\r\n" - "a=msid-semantic: WMS local_stream_1 local_stream_2\r\n" + "a=msid-semantic: WMS local_stream_1\r\n" "m=audio 2345 RTP/SAVPF 111 103 104\r\n" "c=IN IP4 74.125.127.126\r\n" "a=rtcp:2347 IN IP4 74.125.127.126\r\n" @@ -152,10 +150,6 @@ static const char kSdpFullString[] = "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n" "a=ssrc:1 mslabel:local_stream_1\r\n" "a=ssrc:1 label:audio_track_id_1\r\n" - "a=ssrc:4 cname:stream_2_cname\r\n" - "a=ssrc:4 msid:local_stream_2 audio_track_id_2\r\n" - "a=ssrc:4 mslabel:local_stream_2\r\n" - "a=ssrc:4 label:audio_track_id_2\r\n" "m=video 3457 RTP/SAVPF 120\r\n" "c=IN IP4 74.125.224.39\r\n" "a=rtcp:3456 IN IP4 74.125.224.39\r\n" @@ -177,23 +171,15 @@ static const char kSdpFullString[] = "a=crypto:1 AES_CM_128_HMAC_SHA1_80 " "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n" "a=rtpmap:120 VP8/90000\r\n" + "a=ssrc-group:FEC 2 3\r\n" "a=ssrc:2 cname:stream_1_cname\r\n" "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n" "a=ssrc:2 mslabel:local_stream_1\r\n" "a=ssrc:2 label:video_track_id_1\r\n" "a=ssrc:3 cname:stream_1_cname\r\n" - "a=ssrc:3 msid:local_stream_1 video_track_id_2\r\n" + "a=ssrc:3 msid:local_stream_1 video_track_id_1\r\n" "a=ssrc:3 mslabel:local_stream_1\r\n" - "a=ssrc:3 label:video_track_id_2\r\n" - "a=ssrc-group:FEC 5 6\r\n" - "a=ssrc:5 cname:stream_2_cname\r\n" - "a=ssrc:5 msid:local_stream_2 video_track_id_3\r\n" - "a=ssrc:5 mslabel:local_stream_2\r\n" - "a=ssrc:5 label:video_track_id_3\r\n" - "a=ssrc:6 cname:stream_2_cname\r\n" - "a=ssrc:6 msid:local_stream_2 video_track_id_3\r\n" - "a=ssrc:6 mslabel:local_stream_2\r\n" - "a=ssrc:6 label:video_track_id_3\r\n"; + "a=ssrc:3 label:video_track_id_1\r\n"; // SDP reference string without the candidates. static const char kSdpString[] = @@ -201,7 +187,7 @@ static const char kSdpString[] = "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n" "s=-\r\n" "t=0 0\r\n" - "a=msid-semantic: WMS local_stream_1 local_stream_2\r\n" + "a=msid-semantic: WMS local_stream_1\r\n" "m=audio 9 RTP/SAVPF 111 103 104\r\n" "c=IN IP4 0.0.0.0\r\n" "a=rtcp:9 IN IP4 0.0.0.0\r\n" @@ -220,10 +206,6 @@ static const char kSdpString[] = "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n" "a=ssrc:1 mslabel:local_stream_1\r\n" "a=ssrc:1 label:audio_track_id_1\r\n" - "a=ssrc:4 cname:stream_2_cname\r\n" - "a=ssrc:4 msid:local_stream_2 audio_track_id_2\r\n" - "a=ssrc:4 mslabel:local_stream_2\r\n" - "a=ssrc:4 label:audio_track_id_2\r\n" "m=video 9 RTP/SAVPF 120\r\n" "c=IN IP4 0.0.0.0\r\n" "a=rtcp:9 IN IP4 0.0.0.0\r\n" @@ -233,23 +215,15 @@ static const char kSdpString[] = "a=crypto:1 AES_CM_128_HMAC_SHA1_80 " "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n" "a=rtpmap:120 VP8/90000\r\n" + "a=ssrc-group:FEC 2 3\r\n" "a=ssrc:2 cname:stream_1_cname\r\n" "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n" "a=ssrc:2 mslabel:local_stream_1\r\n" "a=ssrc:2 label:video_track_id_1\r\n" "a=ssrc:3 cname:stream_1_cname\r\n" - "a=ssrc:3 msid:local_stream_1 video_track_id_2\r\n" + "a=ssrc:3 msid:local_stream_1 video_track_id_1\r\n" "a=ssrc:3 mslabel:local_stream_1\r\n" - "a=ssrc:3 label:video_track_id_2\r\n" - "a=ssrc-group:FEC 5 6\r\n" - "a=ssrc:5 cname:stream_2_cname\r\n" - "a=ssrc:5 msid:local_stream_2 video_track_id_3\r\n" - "a=ssrc:5 mslabel:local_stream_2\r\n" - "a=ssrc:5 label:video_track_id_3\r\n" - "a=ssrc:6 cname:stream_2_cname\r\n" - "a=ssrc:6 msid:local_stream_2 video_track_id_3\r\n" - "a=ssrc:6 mslabel:local_stream_2\r\n" - "a=ssrc:6 label:video_track_id_3\r\n"; + "a=ssrc:3 label:video_track_id_1\r\n"; static const char kSdpRtpDataChannelString[] = "m=application 9 RTP/SAVPF 101\r\n" @@ -355,6 +329,195 @@ static const char kSdpVideoString[] = "a=ssrc:2 mslabel:local_stream\r\n" "a=ssrc:2 label:video_track_id_1\r\n"; +// Plan B SDP reference string, with 2 streams, 2 audio tracks and 3 video +// tracks. +static const char kPlanBSdpFullString[] = + "v=0\r\n" + "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n" + "s=-\r\n" + "t=0 0\r\n" + "a=msid-semantic: WMS local_stream_1 local_stream_2\r\n" + "m=audio 2345 RTP/SAVPF 111 103 104\r\n" + "c=IN IP4 74.125.127.126\r\n" + "a=rtcp:2347 IN IP4 74.125.127.126\r\n" + "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host " + "generation 2\r\n" + "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1235 typ host " + "generation 2\r\n" + "a=candidate:a0+B/2 1 udp 2130706432 ::1 1238 typ host " + "generation 2\r\n" + "a=candidate:a0+B/2 2 udp 2130706432 ::1 1239 typ host " + "generation 2\r\n" + "a=candidate:a0+B/3 1 udp 2130706432 74.125.127.126 2345 typ srflx " + "raddr 192.168.1.5 rport 2346 " + "generation 2\r\n" + "a=candidate:a0+B/3 2 udp 2130706432 74.125.127.126 2347 typ srflx " + "raddr 192.168.1.5 rport 2348 " + "generation 2\r\n" + "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n" + "a=mid:audio_content_name\r\n" + "a=sendrecv\r\n" + "a=rtcp-mux\r\n" + "a=rtcp-rsize\r\n" + "a=crypto:1 AES_CM_128_HMAC_SHA1_32 " + "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 " + "dummy_session_params\r\n" + "a=rtpmap:111 opus/48000/2\r\n" + "a=rtpmap:103 ISAC/16000\r\n" + "a=rtpmap:104 ISAC/32000\r\n" + "a=ssrc:1 cname:stream_1_cname\r\n" + "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n" + "a=ssrc:1 mslabel:local_stream_1\r\n" + "a=ssrc:1 label:audio_track_id_1\r\n" + "a=ssrc:4 cname:stream_2_cname\r\n" + "a=ssrc:4 msid:local_stream_2 audio_track_id_2\r\n" + "a=ssrc:4 mslabel:local_stream_2\r\n" + "a=ssrc:4 label:audio_track_id_2\r\n" + "m=video 3457 RTP/SAVPF 120\r\n" + "c=IN IP4 74.125.224.39\r\n" + "a=rtcp:3456 IN IP4 74.125.224.39\r\n" + "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1236 typ host " + "generation 2\r\n" + "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1237 typ host " + "generation 2\r\n" + "a=candidate:a0+B/2 2 udp 2130706432 ::1 1240 typ host " + "generation 2\r\n" + "a=candidate:a0+B/2 1 udp 2130706432 ::1 1241 typ host " + "generation 2\r\n" + "a=candidate:a0+B/4 2 udp 2130706432 74.125.224.39 3456 typ relay " + "generation 2\r\n" + "a=candidate:a0+B/4 1 udp 2130706432 74.125.224.39 3457 typ relay " + "generation 2\r\n" + "a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n" + "a=mid:video_content_name\r\n" + "a=sendrecv\r\n" + "a=crypto:1 AES_CM_128_HMAC_SHA1_80 " + "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n" + "a=rtpmap:120 VP8/90000\r\n" + "a=ssrc-group:FEC 2 3\r\n" + "a=ssrc:2 cname:stream_1_cname\r\n" + "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n" + "a=ssrc:2 mslabel:local_stream_1\r\n" + "a=ssrc:2 label:video_track_id_1\r\n" + "a=ssrc:3 cname:stream_1_cname\r\n" + "a=ssrc:3 msid:local_stream_1 video_track_id_1\r\n" + "a=ssrc:3 mslabel:local_stream_1\r\n" + "a=ssrc:3 label:video_track_id_1\r\n" + "a=ssrc:5 cname:stream_2_cname\r\n" + "a=ssrc:5 msid:local_stream_2 video_track_id_2\r\n" + "a=ssrc:5 mslabel:local_stream_2\r\n" + "a=ssrc:5 label:video_track_id_2\r\n" + "a=ssrc:6 cname:stream_2_cname\r\n" + "a=ssrc:6 msid:local_stream_2 video_track_id_3\r\n" + "a=ssrc:6 mslabel:local_stream_2\r\n" + "a=ssrc:6 label:video_track_id_3\r\n"; + +// Unified Plan SDP reference string, with 2 streams, 2 audio tracks and 3 video +// tracks. +static const char kUnifiedPlanSdpFullString[] = + "v=0\r\n" + "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n" + "s=-\r\n" + "t=0 0\r\n" + "a=msid-semantic: WMS local_stream_1\r\n" + // Audio track 1, stream 1 (with candidates). + "m=audio 2345 RTP/SAVPF 111 103 104\r\n" + "c=IN IP4 74.125.127.126\r\n" + "a=rtcp:2347 IN IP4 74.125.127.126\r\n" + "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host " + "generation 2\r\n" + "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1235 typ host " + "generation 2\r\n" + "a=candidate:a0+B/2 1 udp 2130706432 ::1 1238 typ host " + "generation 2\r\n" + "a=candidate:a0+B/2 2 udp 2130706432 ::1 1239 typ host " + "generation 2\r\n" + "a=candidate:a0+B/3 1 udp 2130706432 74.125.127.126 2345 typ srflx " + "raddr 192.168.1.5 rport 2346 " + "generation 2\r\n" + "a=candidate:a0+B/3 2 udp 2130706432 74.125.127.126 2347 typ srflx " + "raddr 192.168.1.5 rport 2348 " + "generation 2\r\n" + "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n" + "a=mid:audio_content_name\r\n" + "a=msid:local_stream_1 audio_track_id_1\r\n" + "a=sendrecv\r\n" + "a=rtcp-mux\r\n" + "a=rtcp-rsize\r\n" + "a=crypto:1 AES_CM_128_HMAC_SHA1_32 " + "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 " + "dummy_session_params\r\n" + "a=rtpmap:111 opus/48000/2\r\n" + "a=rtpmap:103 ISAC/16000\r\n" + "a=rtpmap:104 ISAC/32000\r\n" + "a=ssrc:1 cname:stream_1_cname\r\n" + // Video track 1, stream 1 (with candidates). + "m=video 3457 RTP/SAVPF 120\r\n" + "c=IN IP4 74.125.224.39\r\n" + "a=rtcp:3456 IN IP4 74.125.224.39\r\n" + "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1236 typ host " + "generation 2\r\n" + "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1237 typ host " + "generation 2\r\n" + "a=candidate:a0+B/2 2 udp 2130706432 ::1 1240 typ host " + "generation 2\r\n" + "a=candidate:a0+B/2 1 udp 2130706432 ::1 1241 typ host " + "generation 2\r\n" + "a=candidate:a0+B/4 2 udp 2130706432 74.125.224.39 3456 typ relay " + "generation 2\r\n" + "a=candidate:a0+B/4 1 udp 2130706432 74.125.224.39 3457 typ relay " + "generation 2\r\n" + "a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n" + "a=mid:video_content_name\r\n" + "a=msid:local_stream_1 video_track_id_1\r\n" + "a=sendrecv\r\n" + "a=crypto:1 AES_CM_128_HMAC_SHA1_80 " + "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n" + "a=rtpmap:120 VP8/90000\r\n" + "a=ssrc-group:FEC 2 3\r\n" + "a=ssrc:2 cname:stream_1_cname\r\n" + "a=ssrc:3 cname:stream_1_cname\r\n" + // Audio track 2, stream 2. + "m=audio 9 RTP/SAVPF 111 103 104\r\n" + "c=IN IP4 0.0.0.0\r\n" + "a=rtcp:9 IN IP4 0.0.0.0\r\n" + "a=ice-ufrag:ufrag_voice_2\r\na=ice-pwd:pwd_voice_2\r\n" + "a=mid:audio_content_name_2\r\n" + "a=msid:local_stream_2 audio_track_id_2\r\n" + "a=sendrecv\r\n" + "a=rtcp-mux\r\n" + "a=rtcp-rsize\r\n" + "a=crypto:1 AES_CM_128_HMAC_SHA1_32 " + "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 " + "dummy_session_params\r\n" + "a=rtpmap:111 opus/48000/2\r\n" + "a=rtpmap:103 ISAC/16000\r\n" + "a=rtpmap:104 ISAC/32000\r\n" + "a=ssrc:4 cname:stream_2_cname\r\n" + // Video track 2, stream 2. + "m=video 9 RTP/SAVPF 120\r\n" + "c=IN IP4 0.0.0.0\r\n" + "a=rtcp:9 IN IP4 0.0.0.0\r\n" + "a=ice-ufrag:ufrag_video_2\r\na=ice-pwd:pwd_video_2\r\n" + "a=mid:video_content_name_2\r\n" + "a=msid:local_stream_2 video_track_id_2\r\n" + "a=sendrecv\r\n" + "a=crypto:1 AES_CM_128_HMAC_SHA1_80 " + "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n" + "a=rtpmap:120 VP8/90000\r\n" + "a=ssrc:5 cname:stream_2_cname\r\n" + // Video track 3, stream 2. + "m=video 9 RTP/SAVPF 120\r\n" + "c=IN IP4 0.0.0.0\r\n" + "a=rtcp:9 IN IP4 0.0.0.0\r\n" + "a=ice-ufrag:ufrag_video_3\r\na=ice-pwd:pwd_video_3\r\n" + "a=mid:video_content_name_3\r\n" + "a=msid:local_stream_2 video_track_id_3\r\n" + "a=sendrecv\r\n" + "a=crypto:1 AES_CM_128_HMAC_SHA1_80 " + "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n" + "a=rtpmap:120 VP8/90000\r\n" + "a=ssrc:6 cname:stream_2_cname\r\n"; // One candidate reference string as per W3c spec. // candidate: not a=candidate:CRLF @@ -392,34 +555,55 @@ static const char kSdpOneCandidateWithUfragPwd[] = static const char kSessionId[] = "18446744069414584320"; static const char kSessionVersion[] = "18446462598732840960"; -// Ice options +// ICE options. static const char kIceOption1[] = "iceoption1"; static const char kIceOption2[] = "iceoption2"; static const char kIceOption3[] = "iceoption3"; +// ICE ufrags/passwords. +static const char kUfragVoice[] = "ufrag_voice"; +static const char kPwdVoice[] = "pwd_voice"; +static const char kUfragVideo[] = "ufrag_video"; +static const char kPwdVideo[] = "pwd_video"; +static const char kUfragData[] = "ufrag_data"; +static const char kPwdData[] = "pwd_data"; + +// Extra ufrags/passwords for extra unified plan m= sections. +static const char kUfragVoice2[] = "ufrag_voice_2"; +static const char kPwdVoice2[] = "pwd_voice_2"; +static const char kUfragVideo2[] = "ufrag_video_2"; +static const char kPwdVideo2[] = "pwd_video_2"; +static const char kUfragVideo3[] = "ufrag_video_3"; +static const char kPwdVideo3[] = "pwd_video_3"; + // Content name static const char kAudioContentName[] = "audio_content_name"; static const char kVideoContentName[] = "video_content_name"; static const char kDataContentName[] = "data_content_name"; +// Extra content names for extra unified plan m= sections. +static const char kAudioContentName2[] = "audio_content_name_2"; +static const char kVideoContentName2[] = "video_content_name_2"; +static const char kVideoContentName3[] = "video_content_name_3"; + // MediaStream 1 static const char kStreamLabel1[] = "local_stream_1"; static const char kStream1Cname[] = "stream_1_cname"; static const char kAudioTrackId1[] = "audio_track_id_1"; static const uint32_t kAudioTrack1Ssrc = 1; static const char kVideoTrackId1[] = "video_track_id_1"; -static const uint32_t kVideoTrack1Ssrc = 2; -static const char kVideoTrackId2[] = "video_track_id_2"; -static const uint32_t kVideoTrack2Ssrc = 3; +static const uint32_t kVideoTrack1Ssrc1 = 2; +static const uint32_t kVideoTrack1Ssrc2 = 3; // MediaStream 2 static const char kStreamLabel2[] = "local_stream_2"; static const char kStream2Cname[] = "stream_2_cname"; static const char kAudioTrackId2[] = "audio_track_id_2"; static const uint32_t kAudioTrack2Ssrc = 4; +static const char kVideoTrackId2[] = "video_track_id_2"; +static const uint32_t kVideoTrack2Ssrc = 5; static const char kVideoTrackId3[] = "video_track_id_3"; -static const uint32_t kVideoTrack3Ssrc = 5; -static const uint32_t kVideoTrack4Ssrc = 6; +static const uint32_t kVideoTrack3Ssrc = 6; // DataChannel static const char kDataChannelLabel[] = "data_channel"; @@ -535,60 +719,32 @@ class WebRtcSdpTest : public testing::Test { #endif // AudioContentDescription audio_desc_ = CreateAudioContentDescription(); - AudioCodec opus(111, "opus", 48000, 0, 2, 3); - audio_desc_->AddCodec(opus); - audio_desc_->AddCodec(AudioCodec(103, "ISAC", 16000, 32000, 1, 2)); - audio_desc_->AddCodec(AudioCodec(104, "ISAC", 32000, 56000, 1, 1)); + StreamParams audio_stream; + audio_stream.id = kAudioTrackId1; + audio_stream.cname = kStream1Cname; + audio_stream.sync_label = kStreamLabel1; + audio_stream.ssrcs.push_back(kAudioTrack1Ssrc); + audio_desc_->AddStream(audio_stream); desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_desc_); // VideoContentDescription - rtc::scoped_ptr video( - new VideoContentDescription()); - video_desc_ = video.get(); - StreamParams video_stream1; - video_stream1.id = kVideoTrackId1; - video_stream1.cname = kStream1Cname; - video_stream1.sync_label = kStreamLabel1; - video_stream1.ssrcs.push_back(kVideoTrack1Ssrc); - video->AddStream(video_stream1); - StreamParams video_stream2; - video_stream2.id = kVideoTrackId2; - video_stream2.cname = kStream1Cname; - video_stream2.sync_label = kStreamLabel1; - video_stream2.ssrcs.push_back(kVideoTrack2Ssrc); - video->AddStream(video_stream2); - StreamParams video_stream3; - video_stream3.id = kVideoTrackId3; - video_stream3.cname = kStream2Cname; - video_stream3.sync_label = kStreamLabel2; - video_stream3.ssrcs.push_back(kVideoTrack3Ssrc); - video_stream3.ssrcs.push_back(kVideoTrack4Ssrc); - cricket::SsrcGroup ssrc_group(kFecSsrcGroupSemantics, video_stream3.ssrcs); - video_stream3.ssrc_groups.push_back(ssrc_group); - video->AddStream(video_stream3); - video->AddCrypto(CryptoParams(1, "AES_CM_128_HMAC_SHA1_80", - "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32", "")); - video->set_protocol(cricket::kMediaProtocolSavpf); - video->AddCodec(VideoCodec( - 120, - JsepSessionDescription::kDefaultVideoCodecName, - JsepSessionDescription::kMaxVideoCodecWidth, - JsepSessionDescription::kMaxVideoCodecHeight, - JsepSessionDescription::kDefaultVideoCodecFramerate, - JsepSessionDescription::kDefaultVideoCodecPreference)); - - desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, - video.release()); + video_desc_ = CreateVideoContentDescription(); + StreamParams video_stream; + video_stream.id = kVideoTrackId1; + video_stream.cname = kStream1Cname; + video_stream.sync_label = kStreamLabel1; + video_stream.ssrcs.push_back(kVideoTrack1Ssrc1); + video_stream.ssrcs.push_back(kVideoTrack1Ssrc2); + cricket::SsrcGroup ssrc_group(kFecSsrcGroupSemantics, video_stream.ssrcs); + video_stream.ssrc_groups.push_back(ssrc_group); + video_desc_->AddStream(video_stream); + desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, video_desc_); // TransportInfo - EXPECT_TRUE(desc_.AddTransportInfo( - TransportInfo(kAudioContentName, - TransportDescription(kCandidateUfragVoice, - kCandidatePwdVoice)))); - EXPECT_TRUE(desc_.AddTransportInfo( - TransportInfo(kVideoContentName, - TransportDescription(kCandidateUfragVideo, - kCandidatePwdVideo)))); + EXPECT_TRUE(desc_.AddTransportInfo(TransportInfo( + kAudioContentName, TransportDescription(kUfragVoice, kPwdVoice)))); + EXPECT_TRUE(desc_.AddTransportInfo(TransportInfo( + kVideoContentName, TransportDescription(kUfragVideo, kPwdVideo)))); // v4 host int port = 1234; @@ -698,29 +854,119 @@ class WebRtcSdpTest : public testing::Test { } } + // Turns the existing reference description into a plan B description, + // with 2 audio tracks and 3 video tracks. + void MakePlanBDescription() { + audio_desc_ = static_cast(audio_desc_->Copy()); + video_desc_ = static_cast(video_desc_->Copy()); + + StreamParams audio_track_2; + audio_track_2.id = kAudioTrackId2; + audio_track_2.cname = kStream2Cname; + audio_track_2.sync_label = kStreamLabel2; + audio_track_2.ssrcs.push_back(kAudioTrack2Ssrc); + audio_desc_->AddStream(audio_track_2); + + StreamParams video_track_2; + video_track_2.id = kVideoTrackId2; + video_track_2.cname = kStream2Cname; + video_track_2.sync_label = kStreamLabel2; + video_track_2.ssrcs.push_back(kVideoTrack2Ssrc); + video_desc_->AddStream(video_track_2); + + StreamParams video_track_3; + video_track_3.id = kVideoTrackId3; + video_track_3.cname = kStream2Cname; + video_track_3.sync_label = kStreamLabel2; + video_track_3.ssrcs.push_back(kVideoTrack3Ssrc); + video_desc_->AddStream(video_track_3); + + desc_.RemoveContentByName(kAudioContentName); + desc_.RemoveContentByName(kVideoContentName); + desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_desc_); + desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, video_desc_); + + ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), jdesc_.session_id(), + jdesc_.session_version())); + } + + // Turns the existing reference description into a unified plan description, + // with 2 audio tracks and 3 video tracks. + void MakeUnifiedPlanDescription() { + // Audio track 2. + AudioContentDescription* audio_desc_2 = CreateAudioContentDescription(); + StreamParams audio_track_2; + audio_track_2.id = kAudioTrackId2; + audio_track_2.cname = kStream2Cname; + audio_track_2.sync_label = kStreamLabel2; + audio_track_2.ssrcs.push_back(kAudioTrack2Ssrc); + audio_desc_2->AddStream(audio_track_2); + desc_.AddContent(kAudioContentName2, NS_JINGLE_RTP, audio_desc_2); + EXPECT_TRUE(desc_.AddTransportInfo(TransportInfo( + kAudioContentName2, TransportDescription(kUfragVoice2, kPwdVoice2)))); + + // Video track 2, in stream 2. + VideoContentDescription* video_desc_2 = CreateVideoContentDescription(); + StreamParams video_track_2; + video_track_2.id = kVideoTrackId2; + video_track_2.cname = kStream2Cname; + video_track_2.sync_label = kStreamLabel2; + video_track_2.ssrcs.push_back(kVideoTrack2Ssrc); + video_desc_2->AddStream(video_track_2); + desc_.AddContent(kVideoContentName2, NS_JINGLE_RTP, video_desc_2); + EXPECT_TRUE(desc_.AddTransportInfo(TransportInfo( + kVideoContentName2, TransportDescription(kUfragVideo2, kPwdVideo2)))); + + // Video track 3, in stream 2. + VideoContentDescription* video_desc_3 = CreateVideoContentDescription(); + StreamParams video_track_3; + video_track_3.id = kVideoTrackId3; + video_track_3.cname = kStream2Cname; + video_track_3.sync_label = kStreamLabel2; + video_track_3.ssrcs.push_back(kVideoTrack3Ssrc); + video_desc_3->AddStream(video_track_3); + desc_.AddContent(kVideoContentName3, NS_JINGLE_RTP, video_desc_3); + EXPECT_TRUE(desc_.AddTransportInfo(TransportInfo( + kVideoContentName3, TransportDescription(kUfragVideo3, kPwdVideo3)))); + + ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), jdesc_.session_id(), + jdesc_.session_version())); + } + + // Creates an audio content description with no streams, and some default + // configuration. AudioContentDescription* CreateAudioContentDescription() { AudioContentDescription* audio = new AudioContentDescription(); audio->set_rtcp_mux(true); audio->set_rtcp_reduced_size(true); - StreamParams audio_stream1; - audio_stream1.id = kAudioTrackId1; - audio_stream1.cname = kStream1Cname; - audio_stream1.sync_label = kStreamLabel1; - audio_stream1.ssrcs.push_back(kAudioTrack1Ssrc); - audio->AddStream(audio_stream1); - StreamParams audio_stream2; - audio_stream2.id = kAudioTrackId2; - audio_stream2.cname = kStream2Cname; - audio_stream2.sync_label = kStreamLabel2; - audio_stream2.ssrcs.push_back(kAudioTrack2Ssrc); - audio->AddStream(audio_stream2); audio->AddCrypto(CryptoParams(1, "AES_CM_128_HMAC_SHA1_32", "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32", "dummy_session_params")); audio->set_protocol(cricket::kMediaProtocolSavpf); + AudioCodec opus(111, "opus", 48000, 0, 2, 3); + audio->AddCodec(opus); + audio->AddCodec(AudioCodec(103, "ISAC", 16000, 32000, 1, 2)); + audio->AddCodec(AudioCodec(104, "ISAC", 32000, 56000, 1, 1)); return audio; } + // Creates a video content description with no streams, and some default + // configuration. + VideoContentDescription* CreateVideoContentDescription() { + VideoContentDescription* video = new VideoContentDescription(); + video->AddCrypto(CryptoParams( + 1, "AES_CM_128_HMAC_SHA1_80", + "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32", "")); + video->set_protocol(cricket::kMediaProtocolSavpf); + video->AddCodec( + VideoCodec(120, JsepSessionDescription::kDefaultVideoCodecName, + JsepSessionDescription::kMaxVideoCodecWidth, + JsepSessionDescription::kMaxVideoCodecHeight, + JsepSessionDescription::kDefaultVideoCodecFramerate, + JsepSessionDescription::kDefaultVideoCodecPreference)); + return video; + } + template void CompareMediaContentDescription(const MCD* cd1, const MCD* cd2) { @@ -986,13 +1232,13 @@ class WebRtcSdpTest : public testing::Test { sizeof(kIdentityDigest)); EXPECT_TRUE(desc_.AddTransportInfo(TransportInfo( kAudioContentName, - TransportDescription(std::vector(), kCandidateUfragVoice, - kCandidatePwdVoice, cricket::ICEMODE_FULL, + TransportDescription(std::vector(), kUfragVoice, kPwdVoice, + cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_NONE, &fingerprint)))); EXPECT_TRUE(desc_.AddTransportInfo(TransportInfo( kVideoContentName, - TransportDescription(std::vector(), kCandidateUfragVideo, - kCandidatePwdVideo, cricket::ICEMODE_FULL, + TransportDescription(std::vector(), kUfragVideo, kPwdVideo, + cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_NONE, &fingerprint)))); } @@ -1027,7 +1273,7 @@ class WebRtcSdpTest : public testing::Test { jdesc_.session_version())) { return false; } - std::string message = webrtc::SdpSerialize(jdesc_); + std::string message = webrtc::SdpSerialize(jdesc_, false); EXPECT_EQ(new_sdp, message); return true; } @@ -1043,12 +1289,10 @@ class WebRtcSdpTest : public testing::Test { audio_desc_); desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, video_rejected, video_desc_); - SetIceUfragPwd(kAudioContentName, - audio_rejected ? "" : kCandidateUfragVoice, - audio_rejected ? "" : kCandidatePwdVoice); - SetIceUfragPwd(kVideoContentName, - video_rejected ? "" : kCandidateUfragVideo, - video_rejected ? "" : kCandidatePwdVideo); + SetIceUfragPwd(kAudioContentName, audio_rejected ? "" : kUfragVoice, + audio_rejected ? "" : kPwdVoice); + SetIceUfragPwd(kVideoContentName, video_rejected ? "" : kUfragVideo, + video_rejected ? "" : kPwdVideo); std::string new_sdp = kSdpString; ReplaceRejected(audio_rejected, video_rejected, &new_sdp); @@ -1058,7 +1302,7 @@ class WebRtcSdpTest : public testing::Test { kSessionVersion)) { return false; } - std::string message = webrtc::SdpSerialize(jdesc_no_candidates); + std::string message = webrtc::SdpSerialize(jdesc_no_candidates, false); EXPECT_EQ(new_sdp, message); return true; } @@ -1073,10 +1317,8 @@ class WebRtcSdpTest : public testing::Test { codec.SetParam(cricket::kCodecParamPort, kDefaultSctpPort); data_desc_->AddCodec(codec); desc_.AddContent(kDataContentName, NS_JINGLE_DRAFT_SCTP, data.release()); - EXPECT_TRUE(desc_.AddTransportInfo( - TransportInfo(kDataContentName, - TransportDescription(kCandidateUfragData, - kCandidatePwdData)))); + EXPECT_TRUE(desc_.AddTransportInfo(TransportInfo( + kDataContentName, TransportDescription(kUfragData, kPwdData)))); } void AddRtpDataChannel() { @@ -1096,10 +1338,8 @@ class WebRtcSdpTest : public testing::Test { "inline:FvLcvU2P3ZWmQxgPAgcDu7Zl9vftYElFOjEzhWs5", "")); data_desc_->set_protocol(cricket::kMediaProtocolSavpf); desc_.AddContent(kDataContentName, NS_JINGLE_RTP, data.release()); - EXPECT_TRUE(desc_.AddTransportInfo( - TransportInfo(kDataContentName, - TransportDescription(kCandidateUfragData, - kCandidatePwdData)))); + EXPECT_TRUE(desc_.AddTransportInfo(TransportInfo( + kDataContentName, TransportDescription(kUfragData, kPwdData)))); } bool TestDeserializeDirection(cricket::MediaContentDirection direction) { @@ -1136,12 +1376,10 @@ class WebRtcSdpTest : public testing::Test { audio_desc_); desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, video_rejected, video_desc_); - SetIceUfragPwd(kAudioContentName, - audio_rejected ? "" : kCandidateUfragVoice, - audio_rejected ? "" : kCandidatePwdVoice); - SetIceUfragPwd(kVideoContentName, - video_rejected ? "" : kCandidateUfragVideo, - video_rejected ? "" : kCandidatePwdVideo); + SetIceUfragPwd(kAudioContentName, audio_rejected ? "" : kUfragVoice, + audio_rejected ? "" : kPwdVoice); + SetIceUfragPwd(kVideoContentName, video_rejected ? "" : kUfragVideo, + video_rejected ? "" : kPwdVideo); JsepSessionDescription jdesc_no_candidates(kDummyString); if (!jdesc_no_candidates.Initialize(desc_.Copy(), jdesc_.session_id(), jdesc_.session_version())) { @@ -1340,8 +1578,9 @@ class WebRtcSdpTest : public testing::Test { // no order. If deserializer has already been tested, serializing then // deserializing and comparing JsepSessionDescription will test // the serializer sufficiently. - void TestSerialize(const JsepSessionDescription& jdesc) { - std::string message = webrtc::SdpSerialize(jdesc); + void TestSerialize(const JsepSessionDescription& jdesc, + bool unified_plan_sdp) { + std::string message = webrtc::SdpSerialize(jdesc, unified_plan_sdp); JsepSessionDescription jdesc_output_des(kDummyString); SdpParseError error; EXPECT_TRUE(webrtc::SdpDeserialize(message, &jdesc_output_des, &error)); @@ -1374,13 +1613,13 @@ void TestMismatch(const std::string& string1, const std::string& string2) { TEST_F(WebRtcSdpTest, SerializeSessionDescription) { // SessionDescription with desc and candidates. - std::string message = webrtc::SdpSerialize(jdesc_); + std::string message = webrtc::SdpSerialize(jdesc_, false); TestMismatch(std::string(kSdpFullString), message); } TEST_F(WebRtcSdpTest, SerializeSessionDescriptionEmpty) { JsepSessionDescription jdesc_empty(kDummyString); - EXPECT_EQ("", webrtc::SdpSerialize(jdesc_empty)); + EXPECT_EQ("", webrtc::SdpSerialize(jdesc_empty, false)); } // This tests serialization of SDP with only IPv6 candidates and verifies that @@ -1405,7 +1644,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithIPv6Only) { JsepIceCandidate jice2("audio_content_name", 0, candidate2); ASSERT_TRUE(jdesc.AddCandidate(&jice1)); ASSERT_TRUE(jdesc.AddCandidate(&jice2)); - std::string message = webrtc::SdpSerialize(jdesc); + std::string message = webrtc::SdpSerialize(jdesc, false); // Audio line should have a c line like this one. EXPECT_NE(message.find("c=IN IP6 ::1"), std::string::npos); @@ -1435,7 +1674,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBothIPFamilies) { JsepIceCandidate jice_v6("audio_content_name", 0, candidate_v6); ASSERT_TRUE(jdesc.AddCandidate(&jice_v4)); ASSERT_TRUE(jdesc.AddCandidate(&jice_v6)); - std::string message = webrtc::SdpSerialize(jdesc); + std::string message = webrtc::SdpSerialize(jdesc, false); // Audio line should have a c line like this one. EXPECT_NE(message.find("c=IN IP4 192.168.1.5"), std::string::npos); @@ -1467,7 +1706,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBothProtocols) { JsepIceCandidate jice2("audio_content_name", 0, candidate2); ASSERT_TRUE(jdesc.AddCandidate(&jice1)); ASSERT_TRUE(jdesc.AddCandidate(&jice2)); - std::string message = webrtc::SdpSerialize(jdesc); + std::string message = webrtc::SdpSerialize(jdesc, false); // Audio line should have a c line like this one. EXPECT_NE(message.find("c=IN IP6 fe80::1234:5678:abcd:ef12"), @@ -1498,7 +1737,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithTCPOnly) { JsepIceCandidate jice2("audio_content_name", 0, candidate2); ASSERT_TRUE(jdesc.AddCandidate(&jice1)); ASSERT_TRUE(jdesc.AddCandidate(&jice2)); - std::string message = webrtc::SdpSerialize(jdesc); + std::string message = webrtc::SdpSerialize(jdesc, false); // Audio line should have a c line like this one when no any default exists. EXPECT_NE(message.find("c=IN IP4 0.0.0.0"), std::string::npos); @@ -1511,7 +1750,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprint) { JsepSessionDescription jdesc_with_fingerprint(kDummyString); ASSERT_TRUE(jdesc_with_fingerprint.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); - std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint); + std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint, false); std::string sdp_with_fingerprint = kSdpString; InjectAfter(kAttributeIcePwdVoice, @@ -1530,7 +1769,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprintNoCryptos) { JsepSessionDescription jdesc_with_fingerprint(kDummyString); ASSERT_TRUE(jdesc_with_fingerprint.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); - std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint); + std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint, false); std::string sdp_with_fingerprint = kSdpString; Replace(kAttributeCryptoVoice, "", &sdp_with_fingerprint); @@ -1548,7 +1787,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithoutCandidates) { JsepSessionDescription jdesc_no_candidates(kDummyString); ASSERT_TRUE(jdesc_no_candidates.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); - std::string message = webrtc::SdpSerialize(jdesc_no_candidates); + std::string message = webrtc::SdpSerialize(jdesc_no_candidates, false); EXPECT_EQ(std::string(kSdpString), message); } @@ -1560,7 +1799,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBundle) { ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), jdesc_.session_id(), jdesc_.session_version())); - std::string message = webrtc::SdpSerialize(jdesc_); + std::string message = webrtc::SdpSerialize(jdesc_, false); std::string sdp_with_bundle = kSdpFullString; InjectAfter(kSessionTime, "a=group:BUNDLE audio_content_name video_content_name\r\n", @@ -1578,7 +1817,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBandwidth) { ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), jdesc_.session_id(), jdesc_.session_version())); - std::string message = webrtc::SdpSerialize(jdesc_); + std::string message = webrtc::SdpSerialize(jdesc_, false); std::string sdp_with_bandwidth = kSdpFullString; InjectAfter("c=IN IP4 74.125.224.39\r\n", "b=AS:100\r\n", @@ -1601,7 +1840,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithIceOptions) { ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), jdesc_.session_id(), jdesc_.session_version())); - std::string message = webrtc::SdpSerialize(jdesc_); + std::string message = webrtc::SdpSerialize(jdesc_, false); std::string sdp_with_ice_options = kSdpFullString; InjectAfter(kAttributeIcePwdVoice, "a=ice-options:iceoption1 iceoption3\r\n", @@ -1641,7 +1880,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithRtpDataChannel) { JsepSessionDescription jsep_desc(kDummyString); ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); - std::string message = webrtc::SdpSerialize(jsep_desc); + std::string message = webrtc::SdpSerialize(jsep_desc, false); std::string expected_sdp = kSdpString; expected_sdp.append(kSdpRtpDataChannelString); @@ -1653,7 +1892,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithSctpDataChannel) { JsepSessionDescription jsep_desc(kDummyString); ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); - std::string message = webrtc::SdpSerialize(jsep_desc); + std::string message = webrtc::SdpSerialize(jsep_desc, false); std::string expected_sdp = kSdpString; expected_sdp.append(kSdpSctpDataChannelString); @@ -1674,7 +1913,7 @@ TEST_F(WebRtcSdpTest, SerializeWithSctpDataChannelAndNewPort) { codec.SetParam(cricket::kCodecParamPort, kNewPort); dcdesc->AddOrReplaceCodec(codec); - std::string message = webrtc::SdpSerialize(jsep_desc); + std::string message = webrtc::SdpSerialize(jsep_desc, false); std::string expected_sdp = kSdpString; expected_sdp.append(kSdpSctpDataChannelString); @@ -1697,7 +1936,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithDataChannelAndBandwidth) { JsepSessionDescription jsep_desc(kDummyString); ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); - std::string message = webrtc::SdpSerialize(jsep_desc); + std::string message = webrtc::SdpSerialize(jsep_desc, false); std::string expected_sdp = kSdpString; expected_sdp.append(kSdpRtpDataChannelString); @@ -1713,7 +1952,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithExtmap) { JsepSessionDescription desc_with_extmap("dummy"); ASSERT_TRUE(desc_with_extmap.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); - std::string message = webrtc::SdpSerialize(desc_with_extmap); + std::string message = webrtc::SdpSerialize(desc_with_extmap, false); std::string sdp_with_extmap = kSdpString; InjectAfter("a=mid:audio_content_name\r\n", @@ -2312,7 +2551,7 @@ TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutEndLineBreak) { // Deserialize SdpParseError error; EXPECT_FALSE(webrtc::SdpDeserialize(sdp, &jdesc, &error)); - const std::string lastline = "a=ssrc:6 label:video_track_id_3"; + const std::string lastline = "a=ssrc:3 label:video_track_id_1"; EXPECT_EQ(lastline, error.line); EXPECT_EQ("Invalid SDP line.", error.description); } @@ -2412,7 +2651,7 @@ TEST_F(WebRtcSdpTest, DeserializeBrokenSdp) { TEST_F(WebRtcSdpTest, DeserializeSdpWithInvalidAttributeValue) { // ssrc ExpectParseFailure("a=ssrc:1", "a=ssrc:badvalue"); - ExpectParseFailure("a=ssrc-group:FEC 5 6", "a=ssrc-group:FEC badvalue 6"); + ExpectParseFailure("a=ssrc-group:FEC 2 3", "a=ssrc-group:FEC badvalue 3"); // crypto ExpectParseFailure("a=crypto:1 ", "a=crypto:badvalue "); // rtpmap @@ -2482,21 +2721,21 @@ TEST_F(WebRtcSdpTest, DeserializeSerializeCodecParams) { params.useinband = 1; params.maxaveragebitrate = 128000; TestDeserializeCodecParams(params, &jdesc_output); - TestSerialize(jdesc_output); + TestSerialize(jdesc_output, false); } TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpFb) { const bool kUseWildcard = false; JsepSessionDescription jdesc_output(kDummyString); TestDeserializeRtcpFb(&jdesc_output, kUseWildcard); - TestSerialize(jdesc_output); + TestSerialize(jdesc_output, false); } TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpFbWildcard) { const bool kUseWildcard = true; JsepSessionDescription jdesc_output(kDummyString); TestDeserializeRtcpFb(&jdesc_output, kUseWildcard); - TestSerialize(jdesc_output); + TestSerialize(jdesc_output, false); } TEST_F(WebRtcSdpTest, DeserializeVideoFmtp) { @@ -2578,7 +2817,7 @@ TEST_F(WebRtcSdpTest, SerializeVideoFmtp) { ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), jdesc_.session_id(), jdesc_.session_version())); - std::string message = webrtc::SdpSerialize(jdesc_); + std::string message = webrtc::SdpSerialize(jdesc_, false); std::string sdp_with_fmtp = kSdpFullString; InjectAfter("a=rtpmap:120 VP8/90000\r\n", "a=fmtp:120 x-google-min-bitrate=10\r\n", @@ -2618,7 +2857,7 @@ TEST_F(WebRtcSdpTest, RoundTripSdpWithSctpDataChannelsWithCandidates) { JsepSessionDescription jdesc_output(kDummyString); EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output)); - EXPECT_EQ(sdp_with_data, webrtc::SdpSerialize(jdesc_output)); + EXPECT_EQ(sdp_with_data, webrtc::SdpSerialize(jdesc_output, false)); } TEST_F(WebRtcSdpTest, SerializeDtlsSetupAttribute) { @@ -2646,7 +2885,7 @@ TEST_F(WebRtcSdpTest, SerializeDtlsSetupAttribute) { ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), jdesc_.session_id(), jdesc_.session_version())); - std::string message = webrtc::SdpSerialize(jdesc_); + std::string message = webrtc::SdpSerialize(jdesc_, false); std::string sdp_with_dtlssetup = kSdpFullString; // Fingerprint attribute is necessary to add DTLS setup attribute. @@ -2718,7 +2957,36 @@ TEST_F(WebRtcSdpTest, MediaContentOrderMaintainedRoundTrip) { EXPECT_EQ(media_types[media_content_in_sdp[i]], mdesc->type()); } - std::string serialized_sdp = webrtc::SdpSerialize(jdesc); + std::string serialized_sdp = webrtc::SdpSerialize(jdesc, false); EXPECT_EQ(sdp_string, serialized_sdp); } } + +TEST_F(WebRtcSdpTest, DeserializePlanBSessionDescription) { + MakePlanBDescription(); + + JsepSessionDescription deserialized_description(kDummyString); + EXPECT_TRUE(SdpDeserialize(kPlanBSdpFullString, &deserialized_description)); + + EXPECT_TRUE(CompareSessionDescription(jdesc_, deserialized_description)); +} + +TEST_F(WebRtcSdpTest, SerializePlanBSessionDescription) { + MakePlanBDescription(); + TestSerialize(jdesc_, false); +} + +TEST_F(WebRtcSdpTest, DeserializeUnifiedPlanSessionDescription) { + MakeUnifiedPlanDescription(); + + JsepSessionDescription deserialized_description(kDummyString); + EXPECT_TRUE( + SdpDeserialize(kUnifiedPlanSdpFullString, &deserialized_description)); + + EXPECT_TRUE(CompareSessionDescription(jdesc_, deserialized_description)); +} + +TEST_F(WebRtcSdpTest, SerializeUnifiedPlanSessionDescription) { + MakeUnifiedPlanDescription(); + TestSerialize(jdesc_, true); +}