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}
diff --git a/webrtc/api/webrtcsdp.cc b/webrtc/api/webrtcsdp.cc
index a308a08..a6edf7c 100644
--- a/webrtc/api/webrtcsdp.cc
+++ b/webrtc/api/webrtcsdp.cc
@@ -84,7 +84,7 @@
 // the form:
 // <type>=<value>
 // where <type> MUST be exactly one case-significant character.
-static const int kLinePrefixLength = 2;  // Lenght of <type>=
+static const int kLinePrefixLength = 2;  // Length of <type>=
 static const char kLineTypeVersion = 'v';
 static const char kLineTypeOrigin = 'o';
 static const char kLineTypeSessionName = 's';
@@ -104,6 +104,7 @@
 // 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 @@
 
 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 @@
                                   const TransportInfo* transport_info,
                                   const MediaType media_type,
                                   const std::vector<Candidate>& 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 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 @@
 
     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 @@
   }
 }
 
-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 @@
       static_cast<const MediaContentDescription*>(it->description);
     std::vector<Candidate> 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 @@
                            const TransportInfo* transport_info,
                            const MediaType media_type,
                            const std::vector<Candidate>& candidates,
+                           bool unified_plan_sdp,
                            std::string* message) {
   ASSERT(message != NULL);
   if (content_info == NULL || message == NULL) {
@@ -1337,7 +1339,8 @@
   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 @@
   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:<value>["/"<direction>] <URI> <extensionattributes>
@@ -1389,6 +1393,22 @@
   }
   AddLine(os.str(), message);
 
+  // draft-ietf-mmusic-msid-11
+  // a=msid:<stream id> <track id>
+  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 @@
 
       // draft-alvestrand-mmusic-msid-00
       // a=ssrc:<ssrc-id> 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:<ssrc-id> mslabel:<value>
       // The label isn't yet defined.
@@ -2032,6 +2053,29 @@
   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:<stream id> <track id>
+  // 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:<msid-id>
+  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 @@
   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 @@
         }
         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 @@
     }
   }
 
+  // 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 @@
   }
 
   // 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 @@
                          "Expected format \"msid:<identifier>[ <appdata>]\".",
                          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