Bug 1476085: Added SDP candidate serialization, r=bwc,dminor draft
authorJohannes Willbold <j.willbold@mozilla.com>
Mon, 16 Jul 2018 13:59:04 -0700
changeset 820922 5d49e3a39af3395cf16bedef387728d7de663271
parent 820804 4f12d77b4f9b6adaf06615c1c8cdc14de836dc1a
push id116985
push userbmo:johannes.willbold@rub.de
push dateFri, 20 Jul 2018 17:22:45 +0000
reviewersbwc, dminor
bugs1476085
milestone63.0a1
Bug 1476085: Added SDP candidate serialization, r=bwc,dminor Added SDP candidate serializaton in rust Extended Rust unit test for candidates Added C++/Rust glue code for candidates Added free_boxed_string_vec MozReview-Commit-ID: CeVM2p47fQ7
media/webrtc/signaling/gtest/sdp_unittests.cpp
media/webrtc/signaling/src/sdp/RsdparsaSdpAttributeList.cpp
media/webrtc/signaling/src/sdp/RsdparsaSdpAttributeList.h
media/webrtc/signaling/src/sdp/RsdparsaSdpInc.h
media/webrtc/signaling/src/sdp/rsdparsa/src/attribute_type.rs
media/webrtc/signaling/src/sdp/rsdparsa_capi/src/attribute.rs
media/webrtc/signaling/src/sdp/rsdparsa_capi/src/types.rs
--- a/media/webrtc/signaling/gtest/sdp_unittests.cpp
+++ b/media/webrtc/signaling/gtest/sdp_unittests.cpp
@@ -2818,17 +2818,16 @@ TEST_P(NewSdpTest, CheckDirections) {
             mSdp->GetMediaSection(0).GetAttributeList().GetDirection());
   ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
             mSdp->GetMediaSection(1).GetAttributeList().GetDirection());
   ASSERT_EQ(SdpDirectionAttribute::kSendrecv,
             mSdp->GetMediaSection(2).GetAttributeList().GetDirection());
 }
 
 TEST_P(NewSdpTest, CheckCandidates) {
-  SKIP_TEST_WITH_RUST_PARSER; // See Bug 1432920
   ParseSdp(kBasicAudioVideoOffer);
   ASSERT_TRUE(!!mSdp) << "Parse failed: " << GetParseErrors();
   ASSERT_EQ(3U, mSdp->GetMediaSectionCount()) << "Wrong number of media sections";
 
   ASSERT_TRUE(mSdp->GetMediaSection(0).GetAttributeList().HasAttribute(
       SdpAttribute::kCandidateAttribute));
   auto audio_candidates =
       mSdp->GetMediaSection(0).GetAttributeList().GetCandidate();
--- a/media/webrtc/signaling/src/sdp/RsdparsaSdpAttributeList.cpp
+++ b/media/webrtc/signaling/src/sdp/RsdparsaSdpAttributeList.cpp
@@ -481,21 +481,24 @@ RsdparsaSdpAttributeList::LoadAttribute(
         LoadExtmap(attributeList);
         return;
       case SdpAttribute::kSimulcastAttribute:
         LoadSimulcast(attributeList);
         return;
       case SdpAttribute::kMaxptimeAttribute:
         LoadMaxPtime(attributeList);
         return;
+      case SdpAttribute::kCandidateAttribute:
+        LoadCandidate(attributeList);
+        return;
+
 
       case SdpAttribute::kLabelAttribute:
       case SdpAttribute::kSsrcGroupAttribute:
       case SdpAttribute::kRtcpRsizeAttribute:
-      case SdpAttribute::kCandidateAttribute:
       case SdpAttribute::kConnectionAttribute:
       case SdpAttribute::kIceMismatchAttribute:
         // TODO: Not implemented, or not applicable.
         // Sort this out in Bug 1437165.
         return;
     }
   }
 }
@@ -1284,16 +1287,38 @@ RsdparsaSdpAttributeList::LoadMaxPtime(R
   uint64_t maxPtime = 0;
   nsresult nr = sdp_get_maxptime(attributeList, &maxPtime);
   if (NS_SUCCEEDED(nr)) {
     SetAttribute(new SdpNumberAttribute(SdpAttribute::kMaxptimeAttribute,
                                         maxPtime));
   }
 }
 
+void
+RsdparsaSdpAttributeList::LoadCandidate(RustAttributeList* attributeList)
+{
+  size_t candidatesCount = sdp_get_candidate_count(attributeList);
+  if (!candidatesCount) {
+    return;
+  }
+
+  StringVec* rustCandidatesStrings;
+  sdp_get_candidates(attributeList, candidatesCount, &rustCandidatesStrings);
+
+  std::vector<std::string> candidatesStrings =
+                                  convertStringVec(rustCandidatesStrings);
+  free_boxed_string_vec(rustCandidatesStrings);
+
+  auto candidates = MakeUnique<SdpMultiStringAttribute>(
+                                  SdpAttribute::kCandidateAttribute);
+  candidates->mValues = candidatesStrings;
+
+  SetAttribute(candidates.release());
+}
+
 bool
 RsdparsaSdpAttributeList::IsAllowedHere(SdpAttribute::AttributeType type)
 {
   if (AtSessionLevel() && !SdpAttribute::IsAllowedAtSessionLevel(type)) {
     return false;
   }
 
   if (!AtSessionLevel() && !SdpAttribute::IsAllowedAtMediaLevel(type)) {
--- a/media/webrtc/signaling/src/sdp/RsdparsaSdpAttributeList.h
+++ b/media/webrtc/signaling/src/sdp/RsdparsaSdpAttributeList.h
@@ -141,16 +141,17 @@ private:
   void LoadSimulcast(RustAttributeList* attributeList);
   void LoadImageattr(RustAttributeList* attributeList);
   void LoadSctpmaps(RustAttributeList* attributeList);
   void LoadDirection(RustAttributeList* attributeList);
   void LoadRemoteCandidates(RustAttributeList* attributeList);
   void LoadRids(RustAttributeList* attributeList);
   void LoadExtmap(RustAttributeList* attributeList);
   void LoadMaxPtime(RustAttributeList* attributeList);
+  void LoadCandidate(RustAttributeList* attributeList);
 
   void WarnAboutMisplacedAttribute(SdpAttribute::AttributeType type,
                                    uint32_t lineNumber,
                                    SdpErrorHolder& errorHolder);
 
 
   SdpAttribute* mAttributes[kNumAttributeTypes];
 
--- a/media/webrtc/signaling/src/sdp/RsdparsaSdpInc.h
+++ b/media/webrtc/signaling/src/sdp/RsdparsaSdpInc.h
@@ -296,16 +296,17 @@ struct RustSdpAttributeExtmap {
   StringView extensionAttributes;
 };
 
 extern "C" {
 
 size_t string_vec_len(const StringVec* vec);
 nsresult string_vec_get_view(const StringVec* vec, size_t index,
                              StringView* str);
+nsresult free_boxed_string_vec(StringVec* vec);
 
 size_t f32_vec_len(const F32Vec* vec);
 nsresult f32_vec_get(const F32Vec* vec, size_t index, float* ret);
 
 size_t u32_vec_len(const U32Vec* vec);
 nsresult u32_vec_get(const U32Vec* vec, size_t index, uint32_t* ret);
 
 size_t u16_vec_len(const U16Vec* vec);
@@ -454,16 +455,21 @@ void sdp_simulcast_get_ids(const RustSdp
 RustDirection sdp_get_direction(const RustAttributeList* aList);
 
 
 size_t sdp_get_remote_candidate_count(const RustAttributeList* aList);
 void sdp_get_remote_candidates(const RustAttributeList* aList,
                                size_t listSize,
                                RustSdpAttributeRemoteCandidate* ret);
 
+size_t sdp_get_candidate_count(const RustAttributeList* aList);
+void sdp_get_candidates(const RustAttributeList* aLisst,
+                        size_t listSize,
+                        StringVec** ret);
+
 size_t sdp_get_rid_count(const RustAttributeList* aList);
 void sdp_get_rids(const RustAttributeList* aList, size_t listSize,
                   RustSdpAttributeRid* ret);
 
 
 size_t sdp_get_extmap_count(const RustAttributeList* aList);
 void sdp_get_extmaps(const RustAttributeList* aList, size_t listSize,
                      RustSdpAttributeExtmap* ret);
--- a/media/webrtc/signaling/src/sdp/rsdparsa/src/attribute_type.rs
+++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/attribute_type.rs
@@ -17,40 +17,70 @@ pub enum SdpSingleDirection{
 
 #[derive(Debug, PartialEq, Clone)]
 #[cfg_attr(feature="serialize", derive(Serialize))]
 pub enum SdpAttributePayloadType {
     PayloadType(u8),
     Wildcard, // Wildcard means "*",
 }
 
-#[derive(Clone)]
+#[derive(Debug, Clone, PartialEq)]
 #[cfg_attr(feature="serialize", derive(Serialize))]
 pub enum SdpAttributeCandidateTransport {
     Udp,
     Tcp,
 }
 
-#[derive(Clone)]
+impl ToString for SdpAttributeCandidateTransport {
+    fn to_string(&self) -> String {
+        match *self {
+            SdpAttributeCandidateTransport::Udp => "UDP".to_string(),
+            SdpAttributeCandidateTransport::Tcp => "TCP".to_string(),
+        }
+    }
+}
+
+#[derive(Debug, Clone, PartialEq)]
 #[cfg_attr(feature="serialize", derive(Serialize))]
 pub enum SdpAttributeCandidateType {
     Host,
     Srflx,
     Prflx,
     Relay,
 }
 
-#[derive(Clone)]
+impl ToString for SdpAttributeCandidateType {
+    fn to_string(&self) -> String {
+        match *self {
+            SdpAttributeCandidateType::Host => "host".to_string(),
+            SdpAttributeCandidateType::Srflx => "srflx".to_string(),
+            SdpAttributeCandidateType::Prflx => "prflx".to_string(),
+            SdpAttributeCandidateType::Relay => "relay".to_string(),
+        }
+    }
+}
+
+#[derive(Debug, Clone, PartialEq)]
 #[cfg_attr(feature="serialize", derive(Serialize))]
 pub enum SdpAttributeCandidateTcpType {
     Active,
     Passive,
     Simultaneous,
 }
 
+impl ToString for SdpAttributeCandidateTcpType {
+    fn to_string(&self) -> String {
+        match *self {
+            SdpAttributeCandidateTcpType::Active => "active".to_string(),
+            SdpAttributeCandidateTcpType::Passive => "passive".to_string(),
+            SdpAttributeCandidateTcpType::Simultaneous => "so".to_string(),
+        }
+    }
+}
+
 #[derive(Clone)]
 #[cfg_attr(feature="serialize", derive(Serialize))]
 pub struct SdpAttributeCandidate {
     pub foundation: String,
     pub component: u32,
     pub transport: SdpAttributeCandidateTransport,
     pub priority: u64,
     pub address: IpAddr,
@@ -110,16 +140,46 @@ impl SdpAttributeCandidate {
         self.ufrag = Some(u)
     }
 
     fn set_network_cost(&mut self, n: u32) {
         self.networkcost = Some(n)
     }
 }
 
+impl ToString for SdpAttributeCandidate {
+    fn to_string(&self) -> String {
+        macro_rules! option_to_string {
+            ($fmt_str:expr, $opt:expr) => {
+                match $opt {
+                    Some(ref x) => format!($fmt_str, x.to_string()),
+                    None => "".to_string()
+                }
+            };
+        }
+
+        format!("candidate:{foundation} {component_id} {transport} {priority} \
+                           {connection_address} {port} typ {cand_type}\
+                           {rel_addr}{rel_port}{tcp_type}{generation}{ufrag}{network_cost}",
+                foundation = self.foundation,
+                component_id = self.component.to_string(),
+                transport = self.transport.to_string(),
+                priority = self.priority.to_string(),
+                connection_address = self.address.to_string(),
+                port = self.port.to_string(),
+                cand_type = self.c_type.to_string(),
+                rel_addr = option_to_string!(" raddr {}", self.raddr),
+                rel_port = option_to_string!(" rport {}", self.rport),
+                tcp_type = option_to_string!(" tcptype {}", self.tcp_type),
+                generation = option_to_string!(" generation {}", self.generation),
+                ufrag = option_to_string!(" ufrag {}", self.ufrag),
+                network_cost = option_to_string!(" network-cost {}", self.networkcost))
+    }
+}
+
 #[derive(Clone)]
 #[cfg_attr(feature="serialize", derive(Serialize))]
 pub enum SdpAttributeDtlsMessage {
     Client(String),
     Server(String),
 }
 
 #[derive(Clone)]
@@ -2041,55 +2101,73 @@ fn parse_ssrc(to_parse: &str) -> Result<
     };
     Ok(SdpAttribute::Ssrc(ssrc))
 }
 
 pub fn parse_attribute(value: &str) -> Result<SdpType, SdpParserInternalError> {
     Ok(SdpType::Attribute(value.trim().parse()?))
 }
 
+
+#[cfg(test)]
+macro_rules! make_check_parse {
+    ($attr_type:ty, $attr_kind:path) => {
+        |attr_str: &str| -> $attr_type {
+            if let Ok(SdpType::Attribute($attr_kind(attr))) = parse_attribute(attr_str) {
+                attr
+            } else {
+                unreachable!();
+            }
+        }
+    }
+}
+
 #[test]
 fn test_parse_attribute_candidate() {
-    assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ host").is_ok());
-    assert!(parse_attribute("candidate:foo 1 UDP 2122252543 172.16.156.106 49760 typ host")
-                .is_ok());
-    assert!(parse_attribute("candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host").is_ok());
-    assert!(parse_attribute("candidate:0 1 TCP 2122252543 ::1 49760 typ host").is_ok());
-    assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ srflx").is_ok());
-    assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ prflx").is_ok());
-    assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ relay").is_ok());
-    assert!(
-        parse_attribute(
-            "candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host tcptype active"
-        ).is_ok()
-    );
-    assert!(
-        parse_attribute(
-            "candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host tcptype passive"
-        ).is_ok()
-    );
-    assert!(
-        parse_attribute("candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host tcptype so")
-            .is_ok()
-    );
-    assert!(
-        parse_attribute("candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host ufrag foobar")
-            .is_ok()
-    );
-    assert!(
-        parse_attribute(
-            "candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host network-cost 50"
-        ).is_ok()
-    );
-    assert!(parse_attribute("candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 generation 0").is_ok());
-    assert!(parse_attribute("candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665").is_ok());
-    assert!(parse_attribute("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive").is_ok());
-    assert!(parse_attribute("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive generation 1").is_ok());
-    assert!(parse_attribute("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive generation 1 ufrag +DGd").is_ok());
-    assert!(parse_attribute("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive generation 1 ufrag +DGd network-cost 1").is_ok());
+    let check_parse = make_check_parse!(SdpAttributeCandidate, SdpAttribute::Candidate);
+
+    let check_parse_and_serialize = |attr_str| {
+        let parsed = check_parse(attr_str);
+        assert_eq!(parsed.to_string(), attr_str.to_string());
+    };
+
+    check_parse_and_serialize("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ host");
+    check_parse_and_serialize("candidate:foo 1 UDP 2122252543 172.16.156.106 49760 typ host");
+    check_parse_and_serialize("candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host");
+    check_parse_and_serialize("candidate:0 1 TCP 2122252543 ::1 49760 typ host");
+    check_parse_and_serialize("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ srflx");
+    check_parse_and_serialize("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ prflx");
+    check_parse_and_serialize("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ relay");
+    check_parse_and_serialize("candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host tcptype active");
+    check_parse_and_serialize("candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host tcptype passive");
+    check_parse_and_serialize("candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host tcptype so");
+    check_parse_and_serialize("candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host ufrag foobar");
+    check_parse_and_serialize("candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host network-cost 50");
+    check_parse_and_serialize("candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 generation 0");
+    check_parse_and_serialize("candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665");
+    check_parse_and_serialize("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive");
+    check_parse_and_serialize("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive generation 1");
+    check_parse_and_serialize("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive generation 1 ufrag +DGd");
+    check_parse_and_serialize("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive generation 1 ufrag +DGd network-cost 1");
+
+    let candidate = check_parse("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive generation 1 ufrag +DGd network-cost 1");
+    assert_eq!(candidate.foundation, "1".to_string());
+    assert_eq!(candidate.component, 1);
+    assert_eq!(candidate.transport, SdpAttributeCandidateTransport::Tcp);
+    assert_eq!(candidate.priority, 1685987071);
+    assert_eq!(candidate.address, IpAddr::from_str("24.23.204.141").unwrap());
+    assert_eq!(candidate.port, 54609);
+    assert_eq!(candidate.c_type, SdpAttributeCandidateType::Srflx);
+    assert_eq!(candidate.raddr, Some(IpAddr::from_str("192.168.1.4").unwrap()));
+    assert_eq!(candidate.rport, Some(61665));
+    assert_eq!(candidate.tcp_type, Some(SdpAttributeCandidateTcpType::Passive));
+    assert_eq!(candidate.generation, Some(1));
+    assert_eq!(candidate.ufrag, Some("+DGd".to_string()));
+    assert_eq!(candidate.networkcost, Some(1));
+
 
     assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ").is_err());
     assert!(parse_attribute("candidate:0 foo UDP 2122252543 172.16.156.106 49760 typ host")
                 .is_err());
     assert!(parse_attribute("candidate:0 1 FOO 2122252543 172.16.156.106 49760 typ host").is_err());
     assert!(parse_attribute("candidate:0 1 UDP foo 172.16.156.106 49760 typ host").is_err());
     assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156 49760 typ host").is_err());
     assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 70000 typ host").is_err());
--- a/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/attribute.rs
+++ b/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/attribute.rs
@@ -1146,16 +1146,36 @@ pub unsafe extern "C" fn sdp_get_remote_
         None
     });
     let candidates = slice::from_raw_parts_mut(ret_candidates, ret_size);
     for (source, destination) in attrs.zip(candidates) {
         *destination = source
     }
 }
 
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_candidate_count(attributes: *const Vec<SdpAttribute>) -> size_t {
+    count_attribute((*attributes).as_slice(), RustSdpAttributeType::Candidate)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_candidates(attributes: *const Vec<SdpAttribute>, _ret_size: size_t,
+                                            ret: *mut *const Vec<String>) {
+    let attr_strings: Vec<String> = (*attributes).iter().filter_map( |x| {
+        if let SdpAttribute::Candidate(ref attr) = *x {
+            // The serialized attribute starts with "candidate:...", this needs to be removed
+           Some(attr.to_string()[10..].to_string())
+        } else {
+           None
+        }
+    }).collect();
+
+    *ret = Box::into_raw(Box::from(attr_strings));
+}
+
 #[repr(C)]
 #[derive(Clone, Copy)]
 pub struct RustSdpAttributeRidParameters {
     pub max_width: uint32_t,
     pub max_height: uint32_t,
     pub max_fps: uint32_t,
     pub max_fs: uint32_t,
     pub max_br: uint32_t,
--- a/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/types.rs
+++ b/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/types.rs
@@ -72,16 +72,22 @@ pub unsafe extern "C" fn string_vec_get_
             *ret = StringView::from(string.as_str());
             NS_OK
         },
         None => NS_ERROR_INVALID_ARG
     }
 }
 
 #[no_mangle]
+pub unsafe extern "C" fn free_boxed_string_vec(ptr: *mut Vec<String>) -> nsresult {
+    drop(Box::from_raw(ptr));
+    NS_OK
+}
+
+#[no_mangle]
 pub unsafe extern "C" fn f32_vec_len(vec: *const Vec<f32>) -> size_t {
     (*vec).len()
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn f32_vec_get(vec: *const Vec<f32>,
                                      index: size_t,
                                      ret: *mut f32) -> nsresult {