Updated SCTP SDP attributes according to draft-ietf-mmusic-sctp-sdp-07

- SDP sctpmap attribute replaced with fmtp attribute
- SDP sctp-port attribute is newly added

BUG=3592
R=jiayl@webrtc.org, juberti@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/16169004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7087 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/AUTHORS b/AUTHORS
index fe010e2..6ca1d0e 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -6,6 +6,7 @@
 Bridger Maxwell <bridgeyman@gmail.com>
 Christophe Dumez <ch.dumez@samsung.com>
 Eric Rescorla, RTFM Inc.
+Giji Gangadharan <giji.g@samsung.com>
 Jie Mao <maojie0924@gmail.com>
 Luke Weber
 Martin Storsjo <martin@martin.st>
diff --git a/talk/app/webrtc/webrtcsdp.cc b/talk/app/webrtc/webrtcsdp.cc
index 792a091..f4b38c5 100644
--- a/talk/app/webrtc/webrtcsdp.cc
+++ b/talk/app/webrtc/webrtcsdp.cc
@@ -155,6 +155,9 @@
 static const char kAttributeRtcpFb[] = "rtcp-fb";
 static const char kAttributeSendRecv[] = "sendrecv";
 static const char kAttributeInactive[] = "inactive";
+// draft-ietf-mmusic-sctp-sdp-07
+// a=sctp-port
+static const char kAttributeSctpPort[] = "sctp-port";
 
 // Experimental flags
 static const char kAttributeXGoogleFlag[] = "x-google-flag";
@@ -1100,6 +1103,26 @@
   return true;
 }
 
+bool ParseSctpPort(const std::string& line,
+                   int* sctp_port,
+                   SdpParseError* error) {
+  // draft-ietf-mmusic-sctp-sdp-07
+  // a=sctp-port
+  std::vector<std::string> fields;
+  rtc::split(line.substr(kLinePrefixLength),
+                   kSdpDelimiterSpace, &fields);
+  const size_t expected_min_fields = 2;
+  if (fields.size() < expected_min_fields) {
+    return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
+  }
+  if (!rtc::FromString(fields[1], sctp_port)) {
+    return ParseFailed(line,
+                       "Invalid sctp port value.",
+                       error);
+  }
+  return true;
+}
+
 bool ParseExtmap(const std::string& line, RtpHeaderExtension* extmap,
                  SdpParseError* error) {
   // RFC 5285
@@ -1563,6 +1586,24 @@
   }
 }
 
+bool AddSctpDataCodec(DataContentDescription* media_desc,
+                      int sctp_port) {
+  if (media_desc->HasCodec(cricket::kGoogleSctpDataCodecId)) {
+    return ParseFailed("",
+                       "Can't have multiple sctp port attributes.",
+                       NULL);
+  }
+  // Add the SCTP Port number as a pseudo-codec "port" parameter
+  cricket::DataCodec codec_port(
+      cricket::kGoogleSctpDataCodecId, cricket::kGoogleSctpDataCodecName,
+      0);
+  codec_port.SetParam(cricket::kCodecParamPort, sctp_port);
+  LOG(INFO) << "AddSctpDataCodec: Got SCTP Port Number "
+            << sctp_port;
+  media_desc->AddCodec(codec_port);
+  return true;
+}
+
 bool GetMinValue(const std::vector<int>& values, int* value) {
   if (values.empty()) {
     return false;
@@ -2129,18 +2170,20 @@
 
     // <fmt>
     std::vector<int> codec_preference;
-    for (size_t j = 3 ; j < fields.size(); ++j) {
-      // TODO(wu): Remove when below bug is fixed.
-      // https://bugzilla.mozilla.org/show_bug.cgi?id=996329
-      if (fields[j] == "" && j == fields.size() - 1) {
-        continue;
-      }
+    if (!is_sctp) {
+      for (size_t j = 3 ; j < fields.size(); ++j) {
+        // TODO(wu): Remove when below bug is fixed.
+        // https://bugzilla.mozilla.org/show_bug.cgi?id=996329
+        if (fields[j] == "" && j == fields.size() - 1) {
+          continue;
+        }
 
-      int pl = 0;
-      if (!GetValueFromString(line, fields[j], &pl, error)) {
-        return false;
+        int pl = 0;
+        if (!GetValueFromString(line, fields[j], &pl, error)) {
+          return false;
+        }
+        codec_preference.push_back(pl);
       }
-      codec_preference.push_back(pl);
     }
 
     // Make a temporary TransportDescription based on |session_td|.
@@ -2173,20 +2216,14 @@
                     codec_preference, pos, &content_name,
                     &transport, candidates, error);
 
-      if (desc && protocol == cricket::kMediaProtocolDtlsSctp) {
-        // Add the SCTP Port number as a pseudo-codec "port" parameter
-        cricket::DataCodec codec_port(
-            cricket::kGoogleSctpDataCodecId, cricket::kGoogleSctpDataCodecName,
-            0);
-        codec_port.SetParam(cricket::kCodecParamPort, fields[3]);
-        LOG(INFO) << "ParseMediaDescription: Got SCTP Port Number "
-                  << fields[3];
-        ASSERT(!desc->HasCodec(cricket::kGoogleSctpDataCodecId));
-        desc->AddCodec(codec_port);
+      int p;
+      if (desc && protocol == cricket::kMediaProtocolDtlsSctp &&
+          rtc::FromString(fields[3], &p)) {
+        if (!AddSctpDataCodec(desc, p))
+          return false;
       }
 
       content.reset(desc);
-
       // We should always use the default bandwidth for RTP-based data
       // channels.  Don't allow SDP to set the bandwidth, because that
       // would give JS the opportunity to "break the Internet".
@@ -2518,6 +2555,15 @@
       if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
         return false;
       }
+    } else if (HasAttribute(line, kAttributeSctpPort)) {
+      int sctp_port;
+      if (!ParseSctpPort(line, &sctp_port, error)) {
+        return false;
+      }
+      if (!AddSctpDataCodec(static_cast<DataContentDescription*>(media_desc),
+                            sctp_port)) {
+        return false;
+      }
     } else if (is_rtp) {
       //
       // RTP specific attrubtes
diff --git a/talk/app/webrtc/webrtcsdp_unittest.cc b/talk/app/webrtc/webrtcsdp_unittest.cc
index 6a22e38..560d5da 100644
--- a/talk/app/webrtc/webrtcsdp_unittest.cc
+++ b/talk/app/webrtc/webrtcsdp_unittest.cc
@@ -284,6 +284,16 @@
     "a=mid:data_content_name\r\n"
     "a=sctpmap:5000 webrtc-datachannel 1024\r\n";
 
+// draft-ietf-mmusic-sctp-sdp-07
+static const char kSdpSctpDataChannelStringWithSctpPort[] =
+    "m=application 1 DTLS/SCTP webrtc-datachannel\r\n"
+    "a=fmtp:webrtc-datachannel max-message-size=100000\r\n"
+    "a=sctp-port 5000\r\n"
+    "c=IN IP4 0.0.0.0\r\n"
+    "a=ice-ufrag:ufrag_data\r\n"
+    "a=ice-pwd:pwd_data\r\n"
+    "a=mid:data_content_name\r\n";
+
 static const char kSdpSctpDataChannelWithCandidatesString[] =
     "m=application 2345 DTLS/SCTP 5000\r\n"
     "c=IN IP4 74.125.127.126\r\n"
@@ -2023,6 +2033,36 @@
   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
 }
 
+TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelsWithSctpPort) {
+  AddSctpDataChannel();
+  JsepSessionDescription jdesc(kDummyString);
+  ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
+
+  std::string sdp_with_data = kSdpString;
+  sdp_with_data.append(kSdpSctpDataChannelStringWithSctpPort);
+  JsepSessionDescription jdesc_output(kDummyString);
+
+  EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
+  EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
+}
+
+// Test to check the behaviour if sctp-port is specified
+// on the m= line and in a=sctp-port.
+TEST_F(WebRtcSdpTest, DeserializeSdpWithMultiSctpPort) {
+  AddSctpDataChannel();
+  JsepSessionDescription jdesc(kDummyString);
+  ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
+
+  std::string sdp_with_data = kSdpString;
+  // Append m= attributes
+  sdp_with_data.append(kSdpSctpDataChannelString);
+  // Append a=sctp-port attribute
+  sdp_with_data.append("a=sctp-port 5000\r\n");
+  JsepSessionDescription jdesc_output(kDummyString);
+
+  EXPECT_FALSE(SdpDeserialize(sdp_with_data, &jdesc_output));
+}
+
 // For crbug/344475.
 TEST_F(WebRtcSdpTest, DeserializeSdpWithCorruptedSctpDataChannels) {
   std::string sdp_with_data = kSdpString;
@@ -2071,6 +2111,19 @@
 
   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
+
+  // We need to test the deserialized JsepSessionDescription from
+  // kSdpSctpDataChannelStringWithSctpPort for
+  // draft-ietf-mmusic-sctp-sdp-07
+  // a=sctp-port
+  sdp_with_data = kSdpString;
+  sdp_with_data.append(kSdpSctpDataChannelStringWithSctpPort);
+  rtc::replace_substrs(default_portstr, strlen(default_portstr),
+                             unusual_portstr, strlen(unusual_portstr),
+                             &sdp_with_data);
+
+  EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
+  EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
 }
 
 TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpDataChannelsAndBandwidth) {