From 5ba5af588b319a075b1ef286ab126c6db8557310 Mon Sep 17 00:00:00 2001 From: Nico Grunbaum Date: Thu, 30 May 2019 04:44:02 +0000 Subject: [PATCH] Bug 1553011 - update import of Rust SDP parser - r=drno update import of Rust SDP parser Differential Revision: https://phabricator.services.mozilla.com/D31943 --HG-- rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/10.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/10.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/11.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/11.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/12.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/12.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/13.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/13.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/14.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/14.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/15.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/15.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/16.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/16.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/17.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/17.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/18.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/18.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/19.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/19.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/2.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/2.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/20.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/20.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/21.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/21.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/22.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/22.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/23.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/23.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/24.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/24.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/25.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/25.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/26.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/26.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/27.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/27.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/28.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/28.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/29.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/29.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/3.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/3.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/30.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/30.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/31.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/31.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/32.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/32.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/33.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/33.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/34.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/34.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/34.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/35.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/34.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/36.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/37.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/37.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/38.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/38.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/39.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/39.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/4.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/4.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/39.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/40.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/41.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/41.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/5.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/5.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/6.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/6.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/7.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/7.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/8.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/8.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/9.sdp => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/9.sdp rename : media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/extract.sh => media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/extract.sh extra : moz-landing-system : lando --- .../webrtc/signaling/src/sdp/RsdparsaSdpInc.h | 3 + .../src/sdp/RsdparsaSdpMediaSection.cpp | 7 +- .../signaling/src/sdp/rsdparsa/.travis.yml | 49 +- .../signaling/src/sdp/rsdparsa/Cargo.toml | 17 +- .../signaling/src/sdp/rsdparsa/README.md | 56 +- .../{src/bin => examples}/file_parser.rs | 26 +- .../{src/bin => examples}/sdps/10.sdp | 0 .../{src/bin => examples}/sdps/11.sdp | 0 .../{src/bin => examples}/sdps/12.sdp | 0 .../{src/bin => examples}/sdps/13.sdp | 0 .../{src/bin => examples}/sdps/14.sdp | 0 .../{src/bin => examples}/sdps/15.sdp | 0 .../{src/bin => examples}/sdps/16.sdp | 0 .../{src/bin => examples}/sdps/17.sdp | 0 .../{src/bin => examples}/sdps/18.sdp | 0 .../{src/bin => examples}/sdps/19.sdp | 0 .../rsdparsa/{src/bin => examples}/sdps/2.sdp | 0 .../{src/bin => examples}/sdps/20.sdp | 0 .../{src/bin => examples}/sdps/21.sdp | 0 .../{src/bin => examples}/sdps/22.sdp | 0 .../{src/bin => examples}/sdps/23.sdp | 0 .../{src/bin => examples}/sdps/24.sdp | 0 .../{src/bin => examples}/sdps/25.sdp | 0 .../{src/bin => examples}/sdps/26.sdp | 0 .../{src/bin => examples}/sdps/27.sdp | 0 .../{src/bin => examples}/sdps/28.sdp | 0 .../{src/bin => examples}/sdps/29.sdp | 0 .../rsdparsa/{src/bin => examples}/sdps/3.sdp | 0 .../{src/bin => examples}/sdps/30.sdp | 0 .../{src/bin => examples}/sdps/31.sdp | 0 .../{src/bin => examples}/sdps/32.sdp | 0 .../{src/bin => examples}/sdps/33.sdp | 0 .../{src/bin => examples}/sdps/34.sdp | 0 .../{src/bin => examples}/sdps/35.sdp | 0 .../{src/bin => examples}/sdps/36.sdp | 0 .../{src/bin => examples}/sdps/37.sdp | 0 .../{src/bin => examples}/sdps/38.sdp | 0 .../{src/bin => examples}/sdps/39.sdp | 0 .../rsdparsa/{src/bin => examples}/sdps/4.sdp | 0 .../{src/bin => examples}/sdps/40.sdp | 0 .../{src/bin => examples}/sdps/41.sdp | 0 .../rsdparsa/{src/bin => examples}/sdps/5.sdp | 0 .../rsdparsa/{src/bin => examples}/sdps/6.sdp | 0 .../rsdparsa/{src/bin => examples}/sdps/7.sdp | 0 .../rsdparsa/{src/bin => examples}/sdps/8.sdp | 0 .../rsdparsa/{src/bin => examples}/sdps/9.sdp | 0 .../{src/bin => examples}/sdps/extract.sh | 0 .../src/sdp/rsdparsa/src/attribute_type.rs | 3009 +++++++++++------ .../signaling/src/sdp/rsdparsa/src/error.rs | 167 +- .../signaling/src/sdp/rsdparsa/src/lib.rs | 846 +++-- .../src/sdp/rsdparsa/src/media_type.rs | 475 +-- .../signaling/src/sdp/rsdparsa/src/network.rs | 28 +- .../src/sdp/rsdparsa/src/unsupported_types.rs | 74 - .../src/sdp/rsdparsa/tests/unit_tests.rs | 538 +-- .../src/sdp/rsdparsa_capi/Cargo.toml | 2 +- .../src/sdp/rsdparsa_capi/src/attribute.rs | 2 +- .../src/sdp/rsdparsa_capi/src/lib.rs | 4 +- .../sdp/rsdparsa_capi/src/media_section.rs | 6 + 58 files changed, 3308 insertions(+), 2001 deletions(-) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/file_parser.rs (55%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/10.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/11.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/12.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/13.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/14.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/15.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/16.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/17.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/18.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/19.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/2.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/20.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/21.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/22.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/23.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/24.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/25.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/26.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/27.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/28.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/29.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/3.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/30.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/31.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/32.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/33.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/34.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/35.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/36.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/37.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/38.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/39.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/4.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/40.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/41.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/5.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/6.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/7.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/8.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/9.sdp (100%) rename media/webrtc/signaling/src/sdp/rsdparsa/{src/bin => examples}/sdps/extract.sh (100%) delete mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/unsupported_types.rs diff --git a/media/webrtc/signaling/src/sdp/RsdparsaSdpInc.h b/media/webrtc/signaling/src/sdp/RsdparsaSdpInc.h index 648100890f5c..4631b78c0ca8 100644 --- a/media/webrtc/signaling/src/sdp/RsdparsaSdpInc.h +++ b/media/webrtc/signaling/src/sdp/RsdparsaSdpInc.h @@ -60,6 +60,9 @@ enum class RustSdpProtocolValue { kRustDtlsSctp, kRustUdpDtlsSctp, kRustTcpDtlsSctp, + kRustRtpAvp, + kRustRtpAvpf, + kRustRtpSavp, }; enum class RustSdpFormatType { kRustIntegers, kRustStrings }; diff --git a/media/webrtc/signaling/src/sdp/RsdparsaSdpMediaSection.cpp b/media/webrtc/signaling/src/sdp/RsdparsaSdpMediaSection.cpp index f3728ee4e3df..148b89f40096 100644 --- a/media/webrtc/signaling/src/sdp/RsdparsaSdpMediaSection.cpp +++ b/media/webrtc/signaling/src/sdp/RsdparsaSdpMediaSection.cpp @@ -77,8 +77,13 @@ SdpMediaSection::Protocol RsdparsaSdpMediaSection::GetProtocol() const { return kUdpDtlsSctp; case RustSdpProtocolValue::kRustTcpDtlsSctp: return kTcpDtlsSctp; + case RustSdpProtocolValue::kRustRtpAvp: + return kRtpAvp; + case RustSdpProtocolValue::kRustRtpAvpf: + return kRtpAvpf; + case RustSdpProtocolValue::kRustRtpSavp: + return kRtpSavp; } - MOZ_CRASH("invalid media protocol"); } diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/.travis.yml b/media/webrtc/signaling/src/sdp/rsdparsa/.travis.yml index e301aa4b5803..3a2eac0130ff 100644 --- a/media/webrtc/signaling/src/sdp/rsdparsa/.travis.yml +++ b/media/webrtc/signaling/src/sdp/rsdparsa/.travis.yml @@ -3,9 +3,11 @@ cache: cargo sudo: true os: - linux -# Taken out temporarily because it's to slow -# - osx + - osx +env: + - FEATURES="" + - FEATURES="serialize" rust: - nightly - beta @@ -17,13 +19,12 @@ matrix: allow_failures: - rust: nightly -before_install: - - sudo apt-get update - addons: apt: packages: - libcurl4-openssl-dev + - zlib1g-dev + - libiberty-dev - libelf-dev - libdw-dev - cmake @@ -34,34 +35,46 @@ addons: before_script: - export PATH=$PATH:~/.cargo/bin - | - if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then - cargo install --force clippy; + if [[ "$TRAVIS_RUST_VERSION" == "stable" ]]; then + rustup component add rustfmt-preview + rustup component add clippy fi script: - - cargo build --verbose --all + - echo FEATURES="$FEATURES" - | - if [[ "$TRAVIS_RUST_VERSION" == "nightly" && - -f ~/.cargo/bin/cargo-clippy ]]; then - cargo clippy; + if [[ "$TRAVIS_RUST_VERSION" == "stable" ]]; then + cargo fmt --all -- --check fi - - cargo test --verbose --all + - cargo build --verbose --all --features="$FEATURES" + - | + if [[ "$TRAVIS_RUST_VERSION" == "stable" ]]; then + cargo clippy --all-targets --all-features -- -D warnings; + fi + - | + if [[ "$TRAVIS_RUST_VERSION" == "1.17.0" ]]; then + cargo test --all-features --verbose --all + else + cargo test --all-targets --all-features --verbose --all + fi + after_success: - | - if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_RUST_VERSION" == "stable" ]]; then - wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz && - tar xzf master.tar.gz && - cd kcov-master && + if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_RUST_VERSION" == "stable" && "$FEATURES" == "serialize" ]]; then + wget https://github.com/SimonKagstrom/kcov/archive/v34.tar.gz && + tar xzf v34.tar.gz && + cd kcov-34 && mkdir build && cd build && cmake .. && make && sudo make install && cd ../.. && - rm -rf kcov-master && + rm -rf kcov-34 && kcov --version && - for file in target/debug/rsdparsa-*[^\.d]; do echo "$file"; mkdir -p "target/cov/$(basename $file)"; kcov --verify --exclude-pattern=/.cargo,/usr/lib "target/cov/$(basename $file)" "$file"; done && + (cd target/debug/ && ls -al) && + for file in target/debug/webrtc_sdp-*[^\.d]; do echo "$file"; mkdir -p "target/cov/$(basename $file)"; kcov --verify --exclude-pattern=/.cargo,/usr/lib "target/cov/$(basename $file)" "$file"; done && for file in target/debug/unit_tests-*[^\.d]; do echo "$file"; mkdir -p "target/cov/$(basename $file)"; kcov --verify --exclude-pattern=/.cargo,/usr/lib "target/cov/$(basename $file)" "$file"; done && bash <(curl -s https://codecov.io/bash) && echo "Uploaded code coverage" diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/Cargo.toml b/media/webrtc/signaling/src/sdp/rsdparsa/Cargo.toml index 5e02d9d357e6..fa775647375f 100644 --- a/media/webrtc/signaling/src/sdp/rsdparsa/Cargo.toml +++ b/media/webrtc/signaling/src/sdp/rsdparsa/Cargo.toml @@ -1,7 +1,17 @@ [package] -name = "rsdparsa" +name = "webrtc-sdp" version = "0.1.0" authors = ["Nils Ohlmeier "] +description = "This create parses strings in the format of the Session Description Protocol according to RFC4566. It specifically supports the subset of features required to support WebRTC according to the JSEP draft." +homepage = "https://github.com/nils-ohlmeier/rsdparsa" +readme = "README.md" +keywords = ["webrtc", "sdp", "jsep"] +categories = ["parsing", "network-programming"] +license = "MPL-2.0" + +[badges] +travis-ci = { repository = "nils-ohlmeier/rsdparsa", branch = "master" } +codecov = { repository = "nils-ohlmeier/rsdparsa", branch = "master", service = "github" } [features] default = [] @@ -9,10 +19,9 @@ default = [] serialize = ["serde", "serde_derive"] [dependencies] -# clippy = {version = "*", optional = true} -log = "0.4" +log = {version = "0.4.6"} serde = {version = "1.0" , optional = true} serde_derive = {version = "1.0" , optional = true} [dev-dependencies] -# serde_json = {version = "1.0"} +serde_json = {version = "1.0"} diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/README.md b/media/webrtc/signaling/src/sdp/rsdparsa/README.md index 824da17b6345..aa86ed1a5d99 100644 --- a/media/webrtc/signaling/src/sdp/rsdparsa/README.md +++ b/media/webrtc/signaling/src/sdp/rsdparsa/README.md @@ -1,13 +1,61 @@ -# rsdparsa +# webrtc-sdp +[![Crates.io](https://img.shields.io/crates/v/webrtc-sdp.svg)](https://crates.io/crates/webrtc-sdp) [![Build Status](https://travis-ci.org/nils-ohlmeier/rsdparsa.svg?branch=master)](https://travis-ci.org/nils-ohlmeier/rsdparsa) [![Codecov coverage status](https://codecov.io/gh/nils-ohlmeier/rsdparsa/branch/master/graph/badge.svg)](https://codecov.io/gh/nils-ohlmeier/rsdparsa) [![License: MPL 2.0](https://img.shields.io/badge/License-MPL%202.0-brightgreen.svg)](#License) +[![dependency status](https://deps.rs/repo/github/nils-ohlmeier/rsdparsa/status.svg)](https://deps.rs/repo/github/nils-ohlmeier/rsdparsa) -A SDP parser written in Rust specifically aimed for WebRTC +A SDP parser written in Rust specifically aimed to handle WebRTC SDP offers and answers. -Requires minimum Rust 1.17 +## Dependecies + +* Rust >= 1.17.0 +* log module +* serde module +* serde-derive module + +Cargo installs the missing modules automatically when building webrtc-sdp for the first time. + +## The webrtc-sdp API + +The main function is: +``` +fn parse_sdp(sdp: &str, fail_on_warning: bool) -> Result +``` +The `sdp` parameter is the string which will get parsed. The `fail_on_warning` parameter determines how to treat warnings encountered during parsing. Any problems encountered during are stored until the whole string has been parsed. Any problem during parsing falls into two catgeories: + +* Fatal error preventing further parsing or processing of the SDP +* Warning which don't block further processing of the SDP + +Warnings will be for example unknown parameters in attributes. Setting `fail_on_warning` to `true` makes most sense during development, when you want to be aware of all potential problems. In production `fail_on_warning` is expected to be `false`. + +`parse_sdp()` returns either an `SdpSession` struct ([code](https://github.com/nils-ohlmeier/rsdparsa/blob/master/src/lib.rs#L137)) which contains all the parsed information. Or in case a fatal error was encountered (or if `fail_on_warning` was set to `true` and any warnings were encountered) an `SdpParserError` ([code](https://github.com/nils-ohlmeier/rsdparsa/blob/master/src/error.rs#L117)) will be returned as a `Result`. + +## Examples + +The [file parser](https://github.com/nils-ohlmeier/rsdparsa/blob/master/src/bin/file_parser.rs) in the webrtc-sdp package gives you an easy example of how to invoke the webrtc-sdp parser. + +## Contributing + +As the Travis CI runs are checking for code formating and clippy warnings please run the following commands locally, before submitting a Pull Request. + +If you haven't clippy and Rust format installed already you add them like this: +``` +rustup component add rustfmt-preview +rustup component add clippy +``` + +Check with clippy for warnings in the code: +``` +cargo clippy --all-targets --all-features +``` + +And format all of the code according to Rust code style convention: +``` +cargo fmt --all +``` ## License -Licensed under [MPL](https://www.mozilla.org/MPL/2.0/). +Licensed under [MPL-2.0](https://www.mozilla.org/MPL/2.0/) diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/file_parser.rs b/media/webrtc/signaling/src/sdp/rsdparsa/examples/file_parser.rs similarity index 55% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/file_parser.rs rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/file_parser.rs index 04ba61c81c9d..6058066e5dc9 100644 --- a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/file_parser.rs +++ b/media/webrtc/signaling/src/sdp/rsdparsa/examples/file_parser.rs @@ -1,35 +1,31 @@ -use std::error::Error; -use std::io::prelude::*; -use std::fs::File; -use std::path::Path; use std::env; -extern crate rsdparsa; +use std::error::Error; +use std::fs::File; +use std::io::prelude::*; +use std::path::Path; +extern crate webrtc_sdp; fn main() { let filename = match env::args().nth(1) { None => { println!("Missing file name argument!"); return; - }, + } Some(x) => x, }; let path = Path::new(filename.as_str()); let display = path.display(); let mut file = match File::open(&path) { - Err(why) => panic!("Failed to open {}: {}", - display, - why.description()), - Ok(file) => file + Err(why) => panic!("Failed to open {}: {}", display, why.description()), + Ok(file) => file, }; let mut s = String::new(); match file.read_to_string(&mut s) { - Err(why) => panic!("couldn't read {}: {}", - display, - why.description()), - Ok(s) => s + Err(why) => panic!("couldn't read {}: {}", display, why.description()), + Ok(s) => s, }; - rsdparsa::parse_sdp(&s, true).is_ok(); + webrtc_sdp::parse_sdp(&s, true).is_ok(); } diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/10.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/10.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/10.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/10.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/11.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/11.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/11.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/11.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/12.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/12.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/12.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/12.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/13.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/13.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/13.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/13.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/14.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/14.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/14.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/14.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/15.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/15.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/15.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/15.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/16.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/16.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/16.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/16.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/17.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/17.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/17.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/17.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/18.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/18.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/18.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/18.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/19.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/19.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/19.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/19.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/2.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/2.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/2.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/2.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/20.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/20.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/20.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/20.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/21.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/21.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/21.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/21.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/22.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/22.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/22.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/22.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/23.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/23.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/23.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/23.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/24.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/24.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/24.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/24.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/25.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/25.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/25.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/25.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/26.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/26.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/26.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/26.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/27.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/27.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/27.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/27.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/28.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/28.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/28.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/28.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/29.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/29.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/29.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/29.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/3.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/3.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/3.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/3.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/30.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/30.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/30.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/30.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/31.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/31.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/31.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/31.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/32.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/32.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/32.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/32.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/33.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/33.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/33.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/33.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/34.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/34.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/34.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/34.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/35.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/35.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/35.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/35.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/36.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/36.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/36.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/36.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/37.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/37.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/37.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/37.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/38.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/38.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/38.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/38.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/39.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/39.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/39.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/39.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/4.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/4.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/4.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/4.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/40.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/40.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/40.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/40.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/41.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/41.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/41.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/41.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/5.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/5.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/5.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/5.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/6.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/6.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/6.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/6.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/7.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/7.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/7.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/7.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/8.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/8.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/8.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/8.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/9.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/9.sdp similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/9.sdp rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/9.sdp diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/extract.sh b/media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/extract.sh similarity index 100% rename from media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/extract.sh rename to media/webrtc/signaling/src/sdp/rsdparsa/examples/sdps/extract.sh diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/attribute_type.rs b/media/webrtc/signaling/src/sdp/rsdparsa/src/attribute_type.rs index ec92c952206a..6e27b9206865 100644 --- a/media/webrtc/signaling/src/sdp/rsdparsa/src/attribute_type.rs +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/attribute_type.rs @@ -1,29 +1,115 @@ +use std::iter; use std::net::IpAddr; use std::str::FromStr; -use std::fmt; -use std::iter; -use SdpType; use error::SdpParserInternalError; -use network::{parse_nettype, parse_addrtype, parse_unicast_addr}; +use network::{parse_addrtype, parse_nettype, parse_unicast_addr}; +use SdpType; + +// Serialization helper marcos and functions +#[macro_export] +macro_rules! option_to_string { + ($fmt_str:expr, $opt:expr) => { + match $opt { + Some(ref x) => format!($fmt_str, x.to_string()), + None => "".to_string(), + } + }; +} + +#[macro_export] +macro_rules! maybe_vector_to_string { + ($fmt_str:expr, $vec:expr, $sep:expr) => { + match $vec.len() { + 0 => "".to_string(), + _ => format!( + $fmt_str, + $vec.iter() + .map(ToString::to_string) + .collect::>() + .join($sep) + ), + } + }; +} + +#[macro_export] +macro_rules! non_empty_string_vec { + ( $( $x:expr ),* ) => { + { + let mut temp_vec = Vec::new(); + $( + if !$x.is_empty() { + temp_vec.push($x); + } + )* + temp_vec + } + }; +} + +pub fn maybe_print_param(name: &str, param: T, default_value: T) -> String +where + T: PartialEq + ToString, +{ + if param != default_value { + name.to_owned() + ¶m.to_string() + } else { + "".to_string() + } +} + +pub fn maybe_print_bool_param(name: &str, param: bool, default_value: bool) -> String { + if param != default_value { + name.to_owned() + "=" + &(if param { "1" } else { "0" }).to_string() + } else { + "".to_string() + } +} + +pub fn addr_to_string(addr: IpAddr) -> String { + match addr { + IpAddr::V4(ipv4) => format!("IN IP4 {}", ipv4.to_string()), + IpAddr::V6(ipv6) => format!("IN IP4 {}", ipv6.to_string()), + } +} #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature="serialize", derive(Serialize))] -pub enum SdpSingleDirection{ +#[cfg_attr(feature = "serialize", derive(Serialize))] +pub enum SdpSingleDirection { // This is explicitly 1 and 2 to match the defines in the C++ glue code. Send = 1, Recv = 2, } +impl ToString for SdpSingleDirection { + fn to_string(&self) -> String { + match *self { + SdpSingleDirection::Send => "send", + SdpSingleDirection::Recv => "recv", + } + .to_string() + } +} + #[derive(Debug, PartialEq, Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpAttributePayloadType { PayloadType(u8), Wildcard, // Wildcard means "*", } +impl ToString for SdpAttributePayloadType { + fn to_string(&self) -> String { + match *self { + SdpAttributePayloadType::PayloadType(pt) => pt.to_string(), + SdpAttributePayloadType::Wildcard => "*".to_string(), + } + } +} + #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpAttributeCandidateTransport { Udp, Tcp, @@ -39,7 +125,7 @@ impl ToString for SdpAttributeCandidateTransport { } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpAttributeCandidateType { Host, Srflx, @@ -59,7 +145,7 @@ impl ToString for SdpAttributeCandidateType { } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpAttributeCandidateTcpType { Active, Passive, @@ -77,7 +163,7 @@ impl ToString for SdpAttributeCandidateTcpType { } #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeCandidate { pub foundation: String, pub component: u32, @@ -92,17 +178,19 @@ pub struct SdpAttributeCandidate { pub generation: Option, pub ufrag: Option, pub networkcost: Option, + pub unknown_extensions: Vec<(String, String)>, } impl SdpAttributeCandidate { - pub fn new(foundation: String, - component: u32, - transport: SdpAttributeCandidateTransport, - priority: u64, - address: IpAddr, - port: u32, - c_type: SdpAttributeCandidateType) - -> SdpAttributeCandidate { + pub fn new( + foundation: String, + component: u32, + transport: SdpAttributeCandidateTransport, + priority: u64, + address: IpAddr, + port: u32, + c_type: SdpAttributeCandidateType, + ) -> SdpAttributeCandidate { SdpAttributeCandidate { foundation, component, @@ -117,6 +205,7 @@ impl SdpAttributeCandidate { generation: None, ufrag: None, networkcost: None, + unknown_extensions: Vec::new(), } } @@ -143,55 +232,78 @@ impl SdpAttributeCandidate { fn set_network_cost(&mut self, n: u32) { self.networkcost = Some(n) } + + fn add_unknown_extension(&mut self, name: String, value: String) { + self.unknown_extensions.push((name, value)); + } } 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)) + format!( + "{foundation} {component_id} {transport} {priority} \ + {connection_address} {port} typ {cand_type}\ + {rel_addr}{rel_port}{tcp_type}{generation}{ufrag}{network_cost}\ + {unknown_extensions}", + 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), + unknown_extensions = self + .unknown_extensions + .iter() + .map(|&(ref name, ref value)| format!(" {} {}", name, value)) + .collect::() + ) } } #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpAttributeDtlsMessage { Client(String), Server(String), } +impl ToString for SdpAttributeDtlsMessage { + fn to_string(&self) -> String { + match *self { + SdpAttributeDtlsMessage::Client(ref msg) => format!("client {}", msg), + SdpAttributeDtlsMessage::Server(ref msg) => format!("server {}", msg), + } + } +} + #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeRemoteCandidate { pub component: u32, pub address: IpAddr, pub port: u32, } +impl ToString for SdpAttributeRemoteCandidate { + fn to_string(&self) -> String { + format!( + "{component} {addr} {port}", + component = self.component.to_string(), + addr = self.address.to_string(), + port = self.port.to_string() + ) + } +} + #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeSimulcastId { pub id: String, pub paused: bool, @@ -213,9 +325,19 @@ impl SdpAttributeSimulcastId { } } +impl ToString for SdpAttributeSimulcastId { + fn to_string(&self) -> String { + if self.paused { + format!("~{}", self.id) + } else { + self.id.clone() + } + } +} + #[repr(C)] #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeSimulcastVersion { pub ids: Vec, } @@ -231,15 +353,36 @@ impl SdpAttributeSimulcastVersion { } } +impl ToString for SdpAttributeSimulcastVersion { + fn to_string(&self) -> String { + self.ids + .iter() + .map(ToString::to_string) + .collect::>() + .join(",") + } +} + #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeSimulcast { pub send: Vec, pub receive: Vec, } +impl ToString for SdpAttributeSimulcast { + fn to_string(&self) -> String { + non_empty_string_vec![ + maybe_vector_to_string!("send {}", self.send, ";"), + maybe_vector_to_string!("recv {}", self.receive, ";") + ] + .join(" ") + .to_string() + } +} + #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeRtcp { pub port: u16, pub unicast_addr: Option, @@ -258,21 +401,49 @@ impl SdpAttributeRtcp { } } -#[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] -pub enum SdpAttributeRtcpFbType { - Ack = 0, - Ccm = 2, // This is explicitly 2 to make the conversion to the - // enum used in the glue-code possible. The glue code has "app" - // in the place of 1 - Nack, - TrrInt, - Remb, - TransCC +impl ToString for SdpAttributeRtcp { + fn to_string(&self) -> String { + let unicast_addr_str_opt = match self.unicast_addr { + None => None, + Some(x) => Some(addr_to_string(x)), + }; + format!( + "{port}{unicast_addr}", + port = self.port.to_string(), + unicast_addr = option_to_string!(" {}", unicast_addr_str_opt) + ) + } } #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] +pub enum SdpAttributeRtcpFbType { + Ack = 0, + Ccm = 2, // This is explicitly 2 to make the conversion to the + // enum used in the glue-code possible. The glue code has "app" + // in the place of 1 + Nack, + TrrInt, + Remb, + TransCC, +} + +impl ToString for SdpAttributeRtcpFbType { + fn to_string(&self) -> String { + match *self { + SdpAttributeRtcpFbType::Ack => "ack", + SdpAttributeRtcpFbType::Ccm => "ccm", + SdpAttributeRtcpFbType::Nack => "nack", + SdpAttributeRtcpFbType::TrrInt => "trr-int", + SdpAttributeRtcpFbType::Remb => "goog-remb", + SdpAttributeRtcpFbType::TransCC => "transport-cc", + } + .to_string() + } +} + +#[derive(Clone)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeRtcpFb { pub payload_type: SdpAttributePayloadType, pub feedback_type: SdpAttributeRtcpFbType, @@ -280,16 +451,46 @@ pub struct SdpAttributeRtcpFb { pub extra: String, } +impl ToString for SdpAttributeRtcpFb { + fn to_string(&self) -> String { + format!( + "{pt} {feeback}{parameter_and_extra}", + pt = self.payload_type.to_string(), + feeback = self.feedback_type.to_string(), + parameter_and_extra = if self.parameter.is_empty() { + "".to_string() + } else { + format!( + " {parameter}{extra}", + parameter = self.parameter, + extra = maybe_print_param(" ", self.extra.clone(), "".to_string()), + ) + } + ) + } +} + #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpAttributeDirection { Recvonly, Sendonly, Sendrecv, } +impl ToString for SdpAttributeDirection { + fn to_string(&self) -> String { + match *self { + SdpAttributeDirection::Recvonly => "recvonly", + SdpAttributeDirection::Sendonly => "sendonly", + SdpAttributeDirection::Sendrecv => "sendrecv", + } + .to_string() + } +} + #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeExtmap { pub id: u16, pub direction: Option, @@ -297,8 +498,20 @@ pub struct SdpAttributeExtmap { pub extension_attributes: Option, } +impl ToString for SdpAttributeExtmap { + fn to_string(&self) -> String { + format!( + "{id}{direction} {url}{ext}", + id = self.id.to_string(), + direction = option_to_string!("/{}", self.direction), + url = self.url, + ext = option_to_string!(" {}", self.extension_attributes) + ) + } +} + #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeFmtpParameters { // H264 // TODO(bug 1466859): Support sprop-parameter-sets @@ -334,16 +547,57 @@ pub struct SdpAttributeFmtpParameters { pub unknown_tokens: Vec, } +impl ToString for SdpAttributeFmtpParameters { + fn to_string(&self) -> String { + format!( + "{parameters}{red}{dtmf_tones}{unknown}", + parameters = non_empty_string_vec![ + maybe_print_param("packetization-mode=", self.packetization_mode, 0), + maybe_print_bool_param( + "level-asymmetry-allowed", + self.level_asymmetry_allowed, + false + ), + maybe_print_param("profile-level-id=", self.profile_level_id, 0x0042_0010), + maybe_print_param("max-fs=", self.max_fs, 0), + maybe_print_param("max-cpb=", self.max_cpb, 0), + maybe_print_param("max-dpb=", self.max_dpb, 0), + maybe_print_param("max-br=", self.max_br, 0), + maybe_print_param("max-mbps=", self.max_mbps, 0), + maybe_print_param("max-fr=", self.max_fr, 0), + maybe_print_param("maxplaybackrate=", self.maxplaybackrate, 48000), + maybe_print_bool_param("usedtx", self.usedtx, false), + maybe_print_bool_param("stereo", self.stereo, false), + maybe_print_bool_param("useinbandfec", self.useinbandfec, false), + maybe_print_bool_param("cbr", self.cbr, false) + ] + .join(";"), + red = maybe_vector_to_string!("{}", self.encodings, "/"), + dtmf_tones = maybe_print_param("", self.dtmf_tones.clone(), "".to_string()), + unknown = maybe_vector_to_string!("{}", self.unknown_tokens, ",") + ) + } +} #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeFmtp { pub payload_type: u8, pub parameters: SdpAttributeFmtpParameters, } +impl ToString for SdpAttributeFmtp { + fn to_string(&self) -> String { + format!( + "{pt} {params}", + pt = self.payload_type.to_string(), + params = self.parameters.to_string() + ) + } +} + #[derive(Clone, Copy)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpAttributeFingerprintHashType { Sha1, Sha224, @@ -352,36 +606,114 @@ pub enum SdpAttributeFingerprintHashType { Sha512, } +impl ToString for SdpAttributeFingerprintHashType { + fn to_string(&self) -> String { + match *self { + SdpAttributeFingerprintHashType::Sha1 => "sha-1", + SdpAttributeFingerprintHashType::Sha224 => "sha-224", + SdpAttributeFingerprintHashType::Sha256 => "sha-256", + SdpAttributeFingerprintHashType::Sha384 => "sha-384", + SdpAttributeFingerprintHashType::Sha512 => "sha-512", + } + .to_string() + } +} + #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeFingerprint { pub hash_algorithm: SdpAttributeFingerprintHashType, - pub fingerprint: Vec + pub fingerprint: Vec, +} + +impl ToString for SdpAttributeFingerprint { + fn to_string(&self) -> String { + format!( + "{hash_algo} {fingerprint}", + hash_algo = self.hash_algorithm.to_string(), + fingerprint = self + .fingerprint + .iter() + .map(|byte| format!("{:02X}", byte)) + .collect::>() + .join(":") + ) + } +} + +fn imageattr_discrete_value_list_to_string(values: Vec) -> String +where + T: ToString, +{ + match values.len() { + 1 => values[0].to_string(), + _ => format!( + "[{}]", + values + .iter() + .map(ToString::to_string) + .collect::>() + .join(",") + ), + } } #[derive(Debug, PartialEq, Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpAttributeImageAttrXYRange { - Range(u32,u32,Option), // min, max, step + Range(u32, u32, Option), // min, max, step DiscreteValues(Vec), } -#[derive(Debug, PartialEq, Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] -pub enum SdpAttributeImageAttrSRange { - Range(f32,f32), // min, max - DiscreteValues(Vec), +impl ToString for SdpAttributeImageAttrXYRange { + fn to_string(&self) -> String { + match *self { + SdpAttributeImageAttrXYRange::Range(ref min, ref max, ref step_opt) => { + match *step_opt { + Some(step) => format!("[{}:{}:{}]", min, step, max), + None => format!("[{}:{}]", min, max), + } + } + SdpAttributeImageAttrXYRange::DiscreteValues(ref values) => { + imageattr_discrete_value_list_to_string(values.to_vec()) + } + } + } } #[derive(Debug, PartialEq, Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] +pub enum SdpAttributeImageAttrSRange { + Range(f32, f32), // min, max + DiscreteValues(Vec), +} + +impl ToString for SdpAttributeImageAttrSRange { + fn to_string(&self) -> String { + match *self { + SdpAttributeImageAttrSRange::Range(ref min, ref max) => format!("[{}-{}]", min, max), + SdpAttributeImageAttrSRange::DiscreteValues(ref values) => { + imageattr_discrete_value_list_to_string(values.to_vec()) + } + } + } +} + +#[derive(Debug, PartialEq, Clone)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeImageAttrPRange { pub min: f32, pub max: f32, } +impl ToString for SdpAttributeImageAttrPRange { + fn to_string(&self) -> String { + format!("[{}-{}]", self.min, self.max) + } +} + #[derive(Debug, PartialEq, Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeImageAttrSet { pub x: SdpAttributeImageAttrXYRange, pub y: SdpAttributeImageAttrXYRange, @@ -390,64 +722,166 @@ pub struct SdpAttributeImageAttrSet { pub q: Option, } +impl ToString for SdpAttributeImageAttrSet { + fn to_string(&self) -> String { + format!( + "[x={x},y={y}{sar}{par}{q}]", + x = self.x.to_string(), + y = self.y.to_string(), + sar = option_to_string!(",sar={}", self.sar), + par = option_to_string!(",par={}", self.par), + q = option_to_string!(",q={}", self.q) + ) + } +} + #[derive(Debug, PartialEq, Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpAttributeImageAttrSetList { Sets(Vec), Wildcard, } +impl ToString for SdpAttributeImageAttrSetList { + fn to_string(&self) -> String { + match *self { + SdpAttributeImageAttrSetList::Sets(ref sets) => sets + .iter() + .map(ToString::to_string) + .collect::>() + .join(" "), + SdpAttributeImageAttrSetList::Wildcard => "*".to_string(), + } + } +} + #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeImageAttr { pub pt: SdpAttributePayloadType, pub send: SdpAttributeImageAttrSetList, pub recv: SdpAttributeImageAttrSetList, } +impl ToString for SdpAttributeImageAttr { + fn to_string(&self) -> String { + let maybe_sets_to_string = |set_list| match set_list { + SdpAttributeImageAttrSetList::Sets(sets) => match sets.len() { + 0 => None, + _ => Some(SdpAttributeImageAttrSetList::Sets(sets)), + }, + x => Some(x), + }; + format!( + "{pt}{send_sets}{recv_sets}", + pt = self.pt.to_string(), + send_sets = option_to_string!(" send {}", maybe_sets_to_string(self.send.clone())), + recv_sets = option_to_string!(" recv {}", maybe_sets_to_string(self.recv.clone())) + ) + } +} + #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeSctpmap { pub port: u16, pub channels: u32, } -#[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] -pub enum SdpAttributeGroupSemantic { - LipSynchronization, - FlowIdentification, - SingleReservationFlow, - AlternateNetworkAddressType, - ForwardErrorCorrection, - DecodingDependency, - Bundle, +impl ToString for SdpAttributeSctpmap { + fn to_string(&self) -> String { + format!( + "{port} webrtc-datachannel {channels}", + port = self.port.to_string(), + channels = self.channels.to_string() + ) + } } #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] +pub enum SdpAttributeGroupSemantic { + LipSynchronization, // rfc5888 + FlowIdentification, // rfc5888 + SingleReservationFlow, // rfc3524 + AlternateNetworkAddressType, // rfc4091 + ForwardErrorCorrection, // rfc4756 + DecodingDependency, // rfc5583 + Bundle, // +} + +impl ToString for SdpAttributeGroupSemantic { + fn to_string(&self) -> String { + match *self { + SdpAttributeGroupSemantic::LipSynchronization => "LS", + SdpAttributeGroupSemantic::FlowIdentification => "FID", + SdpAttributeGroupSemantic::SingleReservationFlow => "SRF", + SdpAttributeGroupSemantic::AlternateNetworkAddressType => "ANAT", + SdpAttributeGroupSemantic::ForwardErrorCorrection => "FEC", + SdpAttributeGroupSemantic::DecodingDependency => "DDP", + SdpAttributeGroupSemantic::Bundle => "BUNDLE", + } + .to_string() + } +} + +#[derive(Clone)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeGroup { pub semantics: SdpAttributeGroupSemantic, pub tags: Vec, } +impl ToString for SdpAttributeGroup { + fn to_string(&self) -> String { + format!( + "{semantics}{tags}", + semantics = self.semantics.to_string(), + tags = maybe_vector_to_string!(" {}", self.tags, " ") + ) + } +} + #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeMsid { pub id: String, pub appdata: Option, } +impl ToString for SdpAttributeMsid { + fn to_string(&self) -> String { + format!( + "{id}{appdata}", + id = self.id, + appdata = option_to_string!(" {}", self.appdata) + ) + } +} + #[derive(Clone, Debug)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeMsidSemantic { pub semantic: String, pub msids: Vec, } +impl ToString for SdpAttributeMsidSemantic { + fn to_string(&self) -> String { + format!( + "{semantic} {msids}", + semantic = self.semantic, + msids = match self.msids.len() { + 0 => "*".to_string(), + _ => self.msids.join(" "), + } + ) + } +} + #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] -pub struct SdpAttributeRidParameters{ +#[cfg_attr(feature = "serialize", derive(Serialize))] +pub struct SdpAttributeRidParameters { pub max_width: u32, pub max_height: u32, pub max_fps: u32, @@ -455,21 +889,57 @@ pub struct SdpAttributeRidParameters{ pub max_br: u32, pub max_pps: u32, - pub unknown: Vec + pub unknown: Vec, +} + +impl ToString for SdpAttributeRidParameters { + fn to_string(&self) -> String { + non_empty_string_vec![ + maybe_print_param("max-width=", self.max_width, 0), + maybe_print_param("max-height=", self.max_height, 0), + maybe_print_param("max-fps=", self.max_fps, 0), + maybe_print_param("max-fs=", self.max_fs, 0), + maybe_print_param("max-br=", self.max_br, 0), + maybe_print_param("max-pps=", self.max_pps, 0), + maybe_vector_to_string!("{}", self.unknown, ";") + ] + .join(";") + } } #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeRid { pub id: String, pub direction: SdpSingleDirection, pub formats: Vec, pub params: SdpAttributeRidParameters, - pub depends: Vec + pub depends: Vec, +} + +impl ToString for SdpAttributeRid { + fn to_string(&self) -> String { + format!( + "{id} {direction}{formats_and_prameters_and_depends}", + id = self.id, + direction = self.direction.to_string(), + formats_and_prameters_and_depends = match non_empty_string_vec![ + maybe_vector_to_string!("pt={}", self.formats, ","), + self.params.to_string(), + maybe_vector_to_string!("depends={}", self.depends, ",") + ] + .join(";") + .as_str() + { + "" => "".to_string(), + x => format!(" {}", x), + } + ) + } } #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeRtpmap { pub payload_type: u8, pub codec_name: String, @@ -492,8 +962,20 @@ impl SdpAttributeRtpmap { } } +impl ToString for SdpAttributeRtpmap { + fn to_string(&self) -> String { + format!( + "{pt} {codec_name}/{freq}{channels}", + pt = self.payload_type.to_string(), + codec_name = self.codec_name.clone(), + freq = self.frequency.to_string(), + channels = option_to_string!("/{}", self.channels) + ) + } +} + #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpAttributeSetup { Active, Actpass, @@ -501,8 +983,20 @@ pub enum SdpAttributeSetup { Passive, } +impl ToString for SdpAttributeSetup { + fn to_string(&self) -> String { + match *self { + SdpAttributeSetup::Active => "active", + SdpAttributeSetup::Actpass => "actpass", + SdpAttributeSetup::Holdconn => "holdconn", + SdpAttributeSetup::Passive => "passive", + } + .to_string() + } +} + #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeSsrc { pub id: u32, pub attribute: Option, @@ -529,8 +1023,19 @@ impl SdpAttributeSsrc { } } +impl ToString for SdpAttributeSsrc { + fn to_string(&self) -> String { + format!( + "{id}{attribute}{value}", + id = self.id.to_string(), + attribute = option_to_string!(" {}", self.attribute.clone()), + value = option_to_string!(":{}", self.value.clone()) + ) + } +} + #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpAttribute { BundleOnly, Candidate(SdpAttributeCandidate), @@ -576,141 +1081,95 @@ pub enum SdpAttribute { impl SdpAttribute { pub fn allowed_at_session_level(&self) -> bool { match *self { - SdpAttribute::BundleOnly | - SdpAttribute::Candidate(..) | - SdpAttribute::Fmtp(..) | - SdpAttribute::IceMismatch | - SdpAttribute::ImageAttr(..) | - SdpAttribute::Label(..) | - SdpAttribute::MaxMessageSize(..) | - SdpAttribute::MaxPtime(..) | - SdpAttribute::Mid(..) | - SdpAttribute::Msid(..) | - SdpAttribute::Ptime(..) | - SdpAttribute::Rid(..) | - SdpAttribute::RemoteCandidate(..) | - SdpAttribute::Rtpmap(..) | - SdpAttribute::Rtcp(..) | - SdpAttribute::Rtcpfb(..) | - SdpAttribute::RtcpMux | - SdpAttribute::RtcpRsize | - SdpAttribute::Sctpmap(..) | - SdpAttribute::SctpPort(..) | - SdpAttribute::Simulcast(..) | - SdpAttribute::Ssrc(..) | - SdpAttribute::SsrcGroup(..) => false, + SdpAttribute::BundleOnly + | SdpAttribute::Candidate(..) + | SdpAttribute::Fmtp(..) + | SdpAttribute::IceMismatch + | SdpAttribute::ImageAttr(..) + | SdpAttribute::Label(..) + | SdpAttribute::MaxMessageSize(..) + | SdpAttribute::MaxPtime(..) + | SdpAttribute::Mid(..) + | SdpAttribute::Msid(..) + | SdpAttribute::Ptime(..) + | SdpAttribute::Rid(..) + | SdpAttribute::RemoteCandidate(..) + | SdpAttribute::Rtpmap(..) + | SdpAttribute::Rtcp(..) + | SdpAttribute::Rtcpfb(..) + | SdpAttribute::RtcpMux + | SdpAttribute::RtcpRsize + | SdpAttribute::Sctpmap(..) + | SdpAttribute::SctpPort(..) + | SdpAttribute::Simulcast(..) + | SdpAttribute::Ssrc(..) + | SdpAttribute::SsrcGroup(..) => false, - SdpAttribute::DtlsMessage{..} | - SdpAttribute::EndOfCandidates | - SdpAttribute::Extmap(..) | - SdpAttribute::Fingerprint(..) | - SdpAttribute::Group(..) | - SdpAttribute::IceLite | - SdpAttribute::IceOptions(..) | - SdpAttribute::IcePwd(..) | - SdpAttribute::IceUfrag(..) | - SdpAttribute::Identity(..) | - SdpAttribute::Inactive | - SdpAttribute::MsidSemantic(..) | - SdpAttribute::Recvonly | - SdpAttribute::Sendonly | - SdpAttribute::Sendrecv | - SdpAttribute::Setup(..) => true, + SdpAttribute::DtlsMessage { .. } + | SdpAttribute::EndOfCandidates + | SdpAttribute::Extmap(..) + | SdpAttribute::Fingerprint(..) + | SdpAttribute::Group(..) + | SdpAttribute::IceLite + | SdpAttribute::IceOptions(..) + | SdpAttribute::IcePwd(..) + | SdpAttribute::IceUfrag(..) + | SdpAttribute::Identity(..) + | SdpAttribute::Inactive + | SdpAttribute::MsidSemantic(..) + | SdpAttribute::Recvonly + | SdpAttribute::Sendonly + | SdpAttribute::Sendrecv + | SdpAttribute::Setup(..) => true, } } pub fn allowed_at_media_level(&self) -> bool { match *self { - SdpAttribute::DtlsMessage{..} | - SdpAttribute::Group(..) | - SdpAttribute::IceLite | - SdpAttribute::Identity(..) | - SdpAttribute::MsidSemantic(..) => false, + SdpAttribute::DtlsMessage { .. } + | SdpAttribute::Group(..) + | SdpAttribute::IceLite + | SdpAttribute::Identity(..) + | SdpAttribute::MsidSemantic(..) => false, - SdpAttribute::BundleOnly | - SdpAttribute::Candidate(..) | - SdpAttribute::EndOfCandidates | - SdpAttribute::Extmap(..) | - SdpAttribute::Fingerprint(..) | - SdpAttribute::Fmtp(..) | - SdpAttribute::IceMismatch | - SdpAttribute::IceOptions(..) | - SdpAttribute::IcePwd(..) | - SdpAttribute::IceUfrag(..) | - SdpAttribute::ImageAttr(..) | - SdpAttribute::Inactive | - SdpAttribute::Label(..) | - SdpAttribute::MaxMessageSize(..) | - SdpAttribute::MaxPtime(..) | - SdpAttribute::Mid(..) | - SdpAttribute::Msid(..) | - SdpAttribute::Ptime(..) | - SdpAttribute::Rid(..) | - SdpAttribute::Recvonly | - SdpAttribute::RemoteCandidate(..) | - SdpAttribute::Rtpmap(..) | - SdpAttribute::Rtcp(..) | - SdpAttribute::Rtcpfb(..) | - SdpAttribute::RtcpMux | - SdpAttribute::RtcpRsize | - SdpAttribute::Sctpmap(..) | - SdpAttribute::SctpPort(..) | - SdpAttribute::Sendonly | - SdpAttribute::Sendrecv | - SdpAttribute::Setup(..) | - SdpAttribute::Simulcast(..) | - SdpAttribute::Ssrc(..) | - SdpAttribute::SsrcGroup(..) => true, + SdpAttribute::BundleOnly + | SdpAttribute::Candidate(..) + | SdpAttribute::EndOfCandidates + | SdpAttribute::Extmap(..) + | SdpAttribute::Fingerprint(..) + | SdpAttribute::Fmtp(..) + | SdpAttribute::IceMismatch + | SdpAttribute::IceOptions(..) + | SdpAttribute::IcePwd(..) + | SdpAttribute::IceUfrag(..) + | SdpAttribute::ImageAttr(..) + | SdpAttribute::Inactive + | SdpAttribute::Label(..) + | SdpAttribute::MaxMessageSize(..) + | SdpAttribute::MaxPtime(..) + | SdpAttribute::Mid(..) + | SdpAttribute::Msid(..) + | SdpAttribute::Ptime(..) + | SdpAttribute::Rid(..) + | SdpAttribute::Recvonly + | SdpAttribute::RemoteCandidate(..) + | SdpAttribute::Rtpmap(..) + | SdpAttribute::Rtcp(..) + | SdpAttribute::Rtcpfb(..) + | SdpAttribute::RtcpMux + | SdpAttribute::RtcpRsize + | SdpAttribute::Sctpmap(..) + | SdpAttribute::SctpPort(..) + | SdpAttribute::Sendonly + | SdpAttribute::Sendrecv + | SdpAttribute::Setup(..) + | SdpAttribute::Simulcast(..) + | SdpAttribute::Ssrc(..) + | SdpAttribute::SsrcGroup(..) => true, } } } -impl fmt::Display for SdpAttribute { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let printable = match *self { - SdpAttribute::BundleOnly => "bundle-only", - SdpAttribute::Candidate(..) => "candidate", - SdpAttribute::DtlsMessage{..} => "dtls-message", - SdpAttribute::EndOfCandidates => "end-of-candidates", - SdpAttribute::Extmap(..) => "extmap", - SdpAttribute::Fingerprint(..) => "fingerprint", - SdpAttribute::Fmtp(..) => "fmtp", - SdpAttribute::Group(..) => "group", - SdpAttribute::IceLite => "ice-lite", - SdpAttribute::IceMismatch => "ice-mismatch", - SdpAttribute::IceOptions(..) => "ice-options", - SdpAttribute::IcePwd(..) => "ice-pwd", - SdpAttribute::IceUfrag(..) => "ice-ufrag", - SdpAttribute::Identity(..) => "identity", - SdpAttribute::ImageAttr(..) => "imageattr", - SdpAttribute::Inactive => "inactive", - SdpAttribute::Label(..) => "label", - SdpAttribute::MaxMessageSize(..) => "max-message-size", - SdpAttribute::MaxPtime(..) => "max-ptime", - SdpAttribute::Mid(..) => "mid", - SdpAttribute::Msid(..) => "msid", - SdpAttribute::MsidSemantic(..) => "msid-semantic", - SdpAttribute::Ptime(..) => "ptime", - SdpAttribute::Rid(..) => "rid", - SdpAttribute::Recvonly => "recvonly", - SdpAttribute::RemoteCandidate(..) => "remote-candidate", - SdpAttribute::Rtpmap(..) => "rtpmap", - SdpAttribute::Rtcp(..) => "rtcp", - SdpAttribute::Rtcpfb(..) => "rtcp-fb", - SdpAttribute::RtcpMux => "rtcp-mux", - SdpAttribute::RtcpRsize => "rtcp-rsize", - SdpAttribute::Sctpmap(..) => "sctpmap", - SdpAttribute::SctpPort(..) => "sctp-port", - SdpAttribute::Sendonly => "sendonly", - SdpAttribute::Sendrecv => "sendrecv", - SdpAttribute::Setup(..) => "setup", - SdpAttribute::Simulcast(..) => "simulcast", - SdpAttribute::Ssrc(..) => "ssrc", - SdpAttribute::SsrcGroup(..) => "ssrc-group", - }; - write!(f, "attribute: {}", printable) - } -} impl FromStr for SdpAttribute { type Err = SdpParserInternalError; @@ -723,18 +1182,12 @@ impl FromStr for SdpAttribute { }; if tokens.len() > 1 { match name.as_str() { - "bundle-only" | - "end-of-candidates" | - "ice-lite" | - "ice-mismatch" | - "inactive" | - "recvonly" | - "rtcp-mux" | - "rtcp-rsize" | - "sendonly" | - "sendrecv" => { - return Err(SdpParserInternalError::Generic(format!("{} attribute is not allowed to have a value", - name))); + "bundle-only" | "end-of-candidates" | "ice-lite" | "ice-mismatch" | "inactive" + | "recvonly" | "rtcp-mux" | "rtcp-rsize" | "sendonly" | "sendrecv" => { + return Err(SdpParserInternalError::Generic(format!( + "{} attribute is not allowed to have a value", + name + ))); } _ => (), } @@ -756,7 +1209,7 @@ impl FromStr for SdpAttribute { "mid" => Ok(SdpAttribute::Mid(string_or_empty(val)?)), "msid-semantic" => parse_msid_semantic(val), "ptime" => Ok(SdpAttribute::Ptime(val.parse()?)), - "rid" => parse_rid(val), + "rid" => parse_rid(val), "recvonly" => Ok(SdpAttribute::Recvonly), "rtcp-mux" => Ok(SdpAttribute::RtcpMux), "rtcp-rsize" => Ok(SdpAttribute::RtcpRsize), @@ -779,9 +1232,58 @@ impl FromStr for SdpAttribute { "setup" => parse_setup(val), "simulcast" => parse_simulcast(val), "ssrc" => parse_ssrc(val), - _ => { - Err(SdpParserInternalError::Unsupported(format!("Unknown attribute type {}", name))) - } + _ => Err(SdpParserInternalError::Unsupported(format!( + "Unknown attribute type {}", + name + ))), + } + } +} + +impl ToString for SdpAttribute { + fn to_string(&self) -> String { + let attr_type_name = SdpAttributeType::from(self).to_string(); + let attr_to_string = |attr_str: String| attr_type_name + ":" + &attr_str; + match *self { + SdpAttribute::BundleOnly => SdpAttributeType::BundleOnly.to_string(), + SdpAttribute::Candidate(ref a) => attr_to_string(a.to_string()), + SdpAttribute::DtlsMessage(ref a) => attr_to_string(a.to_string()), + SdpAttribute::EndOfCandidates => SdpAttributeType::EndOfCandidates.to_string(), + SdpAttribute::Extmap(ref a) => attr_to_string(a.to_string()), + SdpAttribute::Fingerprint(ref a) => attr_to_string(a.to_string()), + SdpAttribute::Fmtp(ref a) => attr_to_string(a.to_string()), + SdpAttribute::Group(ref a) => attr_to_string(a.to_string()), + SdpAttribute::IceLite => SdpAttributeType::IceLite.to_string(), + SdpAttribute::IceMismatch => SdpAttributeType::IceMismatch.to_string(), + SdpAttribute::IceOptions(ref a) => attr_to_string(a.join(" ")), + SdpAttribute::IcePwd(ref a) => attr_to_string(a.to_string()), + SdpAttribute::IceUfrag(ref a) => attr_to_string(a.to_string()), + SdpAttribute::Identity(ref a) => attr_to_string(a.to_string()), + SdpAttribute::ImageAttr(ref a) => attr_to_string(a.to_string()), + SdpAttribute::Inactive => SdpAttributeType::Inactive.to_string(), + SdpAttribute::Label(ref a) => attr_to_string(a.to_string()), + SdpAttribute::MaxMessageSize(ref a) => attr_to_string(a.to_string()), + SdpAttribute::MaxPtime(ref a) => attr_to_string(a.to_string()), + SdpAttribute::Mid(ref a) => attr_to_string(a.to_string()), + SdpAttribute::Msid(ref a) => attr_to_string(a.to_string()), + SdpAttribute::MsidSemantic(ref a) => attr_to_string(a.to_string()), + SdpAttribute::Ptime(ref a) => attr_to_string(a.to_string()), + SdpAttribute::Rid(ref a) => attr_to_string(a.to_string()), + SdpAttribute::Recvonly => SdpAttributeType::Recvonly.to_string(), + SdpAttribute::RemoteCandidate(ref a) => attr_to_string(a.to_string()), + SdpAttribute::Rtpmap(ref a) => attr_to_string(a.to_string()), + SdpAttribute::Rtcp(ref a) => attr_to_string(a.to_string()), + SdpAttribute::Rtcpfb(ref a) => attr_to_string(a.to_string()), + SdpAttribute::RtcpMux => SdpAttributeType::RtcpMux.to_string(), + SdpAttribute::RtcpRsize => SdpAttributeType::RtcpRsize.to_string(), + SdpAttribute::Sctpmap(ref a) => attr_to_string(a.to_string()), + SdpAttribute::SctpPort(ref a) => attr_to_string(a.to_string()), + SdpAttribute::Sendonly => SdpAttributeType::Sendonly.to_string(), + SdpAttribute::Sendrecv => SdpAttributeType::Sendrecv.to_string(), + SdpAttribute::Setup(ref a) => attr_to_string(a.to_string()), + SdpAttribute::Simulcast(ref a) => attr_to_string(a.to_string()), + SdpAttribute::Ssrc(ref a) => attr_to_string(a.to_string()), + SdpAttribute::SsrcGroup(ref a) => attr_to_string(a.to_string()), } } } @@ -832,82 +1334,130 @@ pub enum SdpAttributeType { impl<'a> From<&'a SdpAttribute> for SdpAttributeType { fn from(other: &SdpAttribute) -> Self { match *other { - SdpAttribute::BundleOnly{..} => SdpAttributeType::BundleOnly, - SdpAttribute::Candidate{..} => SdpAttributeType::Candidate, - SdpAttribute::DtlsMessage{..} => SdpAttributeType::DtlsMessage, - SdpAttribute::EndOfCandidates{..} => SdpAttributeType::EndOfCandidates, - SdpAttribute::Extmap{..} => SdpAttributeType::Extmap, - SdpAttribute::Fingerprint{..} => SdpAttributeType::Fingerprint, - SdpAttribute::Fmtp{..} => SdpAttributeType::Fmtp, - SdpAttribute::Group{..} => SdpAttributeType::Group, - SdpAttribute::IceLite{..} => SdpAttributeType::IceLite, - SdpAttribute::IceMismatch{..} => SdpAttributeType::IceMismatch, - SdpAttribute::IceOptions{..} => SdpAttributeType::IceOptions, - SdpAttribute::IcePwd{..} => SdpAttributeType::IcePwd, - SdpAttribute::IceUfrag{..} => SdpAttributeType::IceUfrag, - SdpAttribute::Identity{..} => SdpAttributeType::Identity, - SdpAttribute::ImageAttr{..} => SdpAttributeType::ImageAttr, - SdpAttribute::Inactive{..} => SdpAttributeType::Inactive, - SdpAttribute::Label{..} => SdpAttributeType::Label, - SdpAttribute::MaxMessageSize{..} => SdpAttributeType::MaxMessageSize, - SdpAttribute::MaxPtime{..} => SdpAttributeType::MaxPtime, - SdpAttribute::Mid{..} => SdpAttributeType::Mid, - SdpAttribute::Msid{..} => SdpAttributeType::Msid, - SdpAttribute::MsidSemantic{..} => SdpAttributeType::MsidSemantic, - SdpAttribute::Ptime{..} => SdpAttributeType::Ptime, - SdpAttribute::Rid{..} => SdpAttributeType::Rid, - SdpAttribute::Recvonly{..} => SdpAttributeType::Recvonly, - SdpAttribute::RemoteCandidate{..} => SdpAttributeType::RemoteCandidate, - SdpAttribute::Rtcp{..} => SdpAttributeType::Rtcp, - SdpAttribute::Rtcpfb{..} => SdpAttributeType::Rtcpfb, - SdpAttribute::RtcpMux{..} => SdpAttributeType::RtcpMux, - SdpAttribute::RtcpRsize{..} => SdpAttributeType::RtcpRsize, - SdpAttribute::Rtpmap{..} => SdpAttributeType::Rtpmap, - SdpAttribute::Sctpmap{..} => SdpAttributeType::Sctpmap, - SdpAttribute::SctpPort{..} => SdpAttributeType::SctpPort, - SdpAttribute::Sendonly{..} => SdpAttributeType::Sendonly, - SdpAttribute::Sendrecv{..} => SdpAttributeType::Sendrecv, - SdpAttribute::Setup{..} => SdpAttributeType::Setup, - SdpAttribute::Simulcast{..} => SdpAttributeType::Simulcast, - SdpAttribute::Ssrc{..} => SdpAttributeType::Ssrc, - SdpAttribute::SsrcGroup{..} => SdpAttributeType::SsrcGroup + SdpAttribute::BundleOnly { .. } => SdpAttributeType::BundleOnly, + SdpAttribute::Candidate { .. } => SdpAttributeType::Candidate, + SdpAttribute::DtlsMessage { .. } => SdpAttributeType::DtlsMessage, + SdpAttribute::EndOfCandidates { .. } => SdpAttributeType::EndOfCandidates, + SdpAttribute::Extmap { .. } => SdpAttributeType::Extmap, + SdpAttribute::Fingerprint { .. } => SdpAttributeType::Fingerprint, + SdpAttribute::Fmtp { .. } => SdpAttributeType::Fmtp, + SdpAttribute::Group { .. } => SdpAttributeType::Group, + SdpAttribute::IceLite { .. } => SdpAttributeType::IceLite, + SdpAttribute::IceMismatch { .. } => SdpAttributeType::IceMismatch, + SdpAttribute::IceOptions { .. } => SdpAttributeType::IceOptions, + SdpAttribute::IcePwd { .. } => SdpAttributeType::IcePwd, + SdpAttribute::IceUfrag { .. } => SdpAttributeType::IceUfrag, + SdpAttribute::Identity { .. } => SdpAttributeType::Identity, + SdpAttribute::ImageAttr { .. } => SdpAttributeType::ImageAttr, + SdpAttribute::Inactive { .. } => SdpAttributeType::Inactive, + SdpAttribute::Label { .. } => SdpAttributeType::Label, + SdpAttribute::MaxMessageSize { .. } => SdpAttributeType::MaxMessageSize, + SdpAttribute::MaxPtime { .. } => SdpAttributeType::MaxPtime, + SdpAttribute::Mid { .. } => SdpAttributeType::Mid, + SdpAttribute::Msid { .. } => SdpAttributeType::Msid, + SdpAttribute::MsidSemantic { .. } => SdpAttributeType::MsidSemantic, + SdpAttribute::Ptime { .. } => SdpAttributeType::Ptime, + SdpAttribute::Rid { .. } => SdpAttributeType::Rid, + SdpAttribute::Recvonly { .. } => SdpAttributeType::Recvonly, + SdpAttribute::RemoteCandidate { .. } => SdpAttributeType::RemoteCandidate, + SdpAttribute::Rtcp { .. } => SdpAttributeType::Rtcp, + SdpAttribute::Rtcpfb { .. } => SdpAttributeType::Rtcpfb, + SdpAttribute::RtcpMux { .. } => SdpAttributeType::RtcpMux, + SdpAttribute::RtcpRsize { .. } => SdpAttributeType::RtcpRsize, + SdpAttribute::Rtpmap { .. } => SdpAttributeType::Rtpmap, + SdpAttribute::Sctpmap { .. } => SdpAttributeType::Sctpmap, + SdpAttribute::SctpPort { .. } => SdpAttributeType::SctpPort, + SdpAttribute::Sendonly { .. } => SdpAttributeType::Sendonly, + SdpAttribute::Sendrecv { .. } => SdpAttributeType::Sendrecv, + SdpAttribute::Setup { .. } => SdpAttributeType::Setup, + SdpAttribute::Simulcast { .. } => SdpAttributeType::Simulcast, + SdpAttribute::Ssrc { .. } => SdpAttributeType::Ssrc, + SdpAttribute::SsrcGroup { .. } => SdpAttributeType::SsrcGroup, } } } +impl ToString for SdpAttributeType { + fn to_string(&self) -> String { + match *self { + SdpAttributeType::BundleOnly => "bundle-only", + SdpAttributeType::Candidate => "candidate", + SdpAttributeType::DtlsMessage => "dtls-message", + SdpAttributeType::EndOfCandidates => "end-of-candidates", + SdpAttributeType::Extmap => "extmap", + SdpAttributeType::Fingerprint => "fingerprint", + SdpAttributeType::Fmtp => "fmtp", + SdpAttributeType::Group => "group", + SdpAttributeType::IceLite => "ice-lite", + SdpAttributeType::IceMismatch => "ice-mismatch", + SdpAttributeType::IceOptions => "ice-options", + SdpAttributeType::IcePwd => "ice-pwd", + SdpAttributeType::IceUfrag => "ice-ufrag", + SdpAttributeType::Identity => "identity", + SdpAttributeType::ImageAttr => "imageattr", + SdpAttributeType::Inactive => "inactive", + SdpAttributeType::Label => "label", + SdpAttributeType::MaxMessageSize => "max-message-size", + SdpAttributeType::MaxPtime => "maxptime", + SdpAttributeType::Mid => "mid", + SdpAttributeType::Msid => "msid", + SdpAttributeType::MsidSemantic => "msid-semantic", + SdpAttributeType::Ptime => "ptime", + SdpAttributeType::Rid => "rid", + SdpAttributeType::Recvonly => "recvonly", + SdpAttributeType::RemoteCandidate => "remote-candidates", + SdpAttributeType::Rtpmap => "rtpmap", + SdpAttributeType::Rtcp => "rtcp", + SdpAttributeType::Rtcpfb => "rtcp-fb", + SdpAttributeType::RtcpMux => "rtcp-mux", + SdpAttributeType::RtcpRsize => "rtcp-rsize", + SdpAttributeType::Sctpmap => "sctpmap", + SdpAttributeType::SctpPort => "sctp-port", + SdpAttributeType::Sendonly => "sendonly", + SdpAttributeType::Sendrecv => "sendrecv", + SdpAttributeType::Setup => "setup", + SdpAttributeType::Simulcast => "simulcast", + SdpAttributeType::Ssrc => "ssrc", + SdpAttributeType::SsrcGroup => "ssrc-group", + } + .to_string() + } +} fn string_or_empty(to_parse: &str) -> Result { if to_parse.is_empty() { - Err(SdpParserInternalError::Generic("This attribute is required to have a value" - .to_string())) + Err(SdpParserInternalError::Generic( + "This attribute is required to have a value".to_string(), + )) } else { Ok(to_parse.to_string()) } } -fn parse_payload_type(to_parse: &str) -> Result -{ +fn parse_payload_type(to_parse: &str) -> Result { Ok(match to_parse { - "*" => SdpAttributePayloadType::Wildcard, - _ => SdpAttributePayloadType::PayloadType(to_parse.parse::()?) - }) + "*" => SdpAttributePayloadType::Wildcard, + _ => SdpAttributePayloadType::PayloadType(to_parse.parse::()?), + }) } fn parse_single_direction(to_parse: &str) -> Result { match to_parse { "send" => Ok(SdpSingleDirection::Send), "recv" => Ok(SdpSingleDirection::Recv), - x @ _ => Err(SdpParserInternalError::Generic( - format!("Unknown direction description found: '{:}'",x).to_string() - )) + x => Err(SdpParserInternalError::Generic( + format!("Unknown direction description found: '{:}'", x).to_string(), + )), } } fn parse_sctp_port(to_parse: &str) -> Result { let port = to_parse.parse()?; if port > 65535 { - return Err(SdpParserInternalError::Generic(format!("Sctpport port {} can only be a bit 16bit number", - port))); + return Err(SdpParserInternalError::Generic(format!( + "Sctpport port {} can only be a bit 16bit number", + port + ))); } Ok(SdpAttribute::SctpPort(port)) } @@ -915,29 +1465,34 @@ fn parse_sctp_port(to_parse: &str) -> Result Result { let tokens: Vec<&str> = to_parse.split_whitespace().collect(); if tokens.len() < 8 { - return Err(SdpParserInternalError::Generic("Candidate needs to have minimum eigth tokens" - .to_string())); + return Err(SdpParserInternalError::Generic( + "Candidate needs to have minimum eigth tokens".to_string(), + )); } let component = tokens[1].parse::()?; let transport = match tokens[2].to_lowercase().as_ref() { "udp" => SdpAttributeCandidateTransport::Udp, "tcp" => SdpAttributeCandidateTransport::Tcp, _ => { - return Err(SdpParserInternalError::Generic("Unknonw candidate transport value" - .to_string())) + return Err(SdpParserInternalError::Generic( + "Unknonw candidate transport value".to_string(), + )); } }; let priority = tokens[3].parse::()?; let address = parse_unicast_addr(tokens[4])?; let port = tokens[5].parse::()?; if port > 65535 { - return Err(SdpParserInternalError::Generic("ICE candidate port can only be a bit 16bit number".to_string())); + return Err(SdpParserInternalError::Generic( + "ICE candidate port can only be a bit 16bit number".to_string(), + )); } match tokens[6].to_lowercase().as_ref() { "typ" => (), _ => { - return Err(SdpParserInternalError::Generic("Candidate attribute token must be 'typ'" - .to_string())) + return Err(SdpParserInternalError::Generic( + "Candidate attribute token must be 'typ'".to_string(), + )); } }; let cand_type = match tokens[7].to_lowercase().as_ref() { @@ -945,15 +1500,21 @@ fn parse_candidate(to_parse: &str) -> Result SdpAttributeCandidateType::Srflx, "prflx" => SdpAttributeCandidateType::Prflx, "relay" => SdpAttributeCandidateType::Relay, - _ => return Err(SdpParserInternalError::Generic("Unknow candidate type value".to_string())), + _ => { + return Err(SdpParserInternalError::Generic( + "Unknow candidate type value".to_string(), + )); + } }; - let mut cand = SdpAttributeCandidate::new(tokens[0].to_string(), - component, - transport, - priority, - address, - port, - cand_type); + let mut cand = SdpAttributeCandidate::new( + tokens[0].to_string(), + component, + transport, + priority, + address, + port, + cand_type, + ); if tokens.len() > 8 { let mut index = 8; while tokens.len() > index + 1 { @@ -976,20 +1537,24 @@ fn parse_candidate(to_parse: &str) -> Result { let port = tokens[index + 1].parse::()?; if port > 65535 { - return Err(SdpParserInternalError::Generic( "ICE candidate rport can only be a bit 16bit number".to_string())); + return Err(SdpParserInternalError::Generic( + "ICE candidate rport can only be a bit 16bit number".to_string(), + )); } cand.set_remote_port(port); index += 2; } "tcptype" => { cand.set_tcp_type(match tokens[index + 1].to_lowercase().as_ref() { - "active" => SdpAttributeCandidateTcpType::Active, - "passive" => SdpAttributeCandidateTcpType::Passive, - "so" => SdpAttributeCandidateTcpType::Simultaneous, - _ => { - return Err(SdpParserInternalError::Generic("Unknown tcptype value in candidate line".to_string())) + "active" => SdpAttributeCandidateTcpType::Active, + "passive" => SdpAttributeCandidateTcpType::Passive, + "so" => SdpAttributeCandidateTcpType::Simultaneous, + _ => { + return Err(SdpParserInternalError::Generic( + "Unknown tcptype value in candidate line".to_string(), + )); } - }); + }); index += 2; } "ufrag" => { @@ -998,30 +1563,37 @@ fn parse_candidate(to_parse: &str) -> Result { - return Err(SdpParserInternalError::Unsupported("Uknown candidate extension name" - .to_string())) + let name = tokens[index].to_string(); + let value = tokens[index + 1].to_string(); + cand.add_unknown_extension(name, value); + index += 2; } }; } + if tokens.len() > index { + return Err(SdpParserInternalError::Unsupported( + "Ice candidate extension name without value".to_string(), + )); + } } Ok(SdpAttribute::Candidate(cand)) } fn parse_dtls_message(to_parse: &str) -> Result { - let tokens:Vec<&str> = to_parse.split(" ").collect(); + let tokens: Vec<&str> = to_parse.split(' ').collect(); if tokens.len() != 2 { return Err(SdpParserInternalError::Generic( - "dtls-message must have a role token and a value token.".to_string() + "dtls-message must have a role token and a value token.".to_string(), )); } Ok(SdpAttribute::DtlsMessage(match tokens[0] { "client" => SdpAttributeDtlsMessage::Client(tokens[1].to_string()), "server" => SdpAttributeDtlsMessage::Server(tokens[1].to_string()), - e @ _ => { + e => { return Err(SdpParserInternalError::Generic( - format!("dtls-message has unknown role token '{}'",e).to_string() + format!("dtls-message has unknown role token '{}'", e).to_string(), )); } })) @@ -1032,8 +1604,9 @@ fn parse_dtls_message(to_parse: &str) -> Result Result { let tokens: Vec<&str> = to_parse.split_whitespace().collect(); if tokens.len() < 2 { - return Err(SdpParserInternalError::Generic("Extmap needs to have at least two tokens" - .to_string())); + return Err(SdpParserInternalError::Generic( + "Extmap needs to have at least two tokens".to_string(), + )); } let id: u16; let mut direction: Option = None; @@ -1043,13 +1616,15 @@ fn parse_extmap(to_parse: &str) -> Result let id_dir: Vec<&str> = tokens[0].splitn(2, '/').collect(); id = id_dir[0].parse::()?; direction = Some(match id_dir[1].to_lowercase().as_ref() { - "recvonly" => SdpAttributeDirection::Recvonly, - "sendonly" => SdpAttributeDirection::Sendonly, - "sendrecv" => SdpAttributeDirection::Sendrecv, - _ => { - return Err(SdpParserInternalError::Generic("Unsupported direction in extmap value".to_string())) - } - }) + "recvonly" => SdpAttributeDirection::Recvonly, + "sendonly" => SdpAttributeDirection::Sendonly, + "sendrecv" => SdpAttributeDirection::Sendrecv, + _ => { + return Err(SdpParserInternalError::Generic( + "Unsupported direction in extmap value".to_string(), + )); + } + }) } // Consider replacing to_parse.split_whitespace() above with splitn on space. Would we want the pattern to split on any amout of any kind of whitespace? let extension_attributes = if tokens.len() == 2 { @@ -1057,50 +1632,53 @@ fn parse_extmap(to_parse: &str) -> Result } else { let ext_string: String = tokens[2..].join(" "); if !valid_byte_string(&ext_string) { - return Err(SdpParserInternalError::Generic("Illegal character in extmap extension attributes".to_string())); + return Err(SdpParserInternalError::Generic( + "Illegal character in extmap extension attributes".to_string(), + )); } Some(ext_string) }; Ok(SdpAttribute::Extmap(SdpAttributeExtmap { - id, - direction, - url: tokens[1].to_string(), - extension_attributes: extension_attributes, - })) + id, + direction, + url: tokens[1].to_string(), + extension_attributes, + })) } fn parse_fingerprint(to_parse: &str) -> Result { let tokens: Vec<&str> = to_parse.split_whitespace().collect(); if tokens.len() != 2 { - return Err(SdpParserInternalError::Generic("Fingerprint needs to have two tokens" - .to_string())); + return Err(SdpParserInternalError::Generic( + "Fingerprint needs to have two tokens".to_string(), + )); } let fingerprint_token = tokens[1].to_string(); - let parse_tokens = |expected_len| -> Result, SdpParserInternalError>{ - let bytes = fingerprint_token.split(":") - .map(|byte_token| { - if byte_token.len() != 2 { - return Err(SdpParserInternalError::Generic( - "fingerpint's byte tokens must have 2 hexdigits" - .to_string() - )) - } - Ok(u8::from_str_radix(byte_token, 16)?) - }) - .collect::,_>>()?; + let parse_tokens = |expected_len| -> Result, SdpParserInternalError> { + let bytes = fingerprint_token + .split(':') + .map(|byte_token| { + if byte_token.len() != 2 { + return Err(SdpParserInternalError::Generic( + "fingerpint's byte tokens must have 2 hexdigits".to_string(), + )); + } + Ok(u8::from_str_radix(byte_token, 16)?) + }) + .collect::, _>>()?; if bytes.len() != expected_len { - return Err(SdpParserInternalError::Generic( - format!("fingerprint has {} bytes but should have {} bytes", - bytes.len(), expected_len) - )) + return Err(SdpParserInternalError::Generic(format!( + "fingerprint has {} bytes but should have {} bytes", + bytes.len(), + expected_len + ))); } Ok(bytes) }; - let hash_algorithm = match tokens[0] { "sha-1" => SdpAttributeFingerprintHashType::Sha1, "sha-224" => SdpAttributeFingerprintHashType::Sha224, @@ -1108,9 +1686,10 @@ fn parse_fingerprint(to_parse: &str) -> Result SdpAttributeFingerprintHashType::Sha384, "sha-512" => SdpAttributeFingerprintHashType::Sha512, unknown => { - return Err(SdpParserInternalError::Unsupported( - format!("fingerprint contains an unsupported hash algorithm '{}'", unknown) - )) + return Err(SdpParserInternalError::Unsupported(format!( + "fingerprint contains an unsupported hash algorithm '{}'", + unknown + ))); } }; @@ -1123,29 +1702,28 @@ fn parse_fingerprint(to_parse: &str) -> Result Result { - let tokens: Vec<&str> = to_parse.splitn(2," ").collect(); + let tokens: Vec<&str> = to_parse.splitn(2, ' ').collect(); if tokens.len() != 2 { return Err(SdpParserInternalError::Unsupported( - "Fmtp attributes require a payload type and a parameter block.".to_string() + "Fmtp attributes require a payload type and a parameter block.".to_string(), )); } let payload_token = tokens[0]; let parameter_token = tokens[1]; - // Default initiliaze SdpAttributeFmtpParameters - let mut parameters = SdpAttributeFmtpParameters{ + let mut parameters = SdpAttributeFmtpParameters { packetization_mode: 0, level_asymmetry_allowed: false, - profile_level_id: 0x420010, + profile_level_id: 0x0042_0010, max_fs: 0, max_cpb: 0, max_dpb: 0, @@ -1162,27 +1740,29 @@ fn parse_fmtp(to_parse: &str) -> Result { unknown_tokens: Vec::new(), }; - if parameter_token.contains("=") { - let parameter_tokens: Vec<&str> = parameter_token.split(";").collect(); + if parameter_token.contains('=') { + let parameter_tokens: Vec<&str> = parameter_token.split(';').collect(); for parameter_token in parameter_tokens.iter() { - let name_value_pair: Vec<&str> = parameter_token.splitn(2,"=").collect(); + let name_value_pair: Vec<&str> = parameter_token.splitn(2, '=').collect(); if name_value_pair.len() != 2 { return Err(SdpParserInternalError::Generic( "A fmtp parameter must be either a telephone event, a parameter list or - a red codec list".to_string() - )) + a red codec list" + .to_string(), + )); } - let parse_bool = |val: &str, param_name: &str| -> Result { + let parse_bool = |val: &str, + param_name: &str| + -> Result { match val.parse::()? { 0 => Ok(false), 1 => Ok(true), - _ => return Err(SdpParserInternalError::Generic( - format!("The fmtp parameter '{:}' must be 0 or 1", param_name) - .to_string() - )) + _ => Err(SdpParserInternalError::Generic( + format!("The fmtp parameter '{:}' must be 0 or 1", param_name).to_string(), + )), } }; @@ -1191,24 +1771,30 @@ fn parse_fmtp(to_parse: &str) -> Result { match parameter_name.to_uppercase().as_str() { // H264 - "PROFILE-LEVEL-ID" => parameters.profile_level_id = - match u32::from_str_radix(parameter_val,16)? { - x @ 0 ... 0xffffff => x, - _ => return Err(SdpParserInternalError::Generic( - "The fmtp parameter 'profile-level-id' must be in range [0,0xffffff]" - .to_string() - )) - }, - "PACKETIZATION-MODE" => parameters.packetization_mode = - match parameter_val.parse::()? { - x @ 0...2 => x, - _ => return Err(SdpParserInternalError::Generic( - "The fmtp parameter 'packetization-mode' must be 0,1 or 2" - .to_string() - )) - }, - "LEVEL-ASYMMETRY-ALLOWED" => parameters.level_asymmetry_allowed = - parse_bool(parameter_val,"level-asymmetry-allowed")?, + "PROFILE-LEVEL-ID" => { + parameters.profile_level_id = match u32::from_str_radix(parameter_val, 16)? { + x @ 0...0x00ff_ffff => x, + _ => return Err(SdpParserInternalError::Generic( + "The fmtp parameter 'profile-level-id' must be in range [0,0xffffff]" + .to_string(), + )), + } + } + "PACKETIZATION-MODE" => { + parameters.packetization_mode = match parameter_val.parse::()? { + x @ 0...2 => x, + _ => { + return Err(SdpParserInternalError::Generic( + "The fmtp parameter 'packetization-mode' must be 0,1 or 2" + .to_string(), + )); + } + } + } + "LEVEL-ASYMMETRY-ALLOWED" => { + parameters.level_asymmetry_allowed = + parse_bool(parameter_val, "level-asymmetry-allowed")? + } "MAX-MBPS" => parameters.max_mbps = parameter_val.parse::()?, "MAX-FS" => parameters.max_fs = parameter_val.parse::()?, "MAX-CPB" => parameters.max_cpb = parameter_val.parse::()?, @@ -1220,121 +1806,128 @@ fn parse_fmtp(to_parse: &str) -> Result { //Opus "MAXPLAYBACKRATE" => parameters.maxplaybackrate = parameter_val.parse::()?, - "USEDTX" => parameters.usedtx = parse_bool(parameter_val,"usedtx")?, - "STEREO" => parameters.stereo = parse_bool(parameter_val,"stereo")?, - "USEINBANDFEC" => parameters.useinbandfec = - parse_bool(parameter_val,"useinbandfec")?, - "CBR" => parameters.cbr = parse_bool(parameter_val,"cbr")?, + "USEDTX" => parameters.usedtx = parse_bool(parameter_val, "usedtx")?, + "STEREO" => parameters.stereo = parse_bool(parameter_val, "stereo")?, + "USEINBANDFEC" => { + parameters.useinbandfec = parse_bool(parameter_val, "useinbandfec")? + } + "CBR" => parameters.cbr = parse_bool(parameter_val, "cbr")?, + _ => parameters.unknown_tokens.push(parameter_token.to_string()), + } + } + } else if parameter_token.contains('/') { + let encodings: Vec<&str> = parameter_token.split('/').collect(); + + for encoding in encodings { + match encoding.parse::()? { + x @ 0...128 => parameters.encodings.push(x), _ => { - parameters.unknown_tokens.push(parameter_token.to_string()) + return Err(SdpParserInternalError::Generic( + "Red codec must be in range [0,128]".to_string(), + )); } } } } else { - if parameter_token.contains("/") { - let encodings: Vec<&str> = parameter_token.split("/").collect(); + // This is the case for the 'telephone-event' codec + let dtmf_tones: Vec<&str> = parameter_token.split(',').collect(); + let mut dtmf_tone_is_ok = true; - for encoding in encodings { - match encoding.parse::()? { - x @ 0...128 => parameters.encodings.push(x), - _ => return Err(SdpParserInternalError::Generic( - "Red codec must be in range [0,128]".to_string() - )) - } + // This closure verifies the output of some_number_as_string.parse::().ok() like calls + let validate_digits = |digit_option: Option| -> Option { + match digit_option { + Some(x) => match x { + 0...100 => Some(x), + _ => None, + }, + None => None, } - } else { // This is the case for the 'telephone-event' codec - let dtmf_tones: Vec<&str> = parameter_token.split(",").collect(); - let mut dtmf_tone_is_ok = true; + }; - // This closure verifies the output of some_number_as_string.parse::().ok() like calls - let validate_digits = |digit_option: Option | -> Option { - match digit_option{ - Some(x) => match x { - 0...100 => Some(x), - _ => None, - }, - None => None, + // This loop does some sanity checking on the passed dtmf tones + for dtmf_tone in dtmf_tones { + let dtmf_tone_range: Vec<&str> = dtmf_tone.splitn(2, '-').collect(); + + dtmf_tone_is_ok = match dtmf_tone_range.len() { + // In this case the dtmf tone is a range + 2 => { + match validate_digits(dtmf_tone_range[0].parse::().ok()) { + Some(l) => match validate_digits(dtmf_tone_range[1].parse::().ok()) { + Some(u) => { + // Check that the first part of the range is smaller than the second part + l < u + } + None => false, + }, + None => false, + } } + // In this case the dtmf tone is a single tone + 1 => validate_digits(dtmf_tone.parse::().ok()).is_some(), + _ => false, }; - // This loop does some sanity checking on the passed dtmf tones - for dtmf_tone in dtmf_tones { - let dtmf_tone_range: Vec<&str> = dtmf_tone.splitn(2,"-").collect(); - - dtmf_tone_is_ok = match dtmf_tone_range.len() { - // In this case the dtmf tone is a range - 2 => { - match validate_digits(dtmf_tone_range[0].parse::().ok()) { - Some(l) => match validate_digits(dtmf_tone_range[1].parse::().ok()) { - Some(u) => { - // Check that the first part of the range is smaller than the second part - l < u - }, - None => false - }, - None => false, - } - }, - // In this case the dtmf tone is a single tone - 1 => validate_digits(dtmf_tone.parse::().ok()).is_some(), - _ => false - }; - - if !dtmf_tone_is_ok { - break ; - } - } - - // Set the parsed dtmf tones or in case the parsing was insuccessfull, set it to the default "0-15" - parameters.dtmf_tones = match dtmf_tone_is_ok{ - true => parameter_token.to_string(), - false => "0-15".to_string() + if !dtmf_tone_is_ok { + break; } } + + // Set the parsed dtmf tones or in case the parsing was insuccessfull, set it to the default "0-15" + parameters.dtmf_tones = if dtmf_tone_is_ok { + parameter_token.to_string() + } else { + "0-15".to_string() + }; } Ok(SdpAttribute::Fmtp(SdpAttributeFmtp { - payload_type: payload_token.parse::()?, - parameters: parameters, - })) + payload_type: payload_token.parse::()?, + parameters, + })) } fn parse_group(to_parse: &str) -> Result { let mut tokens = to_parse.split_whitespace(); let semantics = match tokens.next() { None => { - return Err(SdpParserInternalError::Generic("Group attribute is missing semantics token" - .to_string())) + return Err(SdpParserInternalError::Generic( + "Group attribute is missing semantics token".to_string(), + )); } - Some(x) => { - match x.to_uppercase().as_ref() { - "LS" => SdpAttributeGroupSemantic::LipSynchronization, - "FID" => SdpAttributeGroupSemantic::FlowIdentification, - "SRF" => SdpAttributeGroupSemantic::SingleReservationFlow, - "ANAT" => SdpAttributeGroupSemantic::AlternateNetworkAddressType, - "FEC" => SdpAttributeGroupSemantic::ForwardErrorCorrection, - "DDP" => SdpAttributeGroupSemantic::DecodingDependency, - "BUNDLE" => SdpAttributeGroupSemantic::Bundle, - unknown @ _ => { - return Err(SdpParserInternalError::Unsupported( - format!("Unknown group semantic '{:?}' found", unknown) - )) - } + Some(x) => match x.to_uppercase().as_ref() { + "LS" => SdpAttributeGroupSemantic::LipSynchronization, + "FID" => SdpAttributeGroupSemantic::FlowIdentification, + "SRF" => SdpAttributeGroupSemantic::SingleReservationFlow, + "ANAT" => SdpAttributeGroupSemantic::AlternateNetworkAddressType, + "FEC" => SdpAttributeGroupSemantic::ForwardErrorCorrection, + "DDP" => SdpAttributeGroupSemantic::DecodingDependency, + "BUNDLE" => SdpAttributeGroupSemantic::Bundle, + unknown => { + return Err(SdpParserInternalError::Unsupported(format!( + "Unknown group semantic '{:?}' found", + unknown + ))); } - } + }, }; Ok(SdpAttribute::Group(SdpAttributeGroup { - semantics, - tags: tokens.map(|x| x.to_string()).collect(), - })) + semantics, + tags: tokens.map(ToString::to_string).collect(), + })) } fn parse_ice_options(to_parse: &str) -> Result { if to_parse.is_empty() { - return Err(SdpParserInternalError::Generic("ice-options is required to have a value" - .to_string())); + return Err(SdpParserInternalError::Generic( + "ice-options is required to have a value".to_string(), + )); } - Ok(SdpAttribute::IceOptions(to_parse.split_whitespace().map(|x| x.to_string()).collect())) + Ok(SdpAttribute::IceOptions( + to_parse + .split_whitespace() + .map(ToString::to_string) + .collect(), + )) } fn parse_imageattr_tokens(to_parse: &str, separator: char) -> Vec { @@ -1343,11 +1936,11 @@ fn parse_imageattr_tokens(to_parse: &str, separator: char) -> Vec { let mut current_tokens = Vec::new(); for token in to_parse.split(separator) { - if token.contains("[") { - open_braces_counter+=1; + if token.contains('[') { + open_braces_counter += 1; } - if token.contains("]") { - open_braces_counter-=1; + if token.contains(']') { + open_braces_counter -= 1; } current_tokens.push(token.to_string()); @@ -1362,25 +1955,26 @@ fn parse_imageattr_tokens(to_parse: &str, separator: char) -> Vec { } fn parse_imagettr_braced_token(to_parse: &str) -> Option<&str> { - if !to_parse.ends_with("]") { + if !to_parse.ends_with(']') { return None; } - Some(&to_parse[1..to_parse.len()-1]) + Some(&to_parse[1..to_parse.len() - 1]) } -fn parse_image_attr_xyrange(to_parse: &str) -> Result { - if to_parse.starts_with("[") { - let value_tokens = parse_imagettr_braced_token(to_parse).ok_or( +fn parse_image_attr_xyrange( + to_parse: &str, +) -> Result { + if to_parse.starts_with('[') { + let value_tokens = parse_imagettr_braced_token(to_parse).ok_or_else(|| { SdpParserInternalError::Generic( - "imageattr's xyrange has no closing tag ']'".to_string() + "imageattr's xyrange has no closing tag ']'".to_string(), ) - )?; + })?; - if to_parse.contains(":") { + if to_parse.contains(':') { // Range values - let range_tokens:Vec<&str> = value_tokens.split(":").collect(); + let range_tokens: Vec<&str> = value_tokens.split(':').collect(); if range_tokens.len() == 3 { Ok(SdpAttributeImageAttrXYRange::Range( @@ -1392,55 +1986,57 @@ fn parse_image_attr_xyrange(to_parse: &str) -> Result()?, range_tokens[1].parse::()?, - None + None, )) } else { - return Err(SdpParserInternalError::Generic( - "imageattr's xyrange must contain 2 or 3 fields".to_string() + Err(SdpParserInternalError::Generic( + "imageattr's xyrange must contain 2 or 3 fields".to_string(), )) } } else { // Discrete values - let values = value_tokens.split(",") - .map(|x| x.parse::()) - .collect::, _>>()?; + let values = value_tokens + .split(',') + .map(str::parse::) + .collect::, _>>()?; if values.len() < 2 { return Err(SdpParserInternalError::Generic( - "imageattr's discrete value list must have at least two elements".to_string() - )) + "imageattr's discrete value list must have at least two elements".to_string(), + )); } Ok(SdpAttributeImageAttrXYRange::DiscreteValues(values)) } - } else { - Ok(SdpAttributeImageAttrXYRange::DiscreteValues(vec![to_parse.parse::()?])) + Ok(SdpAttributeImageAttrXYRange::DiscreteValues(vec![ + to_parse.parse::()? + ])) } } -fn parse_image_attr_set(to_parse: &str) -> Result { +fn parse_image_attr_set( + to_parse: &str, +) -> Result { let mut tokens = parse_imageattr_tokens(to_parse, ',').into_iter(); - let x_token = tokens.next().ok_or(SdpParserInternalError::Generic( - "imageattr set is missing the 'x=' token".to_string() - ))?; + let x_token = tokens.next().ok_or_else(|| { + SdpParserInternalError::Generic("imageattr set is missing the 'x=' token".to_string()) + })?; if !x_token.starts_with("x=") { return Err(SdpParserInternalError::Generic( - "The first token in an imageattr set must begin with 'x='".to_string() - )) + "The first token in an imageattr set must begin with 'x='".to_string(), + )); } let x = parse_image_attr_xyrange(&x_token[2..])?; - - let y_token = tokens.next().ok_or(SdpParserInternalError::Generic( - "imageattr set is missing the 'y=' token".to_string() - ))?; + let y_token = tokens.next().ok_or_else(|| { + SdpParserInternalError::Generic("imageattr set is missing the 'y=' token".to_string()) + })?; if !y_token.starts_with("y=") { return Err(SdpParserInternalError::Generic( - "The second token in an imageattr set must begin with 'y='".to_string() - )) + "The second token in an imageattr set must begin with 'y='".to_string(), + )); } let y = parse_image_attr_xyrange(&y_token[2..])?; @@ -1449,12 +2045,12 @@ fn parse_image_attr_set(to_parse: &str) -> Result Result<(f32, f32), SdpParserInternalError> { - let minmax_pair:Vec<&str> = resolution_range.split("-").collect(); + let minmax_pair: Vec<&str> = resolution_range.split('-').collect(); if minmax_pair.len() != 2 { return Err(SdpParserInternalError::Generic( - "imageattr's par and sar ranges must have two components".to_string() - )) + "imageattr's par and sar ranges must have two components".to_string(), + )); } let min = minmax_pair[0].parse::()?; @@ -1462,38 +2058,39 @@ fn parse_image_attr_set(to_parse: &str) -> Result= max { return Err(SdpParserInternalError::Generic( - "In imageattr's par and sar ranges, first must be < than the second".to_string() - )) + "In imageattr's par and sar ranges, first must be < than the second".to_string(), + )); } - Ok((min,max)) + Ok((min, max)) }; - while let Some(current_token) = tokens.next() { + for current_token in tokens { if current_token.starts_with("sar=") { let value_token = ¤t_token[4..]; - if value_token.starts_with("[") { - let sar_values = parse_imagettr_braced_token(value_token).ok_or( + if value_token.starts_with('[') { + let sar_values = parse_imagettr_braced_token(value_token).ok_or_else(|| { SdpParserInternalError::Generic( - "imageattr's sar value is missing closing tag ']'".to_string() + "imageattr's sar value is missing closing tag ']'".to_string(), ) - )?; + })?; - if value_token.contains("-") { + if value_token.contains('-') { // Range let range = parse_ps_range(sar_values)?; - sar = Some(SdpAttributeImageAttrSRange::Range(range.0,range.1)) - } else if value_token.contains(",") { + sar = Some(SdpAttributeImageAttrSRange::Range(range.0, range.1)) + } else if value_token.contains(',') { // Discrete values - let values = sar_values.split(",") - .map(|x| x.parse::()) - .collect::, _>>()?; + let values = sar_values + .split(',') + .map(str::parse::) + .collect::, _>>()?; if values.len() < 2 { return Err(SdpParserInternalError::Generic( "imageattr's sar discrete value list must have at least two values" - .to_string() - )) + .to_string(), + )); } // Check that all the values are ascending @@ -1502,31 +2099,31 @@ fn parse_image_attr_set(to_parse: &str) -> Result= *value { return Err(SdpParserInternalError::Generic( "imageattr's sar discrete value list must contain ascending values" - .to_string() - )) + .to_string(), + )); } last_value = *value; } sar = Some(SdpAttributeImageAttrSRange::DiscreteValues(values)) } } else { - sar = Some(SdpAttributeImageAttrSRange::DiscreteValues( - vec![value_token.parse::()?]) - ) + sar = Some(SdpAttributeImageAttrSRange::DiscreteValues(vec![ + value_token.parse::()?, + ])) } } else if current_token.starts_with("par=") { let braced_value_token = ¤t_token[4..]; - if !braced_value_token.starts_with("[") { + if !braced_value_token.starts_with('[') { return Err(SdpParserInternalError::Generic( - "imageattr's par value must start with '['".to_string() - )) + "imageattr's par value must start with '['".to_string(), + )); } - let par_values = parse_imagettr_braced_token(braced_value_token).ok_or( + let par_values = parse_imagettr_braced_token(braced_value_token).ok_or_else(|| { SdpParserInternalError::Generic( - "imageattr's par value must be enclosed with ']'".to_string() + "imageattr's par value must be enclosed with ']'".to_string(), ) - )?; + })?; let range = parse_ps_range(par_values)?; par = Some(SdpAttributeImageAttrPRange { min: range.0, @@ -1537,33 +2134,39 @@ fn parse_image_attr_set(to_parse: &str) -> Result(tokens: &mut iter::Peekable) - -> Result - where I: Iterator + Clone { - let parse_set = |set_token:&str| -> Result { - Ok(parse_image_attr_set(parse_imagettr_braced_token(set_token).ok_or( - SdpParserInternalError::Generic( - "imageattr sets must be enclosed by ']'".to_string() - ))?)?) +fn parse_image_attr_set_list( + tokens: &mut iter::Peekable, +) -> Result +where + I: Iterator + Clone, +{ + let parse_set = |set_token: &str| -> Result { + Ok(parse_image_attr_set( + parse_imagettr_braced_token(set_token).ok_or_else(|| { + SdpParserInternalError::Generic( + "imageattr sets must be enclosed by ']'".to_string(), + ) + })?, + )?) }; - match tokens.next().ok_or(SdpParserInternalError::Generic( - "imageattr must have a parameter set after a direction token".to_string() - ))?.as_str() { + match tokens + .next() + .ok_or_else(|| { + SdpParserInternalError::Generic( + "imageattr must have a parameter set after a direction token".to_string(), + ) + })? + .as_str() + { "*" => Ok(SdpAttributeImageAttrSetList::Wildcard), x => { let mut sets = vec![parse_set(x)?]; while let Some(set_str) = tokens.clone().peek() { - if set_str.starts_with("[") { + if set_str.starts_with('[') { sets.push(parse_set(&tokens.next().unwrap())?); } else { break; @@ -1578,13 +2181,24 @@ fn parse_image_attr_set_list(tokens: &mut iter::Peekable) fn parse_image_attr(to_parse: &str) -> Result { let mut tokens = parse_imageattr_tokens(to_parse, ' ').into_iter().peekable(); - let pt = parse_payload_type(tokens.next().ok_or(SdpParserInternalError::Generic( - "imageattr requires a payload token".to_string() - ))?.as_str())?; - let first_direction = parse_single_direction(tokens.next().ok_or( - SdpParserInternalError::Generic( - "imageattr's second token must be a direction token".to_string() - ))?.as_str())?; + let pt = parse_payload_type( + tokens + .next() + .ok_or_else(|| { + SdpParserInternalError::Generic("imageattr requires a payload token".to_string()) + })? + .as_str(), + )?; + let first_direction = parse_single_direction( + tokens + .next() + .ok_or_else(|| { + SdpParserInternalError::Generic( + "imageattr's second token must be a direction token".to_string(), + ) + })? + .as_str(), + )?; let first_set_list = parse_image_attr_set_list(&mut tokens)?; @@ -1595,32 +2209,30 @@ fn parse_image_attr(to_parse: &str) -> Result - SdpAttributeImageAttr { - pt, - send: first_set_list, - recv: second_set_list, - }, - SdpSingleDirection::Recv => - SdpAttributeImageAttr { - pt, - send: second_set_list, - recv: first_set_list, - } + SdpSingleDirection::Send => SdpAttributeImageAttr { + pt, + send: first_set_list, + recv: second_set_list, + }, + SdpSingleDirection::Recv => SdpAttributeImageAttr { + pt, + send: second_set_list, + recv: first_set_list, + }, })) } @@ -1628,8 +2240,9 @@ fn parse_msid(to_parse: &str) -> Result { let mut tokens = to_parse.split_whitespace(); let id = match tokens.next() { None => { - return Err(SdpParserInternalError::Generic("Msid attribute is missing msid-id token" - .to_string())) + return Err(SdpParserInternalError::Generic( + "Msid attribute is missing msid-id token".to_string(), + )); } Some(x) => x.to_string(), }; @@ -1638,29 +2251,29 @@ fn parse_msid(to_parse: &str) -> Result { Some(x) => Some(x.to_string()), }; Ok(SdpAttribute::Msid(SdpAttributeMsid { id, appdata })) - } fn parse_msid_semantic(to_parse: &str) -> Result { let tokens: Vec<_> = to_parse.split_whitespace().collect(); - if tokens.len() < 1 { - return Err(SdpParserInternalError::Generic("Msid-semantic attribute is missing msid-semantic token" - .to_string())); + if tokens.is_empty() { + return Err(SdpParserInternalError::Generic( + "Msid-semantic attribute is missing msid-semantic token".to_string(), + )); } // TODO: Should msids be checked to ensure they are non empty? let semantic = SdpAttributeMsidSemantic { semantic: tokens[0].to_string(), - msids: tokens[1..].iter().map(|x| x.to_string()).collect(), + msids: tokens[1..].iter().map(ToString::to_string).collect(), }; Ok(SdpAttribute::MsidSemantic(semantic)) } fn parse_rid(to_parse: &str) -> Result { - let tokens: Vec<&str> = to_parse.splitn(3, " ").collect(); + let tokens: Vec<&str> = to_parse.splitn(3, ' ').collect(); if tokens.len() < 2 { return Err(SdpParserInternalError::Generic( - "A rid attribute must at least have an id and a direction token.".to_string() + "A rid attribute must at least have an id and a direction token.".to_string(), )); } @@ -1677,15 +2290,14 @@ fn parse_rid(to_parse: &str) -> Result { let mut formats: Vec = Vec::new(); let mut depends: Vec = Vec::new(); - if let Some(param_token) = tokens.get(2) { - let mut parameters = param_token.split(";").peekable(); + let mut parameters = param_token.split(';').peekable(); // The 'pt' parameter must be the first parameter if present, so it // cannot be checked along with the other parameters below if let Some(maybe_fmt_parameter) = parameters.clone().peek() { if maybe_fmt_parameter.starts_with("pt=") { - let fmt_list = maybe_fmt_parameter[3..].split(","); + let fmt_list = maybe_fmt_parameter[3..].split(','); for fmt in fmt_list { formats.push(fmt.trim().parse::()?); } @@ -1696,10 +2308,10 @@ fn parse_rid(to_parse: &str) -> Result { for param in parameters { // TODO: Bug 1225877. Add support for params without '=' - let param_value_pair: Vec<&str> = param.splitn(2,"=").collect(); + let param_value_pair: Vec<&str> = param.splitn(2, '=').collect(); if param_value_pair.len() != 2 { return Err(SdpParserInternalError::Generic( - "A rid parameter needs to be of form 'param=value'".to_string() + "A rid parameter needs to be of form 'param=value'".to_string(), )); } @@ -1711,19 +2323,19 @@ fn parse_rid(to_parse: &str) -> Result { "max-br" => params.max_br = param_value_pair[1].parse::()?, "max-pps" => params.max_pps = param_value_pair[1].parse::()?, "depends" => { - depends.extend(param_value_pair[1].split(",").map(|x| x.to_string())); - }, + depends.extend(param_value_pair[1].split(',').map(ToString::to_string)); + } _ => params.unknown.push(param.to_string()), } } } - Ok(SdpAttribute::Rid(SdpAttributeRid{ + Ok(SdpAttribute::Rid(SdpAttributeRid { id: tokens[0].to_string(), direction: parse_single_direction(tokens[1])?, - formats: formats, - params: params, - depends: depends, + formats, + params, + depends, })) } @@ -1732,77 +2344,84 @@ fn parse_remote_candidates(to_parse: &str) -> Result { return Err(SdpParserInternalError::Generic( - "Remote-candidate attribute is missing component ID" - .to_string(), - )) + "Remote-candidate attribute is missing component ID".to_string(), + )); } Some(x) => x.parse::()?, }; let address = match tokens.next() { None => { return Err(SdpParserInternalError::Generic( - "Remote-candidate attribute is missing connection address" - .to_string(), - )) + "Remote-candidate attribute is missing connection address".to_string(), + )); } Some(x) => parse_unicast_addr(x)?, }; let port = match tokens.next() { None => { return Err(SdpParserInternalError::Generic( - "Remote-candidate attribute is missing port number".to_string(), - )) + "Remote-candidate attribute is missing port number".to_string(), + )); } Some(x) => x.parse::()?, }; if port > 65535 { return Err(SdpParserInternalError::Generic( - "Remote-candidate port can only be a bit 16bit number".to_string(), - )); + "Remote-candidate port can only be a bit 16bit number".to_string(), + )); }; Ok(SdpAttribute::RemoteCandidate(SdpAttributeRemoteCandidate { - component, - address, - port, - })) + component, + address, + port, + })) } fn parse_rtpmap(to_parse: &str) -> Result { let mut tokens = to_parse.split_whitespace(); let payload_type: u8 = match tokens.next() { None => { - return Err(SdpParserInternalError::Generic("Rtpmap missing payload type".to_string())) + return Err(SdpParserInternalError::Generic( + "Rtpmap missing payload type".to_string(), + )); } Some(x) => { let pt = x.parse::()?; if pt > 127 { - return Err(SdpParserInternalError::Generic("Rtpmap payload type must be less then 127".to_string())); + return Err(SdpParserInternalError::Generic( + "Rtpmap payload type must be less then 127".to_string(), + )); }; pt } }; let mut parameters = match tokens.next() { None => { - return Err(SdpParserInternalError::Generic("Rtpmap missing payload type".to_string())) + return Err(SdpParserInternalError::Generic( + "Rtpmap missing payload type".to_string(), + )); } Some(x) => x.split('/'), }; let name = match parameters.next() { None => { - return Err(SdpParserInternalError::Generic("Rtpmap missing codec name".to_string())) + return Err(SdpParserInternalError::Generic( + "Rtpmap missing codec name".to_string(), + )); } Some(x) => x.to_string(), }; let frequency = match parameters.next() { None => { - return Err(SdpParserInternalError::Generic("Rtpmap missing codec name".to_string())) + return Err(SdpParserInternalError::Generic( + "Rtpmap missing codec name".to_string(), + )); } Some(x) => x.parse::()?, }; let mut rtpmap = SdpAttributeRtpmap::new(payload_type, name, frequency); - match parameters.next() { - Some(x) => rtpmap.set_channels(x.parse::()?), - None => (), + if let Some(x) = parameters.next() { + rtpmap.set_channels(x.parse::()?) }; Ok(SdpAttribute::Rtpmap(rtpmap)) } @@ -1811,8 +2430,9 @@ fn parse_rtcp(to_parse: &str) -> Result { let mut tokens = to_parse.split_whitespace(); let port = match tokens.next() { None => { - return Err(SdpParserInternalError::Generic("Rtcp attribute is missing port number" - .to_string())) + return Err(SdpParserInternalError::Generic( + "Rtcp attribute is missing port number".to_string(), + )); } Some(x) => x.parse::()?, }; @@ -1824,27 +2444,25 @@ fn parse_rtcp(to_parse: &str) -> Result { match tokens.next() { None => { return Err(SdpParserInternalError::Generic( - "Rtcp attribute is missing address type token" - .to_string(), - )) + "Rtcp attribute is missing address type token".to_string(), + )); } Some(x) => { let addrtype = parse_addrtype(x)?; let addr = match tokens.next() { None => { return Err(SdpParserInternalError::Generic( - "Rtcp attribute is missing ip address token" - .to_string(), - )) + "Rtcp attribute is missing ip address token".to_string(), + )); } Some(x) => { let addr = parse_unicast_addr(x)?; if !addrtype.same_protocol(&addr) { return Err(SdpParserInternalError::Generic( - "Failed to parse unicast address attribute.\ - addrtype does not match address." - .to_string(), - )); + "Failed to parse unicast address attribute.\ + addrtype does not match address." + .to_string(), + )); } addr } @@ -1858,11 +2476,11 @@ fn parse_rtcp(to_parse: &str) -> Result { } fn parse_rtcp_fb(to_parse: &str) -> Result { - let tokens: Vec<&str> = to_parse.splitn(4,' ').collect(); + let tokens: Vec<&str> = to_parse.splitn(4, ' ').collect(); // Parse this in advance to use it later in the parameter switch let feedback_type = match tokens.get(1) { - Some(x) => match x.as_ref(){ + Some(x) => match *x { "ack" => SdpAttributeRtcpFbType::Ack, "ccm" => SdpAttributeRtcpFbType::Ccm, "nack" => SdpAttributeRtcpFbType::Nack, @@ -1871,158 +2489,165 @@ fn parse_rtcp_fb(to_parse: &str) -> Result "transport-cc" => SdpAttributeRtcpFbType::TransCC, _ => { return Err(SdpParserInternalError::Unsupported( - format!("Unknown rtcpfb feedback type: {:?}",x).to_string() - )) + format!("Unknown rtcpfb feedback type: {:?}", x).to_string(), + )); } }, None => { return Err(SdpParserInternalError::Generic( - "Error parsing rtcpfb: no feedback type".to_string(), - )) + "Error parsing rtcpfb: no feedback type".to_string(), + )); } }; // Parse this in advance to make the initilization block below better readable - let parameter = match &feedback_type { - &SdpAttributeRtcpFbType::Ack => match tokens.get(2) { - Some(x) => match x.as_ref() { - "rpsi" | "app" => x.to_string(), + let parameter = match feedback_type { + SdpAttributeRtcpFbType::Ack => match tokens.get(2) { + Some(x) => match *x { + "rpsi" | "app" => x.to_string(), _ => { return Err(SdpParserInternalError::Unsupported( - format!("Unknown rtcpfb ack parameter: {:?}",x).to_string() - )) - }, + format!("Unknown rtcpfb ack parameter: {:?}", x).to_string(), + )); + } }, None => { return Err(SdpParserInternalError::Unsupported( - format!("The rtcpfb ack feeback type needs a parameter:").to_string() - )) + "The rtcpfb ack feeback type needs a parameter:".to_string(), + )); } }, - &SdpAttributeRtcpFbType::Ccm => match tokens.get(2) { - Some(x) => match x.as_ref() { - "fir" | "tmmbr" | "tstr" | "vbcm" => x.to_string(), + SdpAttributeRtcpFbType::Ccm => match tokens.get(2) { + Some(x) => match *x { + "fir" | "tmmbr" | "tstr" | "vbcm" => x.to_string(), _ => { return Err(SdpParserInternalError::Unsupported( - format!("Unknown rtcpfb ccm parameter: {:?}",x).to_string() - )) - }, + format!("Unknown rtcpfb ccm parameter: {:?}", x).to_string(), + )); + } }, None => "".to_string(), }, - &SdpAttributeRtcpFbType::Nack => match tokens.get(2) { - Some(x) => match x.as_ref() { - "sli" | "pli" | "rpsi" | "app" => x.to_string(), + SdpAttributeRtcpFbType::Nack => match tokens.get(2) { + Some(x) => match *x { + "sli" | "pli" | "rpsi" | "app" => x.to_string(), _ => { return Err(SdpParserInternalError::Unsupported( - format!("Unknown rtcpfb nack parameter: {:?}",x).to_string() - )) - }, + format!("Unknown rtcpfb nack parameter: {:?}", x).to_string(), + )); + } }, None => "".to_string(), }, - &SdpAttributeRtcpFbType::TrrInt => match tokens.get(2) { + SdpAttributeRtcpFbType::TrrInt => match tokens.get(2) { Some(x) => match x { _ if x.parse::().is_ok() => x.to_string(), _ => { return Err(SdpParserInternalError::Generic( - format!("Unknown rtcpfb trr-int parameter: {:?}",x).to_string() - )) - }, + format!("Unknown rtcpfb trr-int parameter: {:?}", x).to_string(), + )); + } }, None => { - return Err(SdpParserInternalError::Generic( - format!("The rtcpfb trr-int feedback type needs a parameter").to_string() - )) + return Err(SdpParserInternalError::Generic( + "The rtcpfb trr-int feedback type needs a parameter".to_string(), + )); } }, - &SdpAttributeRtcpFbType::Remb => match tokens.get(2) { + SdpAttributeRtcpFbType::Remb => match tokens.get(2) { Some(x) => match x { _ => { return Err(SdpParserInternalError::Unsupported( - format!("Unknown rtcpfb remb parameter: {:?}",x).to_string() - )) - }, + format!("Unknown rtcpfb remb parameter: {:?}", x).to_string(), + )); + } }, None => "".to_string(), }, - &SdpAttributeRtcpFbType::TransCC => match tokens.get(2) { + SdpAttributeRtcpFbType::TransCC => match tokens.get(2) { Some(x) => match x { _ => { return Err(SdpParserInternalError::Unsupported( - format!("Unknown rtcpfb transport-cc parameter: {:?}",x).to_string() - )) - }, + format!("Unknown rtcpfb transport-cc parameter: {:?}", x).to_string(), + )); + } }, None => "".to_string(), - } + }, }; - Ok(SdpAttribute::Rtcpfb(SdpAttributeRtcpFb { - payload_type: parse_payload_type(tokens[0])?, - - feedback_type: feedback_type, - - parameter: parameter, - - extra: match tokens.get(3) { - Some(x) => x.to_string(), - None => "".to_string(), - }, - })) + payload_type: parse_payload_type(tokens[0])?, + feedback_type, + parameter, + extra: match tokens.get(3) { + Some(x) => x.to_string(), + None => "".to_string(), + }, + })) } fn parse_sctpmap(to_parse: &str) -> Result { let tokens: Vec<&str> = to_parse.split_whitespace().collect(); if tokens.len() != 3 { - return Err(SdpParserInternalError::Generic("Sctpmap needs to have three tokens" - .to_string())); + return Err(SdpParserInternalError::Generic( + "Sctpmap needs to have three tokens".to_string(), + )); } let port = tokens[0].parse::()?; if tokens[1].to_lowercase() != "webrtc-datachannel" { - return Err(SdpParserInternalError::Generic("Unsupported sctpmap type token".to_string())); + return Err(SdpParserInternalError::Generic( + "Unsupported sctpmap type token".to_string(), + )); } Ok(SdpAttribute::Sctpmap(SdpAttributeSctpmap { - port, - channels: tokens[2].parse::()?, - })) + port, + channels: tokens[2].parse::()?, + })) } fn parse_setup(to_parse: &str) -> Result { - Ok(SdpAttribute::Setup(match to_parse.to_lowercase().as_ref() { - "active" => SdpAttributeSetup::Active, - "actpass" => SdpAttributeSetup::Actpass, - "holdconn" => SdpAttributeSetup::Holdconn, - "passive" => SdpAttributeSetup::Passive, - _ => { - return Err(SdpParserInternalError::Generic( - "Unsupported setup value".to_string(), - )) - } - })) + Ok(SdpAttribute::Setup( + match to_parse.to_lowercase().as_ref() { + "active" => SdpAttributeSetup::Active, + "actpass" => SdpAttributeSetup::Actpass, + "holdconn" => SdpAttributeSetup::Holdconn, + "passive" => SdpAttributeSetup::Passive, + _ => { + return Err(SdpParserInternalError::Generic( + "Unsupported setup value".to_string(), + )); + } + }, + )) } -fn parse_simulcast_version_list(to_parse: &str) -> Result, - SdpParserInternalError> { +fn parse_simulcast_version_list( + to_parse: &str, +) -> Result, SdpParserInternalError> { let make_version_list = |to_parse: &str| { - to_parse.split(';').map(SdpAttributeSimulcastVersion::new).collect() + to_parse + .split(';') + .map(SdpAttributeSimulcastVersion::new) + .collect() }; - if to_parse.contains("=") { - let mut descriptor_versionlist_pair = to_parse.splitn(2,"="); - match descriptor_versionlist_pair.next().unwrap() { - // TODO Bug 1470568 - "rid" => Ok(make_version_list(descriptor_versionlist_pair.next().unwrap())), - descriptor @ _ => { - return Err(SdpParserInternalError::Generic( - format!("Simulcast attribute has unknown list descriptor '{:?}'", - descriptor) - .to_string() - )) - } - } + if to_parse.contains('=') { + let mut descriptor_versionlist_pair = to_parse.splitn(2, '='); + match descriptor_versionlist_pair.next().unwrap() { + // TODO Bug 1470568 + "rid" => Ok(make_version_list( + descriptor_versionlist_pair.next().unwrap(), + )), + descriptor => Err(SdpParserInternalError::Generic( + format!( + "Simulcast attribute has unknown list descriptor '{:?}'", + descriptor + ) + .to_string(), + )), + } } else { - Ok(make_version_list(to_parse)) + Ok(make_version_list(to_parse)) } } @@ -2033,18 +2658,16 @@ fn parse_simulcast(to_parse: &str) -> Result parse_single_direction(x)?, None => { return Err(SdpParserInternalError::Generic( - "Simulcast attribute is missing send/recv value".to_string(), - )) + "Simulcast attribute is missing send/recv value".to_string(), + )); } }; let first_version_list = match tokens.next() { - Some(x) => { - parse_simulcast_version_list(x)? - }, + Some(x) => parse_simulcast_version_list(x)?, None => { return Err(SdpParserInternalError::Generic( - "Simulcast attribute must have an alternatives list after the direction token" + "Simulcast attribute must have an alternatives list after the direction token" .to_string(), )); } @@ -2054,20 +2677,20 @@ fn parse_simulcast(to_parse: &str) -> Result { - parse_simulcast_version_list(x)? - }, + Some(x) => parse_simulcast_version_list(x)?, None => { return Err(SdpParserInternalError::Generic( - format!("{:?}{:?}", - "Simulcast has defined a second direction but", - "no second list of simulcast stream versions") - .to_string() + format!( + "{:?}{:?}", + "Simulcast has defined a second direction but", + "no second list of simulcast stream versions" + ) + .to_string(), )); } } @@ -2086,11 +2709,12 @@ fn parse_simulcast(to_parse: &str) -> Result Result { - let mut tokens = to_parse.splitn(2,' '); + let mut tokens = to_parse.splitn(2, ' '); let ssrc_id = match tokens.next() { None => { - return Err(SdpParserInternalError::Generic("Ssrc attribute is missing ssrc-id value" - .to_string())) + return Err(SdpParserInternalError::Generic( + "Ssrc attribute is missing ssrc-id value".to_string(), + )); } Some(x) => x.parse::()?, }; @@ -2106,7 +2730,6 @@ pub fn parse_attribute(value: &str) -> Result { Ok(SdpType::Attribute(value.trim().parse()?)) } - #[cfg(test)] macro_rules! make_check_parse { ($attr_type:ty, $attr_kind:path) => { @@ -2117,17 +2740,41 @@ macro_rules! make_check_parse { unreachable!(); } } - } + }; + + ($attr_kind:path) => { + |attr_str: &str| -> SdpAttribute { + if let Ok(SdpType::Attribute($attr_kind)) = parse_attribute(attr_str) { + $attr_kind + } else { + unreachable!(); + } + } + }; +} + +#[cfg(test)] +macro_rules! make_check_parse_and_serialize { + ($check_parse_func:ident, $attr_kind:path) => { + |attr_str: &str| { + let parsed = $attr_kind($check_parse_func(attr_str)); + assert_eq!(parsed.to_string(), attr_str.to_string()); + } + }; + + ($check_parse_func:ident) => { + |attr_str: &str| { + let parsed = $check_parse_func(attr_str); + assert_eq!(parsed.to_string(), attr_str.to_string()); + } + }; } #[test] -fn test_parse_attribute_candidate() { +fn test_parse_attribute_candidate_and_serialize() { 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()); - }; + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::Candidate); 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"); @@ -2136,176 +2783,267 @@ fn test_parse_attribute_candidate() { 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: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 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"); + check_parse_and_serialize( + "candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host unsupported foo", + ); + check_parse_and_serialize("candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host unsupported foo more_unsupported bar"); - 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"); + 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 unsupported foo"); 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.priority, 1_685_987_071); + 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.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.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_eq!( + candidate.unknown_extensions, + vec![("unsupported".to_string(), "foo".to_string())] + ) +} - +#[test] +fn test_parse_attribute_candidate_errors() { 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 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()); - assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 49760 type host") - .is_err()); + assert!( + parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 49760 type host").is_err() + ); assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ fost").is_err()); - // FIXME this should fail without the extra 'foobar' at the end - assert!( - parse_attribute( - "candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host unsupported foobar" - ).is_err() - ); + assert!(parse_attribute( + "candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host unsupported" + ) + .is_err()); + assert!(parse_attribute( + "candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host network-cost" + ) + .is_err()); assert!(parse_attribute("candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 generation B").is_err()); - assert!( - parse_attribute( - "candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host network-cost C" - ).is_err() - ); - assert!( - parse_attribute( - "candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1 rport 61665" - ).is_err() - ); - assert!( - parse_attribute( - "candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host tcptype foobar" - ).is_err() - ); - assert!( - parse_attribute( - "candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1 rport 61665" - ).is_err() - ); - assert!(parse_attribute("candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 70000").is_err()); + assert!(parse_attribute( + "candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host network-cost C" + ) + .is_err()); + assert!(parse_attribute( + "candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1 rport 61665" + ) + .is_err()); + assert!(parse_attribute( + "candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host tcptype foobar" + ) + .is_err()); + assert!(parse_attribute( + "candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1 rport 61665" + ) + .is_err()); + assert!(parse_attribute( + "candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 70000" + ) + .is_err()); } #[test] fn test_parse_dtls_message() { - let check_parse = |x| -> SdpAttributeDtlsMessage { - if let Ok(SdpType::Attribute(SdpAttribute::DtlsMessage(x))) = parse_attribute(x) { - x - } else { - unreachable!(); - } - }; + let check_parse = make_check_parse!(SdpAttributeDtlsMessage, SdpAttribute::DtlsMessage); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::DtlsMessage); - assert!(parse_attribute("dtls-message:client SGVsbG8gV29ybGQ=").is_ok()); - assert!(parse_attribute("dtls-message:server SGVsbG8gV29ybGQ=").is_ok()); - assert!(parse_attribute("dtls-message:client IGlzdCBl/W4gUeiBtaXQg+JSB1bmQCAkJJkSNEQ=").is_ok()); - assert!(parse_attribute("dtls-message:server IGlzdCBl/W4gUeiBtaXQg+JSB1bmQCAkJJkSNEQ=").is_ok()); + check_parse_and_serialize("dtls-message:client SGVsbG8gV29ybGQ="); + check_parse_and_serialize("dtls-message:server SGVsbG8gV29ybGQ="); + check_parse_and_serialize("dtls-message:client IGlzdCBl/W4gUeiBtaXQg+JSB1bmQCAkJJkSNEQ="); + check_parse_and_serialize("dtls-message:server IGlzdCBl/W4gUeiBtaXQg+JSB1bmQCAkJJkSNEQ="); let mut dtls_message = check_parse("dtls-message:client SGVsbG8gV29ybGQ="); match dtls_message { SdpAttributeDtlsMessage::Client(x) => { assert_eq!(x, "SGVsbG8gV29ybGQ="); - }, - _ => { unreachable!(); } + } + _ => { + unreachable!(); + } } dtls_message = check_parse("dtls-message:server SGVsbG8gV29ybGQ="); match dtls_message { SdpAttributeDtlsMessage::Server(x) => { assert_eq!(x, "SGVsbG8gV29ybGQ="); - }, - _ => { unreachable!(); } + } + _ => { + unreachable!(); + } } - assert!(parse_attribute("dtls-message:client").is_err()); assert!(parse_attribute("dtls-message:server").is_err()); } #[test] fn test_parse_attribute_end_of_candidates() { - assert!(parse_attribute("end-of-candidates").is_ok()); + let check_parse = make_check_parse!(SdpAttribute::EndOfCandidates); + let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse); + + check_parse_and_serialize("end-of-candidates"); assert!(parse_attribute("end-of-candidates foobar").is_err()); } #[test] fn test_parse_attribute_extmap() { - assert!(parse_attribute("extmap:1/sendonly urn:ietf:params:rtp-hdrext:ssrc-audio-level") - .is_ok()); - assert!(parse_attribute("extmap:2/sendrecv urn:ietf:params:rtp-hdrext:ssrc-audio-level") - .is_ok()); - assert!(parse_attribute("extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time") - .is_ok()); + let check_parse = make_check_parse!(SdpAttributeExtmap, SdpAttribute::Extmap); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::Extmap); - assert!(parse_attribute("extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time ext_attributes") - .is_ok()); + check_parse_and_serialize("extmap:1/sendonly urn:ietf:params:rtp-hdrext:ssrc-audio-level"); + check_parse_and_serialize("extmap:2/sendrecv urn:ietf:params:rtp-hdrext:ssrc-audio-level"); + check_parse_and_serialize( + "extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time", + ); + check_parse_and_serialize( + "extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time ext_attributes", + ); - assert!(parse_attribute("extmap:a/sendrecv urn:ietf:params:rtp-hdrext:ssrc-audio-level") - .is_err()); - assert!(parse_attribute("extmap:4/unsupported urn:ietf:params:rtp-hdrext:ssrc-audio-level") - .is_err()); + assert!( + parse_attribute("extmap:a/sendrecv urn:ietf:params:rtp-hdrext:ssrc-audio-level").is_err() + ); + assert!( + parse_attribute("extmap:4/unsupported urn:ietf:params:rtp-hdrext:ssrc-audio-level") + .is_err() + ); - let mut bad_char = String::from("extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time ",); + let mut bad_char = + String::from("extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time "); bad_char.push(0x00 as char); assert!(parse_attribute(&bad_char).is_err()); } #[test] fn test_parse_attribute_fingerprint() { - assert!(parse_attribute("fingerprint:sha-1 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC").is_ok()); - assert!(parse_attribute("fingerprint:sha-224 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:\ - 27:97:EB:0B:23:73:AC:BC").is_ok()); - assert!(parse_attribute("fingerprint:sha-256 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:\ - 27:97:EB:0B:23:73:AC:BC:CD:34:D1:62").is_ok()); - assert!(parse_attribute("fingerprint:sha-384 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:\ - 27:97:EB:0B:23:73:AC:BC:CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:\ - 27:97:EB:0B:23:73:AC:BC").is_ok()); - assert!(parse_attribute("fingerprint:sha-512 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:\ - 97:EB:0B:23:73:AC:BC:CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:\ - EB:0B:23:73:AC:BC:27:97:EB:0B:23:73:AC:BC:27:97:EB:0B:23:73:\ - BC:EB:0B:23").is_ok()); + let check_parse = make_check_parse!(SdpAttributeFingerprint, SdpAttribute::Fingerprint); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::Fingerprint); - assert!(parse_attribute("fingerprint:sha-1 CX:34:D1:62:16:95:7B:B7:EB:74:E1:39:27:97:EB:0B:23:73:AC:BC").is_err()); - assert!(parse_attribute("fingerprint:sha-1 CDA:34:D1:62:16:95:7B:B7:EB:74:E1:39:27:97:EB:0B:23:73:AC:BC").is_err()); - assert!(parse_attribute("fingerprint:sha-1 CD:34:D1:62:16:95:7B:B7:EB:74:E1:39:27:97:EB:0B:23:73:AC:").is_err()); - assert!(parse_attribute("fingerprint:sha-1 CD:34:D1:62:16:95:7B:B7:EB:74:E1:39:27:97:EB:0B:23:73:AC").is_err()); - assert!(parse_attribute("fingerprint:sha-1 CX:34:D1:62:16:95:7B:B7:EB:74:E1:39:27:97:EB:0B:23:73:AC:BC").is_err()); + check_parse_and_serialize( + "fingerprint:sha-1 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC", + ); + check_parse_and_serialize( + "fingerprint:sha-224 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:\ + 27:97:EB:0B:23:73:AC:BC", + ); + check_parse_and_serialize( + "fingerprint:sha-256 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:\ + 27:97:EB:0B:23:73:AC:BC:CD:34:D1:62", + ); + check_parse_and_serialize( + "fingerprint:sha-384 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:\ + 27:97:EB:0B:23:73:AC:BC:CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:\ + 27:97:EB:0B:23:73:AC:BC", + ); + check_parse_and_serialize( + "fingerprint:sha-512 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:\ + 97:EB:0B:23:73:AC:BC:CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:\ + EB:0B:23:73:AC:BC:27:97:EB:0B:23:73:AC:BC:27:97:EB:0B:23:73:\ + BC:EB:0B:23", + ); - assert!(parse_attribute("fingerprint:sha-1 0xCD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC").is_err()); - assert!(parse_attribute("fingerprint:sha-1 CD:0x34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC").is_err()); - assert!(parse_attribute("fingerprint:sha-1 CD::D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC").is_err()); - assert!(parse_attribute("fingerprint:sha-1 CD:0000A:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC").is_err()); - assert!(parse_attribute("fingerprint:sha-1 CD:B:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC").is_err()); + assert!(parse_attribute( + "fingerprint:sha-1 CX:34:D1:62:16:95:7B:B7:EB:74:E1:39:27:97:EB:0B:23:73:AC:BC" + ) + .is_err()); + assert!(parse_attribute( + "fingerprint:sha-1 CDA:34:D1:62:16:95:7B:B7:EB:74:E1:39:27:97:EB:0B:23:73:AC:BC" + ) + .is_err()); + assert!(parse_attribute( + "fingerprint:sha-1 CD:34:D1:62:16:95:7B:B7:EB:74:E1:39:27:97:EB:0B:23:73:AC:" + ) + .is_err()); + assert!(parse_attribute( + "fingerprint:sha-1 CD:34:D1:62:16:95:7B:B7:EB:74:E1:39:27:97:EB:0B:23:73:AC" + ) + .is_err()); + assert!(parse_attribute( + "fingerprint:sha-1 CX:34:D1:62:16:95:7B:B7:EB:74:E1:39:27:97:EB:0B:23:73:AC:BC" + ) + .is_err()); + + assert!(parse_attribute( + "fingerprint:sha-1 0xCD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC" + ) + .is_err()); + assert!(parse_attribute( + "fingerprint:sha-1 CD:0x34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC" + ) + .is_err()); + assert!(parse_attribute( + "fingerprint:sha-1 CD::D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC" + ) + .is_err()); + assert!(parse_attribute( + "fingerprint:sha-1 CD:0000A:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC" + ) + .is_err()); + assert!(parse_attribute( + "fingerprint:sha-1 CD:B:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC" + ) + .is_err()); } #[test] fn test_parse_attribute_fmtp() { - assert!(parse_attribute("fmtp:109 maxplaybackrate=48000;stereo=1;useinbandfec=1").is_ok()); - assert!(parse_attribute("fmtp:66 0-15").is_ok()); - assert!(parse_attribute("fmtp:109 0-15,66").is_ok()); - assert!(parse_attribute("fmtp:66 111/115").is_ok()); + let check_parse = make_check_parse!(SdpAttributeFmtp, SdpAttribute::Fmtp); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::Fmtp); + + check_parse_and_serialize("fmtp:109 maxplaybackrate=46000;stereo=1;useinbandfec=1"); + check_parse_and_serialize("fmtp:66 0-15"); + check_parse_and_serialize("fmtp:109 0-15,66"); + check_parse_and_serialize("fmtp:66 111/115"); assert!(parse_attribute("fmtp:109 maxplaybackrate=48000;stereo=1;useinbandfec=1").is_ok()); assert!(parse_attribute("fmtp:109 maxplaybackrate=48000; stereo=1; useinbandfec=1").is_ok()); assert!(parse_attribute("fmtp:109 maxplaybackrate=48000; stereo=1;useinbandfec=1").is_ok()); - assert!(parse_attribute("fmtp:8 maxplaybackrate=48000").is_ok()); + check_parse_and_serialize("fmtp:8 maxplaybackrate=46000"); assert!(parse_attribute("fmtp:77 ").is_err()); assert!(parse_attribute("fmtp:109 maxplaybackrate=48000stereo=1;").is_err()); @@ -2314,139 +3052,114 @@ fn test_parse_attribute_fmtp() { #[test] fn test_parse_attribute_group() { - assert!(parse_attribute("group:LS").is_ok()); - assert!(parse_attribute("group:LS 1 2").is_ok()); - assert!(parse_attribute("group:BUNDLE sdparta_0 sdparta_1 sdparta_2").is_ok()); + let check_parse = make_check_parse!(SdpAttributeGroup, SdpAttribute::Group); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::Group); + + check_parse_and_serialize("group:LS"); + check_parse_and_serialize("group:LS 1 2"); + check_parse_and_serialize("group:FID 1 2"); + check_parse_and_serialize("group:SRF 1 2"); + check_parse_and_serialize("group:FEC S1 R1"); + check_parse_and_serialize("group:DDP L1 L2 L3"); + check_parse_and_serialize("group:BUNDLE sdparta_0 sdparta_1 sdparta_2"); assert!(parse_attribute("group:").is_err()); assert!(match parse_attribute("group:NEVER_SUPPORTED_SEMANTICS") { Err(SdpParserInternalError::Unsupported(_)) => true, - _ => false + _ => false, }) } #[test] fn test_parse_attribute_bundle_only() { - assert!(parse_attribute("bundle-only").is_ok()); + let check_parse = make_check_parse!(SdpAttribute::BundleOnly); + let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse); + + check_parse_and_serialize("bundle-only"); + assert!(parse_attribute("bundle-only foobar").is_err()); } #[test] fn test_parse_attribute_ice_lite() { - assert!(parse_attribute("ice-lite").is_ok()); + let check_parse = make_check_parse!(SdpAttribute::IceLite); + let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse); + + check_parse_and_serialize("ice-lite"); assert!(parse_attribute("ice-lite foobar").is_err()); } #[test] fn test_parse_attribute_ice_mismatch() { - assert!(parse_attribute("ice-mismatch").is_ok()); + let check_parse = make_check_parse!(SdpAttribute::IceMismatch); + let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse); + + check_parse_and_serialize("ice-mismatch"); assert!(parse_attribute("ice-mismatch foobar").is_err()); } #[test] fn test_parse_attribute_ice_options() { - assert!(parse_attribute("ice-options:trickle").is_ok()); + let check_parse = make_check_parse!(Vec, SdpAttribute::IceOptions); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::IceOptions); + + check_parse_and_serialize("ice-options:trickle"); assert!(parse_attribute("ice-options:").is_err()); } #[test] fn test_parse_attribute_ice_pwd() { - assert!(parse_attribute("ice-pwd:e3baa26dd2fa5030d881d385f1e36cce").is_ok()); + let check_parse = make_check_parse!(String, SdpAttribute::IcePwd); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::IcePwd); + + check_parse_and_serialize("ice-pwd:e3baa26dd2fa5030d881d385f1e36cce"); assert!(parse_attribute("ice-pwd:").is_err()); } #[test] fn test_parse_attribute_ice_ufrag() { - assert!(parse_attribute("ice-ufrag:58b99ead").is_ok()); + let check_parse = make_check_parse!(String, SdpAttribute::IceUfrag); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::IceUfrag); + + check_parse_and_serialize("ice-ufrag:58b99ead"); assert!(parse_attribute("ice-ufrag:").is_err()); } #[test] fn test_parse_attribute_identity() { - assert!(parse_attribute("identity:eyJpZHAiOnsiZG9tYWluIjoiZXhhbXBsZS5vcmciLCJwcm90b2NvbCI6ImJvZ3VzIn0sImFzc2VydGlvbiI6IntcImlkZW50aXR5XCI6XCJib2JAZXhhbXBsZS5vcmdcIixcImNvbnRlbnRzXCI6XCJhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3l6XCIsXCJzaWduYXR1cmVcIjpcIjAxMDIwMzA0MDUwNlwifSJ9").is_ok()); + let check_parse = make_check_parse!(String, SdpAttribute::Identity); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::Identity); + + check_parse_and_serialize("identity:eyJpZHAiOnsiZG9tYWluIjoiZXhhbXBsZS5vcmciLCJwcm90b2NvbCI6ImJvZ3VzIn0sImFzc2VydGlvbiI6IntcImlkZW50aXR5XCI6XCJib2JAZXhhbXBsZS5vcmdcIixcImNvbnRlbnRzXCI6XCJhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3l6XCIsXCJzaWduYXR1cmVcIjpcIjAxMDIwMzA0MDUwNlwifSJ9"); assert!(parse_attribute("identity:").is_err()); } #[test] fn test_parse_attribute_imageattr() { - let check_parse = |x| -> SdpAttributeImageAttr { - if let Ok(SdpType::Attribute(SdpAttribute::ImageAttr(x))) = parse_attribute(x) { - x - } else { - unreachable!(); - } - }; + let check_parse = make_check_parse!(SdpAttributeImageAttr, SdpAttribute::ImageAttr); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::ImageAttr); - assert!(parse_attribute("imageattr:120 send * recv *").is_ok()); - assert!(parse_attribute("imageattr:99 send [x=320,y=240] recv [x=320,y=240]").is_ok()); - assert!(parse_attribute("imageattr:97 send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320] recv [x=330,y=250]").is_ok()); + check_parse_and_serialize("imageattr:120 send * recv *"); + check_parse_and_serialize("imageattr:99 send [x=320,y=240] recv [x=320,y=240]"); + check_parse_and_serialize( + "imageattr:97 send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320] recv [x=330,y=250]", + ); + check_parse_and_serialize("imageattr:97 send [x=[480:16:800],y=[320:16:640],par=[1.2-1.3],q=0.6] [x=[176:8:208],y=[144:8:176],par=[1.2-1.3]] recv *"); assert!(parse_attribute("imageattr:97 recv [x=800,y=640,sar=1.1] send [x=330,y=250]").is_ok()); - assert!(parse_attribute("imageattr:97 send [x=[480:16:800],y=[320:16:640],par=[1.2-1.3],q=0.6] [x=[176:8:208],y=[144:8:176],par=[1.2-1.3]] recv *").is_ok()); - let mut imageattr = check_parse("imageattr:* recv [x=800,y=[50,80,30],sar=1.1] send [x=330,y=250,sar=[1.1,1.3,1.9],q=0.1]"); - assert_eq!(imageattr.pt, SdpAttributePayloadType::Wildcard); - match imageattr.recv { - SdpAttributeImageAttrSetList::Sets(sets) => { - assert_eq!(sets.len(), 1); - - let set = &sets[0]; - assert_eq!(set.x, SdpAttributeImageAttrXYRange::DiscreteValues(vec![800])); - assert_eq!(set.y, SdpAttributeImageAttrXYRange::DiscreteValues(vec![50,80,30])); - assert_eq!(set.par, None); - assert_eq!(set.sar, Some(SdpAttributeImageAttrSRange::DiscreteValues(vec![1.1]))); - assert_eq!(set.q, None); - - }, - _ => { unreachable!(); } - } - match imageattr.send { - SdpAttributeImageAttrSetList::Sets(sets) => { - assert_eq!(sets.len(), 1); - - let set = &sets[0]; - assert_eq!(set.x, SdpAttributeImageAttrXYRange::DiscreteValues(vec![330])); - assert_eq!(set.y, SdpAttributeImageAttrXYRange::DiscreteValues(vec![250])); - assert_eq!(set.par, None); - assert_eq!(set.sar, Some(SdpAttributeImageAttrSRange::DiscreteValues(vec![1.1,1.3,1.9]))); - assert_eq!(set.q, Some(0.1)); - }, - _ => { unreachable!(); } - } - - imageattr = check_parse("imageattr:97 send [x=[480:16:800],y=[100,200,300],par=[1.2-1.3],q=0.6] [x=1080,y=[144:176],sar=[0.5-0.7]] recv *"); - assert_eq!(imageattr.pt, SdpAttributePayloadType::PayloadType(97)); - match imageattr.send { - SdpAttributeImageAttrSetList::Sets(sets) => { - assert_eq!(sets.len(), 2); - - let first_set = &sets[0]; - assert_eq!(first_set.x, SdpAttributeImageAttrXYRange::Range(480, 800, Some(16))); - assert_eq!(first_set.y, SdpAttributeImageAttrXYRange::DiscreteValues(vec![100, 200, 300])); - assert_eq!(first_set.par, Some(SdpAttributeImageAttrPRange { - min: 1.2, - max: 1.3 - })); - assert_eq!(first_set.sar, None); - assert_eq!(first_set.q, Some(0.6)); - - let second_set = &sets[1]; - assert_eq!(second_set.x, SdpAttributeImageAttrXYRange::DiscreteValues(vec![1080])); - assert_eq!(second_set.y, SdpAttributeImageAttrXYRange::Range(144, 176, None)); - assert_eq!(second_set.par, None); - assert_eq!(second_set.sar, Some(SdpAttributeImageAttrSRange::Range(0.5, 0.7))); - assert_eq!(second_set.q, None); - }, - _ => { unreachable!(); } - } - assert_eq!(imageattr.recv, SdpAttributeImageAttrSetList::Wildcard); - - assert!(parse_attribute("imageattr:99 send [x=320,y=240]").is_ok()); + check_parse_and_serialize("imageattr:99 send [x=320,y=240]"); assert!(parse_attribute("imageattr:100 recv [x=320,y=240]").is_ok()); assert!(parse_attribute("imageattr:97 recv [x=800,y=640,sar=1.1,foo=[123,456],q=0.5] send [x=330,y=250,bar=foo,sar=[20-40]]").is_ok()); assert!(parse_attribute("imageattr:97 recv [x=800,y=640,sar=1.1,foo=abc xyz,q=0.5] send [x=330,y=250,bar=foo,sar=[20-40]]").is_ok()); @@ -2457,43 +3170,170 @@ fn test_parse_attribute_imageattr() { assert!(parse_attribute("imageattr:97 send [x=800,y=640,sar=1.1] send [x=330,y=250]").is_err()); } +#[test] +fn test_parse_attribute_imageattr_recv_and_verify() { + let check_parse = make_check_parse!(SdpAttributeImageAttr, SdpAttribute::ImageAttr); + + let imageattr = check_parse( + "imageattr:* recv [x=800,y=[50,80,30],sar=1.1] send [x=330,y=250,sar=[1.1,1.3,1.9],q=0.1]", + ); + assert_eq!(imageattr.pt, SdpAttributePayloadType::Wildcard); + match imageattr.recv { + SdpAttributeImageAttrSetList::Sets(sets) => { + assert_eq!(sets.len(), 1); + + let set = &sets[0]; + assert_eq!( + set.x, + SdpAttributeImageAttrXYRange::DiscreteValues(vec![800]) + ); + assert_eq!( + set.y, + SdpAttributeImageAttrXYRange::DiscreteValues(vec![50, 80, 30]) + ); + assert_eq!(set.par, None); + assert_eq!( + set.sar, + Some(SdpAttributeImageAttrSRange::DiscreteValues(vec![1.1])) + ); + assert_eq!(set.q, None); + } + _ => { + unreachable!(); + } + } + match imageattr.send { + SdpAttributeImageAttrSetList::Sets(sets) => { + assert_eq!(sets.len(), 1); + + let set = &sets[0]; + assert_eq!( + set.x, + SdpAttributeImageAttrXYRange::DiscreteValues(vec![330]) + ); + assert_eq!( + set.y, + SdpAttributeImageAttrXYRange::DiscreteValues(vec![250]) + ); + assert_eq!(set.par, None); + assert_eq!( + set.sar, + Some(SdpAttributeImageAttrSRange::DiscreteValues(vec![ + 1.1, 1.3, 1.9, + ])) + ); + assert_eq!(set.q, Some(0.1)); + } + _ => { + unreachable!(); + } + } +} + +#[test] +fn test_parse_attribute_imageattr_send_and_verify() { + let check_parse = make_check_parse!(SdpAttributeImageAttr, SdpAttribute::ImageAttr); + + let imageattr = check_parse( + "imageattr:97 send [x=[480:16:800],y=[100,200,300],par=[1.2-1.3],q=0.6] [x=1080,y=[144:176],sar=[0.5-0.7]] recv *" + ); + assert_eq!(imageattr.pt, SdpAttributePayloadType::PayloadType(97)); + match imageattr.send { + SdpAttributeImageAttrSetList::Sets(sets) => { + assert_eq!(sets.len(), 2); + + let first_set = &sets[0]; + assert_eq!( + first_set.x, + SdpAttributeImageAttrXYRange::Range(480, 800, Some(16)) + ); + assert_eq!( + first_set.y, + SdpAttributeImageAttrXYRange::DiscreteValues(vec![100, 200, 300]) + ); + assert_eq!( + first_set.par, + Some(SdpAttributeImageAttrPRange { min: 1.2, max: 1.3 }) + ); + assert_eq!(first_set.sar, None); + assert_eq!(first_set.q, Some(0.6)); + + let second_set = &sets[1]; + assert_eq!( + second_set.x, + SdpAttributeImageAttrXYRange::DiscreteValues(vec![1080]) + ); + assert_eq!( + second_set.y, + SdpAttributeImageAttrXYRange::Range(144, 176, None) + ); + assert_eq!(second_set.par, None); + assert_eq!( + second_set.sar, + Some(SdpAttributeImageAttrSRange::Range(0.5, 0.7)) + ); + assert_eq!(second_set.q, None); + } + _ => { + unreachable!(); + } + } + assert_eq!(imageattr.recv, SdpAttributeImageAttrSetList::Wildcard); +} + #[test] fn test_parse_attribute_inactive() { - assert!(parse_attribute("inactive").is_ok()); + let check_parse = make_check_parse!(SdpAttribute::Inactive); + let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse); + + check_parse_and_serialize("inactive"); assert!(parse_attribute("inactive foobar").is_err()); } #[test] fn test_parse_attribute_label() { - assert!(parse_attribute("label:1").is_ok()); - assert!(parse_attribute("label:foobar").is_ok()); - assert!(parse_attribute("label:foobar barfoo").is_ok()); + let check_parse = make_check_parse!(String, SdpAttribute::Label); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::Label); + + check_parse_and_serialize("label:1"); + check_parse_and_serialize("label:foobar"); + check_parse_and_serialize("label:foobar barfoo"); assert!(parse_attribute("label:").is_err()); } #[test] fn test_parse_attribute_maxptime() { - assert!(parse_attribute("maxptime:60").is_ok()); + let check_parse = make_check_parse!(u64, SdpAttribute::MaxPtime); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::MaxPtime); + + check_parse_and_serialize("maxptime:60"); assert!(parse_attribute("maxptime:").is_err()); } #[test] fn test_parse_attribute_mid() { - assert!(parse_attribute("mid:sdparta_0").is_ok()); - assert!(parse_attribute("mid:sdparta_0 sdparta_1 sdparta_2").is_ok()); + let check_parse = make_check_parse!(String, SdpAttribute::Mid); + let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse, SdpAttribute::Mid); + + check_parse_and_serialize("mid:sdparta_0"); + check_parse_and_serialize("mid:sdparta_0 sdparta_1 sdparta_2"); assert!(parse_attribute("mid:").is_err()); } #[test] fn test_parse_attribute_msid() { - assert!(parse_attribute("msid:{5a990edd-0568-ac40-8d97-310fc33f3411}").is_ok()); - assert!( - parse_attribute( - "msid:{5a990edd-0568-ac40-8d97-310fc33f3411} {218cfa1c-617d-2249-9997-60929ce4c405}" - ).is_ok() + let check_parse = make_check_parse!(SdpAttributeMsid, SdpAttribute::Msid); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::Msid); + + check_parse_and_serialize("msid:{5a990edd-0568-ac40-8d97-310fc33f3411}"); + check_parse_and_serialize( + "msid:{5a990edd-0568-ac40-8d97-310fc33f3411} {218cfa1c-617d-2249-9997-60929ce4c405}", ); assert!(parse_attribute("msid:").is_err()); @@ -2501,70 +3341,47 @@ fn test_parse_attribute_msid() { #[test] fn test_parse_attribute_msid_semantics() { - assert!(parse_attribute("msid-semantic:WMS *").is_ok()) + let check_parse = make_check_parse!(SdpAttributeMsidSemantic, SdpAttribute::MsidSemantic); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::MsidSemantic); + + check_parse_and_serialize("msid-semantic:WMS *"); + check_parse_and_serialize("msid-semantic:WMS foo"); + + assert!(parse_attribute("msid-semantic:").is_err()); } #[test] fn test_parse_attribute_ptime() { - assert!(parse_attribute("ptime:30").is_ok()); + let check_parse = make_check_parse!(u64, SdpAttribute::Ptime); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::Ptime); + + check_parse_and_serialize("ptime:30"); assert!(parse_attribute("ptime:").is_err()); } #[test] fn test_parse_attribute_rid() { + let check_parse = make_check_parse!(SdpAttributeRid, SdpAttribute::Rid); + let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse, SdpAttribute::Rid); - let check_parse = |x| -> SdpAttributeRid { - if let Ok(SdpType::Attribute(SdpAttribute::Rid(x))) = parse_attribute(x) { - x - } else { - unreachable!(); - } - }; + check_parse_and_serialize("rid:foo send pt=10"); + check_parse_and_serialize("rid:110 send pt=9,10"); + check_parse_and_serialize("rid:110 send pt=9,10;max-fs=10"); + check_parse_and_serialize("rid:110 send pt=9,10;max-width=10;depends=1,2,3"); - // assert!(parse_attribute("rid:foo send").is_ok()); - let mut rid = check_parse("rid:foo send"); - assert_eq!(rid.id, "foo"); - assert_eq!(rid.direction, SdpSingleDirection::Send); - - - // assert!(parse_attribute("rid:110 send pt=9").is_ok()); - rid = check_parse("rid:110 send pt=9"); - assert_eq!(rid.id, "110"); - assert_eq!(rid.direction, SdpSingleDirection::Send); - assert_eq!(rid.formats, vec![9]); - - assert!(parse_attribute("rid:foo send pt=10").is_ok()); - assert!(parse_attribute("rid:110 send pt=9,10").is_ok()); - assert!(parse_attribute("rid:110 send pt=9,10;max-fs=10").is_ok()); - assert!(parse_attribute("rid:110 send pt=9,10;max-width=10;depends=1,2,3").is_ok()); - - // assert!(parse_attribute("rid:110 send pt=9,10;max-fs=10;UNKNOWN=100;depends=1,2,3").is_ok()); - rid = check_parse("rid:110 send pt=9,10;max-fs=10;UNKNOWN=100;depends=1,2,3"); - assert_eq!(rid.id, "110"); - assert_eq!(rid.direction, SdpSingleDirection::Send); - assert_eq!(rid.formats, vec![9,10]); - assert_eq!(rid.params.max_fs, 10); - assert_eq!(rid.params.unknown, vec!["UNKNOWN=100"]); - assert_eq!(rid.depends, vec!["1","2","3"]); - - assert!(parse_attribute("rid:110 send pt=9, 10;max-fs=10;UNKNOWN=100; depends=1, 2, 3").is_ok()); + assert!( + parse_attribute("rid:110 send pt=9, 10;max-fs=10;UNKNOWN=100; depends=1, 2, 3").is_ok() + ); assert!(parse_attribute("rid:110 send max-fs=10").is_ok()); assert!(parse_attribute("rid:110 recv max-width=1920;max-height=1080").is_ok()); - // assert!(parse_attribute("rid:110 recv max-fps=42;max-fs=10;max-br=3;max-pps=1000").is_ok()); - rid = check_parse("rid:110 recv max-fps=42;max-fs=10;max-br=3;max-pps=1000"); - assert_eq!(rid.id, "110"); - assert_eq!(rid.direction, SdpSingleDirection::Recv); - assert_eq!(rid.params.max_fps, 42); - assert_eq!(rid.params.max_fs, 10); - assert_eq!(rid.params.max_br, 3); - assert_eq!(rid.params.max_pps, 1000); - - assert!(parse_attribute("rid:110 recv max-mbps=420;max-cpb=3;max-dpb=3").is_ok()); - assert!(parse_attribute("rid:110 recv scale-down-by=1.35;depends=1,2,3").is_ok()); - assert!(parse_attribute("rid:110 recv max-width=10;depends=1,2,3").is_ok()); - assert!(parse_attribute("rid:110 recv max-fs=10;UNKNOWN=100;depends=1,2,3").is_ok()); + check_parse_and_serialize("rid:110 recv max-mbps=420;max-cpb=3;max-dpb=3"); + check_parse_and_serialize("rid:110 recv scale-down-by=1.35;depends=1,2,3"); + check_parse_and_serialize("rid:110 recv max-width=10;depends=1,2,3"); + check_parse_and_serialize("rid:110 recv max-fs=10;UNKNOWN=100;depends=1,2,3"); assert!(parse_attribute("rid:").is_err()); assert!(parse_attribute("rid:120 send pt=").is_err()); @@ -2573,16 +3390,58 @@ fn test_parse_attribute_rid() { assert!(parse_attribute("rid:120 send pt=9;max-width=;max-width=10").is_err()); } +#[test] +fn test_parse_attribute_rid_and_verify() { + let check_parse = make_check_parse!(SdpAttributeRid, SdpAttribute::Rid); + let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse, SdpAttribute::Rid); + + check_parse_and_serialize("rid:foo send"); + let mut rid = check_parse("rid:foo send"); + assert_eq!(rid.id, "foo"); + assert_eq!(rid.direction, SdpSingleDirection::Send); + + check_parse_and_serialize("rid:110 send pt=9"); + rid = check_parse("rid:110 send pt=9"); + assert_eq!(rid.id, "110"); + assert_eq!(rid.direction, SdpSingleDirection::Send); + assert_eq!(rid.formats, vec![9]); + + check_parse_and_serialize("rid:110 send pt=9,10;max-fs=10;UNKNOWN=100;depends=1,2,3"); + rid = check_parse("rid:110 send pt=9,10;max-fs=10;UNKNOWN=100;depends=1,2,3"); + assert_eq!(rid.id, "110"); + assert_eq!(rid.direction, SdpSingleDirection::Send); + assert_eq!(rid.formats, vec![9, 10]); + assert_eq!(rid.params.max_fs, 10); + assert_eq!(rid.params.unknown, vec!["UNKNOWN=100"]); + assert_eq!(rid.depends, vec!["1", "2", "3"]); + + check_parse_and_serialize("rid:110 recv max-fps=42;max-fs=10;max-br=3;max-pps=1000"); + rid = check_parse("rid:110 recv max-fps=42;max-fs=10;max-br=3;max-pps=1000"); + assert_eq!(rid.id, "110"); + assert_eq!(rid.direction, SdpSingleDirection::Recv); + assert_eq!(rid.params.max_fps, 42); + assert_eq!(rid.params.max_fs, 10); + assert_eq!(rid.params.max_br, 3); + assert_eq!(rid.params.max_pps, 1000); +} + #[test] fn test_parse_attribute_recvonly() { - assert!(parse_attribute("recvonly").is_ok()); + let check_parse = make_check_parse!(SdpAttribute::Recvonly); + let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse); + + check_parse_and_serialize("recvonly"); assert!(parse_attribute("recvonly foobar").is_err()); } #[test] fn test_parse_attribute_remote_candidate() { - assert!(parse_attribute("remote-candidates:0 10.0.0.1 5555").is_ok()); - assert!(parse_attribute("remote-candidates:12345 ::1 5555").is_ok()); + let check_parse = make_check_parse!(SdpAttributeRemoteCandidate, SdpAttribute::RemoteCandidate); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::RemoteCandidate); + + check_parse_and_serialize("remote-candidates:0 10.0.0.1 5555"); + check_parse_and_serialize("remote-candidates:12345 ::1 5555"); assert!(parse_attribute("remote-candidates:abc 10.0.0.1 5555").is_err()); assert!(parse_attribute("remote-candidates:0 10.a.0.1 5555").is_err()); @@ -2594,22 +3453,32 @@ fn test_parse_attribute_remote_candidate() { #[test] fn test_parse_attribute_sendonly() { - assert!(parse_attribute("sendonly").is_ok()); + let check_parse = make_check_parse!(SdpAttribute::Sendonly); + let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse); + + check_parse_and_serialize("sendonly"); assert!(parse_attribute("sendonly foobar").is_err()); } #[test] fn test_parse_attribute_sendrecv() { - assert!(parse_attribute("sendrecv").is_ok()); + let check_parse = make_check_parse!(SdpAttribute::Sendrecv); + let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse); + + check_parse_and_serialize("sendrecv"); assert!(parse_attribute("sendrecv foobar").is_err()); } #[test] fn test_parse_attribute_setup() { - assert!(parse_attribute("setup:active").is_ok()); - assert!(parse_attribute("setup:passive").is_ok()); - assert!(parse_attribute("setup:actpass").is_ok()); - assert!(parse_attribute("setup:holdconn").is_ok()); + let check_parse = make_check_parse!(SdpAttributeSetup, SdpAttribute::Setup); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::Setup); + + check_parse_and_serialize("setup:active"); + check_parse_and_serialize("setup:passive"); + check_parse_and_serialize("setup:actpass"); + check_parse_and_serialize("setup:holdconn"); assert!(parse_attribute("setup:").is_err()); assert!(parse_attribute("setup:foobar").is_err()); @@ -2617,8 +3486,12 @@ fn test_parse_attribute_setup() { #[test] fn test_parse_attribute_rtcp() { - assert!(parse_attribute("rtcp:5000").is_ok()); - assert!(parse_attribute("rtcp:9 IN IP4 0.0.0.0").is_ok()); + let check_parse = make_check_parse!(SdpAttributeRtcp, SdpAttribute::Rtcp); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::Rtcp); + + check_parse_and_serialize("rtcp:5000"); + check_parse_and_serialize("rtcp:9 IN IP4 0.0.0.0"); assert!(parse_attribute("rtcp:").is_err()); assert!(parse_attribute("rtcp:70000").is_err()); @@ -2629,21 +3502,25 @@ fn test_parse_attribute_rtcp() { #[test] fn test_parse_attribute_rtcp_fb() { - assert!(parse_attribute("rtcp-fb:101 ack rpsi").is_ok()); - assert!(parse_attribute("rtcp-fb:101 ack app").is_ok()); - assert!(parse_attribute("rtcp-fb:101 ccm").is_ok()); - assert!(parse_attribute("rtcp-fb:101 ccm fir").is_ok()); - assert!(parse_attribute("rtcp-fb:101 ccm tmmbr").is_ok()); - assert!(parse_attribute("rtcp-fb:101 ccm tstr").is_ok()); - assert!(parse_attribute("rtcp-fb:101 ccm vbcm").is_ok()); - assert!(parse_attribute("rtcp-fb:101 nack").is_ok()); - assert!(parse_attribute("rtcp-fb:101 nack sli").is_ok()); - assert!(parse_attribute("rtcp-fb:101 nack pli").is_ok()); - assert!(parse_attribute("rtcp-fb:101 nack rpsi").is_ok()); - assert!(parse_attribute("rtcp-fb:101 nack app").is_ok()); - assert!(parse_attribute("rtcp-fb:101 trr-int 1").is_ok()); - assert!(parse_attribute("rtcp-fb:101 goog-remb").is_ok()); - assert!(parse_attribute("rtcp-fb:101 transport-cc").is_ok()); + let check_parse = make_check_parse!(SdpAttributeRtcpFb, SdpAttribute::Rtcpfb); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::Rtcpfb); + + check_parse_and_serialize("rtcp-fb:101 ack rpsi"); + check_parse_and_serialize("rtcp-fb:101 ack app"); + check_parse_and_serialize("rtcp-fb:101 ccm"); + check_parse_and_serialize("rtcp-fb:101 ccm fir"); + check_parse_and_serialize("rtcp-fb:101 ccm tmmbr"); + check_parse_and_serialize("rtcp-fb:101 ccm tstr"); + check_parse_and_serialize("rtcp-fb:101 ccm vbcm"); + check_parse_and_serialize("rtcp-fb:101 nack"); + check_parse_and_serialize("rtcp-fb:101 nack sli"); + check_parse_and_serialize("rtcp-fb:101 nack pli"); + check_parse_and_serialize("rtcp-fb:101 nack rpsi"); + check_parse_and_serialize("rtcp-fb:101 nack app"); + check_parse_and_serialize("rtcp-fb:101 trr-int 1"); + check_parse_and_serialize("rtcp-fb:101 goog-remb"); + check_parse_and_serialize("rtcp-fb:101 transport-cc"); assert!(parse_attribute("rtcp-fb:101 unknown").is_err()); assert!(parse_attribute("rtcp-fb:101 ack").is_err()); @@ -2657,20 +3534,30 @@ fn test_parse_attribute_rtcp_fb() { #[test] fn test_parse_attribute_rtcp_mux() { - assert!(parse_attribute("rtcp-mux").is_ok()); + let check_parse = make_check_parse!(SdpAttribute::RtcpMux); + let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse); + + check_parse_and_serialize("rtcp-mux"); assert!(parse_attribute("rtcp-mux foobar").is_err()); } #[test] fn test_parse_attribute_rtcp_rsize() { - assert!(parse_attribute("rtcp-rsize").is_ok()); + let check_parse = make_check_parse!(SdpAttribute::RtcpRsize); + let check_parse_and_serialize = make_check_parse_and_serialize!(check_parse); + + check_parse_and_serialize("rtcp-rsize"); assert!(parse_attribute("rtcp-rsize foobar").is_err()); } #[test] fn test_parse_attribute_rtpmap() { - assert!(parse_attribute("rtpmap:109 opus/48000").is_ok()); - assert!(parse_attribute("rtpmap:109 opus/48000/2").is_ok()); + let check_parse = make_check_parse!(SdpAttributeRtpmap, SdpAttribute::Rtpmap); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::Rtpmap); + + check_parse_and_serialize("rtpmap:109 opus/48000"); + check_parse_and_serialize("rtpmap:109 opus/48000/2"); assert!(parse_attribute("rtpmap:109 ").is_err()); assert!(parse_attribute("rtpmap:109 opus").is_err()); @@ -2679,7 +3566,11 @@ fn test_parse_attribute_rtpmap() { #[test] fn test_parse_attribute_sctpmap() { - assert!(parse_attribute("sctpmap:5000 webrtc-datachannel 256").is_ok()); + let check_parse = make_check_parse!(SdpAttributeSctpmap, SdpAttribute::Sctpmap); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::Sctpmap); + + check_parse_and_serialize("sctpmap:5000 webrtc-datachannel 256"); assert!(parse_attribute("sctpmap:70000 webrtc-datachannel 256").is_err()); assert!(parse_attribute("sctpmap:5000 unsupported 256").is_err()); @@ -2688,7 +3579,11 @@ fn test_parse_attribute_sctpmap() { #[test] fn test_parse_attribute_sctp_port() { - assert!(parse_attribute("sctp-port:5000").is_ok()); + let check_parse = make_check_parse!(u64, SdpAttribute::SctpPort); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::SctpPort); + + check_parse_and_serialize("sctp-port:5000"); assert!(parse_attribute("sctp-port:").is_err()); assert!(parse_attribute("sctp-port:70000").is_err()); @@ -2696,10 +3591,14 @@ fn test_parse_attribute_sctp_port() { #[test] fn test_parse_attribute_max_message_size() { - assert!(parse_attribute("max-message-size:1").is_ok()); - assert!(parse_attribute("max-message-size:100000").is_ok()); - assert!(parse_attribute("max-message-size:4294967297").is_ok()); - assert!(parse_attribute("max-message-size:0").is_ok()); + let check_parse = make_check_parse!(u64, SdpAttribute::MaxMessageSize); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::MaxMessageSize); + + check_parse_and_serialize("max-message-size:1"); + check_parse_and_serialize("max-message-size:100000"); + check_parse_and_serialize("max-message-size:4294967297"); + check_parse_and_serialize("max-message-size:0"); assert!(parse_attribute("max-message-size:").is_err()); assert!(parse_attribute("max-message-size:abc").is_err()); @@ -2707,14 +3606,18 @@ fn test_parse_attribute_max_message_size() { #[test] fn test_parse_attribute_simulcast() { - assert!(parse_attribute("simulcast:send 1").is_ok()); - assert!(parse_attribute("simulcast:recv test").is_ok()); - assert!(parse_attribute("simulcast:recv ~test").is_ok()); - assert!(parse_attribute("simulcast:recv test;foo").is_ok()); - assert!(parse_attribute("simulcast:recv foo,bar").is_ok()); - assert!(parse_attribute("simulcast:recv foo,bar;test").is_ok()); - assert!(parse_attribute("simulcast:recv 1;4,5 send 6;7").is_ok()); - assert!(parse_attribute("simulcast:send 1,2,3;~4,~5 recv 6;~7,~8").is_ok()); + let check_parse = make_check_parse!(SdpAttributeSimulcast, SdpAttribute::Simulcast); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::Simulcast); + + check_parse_and_serialize("simulcast:send 1"); + check_parse_and_serialize("simulcast:recv test"); + check_parse_and_serialize("simulcast:recv ~test"); + check_parse_and_serialize("simulcast:recv test;foo"); + check_parse_and_serialize("simulcast:recv foo,bar"); + check_parse_and_serialize("simulcast:recv foo,bar;test"); + check_parse_and_serialize("simulcast:send 1;4,5 recv 6;7"); + check_parse_and_serialize("simulcast:send 1,2,3;~4,~5 recv 6;~7,~8"); // old draft 03 notation used by Firefox 55 assert!(parse_attribute("simulcast: send rid=foo;bar").is_ok()); @@ -2728,12 +3631,14 @@ fn test_parse_attribute_simulcast() { #[test] fn test_parse_attribute_ssrc() { - assert!(parse_attribute("ssrc:2655508255").is_ok()); - assert!(parse_attribute("ssrc:2655508255 foo").is_ok()); - assert!(parse_attribute("ssrc:2655508255 cname:{735484ea-4f6c-f74a-bd66-7425f8476c2e}") - .is_ok()); - assert!(parse_attribute("ssrc:2082260239 msid:1d0cdb4e-5934-4f0f-9f88-40392cb60d31 315b086a-5cb6-4221-89de-caf0b038c79d") - .is_ok()); + let check_parse = make_check_parse!(SdpAttributeSsrc, SdpAttribute::Ssrc); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::Ssrc); + + check_parse_and_serialize("ssrc:2655508255"); + check_parse_and_serialize("ssrc:2655508255 foo"); + check_parse_and_serialize("ssrc:2655508255 cname:{735484ea-4f6c-f74a-bd66-7425f8476c2e}"); + check_parse_and_serialize("ssrc:2082260239 msid:1d0cdb4e-5934-4f0f-9f88-40392cb60d31 315b086a-5cb6-4221-89de-caf0b038c79d"); assert!(parse_attribute("ssrc:").is_err()); assert!(parse_attribute("ssrc:foo").is_err()); @@ -2741,7 +3646,13 @@ fn test_parse_attribute_ssrc() { #[test] fn test_parse_attribute_ssrc_group() { - assert!(parse_attribute("ssrc-group:FID 3156517279 2673335628").is_ok()) + let check_parse = make_check_parse!(String, SdpAttribute::SsrcGroup); + let check_parse_and_serialize = + make_check_parse_and_serialize!(check_parse, SdpAttribute::SsrcGroup); + + check_parse_and_serialize("ssrc-group:FID 3156517279 2673335628"); + + assert!(parse_attribute("ssrc-group:").is_err()); } #[test] diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/error.rs b/media/webrtc/signaling/src/sdp/rsdparsa/src/error.rs index 9adea091f49d..ceaf6cd1e659 100644 --- a/media/webrtc/signaling/src/sdp/rsdparsa/src/error.rs +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/error.rs @@ -1,14 +1,13 @@ -use std::num::ParseIntError; -use std::num::ParseFloatError; -use std::net::AddrParseError; -use std::fmt; +#[cfg(feature = "serialize")] +use serde::ser::{Serialize, SerializeStruct, Serializer}; use std::error; use std::error::Error; -#[cfg(feature = "serialize")] -use serde::ser::{Serializer, Serialize, SerializeStruct}; +use std::fmt; +use std::net::AddrParseError; +use std::num::ParseFloatError; +use std::num::ParseIntError; - -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum SdpParserInternalError { Generic(String), Unsupported(String), @@ -42,8 +41,8 @@ impl fmt::Display for SdpParserInternalError { impl error::Error for SdpParserInternalError { fn description(&self) -> &str { match *self { - SdpParserInternalError::Generic(ref message) | - SdpParserInternalError::Unsupported(ref message) => message, + SdpParserInternalError::Generic(ref message) + | SdpParserInternalError::Unsupported(ref message) => message, SdpParserInternalError::Integer(ref error) => error.description(), SdpParserInternalError::Float(ref error) => error.description(), SdpParserInternalError::Address(ref error) => error.description(), @@ -62,51 +61,63 @@ impl error::Error for SdpParserInternalError { } #[test] +#[allow(deprecated)] // see issue #102 fn test_sdp_parser_internal_error_generic() { let generic = SdpParserInternalError::Generic("generic message".to_string()); - assert_eq!(format!("{}", generic), - "Generic parsing error: generic message"); + assert_eq!( + format!("{}", generic), + "Generic parsing error: generic message" + ); assert_eq!(generic.description(), "generic message"); assert!(generic.cause().is_none()); } #[test] +#[allow(deprecated)] // see issue #102 fn test_sdp_parser_internal_error_unsupported() { - let unsupported = SdpParserInternalError::Unsupported("unsupported internal message" - .to_string()); - assert_eq!(format!("{}", unsupported), - "Unsupported parsing error: unsupported internal message"); + let unsupported = + SdpParserInternalError::Unsupported("unsupported internal message".to_string()); + assert_eq!( + format!("{}", unsupported), + "Unsupported parsing error: unsupported internal message" + ); assert_eq!(unsupported.description(), "unsupported internal message"); assert!(unsupported.cause().is_none()); } #[test] +#[allow(deprecated)] // see issue #102 fn test_sdp_parser_internal_error_integer() { let v = "12a"; let integer = v.parse::(); assert!(integer.is_err()); let int_err = SdpParserInternalError::Integer(integer.err().unwrap()); - assert_eq!(format!("{}", int_err), - "Integer parsing error: invalid digit found in string"); + assert_eq!( + format!("{}", int_err), + "Integer parsing error: invalid digit found in string" + ); assert_eq!(int_err.description(), "invalid digit found in string"); assert!(!int_err.cause().is_none()); } #[test] +#[allow(deprecated)] // see issue #102 fn test_sdp_parser_internal_error_address() { let v = "127.0.0.a"; - use std::str::FromStr; use std::net::IpAddr; + use std::str::FromStr; let addr = IpAddr::from_str(v); assert!(addr.is_err()); let addr_err = SdpParserInternalError::Address(addr.err().unwrap()); - assert_eq!(format!("{}", addr_err), - "IP address parsing error: invalid IP address syntax"); + assert_eq!( + format!("{}", addr_err), + "IP address parsing error: invalid IP address syntax" + ); assert_eq!(addr_err.description(), "invalid IP address syntax"); assert!(!addr_err.cause().is_none()); } -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum SdpParserError { Line { error: SdpParserInternalError, @@ -118,37 +129,57 @@ pub enum SdpParserError { line: String, line_number: usize, }, - Sequence { message: String, line_number: usize }, + Sequence { + message: String, + line_number: usize, + }, } #[cfg(feature = "serialize")] impl Serialize for SdpParserError { - fn serialize(&self, serializer: S) -> Result where S: Serializer { - let mut state = serializer.serialize_struct("error", match self { - &SdpParserError::Sequence{..} => 3, - _ => 4 - })?; - match self { - &SdpParserError::Line {ref error, ref line, ..} => { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct( + "error", + match *self { + SdpParserError::Sequence { .. } => 3, + _ => 4, + }, + )?; + match *self { + SdpParserError::Line { + ref error, + ref line, + .. + } => { state.serialize_field("type", "Line")?; state.serialize_field("message", &format!("{}", error))?; state.serialize_field("line", &line)? - }, - &SdpParserError::Unsupported {ref error, ref line, ..} => { + } + SdpParserError::Unsupported { + ref error, + ref line, + .. + } => { state.serialize_field("type", "Unsupported")?; state.serialize_field("message", &format!("{}", error))?; state.serialize_field("line", &line)? - }, - &SdpParserError::Sequence {ref message, ..} => { + } + SdpParserError::Sequence { ref message, .. } => { state.serialize_field("type", "Sequence")?; state.serialize_field("message", &message)?; } }; - state.serialize_field("line_number", &match self { - &SdpParserError::Line {line_number, ..} => line_number, - &SdpParserError::Unsupported {line_number, ..} => line_number, - &SdpParserError::Sequence {line_number, ..} => line_number, - })?; + state.serialize_field( + "line_number", + &match *self { + SdpParserError::Line { line_number, .. } => line_number, + SdpParserError::Unsupported { line_number, .. } => line_number, + SdpParserError::Sequence { line_number, .. } => line_number, + }, + )?; state.end() } } @@ -160,24 +191,24 @@ impl fmt::Display for SdpParserError { ref error, ref line, ref line_number, - } => { - write!(f, - "Line error: {} in line({}): {}", - error.description(), - line_number, - line) - } + } => write!( + f, + "Line error: {} in line({}): {}", + error.description(), + line_number, + line + ), SdpParserError::Unsupported { ref error, ref line, ref line_number, - } => { - write!(f, - "Unsupported: {} in line({}): {}", - error.description(), - line_number, - line) - } + } => write!( + f, + "Unsupported: {} in line({}): {}", + error.description(), + line_number, + line + ), SdpParserError::Sequence { ref message, ref line_number, @@ -186,20 +217,19 @@ impl fmt::Display for SdpParserError { } } - impl error::Error for SdpParserError { fn description(&self) -> &str { match *self { - SdpParserError::Line { ref error, .. } | - SdpParserError::Unsupported { ref error, .. } => error.description(), + SdpParserError::Line { ref error, .. } + | SdpParserError::Unsupported { ref error, .. } => error.description(), SdpParserError::Sequence { ref message, .. } => message, } } fn cause(&self) -> Option<&error::Error> { match *self { - SdpParserError::Line { ref error, .. } | - SdpParserError::Unsupported { ref error, .. } => Some(error), + SdpParserError::Line { ref error, .. } + | SdpParserError::Unsupported { ref error, .. } => Some(error), // Can't tell much more about our internal errors _ => None, } @@ -225,39 +255,48 @@ impl From for SdpParserInternalError { } #[test] +#[allow(deprecated)] // see issue #102 fn test_sdp_parser_error_line() { let line1 = SdpParserError::Line { error: SdpParserInternalError::Generic("test message".to_string()), line: "test line".to_string(), line_number: 13, }; - assert_eq!(format!("{}", line1), - "Line error: test message in line(13): test line"); + assert_eq!( + format!("{}", line1), + "Line error: test message in line(13): test line" + ); assert_eq!(line1.description(), "test message"); assert!(line1.cause().is_some()); } #[test] +#[allow(deprecated)] // see issue #102 fn test_sdp_parser_error_unsupported() { let unsupported1 = SdpParserError::Unsupported { error: SdpParserInternalError::Generic("unsupported value".to_string()), line: "unsupported line".to_string(), line_number: 21, }; - assert_eq!(format!("{}", unsupported1), - "Unsupported: unsupported value in line(21): unsupported line"); + assert_eq!( + format!("{}", unsupported1), + "Unsupported: unsupported value in line(21): unsupported line" + ); assert_eq!(unsupported1.description(), "unsupported value"); assert!(unsupported1.cause().is_some()); } #[test] +#[allow(deprecated)] // see issue #102 fn test_sdp_parser_error_sequence() { let sequence1 = SdpParserError::Sequence { message: "sequence message".to_string(), line_number: 42, }; - assert_eq!(format!("{}", sequence1), - "Sequence error in line(42): sequence message"); + assert_eq!( + format!("{}", sequence1), + "Sequence error in line(42): sequence message" + ); assert_eq!(sequence1.description(), "sequence message"); assert!(sequence1.cause().is_none()); } diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/lib.rs b/media/webrtc/signaling/src/sdp/rsdparsa/src/lib.rs index cd1f851aab29..6fff6e13c8f3 100644 --- a/media/webrtc/signaling/src/sdp/rsdparsa/src/lib.rs +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/lib.rs @@ -1,34 +1,34 @@ -#![cfg_attr(feature="clippy", feature(plugin))] +#![cfg_attr(feature = "clippy", feature(plugin))] #[macro_use] extern crate log; -#[cfg(feature="serialize")] +#[cfg(feature = "serialize")] #[macro_use] extern crate serde_derive; -#[cfg(feature="serialize")] +#[cfg(feature = "serialize")] extern crate serde; use std::net::IpAddr; use std::str::FromStr; -use std::fmt; +#[macro_use] pub mod attribute_type; pub mod error; pub mod media_type; pub mod network; -pub mod unsupported_types; -use attribute_type::{SdpAttribute, SdpSingleDirection, SdpAttributeType, parse_attribute, - SdpAttributeSimulcastVersion, SdpAttributeRid}; -use error::{SdpParserInternalError, SdpParserError}; -use media_type::{SdpMedia, SdpMediaLine, parse_media, parse_media_vector, SdpProtocolValue, - SdpMediaValue, SdpFormatList}; +use attribute_type::{ + addr_to_string, parse_attribute, SdpAttribute, SdpAttributeRid, SdpAttributeSimulcastVersion, + SdpAttributeType, SdpSingleDirection, +}; +use error::{SdpParserError, SdpParserInternalError}; +use media_type::{ + parse_media, parse_media_vector, SdpFormatList, SdpMedia, SdpMediaLine, SdpMediaValue, + SdpProtocolValue, +}; use network::{parse_addrtype, parse_nettype, parse_unicast_addr}; -use unsupported_types::{parse_email, parse_information, parse_key, parse_phone, parse_repeat, - parse_uri, parse_zone}; -#[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpBandwidth { As(u32), Ct(u32), @@ -36,16 +36,40 @@ pub enum SdpBandwidth { Unknown(String, u32), } +impl ToString for SdpBandwidth { + fn to_string(&self) -> String { + match *self { + SdpBandwidth::As(ref x) => format!("AS:{}", x.to_string()), + SdpBandwidth::Ct(ref x) => format!("CT:{}", x.to_string()), + SdpBandwidth::Tias(ref x) => format!("TIAS:{}", x.to_string()), + SdpBandwidth::Unknown(ref tp, ref x) => format!("{}:{}", tp.to_string(), x.to_string()), + } + } +} + #[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpConnection { pub addr: IpAddr, pub ttl: Option, pub amount: Option, } -#[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +impl ToString for SdpConnection { + fn to_string(&self) -> String { + format!( + "IN {addr}{ttl}{amount}", + addr = match self.addr { + IpAddr::V4(ipv4) => format!("IP4 {}", ipv4.to_string()), + IpAddr::V6(ipv6) => format!("IP6 {}", ipv6.to_string()), + }, + ttl = option_to_string!("/{}", self.ttl), + amount = option_to_string!("/{}", self.amount) + ) + } +} + +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpOrigin { pub username: String, pub session_id: u64, @@ -53,50 +77,51 @@ pub struct SdpOrigin { pub unicast_addr: IpAddr, } -impl fmt::Display for SdpOrigin { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, - "origin: {}, {}, {}, {}", - self.username, - self.session_id, - self.session_version, - self.unicast_addr) +impl ToString for SdpOrigin { + fn to_string(&self) -> String { + format!( + "{username} {sess_id} {sess_vers} {unicast_addr}", + username = self.username.clone(), + sess_id = self.session_id.to_string(), + sess_vers = self.session_version.to_string(), + unicast_addr = addr_to_string(self.unicast_addr) + ) } } -#[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpTiming { pub start: u64, pub stop: u64, } -#[cfg_attr(feature="serialize", derive(Serialize))] +impl ToString for SdpTiming { + fn to_string(&self) -> String { + format!("{} {}", self.start.to_string(), self.stop.to_string()) + } +} + +#[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpType { + // Note: Email, Information, Key, Phone, Repeat, Uri and Zone are left out + // on purposes as we don't want to support them. Attribute(SdpAttribute), Bandwidth(SdpBandwidth), Connection(SdpConnection), - Email(String), - Information(String), - Key(String), Media(SdpMediaLine), - Phone(String), Origin(SdpOrigin), - Repeat(String), Session(String), Timing(SdpTiming), - Uri(String), Version(u64), - Zone(String), } -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpLine { pub line_number: usize, pub sdp_type: SdpType, } -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpSession { pub version: u64, pub origin: SdpOrigin, @@ -106,15 +131,14 @@ pub struct SdpSession { pub timing: Option, pub attribute: Vec, pub media: Vec, - pub warnings: Vec - // unsupported values: - // information: Option, - // uri: Option, - // email: Option, - // phone: Option, - // repeat: Option, - // zone: Option, - // key: Option, + pub warnings: Vec, // unsupported values: + // information: Option, + // uri: Option, + // email: Option, + // phone: Option, + // repeat: Option, + // zone: Option, + // key: Option } impl SdpSession { @@ -148,56 +172,123 @@ impl SdpSession { &self.connection } - pub fn set_connection(&mut self, c: &SdpConnection) { - self.connection = Some(c.clone()) + pub fn set_connection(&mut self, c: SdpConnection) { + self.connection = Some(c) } - pub fn add_bandwidth(&mut self, b: &SdpBandwidth) { - self.bandwidth.push(b.clone()) + pub fn add_bandwidth(&mut self, b: SdpBandwidth) { + self.bandwidth.push(b) } - pub fn set_timing(&mut self, t: &SdpTiming) { - self.timing = Some(t.clone()) + pub fn set_timing(&mut self, t: SdpTiming) { + self.timing = Some(t) } - pub fn add_attribute(&mut self, a: &SdpAttribute) -> Result<(), SdpParserInternalError> { + pub fn add_attribute(&mut self, a: SdpAttribute) -> Result<(), SdpParserInternalError> { if !a.allowed_at_session_level() { - return Err(SdpParserInternalError::Generic(format!("{} not allowed at session level", - a))); + return Err(SdpParserInternalError::Generic(format!( + "{} not allowed at session level", + a.to_string() + ))); }; - Ok(self.attribute.push(a.clone())) + self.attribute.push(a); + Ok(()) } pub fn extend_media(&mut self, v: Vec) { self.media.extend(v) } - pub fn get_attribute(&self, t: SdpAttributeType) -> Option<&SdpAttribute> { - self.attribute.iter().filter(|a| SdpAttributeType::from(*a) == t).next() + pub fn parse_session_vector(&mut self, lines: &mut Vec) -> Result<(), SdpParserError> { + while !lines.is_empty() { + let line = lines.remove(0); + match line.sdp_type { + SdpType::Attribute(a) => { + let _line_number = line.line_number; + self.add_attribute(a).map_err(|e: SdpParserInternalError| { + SdpParserError::Sequence { + message: format!("{}", e), + line_number: _line_number, + } + })? + } + SdpType::Bandwidth(b) => self.add_bandwidth(b), + SdpType::Timing(t) => self.set_timing(t), + SdpType::Connection(c) => self.set_connection(c), + + SdpType::Origin(_) | SdpType::Session(_) | SdpType::Version(_) => { + return Err(SdpParserError::Sequence { + message: "version, origin or session at wrong level".to_string(), + line_number: line.line_number, + }); + } + SdpType::Media(_) => { + return Err(SdpParserError::Sequence { + message: "media line not allowed in session parser".to_string(), + line_number: line.line_number, + }); + } + } + } + Ok(()) } - pub fn add_media(&mut self, media_type: SdpMediaValue, direction: SdpAttribute, port: u32, - protocol: SdpProtocolValue, addr: String) - -> Result<(),SdpParserInternalError> { - let mut media = SdpMedia::new(SdpMediaLine { - media: media_type, - port, - port_count: 1, - proto: protocol, - formats: SdpFormatList::Integers(Vec::new()), - }); + pub fn get_attribute(&self, t: SdpAttributeType) -> Option<&SdpAttribute> { + self.attribute + .iter() + .find(|a| SdpAttributeType::from(*a) == t) + } - media.add_attribute(&direction)?; + pub fn add_media( + &mut self, + media_type: SdpMediaValue, + direction: SdpAttribute, + port: u32, + protocol: SdpProtocolValue, + addr: String, + ) -> Result<(), SdpParserInternalError> { + let mut media = SdpMedia::new(SdpMediaLine { + media: media_type, + port, + port_count: 1, + proto: protocol, + formats: SdpFormatList::Integers(Vec::new()), + }); - media.set_connection(&SdpConnection { - addr: IpAddr::from_str(addr.as_str())?, - ttl: None, - amount: None, - })?; + media.add_attribute(direction)?; - self.media.push(media); + media.set_connection(SdpConnection { + addr: IpAddr::from_str(addr.as_str())?, + ttl: None, + amount: None, + })?; - Ok(()) + self.media.push(media); + + Ok(()) + } +} + +impl ToString for SdpSession { + fn to_string(&self) -> String { + format!( + "v={version}\r\n\ + o={origin}\r\n\ + s={sess}\r\n\ + {timing}\ + {bandwidth}\ + {connection}\ + {sess_attributes}\ + {media_sections}", + version = self.version.to_string(), + origin = self.origin.to_string(), + sess = self.session.clone(), + timing = option_to_string!("t={}\r\n", self.timing), + bandwidth = maybe_vector_to_string!("b={}\r\n", self.bandwidth, "\r\nb="), + connection = option_to_string!("c={}\r\n", self.connection), + sess_attributes = maybe_vector_to_string!("a={}\r\n", self.attribute, "\r\na="), + media_sections = maybe_vector_to_string!("{}", self.media, "\r\n") + ) } } @@ -211,12 +302,13 @@ fn test_session_works() { assert!(parse_session("topic").is_ok()); } - fn parse_version(value: &str) -> Result { let ver = value.parse::()?; if ver != 0 { - return Err(SdpParserInternalError::Generic(format!("version type contains unsupported value {}", - ver))); + return Err(SdpParserInternalError::Generic(format!( + "version type contains unsupported value {}", + ver + ))); }; trace!("version: {}", ver); Ok(SdpType::Version(ver)) @@ -238,51 +330,56 @@ fn parse_origin(value: &str) -> Result { let mut tokens = value.split_whitespace(); let username = match tokens.next() { None => { - return Err(SdpParserInternalError::Generic("Origin type is missing username token" - .to_string())) + return Err(SdpParserInternalError::Generic( + "Origin type is missing username token".to_string(), + )); } Some(x) => x, }; let session_id = match tokens.next() { None => { - return Err(SdpParserInternalError::Generic("Origin type is missing session ID token" - .to_string())) + return Err(SdpParserInternalError::Generic( + "Origin type is missing session ID token".to_string(), + )); } Some(x) => x.parse::()?, }; let session_version = match tokens.next() { None => { return Err(SdpParserInternalError::Generic( - "Origin type is missing session version token" - .to_string())) + "Origin type is missing session version token".to_string(), + )); } Some(x) => x.parse::()?, }; match tokens.next() { None => { return Err(SdpParserInternalError::Generic( - "Origin type is missing session version token".to_string(), - )) + "Origin type is missing session version token".to_string(), + )); } Some(x) => parse_nettype(x)?, }; let addrtype = match tokens.next() { None => { - return Err(SdpParserInternalError::Generic("Origin type is missing address type token" - .to_string())) + return Err(SdpParserInternalError::Generic( + "Origin type is missing address type token".to_string(), + )); } Some(x) => parse_addrtype(x)?, }; let unicast_addr = match tokens.next() { None => { - return Err(SdpParserInternalError::Generic("Origin type is missing IP address token" - .to_string())) + return Err(SdpParserInternalError::Generic( + "Origin type is missing IP address token".to_string(), + )); } Some(x) => parse_unicast_addr(x)?, }; if !addrtype.same_protocol(&unicast_addr) { - return Err(SdpParserInternalError::Generic("Origin addrtype does not match address." - .to_string())); + return Err(SdpParserInternalError::Generic( + "Origin addrtype does not match address.".to_string(), + )); } let o = SdpOrigin { username: String::from(username), @@ -290,7 +387,7 @@ fn parse_origin(value: &str) -> Result { session_version, unicast_addr, }; - trace!("origin: {}", o); + trace!("origin: {}", o.to_string()); Ok(SdpType::Origin(o)) } @@ -330,8 +427,9 @@ fn test_origin_addr_type_mismatch() { fn parse_connection(value: &str) -> Result { let cv: Vec<&str> = value.split_whitespace().collect(); if cv.len() != 3 { - return Err(SdpParserInternalError::Generic("connection attribute must have three tokens" - .to_string())); + return Err(SdpParserInternalError::Generic( + "connection attribute must have three tokens".to_string(), + )); } parse_nettype(cv[0])?; let addrtype = parse_addrtype(cv[1])?; @@ -348,8 +446,9 @@ fn parse_connection(value: &str) -> Result { } let addr = parse_unicast_addr(addr_token)?; if !addrtype.same_protocol(&addr) { - return Err(SdpParserInternalError::Generic("connection addrtype does not match address." - .to_string())); + return Err(SdpParserInternalError::Generic( + "connection addrtype does not match address.".to_string(), + )); } let c = SdpConnection { addr, ttl, amount }; trace!("connection: {}", c.addr); @@ -397,8 +496,9 @@ fn connection_addr_type_mismatch() { fn parse_bandwidth(value: &str) -> Result { let bv: Vec<&str> = value.split(':').collect(); if bv.len() != 2 { - return Err(SdpParserInternalError::Generic("bandwidth attribute must have two tokens" - .to_string())); + return Err(SdpParserInternalError::Generic( + "bandwidth attribute must have two tokens".to_string(), + )); } let bandwidth = bv[1].parse::()?; let bw = match bv[0].to_uppercase().as_ref() { @@ -432,8 +532,9 @@ fn bandwidth_unsupported_type() { fn parse_timing(value: &str) -> Result { let tv: Vec<&str> = value.split_whitespace().collect(); if tv.len() != 2 { - return Err(SdpParserInternalError::Generic("timing attribute must have two tokens" - .to_string())); + return Err(SdpParserInternalError::Generic( + "timing attribute must have two tokens".to_string(), + )); } let start = tv[0].parse::()?; let stop = tv[1].parse::()?; @@ -462,36 +563,35 @@ fn test_timing_wrong_amount_of_tokens() { fn parse_sdp_line(line: &str, line_number: usize) -> Result { if line.find('=') == None { return Err(SdpParserError::Line { - error: SdpParserInternalError::Generic("missing = character in line" - .to_string()), - line: line.to_string(), - line_number: line_number, - }); + error: SdpParserInternalError::Generic("missing = character in line".to_string()), + line: line.to_string(), + line_number, + }); } let mut splitted_line = line.splitn(2, '='); let line_type = match splitted_line.next() { None => { return Err(SdpParserError::Line { - error: SdpParserInternalError::Generic("missing type".to_string()), - line: line.to_string(), - line_number: line_number, - }) + error: SdpParserInternalError::Generic("missing type".to_string()), + line: line.to_string(), + line_number, + }); } Some(t) => { let trimmed = t.trim(); if trimmed.len() > 1 { return Err(SdpParserError::Line { - error: SdpParserInternalError::Generic("type too long".to_string()), - line: line.to_string(), - line_number: line_number, - }); + error: SdpParserInternalError::Generic("type too long".to_string()), + line: line.to_string(), + line_number, + }); } if trimmed.is_empty() { return Err(SdpParserError::Line { - error: SdpParserInternalError::Generic("type is empty".to_string()), - line: line.to_string(), - line_number: line_number, - }); + error: SdpParserInternalError::Generic("type is empty".to_string()), + line: line.to_string(), + line_number, + }); } trimmed } @@ -499,66 +599,83 @@ fn parse_sdp_line(line: &str, line_number: usize) -> Result { return Err(SdpParserError::Line { - error: SdpParserInternalError::Generic("missing value".to_string()), - line: line.to_string(), - line_number: line_number, - }) + error: SdpParserInternalError::Generic("missing value".to_string()), + line: line.to_string(), + line_number, + }); } Some(v) => { let trimmed = v.trim(); if trimmed.is_empty() { return Err(SdpParserError::Line { - error: SdpParserInternalError::Generic("value is empty".to_string()), - line: line.to_string(), - line_number: line_number, - }); + error: SdpParserInternalError::Generic("value is empty".to_string()), + line: line.to_string(), + line_number, + }); } trimmed } }; match line_type.to_lowercase().as_ref() { - "a" => parse_attribute(line_value), - "b" => parse_bandwidth(line_value), - "c" => parse_connection(line_value), - "e" => parse_email(line_value), - "i" => parse_information(line_value), - "k" => parse_key(line_value), - "m" => parse_media(line_value), - "o" => parse_origin(line_value), - "p" => parse_phone(line_value), - "r" => parse_repeat(line_value), - "s" => parse_session(line_value), - "t" => parse_timing(line_value), - "u" => parse_uri(line_value), - "v" => parse_version(line_value), - "z" => parse_zone(line_value), - _ => Err(SdpParserInternalError::Generic("unknown sdp type".to_string())), - } - .map(|sdp_type| { - SdpLine { - line_number, - sdp_type, - } - }) - .map_err(|e| match e { - SdpParserInternalError::Generic(..) | - SdpParserInternalError::Integer(..) | - SdpParserInternalError::Float(..) | - SdpParserInternalError::Address(..) => { - SdpParserError::Line { - error: e, - line: line.to_string(), - line_number: line_number, - } - } - SdpParserInternalError::Unsupported(..) => { - SdpParserError::Unsupported { - error: e, - line: line.to_string(), - line_number: line_number, - } - } - }) + "a" => parse_attribute(line_value), + "b" => parse_bandwidth(line_value), + "c" => parse_connection(line_value), + "e" => Err(SdpParserInternalError::Generic(format!( + "unsupported type email: {}", + line_value + ))), + "i" => Err(SdpParserInternalError::Generic(format!( + "unsupported type information: {}", + line_value + ))), + "k" => Err(SdpParserInternalError::Generic(format!( + "unsupported insecure key exchange: {}", + line_value + ))), + "m" => parse_media(line_value), + "o" => parse_origin(line_value), + "p" => Err(SdpParserInternalError::Generic(format!( + "unsupported type phone: {}", + line_value + ))), + "r" => Err(SdpParserInternalError::Generic(format!( + "unsupported type repeat: {}", + line_value + ))), + "s" => parse_session(line_value), + "t" => parse_timing(line_value), + "u" => Err(SdpParserInternalError::Generic(format!( + "unsupported type uri: {}", + line_value + ))), + "v" => parse_version(line_value), + "z" => Err(SdpParserInternalError::Generic(format!( + "unsupported type zone: {}", + line_value + ))), + _ => Err(SdpParserInternalError::Generic( + "unknown sdp type".to_string(), + )), + } + .map(|sdp_type| SdpLine { + line_number, + sdp_type, + }) + .map_err(|e| match e { + SdpParserInternalError::Generic(..) + | SdpParserInternalError::Integer(..) + | SdpParserInternalError::Float(..) + | SdpParserInternalError::Address(..) => SdpParserError::Line { + error: e, + line: line.to_string(), + line_number, + }, + SdpParserInternalError::Unsupported(..) => SdpParserError::Unsupported { + error: e, + line: line.to_string(), + line_number, + }, + }) } #[test] @@ -572,6 +689,17 @@ fn test_parse_sdp_line_empty_line() { assert!(parse_sdp_line("", 0).is_err()); } +#[test] +fn test_parse_sdp_line_unsupported_types() { + assert!(parse_sdp_line("e=foobar", 0).is_err()); + assert!(parse_sdp_line("i=foobar", 0).is_err()); + assert!(parse_sdp_line("k=foobar", 0).is_err()); + assert!(parse_sdp_line("p=foobar", 0).is_err()); + assert!(parse_sdp_line("r=foobar", 0).is_err()); + assert!(parse_sdp_line("u=foobar", 0).is_err()); + assert!(parse_sdp_line("z=foobar", 0).is_err()); +} + #[test] fn test_parse_sdp_line_unknown_key() { assert!(parse_sdp_line("y=foobar", 0).is_err()); @@ -616,18 +744,11 @@ fn sanity_check_sdp_session(session: &SdpSession) -> Result<(), SdpParserError> line_number: 0, }; - if !session.timing.is_some() { + if session.timing.is_none() { return Err(SdpParserError::Sequence { - message: "Missing timing type".to_string(), - line_number: 0, - }); - } - - if session.media.is_empty() { - return Err(SdpParserError::Sequence { - message: "Missing media section".to_string(), - line_number: 0, - }); + message: "Missing timing type".to_string(), + line_number: 0, + }); } if session.get_connection().is_none() { @@ -635,7 +756,8 @@ fn sanity_check_sdp_session(session: &SdpSession) -> Result<(), SdpParserError> if msection.get_connection().is_none() { return Err(SdpParserError::Sequence { message: "Each media section must define a connection - if it is not defined on session level".to_string(), + if it is not defined on session level" + .to_string(), line_number: 0, }); } @@ -647,28 +769,32 @@ fn sanity_check_sdp_session(session: &SdpSession) -> Result<(), SdpParserError> for msection in &session.media { if msection.get_attribute(SdpAttributeType::Extmap).is_some() { return Err(SdpParserError::Sequence { - message: "Extmap can't be define at session and media level" - .to_string(), - line_number: 0, - }); + message: "Extmap can't be define at session and media level".to_string(), + line_number: 0, + }); } } } for msection in &session.media { if msection.get_attribute(SdpAttributeType::Sendonly).is_some() { - if let Some(&SdpAttribute::Simulcast(ref x)) = msection.get_attribute(SdpAttributeType::Simulcast) { - if x.receive.len() > 0 { + if let Some(&SdpAttribute::Simulcast(ref x)) = + msection.get_attribute(SdpAttributeType::Simulcast) + { + if !x.receive.is_empty() { return Err(SdpParserError::Sequence { - message: "Simulcast can't define receive parameters for sendonly".to_string(), + message: "Simulcast can't define receive parameters for sendonly" + .to_string(), line_number: 0, }); } } } if msection.get_attribute(SdpAttributeType::Recvonly).is_some() { - if let Some(&SdpAttribute::Simulcast(ref x)) = msection.get_attribute(SdpAttributeType::Simulcast) { - if x.send.len() > 0 { + if let Some(&SdpAttribute::Simulcast(ref x)) = + msection.get_attribute(SdpAttributeType::Simulcast) + { + if !x.send.is_empty() { return Err(SdpParserError::Sequence { message: "Simulcast can't define send parameters for recvonly".to_string(), line_number: 0, @@ -677,35 +803,38 @@ fn sanity_check_sdp_session(session: &SdpSession) -> Result<(), SdpParserError> } } - let rids:Vec<&SdpAttributeRid> = msection.get_attributes().iter().filter_map(|attr| { - match attr { - &SdpAttribute::Rid(ref rid) => Some(rid), - _ => None, - } - }).collect(); - let recv_rids:Vec<&str> = rids.iter().filter_map(|rid| { - match rid.direction { - SdpSingleDirection::Recv => Some(rid.id.as_str()), - _ => None, - } - }).collect(); - let send_rids:Vec<&str> = rids.iter().filter_map(|rid| { - match rid.direction { - SdpSingleDirection::Send => Some(rid.id.as_str()), - _ => None, - } - }).collect(); - + let rids: Vec<&SdpAttributeRid> = msection + .get_attributes() + .iter() + .filter_map(|attr| match *attr { + SdpAttribute::Rid(ref rid) => Some(rid), + _ => None, + }) + .collect(); + let recv_rids: Vec<&str> = rids + .iter() + .filter_map(|rid| match rid.direction { + SdpSingleDirection::Recv => Some(rid.id.as_str()), + _ => None, + }) + .collect(); + let send_rids: Vec<&str> = rids + .iter() + .filter_map(|rid| match rid.direction { + SdpSingleDirection::Send => Some(rid.id.as_str()), + _ => None, + }) + .collect(); for rid_format in rids.iter().flat_map(|rid| &rid.formats) { - match msection.get_formats() { - &SdpFormatList::Integers(ref int_fmt) => { - if !int_fmt.contains(&(*rid_format as u32)) { + match *msection.get_formats() { + SdpFormatList::Integers(ref int_fmt) => { + if !int_fmt.contains(&(u32::from(*rid_format))) { return Err(make_error("Rid pts must be declared in the media section")); } - }, - &SdpFormatList::Strings(ref str_fmt) => { - if !str_fmt.contains(&rid_format.to_string()) { + } + SdpFormatList::Strings(ref str_fmt) => { + if !str_fmt.contains(&rid_format.to_string()) { return Err(make_error("Rid pts must be declared in the media section")); } } @@ -713,17 +842,21 @@ fn sanity_check_sdp_session(session: &SdpSession) -> Result<(), SdpParserError> } if let Some(&SdpAttribute::Simulcast(ref simulcast)) = - msection.get_attribute(SdpAttributeType::Simulcast) { - let check_defined_rids = |simulcast_version_list: &Vec, - rid_ids: &[&str]| -> Result<(),SdpParserError> { - for simulcast_rid in simulcast_version_list.iter().flat_map(|x| &x.ids) { - if !rid_ids.contains(&simulcast_rid.id.as_str()) { - return Err(make_error( - "Simulcast RIDs must be defined in any rid attribute")); + msection.get_attribute(SdpAttributeType::Simulcast) + { + let check_defined_rids = + |simulcast_version_list: &Vec, + rid_ids: &[&str]| + -> Result<(), SdpParserError> { + for simulcast_rid in simulcast_version_list.iter().flat_map(|x| &x.ids) { + if !rid_ids.contains(&simulcast_rid.id.as_str()) { + return Err(make_error( + "Simulcast RIDs must be defined in any rid attribute", + )); + } } - } - Ok(()) - }; + Ok(()) + }; check_defined_rids(&simulcast.receive, &recv_rids)?; check_defined_rids(&simulcast.send, &send_rids)?; @@ -765,7 +898,7 @@ fn test_sanity_check_sdp_session_timing() { assert!(sanity_check_sdp_session(&sdp_session).is_err()); let t = SdpTiming { start: 0, stop: 0 }; - sdp_session.set_timing(&t); + sdp_session.set_timing(t); assert!(sanity_check_sdp_session(&sdp_session).is_ok()); } @@ -774,9 +907,9 @@ fn test_sanity_check_sdp_session_timing() { fn test_sanity_check_sdp_session_media() { let mut sdp_session = create_dummy_sdp_session(); let t = SdpTiming { start: 0, stop: 0 }; - sdp_session.set_timing(&t); + sdp_session.set_timing(t); - assert!(sanity_check_sdp_session(&sdp_session).is_err()); + assert!(sanity_check_sdp_session(&sdp_session).is_ok()); sdp_session.extend_media(vec![create_dummy_media_section()]); @@ -787,10 +920,11 @@ fn test_sanity_check_sdp_session_media() { fn test_sanity_check_sdp_session_extmap() { let mut sdp_session = create_dummy_sdp_session(); let t = SdpTiming { start: 0, stop: 0 }; - sdp_session.set_timing(&t); + sdp_session.set_timing(t); sdp_session.extend_media(vec![create_dummy_media_section()]); - let attribute = parse_attribute("extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time",); + let attribute = + parse_attribute("extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"); assert!(attribute.is_ok()); let extmap; if let SdpType::Attribute(a) = attribute.unwrap() { @@ -798,13 +932,16 @@ fn test_sanity_check_sdp_session_extmap() { } else { panic!("SdpType is not Attribute"); } - let ret = sdp_session.add_attribute(&extmap); + let ret = sdp_session.add_attribute(extmap); assert!(ret.is_ok()); - assert!(sdp_session.get_attribute(SdpAttributeType::Extmap).is_some()); + assert!(sdp_session + .get_attribute(SdpAttributeType::Extmap) + .is_some()); assert!(sanity_check_sdp_session(&sdp_session).is_ok()); - let mattribute = parse_attribute("extmap:1/sendonly urn:ietf:params:rtp-hdrext:ssrc-audio-level",); + let mattribute = + parse_attribute("extmap:1/sendonly urn:ietf:params:rtp-hdrext:ssrc-audio-level"); assert!(mattribute.is_ok()); let mextmap; if let SdpType::Attribute(ma) = mattribute.unwrap() { @@ -813,8 +950,10 @@ fn test_sanity_check_sdp_session_extmap() { panic!("SdpType is not Attribute"); } let mut second_media = create_dummy_media_section(); - assert!(second_media.add_attribute(&mextmap).is_ok()); - assert!(second_media.get_attribute(SdpAttributeType::Extmap).is_some()); + assert!(second_media.add_attribute(mextmap).is_ok()); + assert!(second_media + .get_attribute(SdpAttributeType::Extmap) + .is_some()); sdp_session.extend_media(vec![second_media]); assert!(sdp_session.media.len() == 2); @@ -830,87 +969,64 @@ fn test_sanity_check_sdp_session_extmap() { fn test_sanity_check_sdp_session_simulcast() { let mut sdp_session = create_dummy_sdp_session(); let t = SdpTiming { start: 0, stop: 0 }; - sdp_session.set_timing(&t); + sdp_session.set_timing(t); sdp_session.extend_media(vec![create_dummy_media_section()]); assert!(sanity_check_sdp_session(&sdp_session).is_ok()); } // TODO add unit tests -fn parse_sdp_vector(lines: &[SdpLine]) -> Result { - if lines.len() < 5 { +fn parse_sdp_vector(lines: &mut Vec) -> Result { + if lines.len() < 4 { return Err(SdpParserError::Sequence { - message: "SDP neeeds at least 5 lines".to_string(), - line_number: 0, - }); + message: "SDP neeeds at least 4 lines".to_string(), + line_number: 0, + }); } - // TODO are these mataches really the only way to verify the types? - let version: u64 = match lines[0].sdp_type { + let version = match lines.remove(0).sdp_type { SdpType::Version(v) => v, _ => { return Err(SdpParserError::Sequence { - message: "first line needs to be version number".to_string(), - line_number: lines[0].line_number, - }) + message: "first line needs to be version number".to_string(), + line_number: 0, + }); } }; - let origin: SdpOrigin = match lines[1].sdp_type { - SdpType::Origin(ref v) => v.clone(), + let origin = match lines.remove(0).sdp_type { + SdpType::Origin(v) => v, _ => { return Err(SdpParserError::Sequence { - message: "second line needs to be origin".to_string(), - line_number: lines[1].line_number, - }) + message: "second line needs to be origin".to_string(), + line_number: 1, + }); } }; - let session: String = match lines[2].sdp_type { - SdpType::Session(ref v) => v.clone(), + let session = match lines.remove(0).sdp_type { + SdpType::Session(v) => v, _ => { return Err(SdpParserError::Sequence { - message: "third line needs to be session".to_string(), - line_number: lines[2].line_number, - }) + message: "third line needs to be session".to_string(), + line_number: 2, + }); } }; let mut sdp_session = SdpSession::new(version, origin, session); - for (index, line) in lines.iter().enumerate().skip(3) { - match line.sdp_type { - SdpType::Attribute(ref a) => { - sdp_session - .add_attribute(a) - .map_err(|e: SdpParserInternalError| { - SdpParserError::Sequence { - message: format!("{}", e), - line_number: line.line_number, - } - })? - } - SdpType::Bandwidth(ref b) => sdp_session.add_bandwidth(b), - SdpType::Timing(ref t) => sdp_session.set_timing(t), - SdpType::Connection(ref c) => sdp_session.set_connection(c), - SdpType::Media(_) => sdp_session.extend_media(parse_media_vector(&lines[index..])?), - SdpType::Origin(_) | - SdpType::Session(_) | - SdpType::Version(_) => { - return Err(SdpParserError::Sequence { - message: "version, origin or session at wrong level".to_string(), - line_number: line.line_number, - }) - } - // the line parsers throw unsupported errors for these already - SdpType::Email(_) | - SdpType::Information(_) | - SdpType::Key(_) | - SdpType::Phone(_) | - SdpType::Repeat(_) | - SdpType::Uri(_) | - SdpType::Zone(_) => (), - }; - if !sdp_session.media.is_empty() { - break; - }; - } + + let _media_pos = lines.iter().position(|ref l| match l.sdp_type { + SdpType::Media(_) => true, + _ => false, + }); + + match _media_pos { + Some(p) => { + let mut media: Vec<_> = lines.drain(p..).collect(); + sdp_session.parse_session_vector(lines)?; + sdp_session.extend_media(parse_media_vector(&mut media)?); + } + None => sdp_session.parse_session_vector(lines)?, + }; + sanity_check_sdp_session(&sdp_session)?; Ok(sdp_session) } @@ -918,18 +1034,17 @@ fn parse_sdp_vector(lines: &[SdpLine]) -> Result { pub fn parse_sdp(sdp: &str, fail_on_warning: bool) -> Result { if sdp.is_empty() { return Err(SdpParserError::Line { - error: SdpParserInternalError::Generic("empty SDP".to_string()), - line: sdp.to_string(), - line_number: 0, - }); + error: SdpParserInternalError::Generic("empty SDP".to_string()), + line: sdp.to_string(), + line_number: 0, + }); } - if sdp.len() < 62 { + if sdp.len() < 40 { return Err(SdpParserError::Line { - error: SdpParserInternalError::Generic("string to short to be valid SDP" - .to_string()), - line: sdp.to_string(), - line_number: 0, - }); + error: SdpParserInternalError::Generic("string too short to be valid SDP".to_string()), + line: sdp.to_string(), + line_number: 0, + }); } let lines = sdp.lines(); let mut errors: Vec = Vec::new(); @@ -946,45 +1061,41 @@ pub fn parse_sdp(sdp: &str, fail_on_warning: bool) -> Result { match e { - // FIXME is this really a good way to accomplish this? + // TODO is this really a good way to accomplish this? SdpParserError::Line { error, line, line_number, - } => { - errors.push(SdpParserError::Line { - error, - line, - line_number, - }) - } + } => errors.push(SdpParserError::Line { + error, + line, + line_number, + }), SdpParserError::Unsupported { error, line, line_number, } => { warnings.push(SdpParserError::Unsupported { - error, - line, - line_number, - }); + error, + line, + line_number, + }); } SdpParserError::Sequence { message, line_number, - } => { - errors.push(SdpParserError::Sequence { - message, - line_number, - }) - } + } => errors.push(SdpParserError::Sequence { + message, + line_number, + }), } } }; } - if fail_on_warning && (warnings.len() > 0) { - return Err(warnings[0].clone()); + if fail_on_warning && (!warnings.is_empty()) { + return Err(warnings.remove(0)); } // We just return the last of the errors here @@ -992,7 +1103,7 @@ pub fn parse_sdp(sdp: &str, fail_on_warning: bool) -> Result String { + format!( + "{media_line} {port}{port_count} {protocol} {formats}", + media_line = self.media.to_string(), + port_count = maybe_print_param("/", self.port_count, 0), + port = self.port.to_string(), + protocol = self.proto.to_string(), + formats = self.formats.to_string() + ) + } +} + +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpMediaValue { Audio, Video, Application, } -impl fmt::Display for SdpMediaValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let printable = match *self { - SdpMediaValue::Audio => "Audio", - SdpMediaValue::Video => "Video", - SdpMediaValue::Application => "Application", - }; - write!(f, "{}", printable) +impl ToString for SdpMediaValue { + fn to_string(&self) -> String { + match *self { + SdpMediaValue::Audio => "audio", + SdpMediaValue::Video => "video", + SdpMediaValue::Application => "application", + } + .to_string() } } -#[derive(Clone,Debug,PartialEq)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpProtocolValue { + RtpAvp, + RtpAvpf, + RtpSavp, RtpSavpf, - UdpTlsRtpSavp, TcpDtlsRtpSavp, - UdpTlsRtpSavpf, - TcpTlsRtpSavpf, TcpDtlsRtpSavpf, + UdpTlsRtpSavp, + UdpTlsRtpSavpf, DtlsSctp, UdpDtlsSctp, TcpDtlsSctp, + TcpTlsRtpSavpf, /* not standardized - to be removed */ } -impl fmt::Display for SdpProtocolValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let printable = match *self { - SdpProtocolValue::RtpSavpf => "Rtp/Savpf", - SdpProtocolValue::UdpTlsRtpSavp => "Udp/Tls/Rtp/Savp", - SdpProtocolValue::TcpDtlsRtpSavp => "Tcp/Dtls/Rtp/Savp", - SdpProtocolValue::UdpTlsRtpSavpf => "Udp/Tls/Rtp/Savpf", - SdpProtocolValue::TcpTlsRtpSavpf => "Tcp/Tls/Rtp/Savpf", - SdpProtocolValue::TcpDtlsRtpSavpf => "Tcp/Dtls/Rtp/Savpf", - SdpProtocolValue::DtlsSctp => "Dtls/Sctp", - SdpProtocolValue::UdpDtlsSctp => "Udp/Dtls/Sctp", - SdpProtocolValue::TcpDtlsSctp => "Tcp/Dtls/Sctp", - }; - write!(f, "{}", printable) +impl ToString for SdpProtocolValue { + fn to_string(&self) -> String { + match *self { + SdpProtocolValue::RtpAvp => "RTP/AVP", + SdpProtocolValue::RtpAvpf => "RTP/AVPF", + SdpProtocolValue::RtpSavp => "RTP/SAVP", + SdpProtocolValue::RtpSavpf => "RTP/SAVPF", + SdpProtocolValue::TcpDtlsRtpSavp => "TCP/DTLS/RTP/SAVP", + SdpProtocolValue::TcpDtlsRtpSavpf => "TCP/DTLS/RTP/SAVPF", + SdpProtocolValue::UdpTlsRtpSavp => "UDP/TLS/RTP/SAVP", + SdpProtocolValue::UdpTlsRtpSavpf => "UDP/TLS/RTP/SAVPF", + SdpProtocolValue::DtlsSctp => "DTLS/SCTP", + SdpProtocolValue::UdpDtlsSctp => "UDP/DTLS/SCTP", + SdpProtocolValue::TcpDtlsSctp => "TCP/DTLS/SCTP", + SdpProtocolValue::TcpTlsRtpSavpf => "TCP/TLS/RTP/SAVPF", + } + .to_string() } } -#[derive(Clone)] -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpFormatList { Integers(Vec), Strings(Vec), } -impl fmt::Display for SdpFormatList { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl ToString for SdpFormatList { + fn to_string(&self) -> String { match *self { - SdpFormatList::Integers(ref x) => write!(f, "{:?}", x), - SdpFormatList::Strings(ref x) => write!(f, "{:?}", x), + SdpFormatList::Integers(ref x) => maybe_vector_to_string!("{}", x, " "), + SdpFormatList::Strings(ref x) => x.join(" "), } } } -#[cfg_attr(feature="serialize", derive(Serialize))] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpMedia { media: SdpMediaLine, connection: Option, @@ -128,109 +146,136 @@ impl SdpMedia { &self.bandwidth } - pub fn add_bandwidth(&mut self, bw: &SdpBandwidth) { - self.bandwidth.push(bw.clone()) + pub fn add_bandwidth(&mut self, bw: SdpBandwidth) { + self.bandwidth.push(bw) } pub fn get_attributes(&self) -> &Vec { &self.attribute } - pub fn add_attribute(&mut self, attr: &SdpAttribute) -> Result<(), SdpParserInternalError> { + pub fn add_attribute(&mut self, attr: SdpAttribute) -> Result<(), SdpParserInternalError> { if !attr.allowed_at_media_level() { - return Err(SdpParserInternalError::Generic(format!("{} not allowed at media level", - attr))); + return Err(SdpParserInternalError::Generic(format!( + "{} not allowed at media level", + attr.to_string() + ))); } - Ok(self.attribute.push(attr.clone())) + self.attribute.push(attr); + Ok(()) } pub fn get_attribute(&self, t: SdpAttributeType) -> Option<&SdpAttribute> { - self.attribute.iter().filter(|a| SdpAttributeType::from(*a) == t).next() + self.attribute + .iter() + .find(|a| SdpAttributeType::from(*a) == t) } pub fn remove_attribute(&mut self, t: SdpAttributeType) { self.attribute.retain(|a| SdpAttributeType::from(a) != t); } - pub fn set_attribute(&mut self, attr: &SdpAttribute) -> Result<(), SdpParserInternalError> { - self.remove_attribute(SdpAttributeType::from(attr)); + pub fn set_attribute(&mut self, attr: SdpAttribute) -> Result<(), SdpParserInternalError> { + self.remove_attribute(SdpAttributeType::from(&attr)); self.add_attribute(attr) } pub fn remove_codecs(&mut self) { - match self.media.formats{ + match self.media.formats { SdpFormatList::Integers(_) => self.media.formats = SdpFormatList::Integers(Vec::new()), SdpFormatList::Strings(_) => self.media.formats = SdpFormatList::Strings(Vec::new()), } - self.attribute.retain({|x| - match x { - &SdpAttribute::Rtpmap(_) | - &SdpAttribute::Fmtp(_) | - &SdpAttribute::Rtcpfb(_) | - &SdpAttribute::Sctpmap(_) => false, - _ => true + self.attribute.retain({ + |x| match *x { + SdpAttribute::Rtpmap(_) + | SdpAttribute::Fmtp(_) + | SdpAttribute::Rtcpfb(_) + | SdpAttribute::Sctpmap(_) => false, + _ => true, } }); } - pub fn add_codec(&mut self, rtpmap: SdpAttributeRtpmap) -> Result<(),SdpParserInternalError> { - match self.media.formats { - SdpFormatList::Integers(ref mut x) => x.push(rtpmap.payload_type as u32), - SdpFormatList::Strings(ref mut x) => x.push(rtpmap.payload_type.to_string()), - } + pub fn add_codec(&mut self, rtpmap: SdpAttributeRtpmap) -> Result<(), SdpParserInternalError> { + match self.media.formats { + SdpFormatList::Integers(ref mut x) => x.push(u32::from(rtpmap.payload_type)), + SdpFormatList::Strings(ref mut x) => x.push(rtpmap.payload_type.to_string()), + } - self.add_attribute(&SdpAttribute::Rtpmap(rtpmap))?; + self.add_attribute(SdpAttribute::Rtpmap(rtpmap))?; Ok(()) } pub fn get_attributes_of_type(&self, t: SdpAttributeType) -> Vec<&SdpAttribute> { - self.attribute.iter().filter(|a| SdpAttributeType::from(*a) == t).collect() + self.attribute + .iter() + .filter(|a| SdpAttributeType::from(*a) == t) + .collect() } pub fn get_connection(&self) -> &Option { &self.connection } - pub fn set_connection(&mut self, c: &SdpConnection) -> Result<(), SdpParserInternalError> { + pub fn set_connection(&mut self, c: SdpConnection) -> Result<(), SdpParserInternalError> { if self.connection.is_some() { - return Err(SdpParserInternalError::Generic("connection type already exists at this media level" - .to_string(), - )); + return Err(SdpParserInternalError::Generic( + "connection type already exists at this media level".to_string(), + )); } - Ok(self.connection = Some(c.clone())) + self.connection = Some(c); + Ok(()) } - pub fn add_datachannel(&mut self, name: String, port: u16, streams: u16, msg_size:u32) - -> Result<(),SdpParserInternalError> { - // Only one allowed, for now. This may change as the specs (and deployments) evolve. + pub fn add_datachannel( + &mut self, + name: String, + port: u16, + streams: u16, + msg_size: u32, + ) -> Result<(), SdpParserInternalError> { + // Only one allowed, for now. This may change as the specs (and deployments) evolve. match self.media.proto { - SdpProtocolValue::UdpDtlsSctp | - SdpProtocolValue::TcpDtlsSctp => { + SdpProtocolValue::UdpDtlsSctp | SdpProtocolValue::TcpDtlsSctp => { // new data channel format according to draft 21 self.media.formats = SdpFormatList::Strings(vec![name]); - self.set_attribute(&SdpAttribute::SctpPort(port as u64))?; + self.set_attribute(SdpAttribute::SctpPort(u64::from(port)))?; } _ => { // old data channels format according to draft 05 - self.media.formats = SdpFormatList::Integers(vec![port as u32]); - self.set_attribute(&SdpAttribute::Sctpmap(SdpAttributeSctpmap { + self.media.formats = SdpFormatList::Integers(vec![u32::from(port)]); + self.set_attribute(SdpAttribute::Sctpmap(SdpAttributeSctpmap { port, - channels: streams as u32, + channels: u32::from(streams), }))?; } } if msg_size > 0 { - self.set_attribute(&SdpAttribute::MaxMessageSize(msg_size as u64))?; + self.set_attribute(SdpAttribute::MaxMessageSize(u64::from(msg_size)))?; } Ok(()) } } +impl ToString for SdpMedia { + fn to_string(&self) -> String { + format!( + "m={media_line}\r\n\ + {bandwidth}\ + {connection}\ + {attributes}", + media_line = self.media.to_string(), + connection = option_to_string!("c={}\r\n", self.connection), + bandwidth = maybe_vector_to_string!("b={}\r\n", self.bandwidth, "\r\nb="), + attributes = maybe_vector_to_string!("a={}\r\n", self.attribute, "\r\na=") + ) + } +} + #[cfg(test)] -#[cfg_attr(feature="serialize", derive(Serialize))] pub fn create_dummy_media_section() -> SdpMedia { let media_line = SdpMediaLine { media: SdpMediaValue::Audio, @@ -244,14 +289,16 @@ pub fn create_dummy_media_section() -> SdpMedia { fn parse_media_token(value: &str) -> Result { Ok(match value.to_lowercase().as_ref() { - "audio" => SdpMediaValue::Audio, - "video" => SdpMediaValue::Video, - "application" => SdpMediaValue::Application, - _ => { - return Err(SdpParserInternalError::Unsupported(format!("unsupported media value: {}", - value))) - } - }) + "audio" => SdpMediaValue::Audio, + "video" => SdpMediaValue::Video, + "application" => SdpMediaValue::Application, + _ => { + return Err(SdpParserInternalError::Unsupported(format!( + "unsupported media value: {}", + value + ))); + } + }) } #[test] @@ -270,44 +317,66 @@ fn test_parse_media_token() { assert!(parse_media_token("foobar").is_err()); } - fn parse_protocol_token(value: &str) -> Result { Ok(match value.to_uppercase().as_ref() { - "RTP/SAVPF" => SdpProtocolValue::RtpSavpf, - "UDP/TLS/RTP/SAVP" => SdpProtocolValue::UdpTlsRtpSavp, - "TCP/DTLS/RTP/SAVP" => SdpProtocolValue::TcpDtlsRtpSavp, - "TCP/TLS/RTP/SAVPF" => SdpProtocolValue::TcpTlsRtpSavpf, - "TCP/DTLS/RTP/SAVPF" => SdpProtocolValue::TcpDtlsRtpSavpf, - "DTLS/SCTP" => SdpProtocolValue::DtlsSctp, - "UDP/DTLS/SCTP" => SdpProtocolValue::UdpDtlsSctp, - "TCP/DTLS/SCTP" => SdpProtocolValue::TcpDtlsSctp, - _ => { - return Err(SdpParserInternalError::Unsupported(format!("unsupported protocol value: {}", - value))) - } - }) + "RTP/AVP" => SdpProtocolValue::RtpAvp, + "RTP/AVPF" => SdpProtocolValue::RtpAvpf, + "RTP/SAVP" => SdpProtocolValue::RtpSavp, + "RTP/SAVPF" => SdpProtocolValue::RtpSavpf, + "TCP/DTLS/RTP/SAVP" => SdpProtocolValue::TcpDtlsRtpSavp, + "TCP/DTLS/RTP/SAVPF" => SdpProtocolValue::TcpDtlsRtpSavpf, + "UDP/TLS/RTP/SAVP" => SdpProtocolValue::UdpTlsRtpSavp, + "UDP/TLS/RTP/SAVPF" => SdpProtocolValue::UdpTlsRtpSavpf, + "DTLS/SCTP" => SdpProtocolValue::DtlsSctp, + "UDP/DTLS/SCTP" => SdpProtocolValue::UdpDtlsSctp, + "TCP/DTLS/SCTP" => SdpProtocolValue::TcpDtlsSctp, + /* to be removed */ + "TCP/TLS/RTP/SAVPF" => SdpProtocolValue::TcpTlsRtpSavpf, + _ => { + return Err(SdpParserInternalError::Unsupported(format!( + "unsupported protocol value: {}", + value + ))); + } + }) } #[test] -fn test_parse_protocol_token() { +fn test_parse_protocol_rtp_token() { + let rtps = parse_protocol_token("rtp/avp"); + assert!(rtps.is_ok()); + assert_eq!(rtps.unwrap(), SdpProtocolValue::RtpAvp); + let rtps = parse_protocol_token("rtp/avpf"); + assert!(rtps.is_ok()); + assert_eq!(rtps.unwrap(), SdpProtocolValue::RtpAvpf); + let rtps = parse_protocol_token("rtp/savp"); + assert!(rtps.is_ok()); + assert_eq!(rtps.unwrap(), SdpProtocolValue::RtpSavp); let rtps = parse_protocol_token("rtp/savpf"); assert!(rtps.is_ok()); assert_eq!(rtps.unwrap(), SdpProtocolValue::RtpSavpf); let udps = parse_protocol_token("udp/tls/rtp/savp"); assert!(udps.is_ok()); assert_eq!(udps.unwrap(), SdpProtocolValue::UdpTlsRtpSavp); - let tcps = parse_protocol_token("tcp/dtls/rtp/savp"); - assert!(tcps.is_ok()); - assert_eq!(tcps.unwrap(), SdpProtocolValue::TcpDtlsRtpSavp); let udps = parse_protocol_token("udp/tls/rtp/savpf"); assert!(udps.is_ok()); assert_eq!(udps.unwrap(), SdpProtocolValue::UdpTlsRtpSavpf); + let tcps = parse_protocol_token("TCP/dtls/rtp/savp"); + assert!(tcps.is_ok()); + assert_eq!(tcps.unwrap(), SdpProtocolValue::TcpDtlsRtpSavp); + let tcps = parse_protocol_token("TCP/dtls/rtp/savpf"); + assert!(tcps.is_ok()); + assert_eq!(tcps.unwrap(), SdpProtocolValue::TcpDtlsRtpSavpf); let tcps = parse_protocol_token("TCP/tls/rtp/savpf"); assert!(tcps.is_ok()); assert_eq!(tcps.unwrap(), SdpProtocolValue::TcpTlsRtpSavpf); - let tcps = parse_protocol_token("TCP/DtlS/rTp/sAVpf"); - assert!(tcps.is_ok()); - assert_eq!(tcps.unwrap(), SdpProtocolValue::TcpDtlsRtpSavpf); + + assert!(parse_protocol_token("").is_err()); + assert!(parse_protocol_token("foobar").is_err()); +} + +#[test] +fn test_parse_protocol_sctp_token() { let dtls = parse_protocol_token("dtLs/ScTP"); assert!(dtls.is_ok()); assert_eq!(dtls.unwrap(), SdpProtocolValue::DtlsSctp); @@ -317,25 +386,29 @@ fn test_parse_protocol_token() { let tsctp = parse_protocol_token("tcp/dtls/SCTP"); assert!(tsctp.is_ok()); assert_eq!(tsctp.unwrap(), SdpProtocolValue::TcpDtlsSctp); - - assert!(parse_protocol_token("").is_err()); - assert!(parse_protocol_token("foobar").is_err()); } pub fn parse_media(value: &str) -> Result { let mv: Vec<&str> = value.split_whitespace().collect(); if mv.len() < 4 { - return Err(SdpParserInternalError::Generic("media attribute must have at least four tokens" - .to_string())); + return Err(SdpParserInternalError::Generic( + "media attribute must have at least four tokens".to_string(), + )); } let media = parse_media_token(mv[0])?; let mut ptokens = mv[1].split('/'); let port = match ptokens.next() { - None => return Err(SdpParserInternalError::Generic("missing port token".to_string())), + None => { + return Err(SdpParserInternalError::Generic( + "missing port token".to_string(), + )); + } Some(p) => p.parse::()?, }; if port > 65535 { - return Err(SdpParserInternalError::Generic("media port token is too big".to_string())); + return Err(SdpParserInternalError::Generic( + "media port token is too big".to_string(), + )); } let port_count = match ptokens.next() { None => 0, @@ -377,20 +450,41 @@ pub fn parse_media(value: &str) -> Result { proto, formats, }; - trace!("media: {}, {}, {}, {}", m.media, m.port, m.proto, m.formats); + trace!( + "media: {}, {}, {}, {}", + m.media.to_string(), + m.port.to_string(), + m.proto.to_string(), + m.formats.to_string() + ); Ok(SdpType::Media(m)) } +#[cfg(test)] +fn check_parse(media_line_str: &str) -> SdpMediaLine { + if let Ok(SdpType::Media(media_line)) = parse_media(media_line_str) { + media_line + } else { + unreachable!(); + } +} + +#[cfg(test)] +fn check_parse_and_serialize(media_line_str: &str) { + let parsed = check_parse(media_line_str); + assert_eq!(parsed.to_string(), media_line_str.to_string()); +} + #[test] fn test_media_works() { - assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 109").is_ok()); - assert!(parse_media("video 9 UDP/TLS/RTP/SAVPF 126").is_ok()); - assert!(parse_media("application 9 DTLS/SCTP 5000").is_ok()); - assert!(parse_media("application 9 UDP/DTLS/SCTP webrtc-datachannel").is_ok()); + check_parse_and_serialize("audio 9 UDP/TLS/RTP/SAVPF 109"); + check_parse_and_serialize("video 9 UDP/TLS/RTP/SAVPF 126"); + check_parse_and_serialize("application 9 DTLS/SCTP 5000"); + check_parse_and_serialize("application 9 UDP/DTLS/SCTP webrtc-datachannel"); - assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8").is_ok()); - assert!(parse_media("audio 0 UDP/TLS/RTP/SAVPF 8").is_ok()); - assert!(parse_media("audio 9/2 UDP/TLS/RTP/SAVPF 8").is_ok()); + check_parse_and_serialize("audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8"); + check_parse_and_serialize("audio 0 UDP/TLS/RTP/SAVPF 8"); + check_parse_and_serialize("audio 9/2 UDP/TLS/RTP/SAVPF 8"); } #[test] @@ -423,84 +517,68 @@ fn test_media_invalid_payload() { assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 300").is_err()); } -pub fn parse_media_vector(lines: &[SdpLine]) -> Result, SdpParserError> { +pub fn parse_media_vector(lines: &mut Vec) -> Result, SdpParserError> { let mut media_sections: Vec = Vec::new(); - let mut sdp_media = match lines[0].sdp_type { - SdpType::Media(ref v) => SdpMedia::new(v.clone()), + + let media_line = lines.remove(0); + let mut sdp_media = match media_line.sdp_type { + SdpType::Media(v) => SdpMedia::new(v), _ => { return Err(SdpParserError::Sequence { - message: "first line in media section needs to be a media line" - .to_string(), - line_number: lines[0].line_number, - }) + message: "first line in media section needs to be a media line".to_string(), + line_number: media_line.line_number, + }); } }; - for line in lines.iter().skip(1) { + while !lines.is_empty() { + let line = lines.remove(0); + let _line_number = line.line_number; match line.sdp_type { - SdpType::Connection(ref c) => { + SdpType::Connection(c) => { sdp_media .set_connection(c) - .map_err(|e: SdpParserInternalError| { - SdpParserError::Sequence { - message: format!("{}", e), - line_number: line.line_number, - } - })? + .map_err(|e: SdpParserInternalError| SdpParserError::Sequence { + message: format!("{}", e), + line_number: _line_number, + })? } - SdpType::Bandwidth(ref b) => sdp_media.add_bandwidth(b), - SdpType::Attribute(ref a) => { + SdpType::Bandwidth(b) => sdp_media.add_bandwidth(b), + SdpType::Attribute(a) => { match a { - &SdpAttribute::DtlsMessage(_) => { + SdpAttribute::DtlsMessage(_) => { // Ignore this attribute on media level Ok(()) - }, - &SdpAttribute::Rtpmap(ref rtpmap) => { - sdp_media.add_attribute(&SdpAttribute::Rtpmap( - SdpAttributeRtpmap { - payload_type: rtpmap.payload_type, - codec_name: rtpmap.codec_name.clone(), - frequency: rtpmap.frequency, - channels: match sdp_media.media.media { - SdpMediaValue::Video => Some(0), - _ => rtpmap.channels - }, - } - )) - }, - _ => { - sdp_media.add_attribute(a) } - }.map_err(|e: SdpParserInternalError| { - SdpParserError::Sequence { - message: format!("{}", e), - line_number: line.line_number, - } - })? + SdpAttribute::Rtpmap(rtpmap) => { + sdp_media.add_attribute(SdpAttribute::Rtpmap(SdpAttributeRtpmap { + payload_type: rtpmap.payload_type, + codec_name: rtpmap.codec_name.clone(), + frequency: rtpmap.frequency, + channels: match sdp_media.media.media { + SdpMediaValue::Video => Some(0), + _ => rtpmap.channels, + }, + })) + } + _ => sdp_media.add_attribute(a), + } + .map_err(|e: SdpParserInternalError| SdpParserError::Sequence { + message: format!("{}", e), + line_number: _line_number, + })? } - SdpType::Media(ref v) => { + SdpType::Media(v) => { media_sections.push(sdp_media); - sdp_media = SdpMedia::new(v.clone()); + sdp_media = SdpMedia::new(v); } - SdpType::Email(_) | - SdpType::Phone(_) | - SdpType::Origin(_) | - SdpType::Repeat(_) | - SdpType::Session(_) | - SdpType::Timing(_) | - SdpType::Uri(_) | - SdpType::Version(_) | - SdpType::Zone(_) => { + SdpType::Origin(_) | SdpType::Session(_) | SdpType::Timing(_) | SdpType::Version(_) => { return Err(SdpParserError::Sequence { - message: "invalid type in media section".to_string(), - line_number: line.line_number, - }) + message: "invalid type in media section".to_string(), + line_number: line.line_number, + }); } - - // the line parsers throw unsupported errors for these already - SdpType::Information(_) | - SdpType::Key(_) => (), }; } @@ -514,10 +592,10 @@ fn test_media_vector_first_line_failure() { let mut sdp_lines: Vec = Vec::new(); let line = SdpLine { line_number: 0, - sdp_type: SdpType::Session("hello".to_string()) + sdp_type: SdpType::Session("hello".to_string()), }; sdp_lines.push(line); - assert!(parse_media_vector(&sdp_lines).is_err()); + assert!(parse_media_vector(&mut sdp_lines).is_err()); } #[test] @@ -532,26 +610,27 @@ fn test_media_vector_multiple_connections() { }; let media = SdpLine { line_number: 0, - sdp_type: SdpType::Media(media_line) + sdp_type: SdpType::Media(media_line), }; sdp_lines.push(media); - use network::{parse_unicast_addr}; + use network::parse_unicast_addr; let addr = parse_unicast_addr("127.0.0.1").unwrap(); let c = SdpConnection { addr, ttl: None, - amount: None }; + amount: None, + }; let c1 = SdpLine { line_number: 1, - sdp_type: SdpType::Connection(c.clone()) + sdp_type: SdpType::Connection(c.clone()), }; sdp_lines.push(c1); let c2 = SdpLine { line_number: 2, - sdp_type: SdpType::Connection(c) + sdp_type: SdpType::Connection(c), }; sdp_lines.push(c2); - assert!(parse_media_vector(&sdp_lines).is_err()); + assert!(parse_media_vector(&mut sdp_lines).is_err()); } #[test] @@ -566,17 +645,17 @@ fn test_media_vector_invalid_types() { }; let media = SdpLine { line_number: 0, - sdp_type: SdpType::Media(media_line) + sdp_type: SdpType::Media(media_line), }; sdp_lines.push(media); - use {SdpTiming}; + use SdpTiming; let t = SdpTiming { start: 0, stop: 0 }; let tline = SdpLine { line_number: 1, - sdp_type: SdpType::Timing(t) + sdp_type: SdpType::Timing(t), }; sdp_lines.push(tline); - assert!(parse_media_vector(&sdp_lines).is_err()); + assert!(parse_media_vector(&mut sdp_lines).is_err()); } #[test] @@ -591,14 +670,14 @@ fn test_media_vector_invalid_media_level_attribute() { }; let media = SdpLine { line_number: 0, - sdp_type: SdpType::Media(media_line) + sdp_type: SdpType::Media(media_line), }; sdp_lines.push(media); let a = SdpAttribute::IceLite; let aline = SdpLine { line_number: 1, - sdp_type: SdpType::Attribute(a) + sdp_type: SdpType::Attribute(a), }; sdp_lines.push(aline); - assert!(parse_media_vector(&sdp_lines).is_err()); + assert!(parse_media_vector(&mut sdp_lines).is_err()); } diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/network.rs b/media/webrtc/signaling/src/sdp/rsdparsa/src/network.rs index 2d768bfc995e..4b40609f3eff 100644 --- a/media/webrtc/signaling/src/sdp/rsdparsa/src/network.rs +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/network.rs @@ -1,19 +1,18 @@ -use std::str::FromStr; use std::fmt; use std::net::IpAddr; +use std::str::FromStr; use error::SdpParserInternalError; -#[derive(Clone,Copy,Debug,PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum SdpAddrType { IP4 = 4, IP6 = 6, } impl SdpAddrType { - pub fn same_protocol(&self, addr: &IpAddr) -> bool { - (addr.is_ipv6() && *self == SdpAddrType::IP6) || - (addr.is_ipv4() && *self == SdpAddrType::IP4) + pub fn same_protocol(self, addr: &IpAddr) -> bool { + (addr.is_ipv6() && self == SdpAddrType::IP6) || (addr.is_ipv4() && self == SdpAddrType::IP4) } } @@ -29,7 +28,9 @@ impl fmt::Display for SdpAddrType { pub fn parse_nettype(value: &str) -> Result<(), SdpParserInternalError> { if value.to_uppercase() != "IN" { - return Err(SdpParserInternalError::Generic("nettype needs to be IN".to_string())); + return Err(SdpParserInternalError::Generic( + "nettype needs to be IN".to_string(), + )); }; Ok(()) } @@ -45,13 +46,14 @@ fn test_parse_nettype() { pub fn parse_addrtype(value: &str) -> Result { Ok(match value.to_uppercase().as_ref() { - "IP4" => SdpAddrType::IP4, - "IP6" => SdpAddrType::IP6, - _ => { - return Err(SdpParserInternalError::Generic("address type needs to be IP4 or IP6" - .to_string())) - } - }) + "IP4" => SdpAddrType::IP4, + "IP6" => SdpAddrType::IP6, + _ => { + return Err(SdpParserInternalError::Generic( + "address type needs to be IP4 or IP6".to_string(), + )); + } + }) } #[test] diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/unsupported_types.rs b/media/webrtc/signaling/src/sdp/rsdparsa/src/unsupported_types.rs deleted file mode 100644 index 272224b6af0c..000000000000 --- a/media/webrtc/signaling/src/sdp/rsdparsa/src/unsupported_types.rs +++ /dev/null @@ -1,74 +0,0 @@ -use error::SdpParserInternalError; -use SdpType; - -pub fn parse_repeat(value: &str) -> Result { - // TODO implement this if it's ever needed - Err(SdpParserInternalError::Unsupported(format!("unsupported type repeat: {} ", value))) -} - -#[test] -fn test_repeat_works() { - // FIXME use a proper r value here - assert!(parse_repeat("0 0").is_err()); -} - -pub fn parse_zone(value: &str) -> Result { - // TODO implement this if it's ever needed - Err(SdpParserInternalError::Unsupported(format!("unsupported type zone: {}", value))) -} - -#[test] -fn test_zone_works() { - // FIXME use a proper z value here - assert!(parse_zone("0 0").is_err()); -} - -pub fn parse_key(value: &str) -> Result { - // TODO implement this if it's ever needed - Err(SdpParserInternalError::Unsupported(format!("unsupported type key: {}", value))) -} - -#[test] -fn test_keys_works() { - // FIXME use a proper k value here - assert!(parse_key("12345").is_err()); -} - -pub fn parse_information(value: &str) -> Result { - Err(SdpParserInternalError::Unsupported(format!("unsupported type information: {}", value))) -} - -#[test] -fn test_information_works() { - assert!(parse_information("foobar").is_err()); -} - -pub fn parse_uri(value: &str) -> Result { - // TODO check if this is really a URI - Err(SdpParserInternalError::Unsupported(format!("unsupported type uri: {}", value))) -} - -#[test] -fn test_uri_works() { - assert!(parse_uri("http://www.mozilla.org").is_err()); -} - -pub fn parse_email(value: &str) -> Result { - // TODO check if this is really an email address - Err(SdpParserInternalError::Unsupported(format!("unsupported type email: {}", value))) -} - -#[test] -fn test_email_works() { - assert!(parse_email("nils@mozilla.com").is_err()); -} - -pub fn parse_phone(value: &str) -> Result { - // TODO check if this is really a phone number - Err(SdpParserInternalError::Unsupported(format!("unsupported type phone: {}", value))) -} - -#[test] -fn test_phone_works() { - assert!(parse_phone("+123456789").is_err()); -} diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/tests/unit_tests.rs b/media/webrtc/signaling/src/sdp/rsdparsa/tests/unit_tests.rs index 335949d0f678..29ab6a0d85ca 100644 --- a/media/webrtc/signaling/src/sdp/rsdparsa/tests/unit_tests.rs +++ b/media/webrtc/signaling/src/sdp/rsdparsa/tests/unit_tests.rs @@ -1,14 +1,21 @@ -extern crate rsdparsa; +extern crate webrtc_sdp; + +#[cfg(test)] +fn check_parse_and_serialize(sdp_str: &str) { + let sdp = webrtc_sdp::parse_sdp(sdp_str, true); + assert!(sdp.is_ok()); + assert_eq!(sdp.unwrap().to_string(), sdp_str.to_string()) +} #[test] fn parse_minimal_sdp() { - let sdp = "v=0\r\n -o=- 0 0 IN IP4 0.0.0.0\r\n -s=-\r\n -c=IN IP4 0.0.0.0\r\n -t=0 0\r\n -m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n"; - let sdp_res = rsdparsa::parse_sdp(sdp, true); + let sdp_str = "v=0\r\n\ + o=- 0 0 IN IP4 0.0.0.0\r\n\ + s=-\r\n\ + t=0 0\r\n\ + c=IN IP4 0.0.0.0\r\n\ + m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n"; + let sdp_res = webrtc_sdp::parse_sdp(sdp_str, true); assert!(sdp_res.is_ok()); let sdp_opt = sdp_res.ok(); assert!(sdp_opt.is_some()); @@ -20,14 +27,20 @@ m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n"; assert_eq!(sdp.media.len(), 1); let msection = &(sdp.media[0]); - assert_eq!(*msection.get_type(), - rsdparsa::media_type::SdpMediaValue::Audio); + assert_eq!( + *msection.get_type(), + webrtc_sdp::media_type::SdpMediaValue::Audio + ); assert_eq!(msection.get_port(), 0); - assert_eq!(*msection.get_proto(), - rsdparsa::media_type::SdpProtocolValue::UdpTlsRtpSavpf); + assert_eq!( + *msection.get_proto(), + webrtc_sdp::media_type::SdpProtocolValue::UdpTlsRtpSavpf + ); assert!(msection.get_attributes().is_empty()); assert!(msection.get_bandwidth().is_empty()); assert!(msection.get_connection().is_none()); + + check_parse_and_serialize(sdp_str); } #[test] @@ -40,7 +53,7 @@ s=-\r\n c=IN IP4 0.0.0.0\r\n t=0 0\r\n m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n"; - let sdp_res = rsdparsa::parse_sdp(sdp, false); + let sdp_res = webrtc_sdp::parse_sdp(sdp, false); assert!(sdp_res.is_ok()); let sdp_opt = sdp_res.ok(); assert!(sdp_opt.is_some()); @@ -51,17 +64,17 @@ m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n"; #[test] fn parse_minimal_sdp_with_most_session_types() { - let sdp = "v=0\r\n -o=- 0 0 IN IP4 0.0.0.0\r\n -s=-\r\n -t=0 0\r\n -b=AS:1\r\n -b=CT:123\r\n -b=TIAS:12345\r\n -c=IN IP4 0.0.0.0\r\n -a=ice-options:trickle\r\n -m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n"; - let sdp_res = rsdparsa::parse_sdp(sdp, false); + let sdp_str = "v=0\r\n\ + o=- 0 0 IN IP4 0.0.0.0\r\n\ + s=-\r\n\ + t=0 0\r\n\ + b=AS:1\r\n\ + b=CT:123\r\n\ + b=TIAS:12345\r\n\ + c=IN IP4 0.0.0.0\r\n\ + a=ice-options:trickle\r\n\ + m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n"; + let sdp_res = webrtc_sdp::parse_sdp(sdp_str, false); assert!(sdp_res.is_ok()); let sdp_opt = sdp_res.ok(); assert!(sdp_opt.is_some()); @@ -69,21 +82,23 @@ m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n"; assert_eq!(sdp.version, 0); assert_eq!(sdp.session, "-"); assert!(sdp.get_connection().is_some()); + + check_parse_and_serialize(sdp_str); } #[test] fn parse_minimal_sdp_with_most_media_types() { - let sdp = "v=0\r\n -o=- 0 0 IN IP4 0.0.0.0\r\n -s=-\r\n -t=0 0\r\n -m=video 0 UDP/TLS/RTP/SAVPF 0\r\n -b=AS:1\r\n -b=CT:123\r\n -b=TIAS:12345\r\n -c=IN IP4 0.0.0.0\r\n -a=sendrecv\r\n"; - let sdp_res = rsdparsa::parse_sdp(sdp, false); + let sdp_str = "v=0\r\n\ + o=- 0 0 IN IP4 0.0.0.0\r\n\ + s=-\r\n\ + t=0 0\r\n\ + m=video 0 UDP/TLS/RTP/SAVPF 0\r\n\ + b=AS:1\r\n\ + b=CT:123\r\n\ + b=TIAS:12345\r\n\ + c=IN IP4 0.0.0.0\r\n\ + a=sendrecv\r\n"; + let sdp_res = webrtc_sdp::parse_sdp(sdp_str, false); assert!(sdp_res.is_ok()); let sdp_opt = sdp_res.ok(); assert!(sdp_opt.is_some()); @@ -94,44 +109,52 @@ a=sendrecv\r\n"; assert_eq!(sdp.media.len(), 1); let msection = &(sdp.media[0]); - assert_eq!(*msection.get_type(), - rsdparsa::media_type::SdpMediaValue::Video); + assert_eq!( + *msection.get_type(), + webrtc_sdp::media_type::SdpMediaValue::Video + ); assert_eq!(msection.get_port(), 0); - assert_eq!(*msection.get_proto(), - rsdparsa::media_type::SdpProtocolValue::UdpTlsRtpSavpf); + assert_eq!( + *msection.get_proto(), + webrtc_sdp::media_type::SdpProtocolValue::UdpTlsRtpSavpf + ); assert!(!msection.get_bandwidth().is_empty()); assert!(!msection.get_connection().is_none()); assert!(!msection.get_attributes().is_empty()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Sendrecv).is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Sendrecv) + .is_some()); + + check_parse_and_serialize(sdp_str); } #[test] fn parse_firefox_audio_offer() { - let sdp = "v=0\r\n -o=mozilla...THIS_IS_SDPARTA-52.0a1 506705521068071134 0 IN IP4 0.0.0.0\r\n -s=-\r\n -t=0 0\r\n -a=fingerprint:sha-256 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:BF:2F:E3:91:CB:57:A9:9D:4A:A2:0B:40\r\n -a=group:BUNDLE sdparta_0\r\n -a=ice-options:trickle\r\n -a=msid-semantic:WMS *\r\n -m=audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8\r\n -c=IN IP4 0.0.0.0\r\n -a=sendrecv\r\n -a=extmap:1/sendonly urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n -a=fmtp:109 maxplaybackrate=48000;stereo=1;useinbandfec=1\r\n -a=ice-pwd:e3baa26dd2fa5030d881d385f1e36cce\r\n -a=ice-ufrag:58b99ead\r\n -a=mid:sdparta_0\r\n -a=msid:{5a990edd-0568-ac40-8d97-310fc33f3411} {218cfa1c-617d-2249-9997-60929ce4c405}\r\n -a=rtcp-mux\r\n -a=rtpmap:109 opus/48000/2\r\n -a=rtpmap:9 G722/8000/1\r\n -a=rtpmap:0 PCMU/8000\r\n -a=rtpmap:8 PCMA/8000\r\n -a=setup:actpass\r\n + let sdp_str = "v=0\r\n\ +o=mozilla...THIS_IS_SDPARTA-52.0a1 506705521068071134 0 IN IP4 0.0.0.0\r\n\ +s=-\r\n\ +t=0 0\r\n\ +a=fingerprint:sha-256 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:BF:2F:E3:91:CB:57:A9:9D:4A:A2:0B:40\r\n\ +a=group:BUNDLE sdparta_0\r\n\ +a=ice-options:trickle\r\n\ +a=msid-semantic:WMS *\r\n\ +m=audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8\r\n\ +c=IN IP4 0.0.0.0\r\n\ +a=sendrecv\r\n\ +a=extmap:1/sendonly urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n\ +a=fmtp:109 maxplaybackrate=48000;stereo=1;useinbandfec=1\r\n\ +a=ice-pwd:e3baa26dd2fa5030d881d385f1e36cce\r\n\ +a=ice-ufrag:58b99ead\r\n\ +a=mid:sdparta_0\r\n\ +a=msid:{5a990edd-0568-ac40-8d97-310fc33f3411} {218cfa1c-617d-2249-9997-60929ce4c405}\r\n\ +a=rtcp-mux\r\n\ +a=rtpmap:109 opus/48000/2\r\n\ +a=rtpmap:9 G722/8000/1\r\n\ +a=rtpmap:0 PCMU/8000\r\n\ +a=rtpmap:8 PCMA/8000\r\n\ +a=setup:actpass\r\n\ a=ssrc:2655508255 cname:{735484ea-4f6c-f74a-bd66-7425f8476c2e}\r\n"; - let sdp_res = rsdparsa::parse_sdp(sdp, true); + let sdp_res = webrtc_sdp::parse_sdp(sdp_str, true); assert!(sdp_res.is_ok()); let sdp_opt = sdp_res.ok(); assert!(sdp_opt.is_some()); @@ -140,66 +163,94 @@ a=ssrc:2655508255 cname:{735484ea-4f6c-f74a-bd66-7425f8476c2e}\r\n"; assert_eq!(sdp.media.len(), 1); let msection = &(sdp.media[0]); - assert_eq!(*msection.get_type(), - rsdparsa::media_type::SdpMediaValue::Audio); + assert_eq!( + *msection.get_type(), + webrtc_sdp::media_type::SdpMediaValue::Audio + ); assert_eq!(msection.get_port(), 9); - assert_eq!(*msection.get_proto(), - rsdparsa::media_type::SdpProtocolValue::UdpTlsRtpSavpf); + assert_eq!( + *msection.get_proto(), + webrtc_sdp::media_type::SdpProtocolValue::UdpTlsRtpSavpf + ); assert!(msection.get_connection().is_some()); assert!(msection.get_bandwidth().is_empty()); assert!(!msection.get_attributes().is_empty()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Sendrecv).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Extmap).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Fmtp).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::IcePwd).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::IceUfrag).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Mid).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Mid).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Msid).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::RtcpMux).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Rtpmap).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Setup).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Ssrc).is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Sendrecv) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Extmap) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Fmtp) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::IcePwd) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::IceUfrag) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Mid) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Mid) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Msid) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::RtcpMux) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Rtpmap) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Setup) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Ssrc) + .is_some()); } #[test] fn parse_firefox_video_offer() { - let sdp = "v=0\r\n -o=mozilla...THIS_IS_SDPARTA-52.0a1 506705521068071134 0 IN IP4 0.0.0.0\r\n -s=-\r\n -t=0 0\r\n -a=fingerprint:sha-256 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:BF:2F:E3:91:CB:57:A9:9D:4A:A2:0B:40\r\n -a=group:BUNDLE sdparta_2\r\n -a=ice-options:trickle\r\n -a=msid-semantic:WMS *\r\n -m=video 9 UDP/TLS/RTP/SAVPF 126 120 97\r\n -c=IN IP4 0.0.0.0\r\n -a=recvonly\r\n -a=fmtp:126 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1\r\n -a=fmtp:120 max-fs=12288;max-fr=60\r\n -a=fmtp:97 profile-level-id=42e01f;level-asymmetry-allowed=1\r\n -a=ice-pwd:e3baa26dd2fa5030d881d385f1e36cce\r\n -a=ice-ufrag:58b99ead\r\n -a=mid:sdparta_2\r\n -a=rtcp-fb:126 nack\r\n -a=rtcp-fb:126 nack pli\r\n -a=rtcp-fb:126 ccm fir\r\n -a=rtcp-fb:126 goog-remb\r\n -a=rtcp-fb:120 nack\r\n -a=rtcp-fb:120 nack pli\r\n -a=rtcp-fb:120 ccm fir\r\n -a=rtcp-fb:120 goog-remb\r\n -a=rtcp-fb:97 nack\r\n -a=rtcp-fb:97 nack pli\r\n -a=rtcp-fb:97 ccm fir\r\n -a=rtcp-fb:97 goog-remb\r\n -a=rtcp-mux\r\n -a=rtpmap:126 H264/90000\r\n -a=rtpmap:120 VP8/90000\r\n -a=rtpmap:97 H264/90000\r\n -a=setup:actpass\r\n + let sdp_str = "v=0\r\n\ +o=mozilla...THIS_IS_SDPARTA-52.0a1 506705521068071134 0 IN IP4 0.0.0.0\r\n\ +s=-\r\n\ +t=0 0\r\n\ +a=fingerprint:sha-256 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:BF:2F:E3:91:CB:57:A9:9D:4A:A2:0B:40\r\n\ +a=group:BUNDLE sdparta_2\r\n\ +a=ice-options:trickle\r\n\ +a=msid-semantic:WMS *\r\n\ +m=video 9 UDP/TLS/RTP/SAVPF 126 120 97\r\n\ +c=IN IP4 0.0.0.0\r\n\ +a=recvonly\r\n\ +a=fmtp:126 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1\r\n\ +a=fmtp:120 max-fs=12288;max-fr=60\r\n\ +a=fmtp:97 profile-level-id=42e01f;level-asymmetry-allowed=1\r\n\ +a=ice-pwd:e3baa26dd2fa5030d881d385f1e36cce\r\n\ +a=ice-ufrag:58b99ead\r\n\ +a=mid:sdparta_2\r\n\ +a=rtcp-fb:126 nack\r\n\ +a=rtcp-fb:126 nack pli\r\n\ +a=rtcp-fb:126 ccm fir\r\n\ +a=rtcp-fb:126 goog-remb\r\n\ +a=rtcp-fb:120 nack\r\n\ +a=rtcp-fb:120 nack pli\r\n\ +a=rtcp-fb:120 ccm fir\r\n\ +a=rtcp-fb:120 goog-remb\r\n\ +a=rtcp-fb:97 nack\r\n\ +a=rtcp-fb:97 nack pli\r\n\ +a=rtcp-fb:97 ccm fir\r\n\ +a=rtcp-fb:97 goog-remb\r\n\ +a=rtcp-mux\r\n\ +a=rtpmap:126 H264/90000\r\n\ +a=rtpmap:120 VP8/90000\r\n\ +a=rtpmap:97 H264/90000\r\n\ +a=setup:actpass\r\n\ a=ssrc:2709871439 cname:{735484ea-4f6c-f74a-bd66-7425f8476c2e}"; - let sdp_res = rsdparsa::parse_sdp(sdp, true); + let sdp_res = webrtc_sdp::parse_sdp(sdp_str, true); assert!(sdp_res.is_ok()); let sdp_opt = sdp_res.ok(); assert!(sdp_opt.is_some()); @@ -208,51 +259,80 @@ a=ssrc:2709871439 cname:{735484ea-4f6c-f74a-bd66-7425f8476c2e}"; assert_eq!(sdp.media.len(), 1); let msection = &(sdp.media[0]); - assert_eq!(*msection.get_type(), - rsdparsa::media_type::SdpMediaValue::Video); + assert_eq!( + *msection.get_type(), + webrtc_sdp::media_type::SdpMediaValue::Video + ); assert_eq!(msection.get_port(), 9); - assert_eq!(*msection.get_proto(), - rsdparsa::media_type::SdpProtocolValue::UdpTlsRtpSavpf); + assert_eq!( + *msection.get_proto(), + webrtc_sdp::media_type::SdpProtocolValue::UdpTlsRtpSavpf + ); assert!(msection.get_connection().is_some()); assert!(msection.get_bandwidth().is_empty()); assert!(!msection.get_attributes().is_empty()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Recvonly).is_some()); - assert!(!msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Extmap).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Fmtp).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::IcePwd).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::IceUfrag).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Mid).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Mid).is_some()); - assert!(!msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Msid).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Rtcpfb).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::RtcpMux).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Rtpmap).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Setup).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Ssrc).is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Recvonly) + .is_some()); + assert!(!msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Extmap) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Fmtp) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::IcePwd) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::IceUfrag) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Mid) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Mid) + .is_some()); + assert!(!msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Msid) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Rtcpfb) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::RtcpMux) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Rtpmap) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Setup) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Ssrc) + .is_some()); } - #[test] fn parse_firefox_datachannel_offer() { - let sdp = "v=0\r\n -o=mozilla...THIS_IS_SDPARTA-52.0a2 3327975756663609975 0 IN IP4 0.0.0.0\r\n -s=-\r\n -t=0 0\r\n -a=sendrecv\r\n -a=fingerprint:sha-256 AC:72:CB:D6:1E:A3:A3:B0:E7:97:77:25:03:4B:5B:FF:19:6C:02:C6:93:7D:EB:5C:81:6F:36:D9:02:32:F8:23\r\n -a=ice-options:trickle\r\n -a=msid-semantic:WMS *\r\n -m=application 49760 DTLS/SCTP 5000\r\n -c=IN IP4 172.16.156.106\r\n -a=candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ host\r\n -a=sendrecv\r\n -a=end-of-candidates\r\n -a=ice-pwd:24f485c580129b36447b65df77429a82\r\n -a=ice-ufrag:4cba30fe\r\n -a=mid:sdparta_0\r\n -a=sctpmap:5000 webrtc-datachannel 256\r\n -a=setup:active\r\n -a=ssrc:3376683177 cname:{62f78ee0-620f-a043-86ca-b69f189f1aea}\r\n"; - let sdp_res = rsdparsa::parse_sdp(sdp, true); + let sdp_str = "v=0\r\n\ + o=mozilla...THIS_IS_SDPARTA-52.0a2 3327975756663609975 0 IN IP4 0.0.0.0\r\n\ + s=-\r\n\ + t=0 0\r\n\ + a=sendrecv\r\n\ + a=fingerprint:sha-256 AC:72:CB:D6:1E:A3:A3:B0:E7:97:77:25:03:4B:5B:FF:19:6C:02:C6:93:7D:EB:5C:81:6F:36:D9:02:32:F8:23\r\n\ + a=ice-options:trickle\r\n\ + a=msid-semantic:WMS *\r\n\ + m=application 49760 DTLS/SCTP 5000\r\n\ + c=IN IP4 172.16.156.106\r\n\ + a=candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ host\r\n\ + a=sendrecv\r\n\ + a=end-of-candidates\r\n\ + a=ice-pwd:24f485c580129b36447b65df77429a82\r\n\ + a=ice-ufrag:4cba30fe\r\n\ + a=mid:sdparta_0\r\n\ + a=sctpmap:5000 webrtc-datachannel 256\r\n\ + a=setup:active\r\n\ + a=ssrc:3376683177 cname:{62f78ee0-620f-a043-86ca-b69f189f1aea}\r\n"; + let sdp_res = webrtc_sdp::parse_sdp(sdp_str, true); assert!(sdp_res.is_ok()); let sdp_opt = sdp_res.ok(); assert!(sdp_opt.is_some()); @@ -261,27 +341,59 @@ a=ssrc:3376683177 cname:{62f78ee0-620f-a043-86ca-b69f189f1aea}\r\n"; assert_eq!(sdp.media.len(), 1); let msection = &(sdp.media[0]); - assert_eq!(*msection.get_type(), - rsdparsa::media_type::SdpMediaValue::Application); + assert_eq!( + *msection.get_type(), + webrtc_sdp::media_type::SdpMediaValue::Application + ); assert_eq!(msection.get_port(), 49760); - assert_eq!(*msection.get_proto(), - rsdparsa::media_type::SdpProtocolValue::DtlsSctp); + assert_eq!( + *msection.get_proto(), + webrtc_sdp::media_type::SdpProtocolValue::DtlsSctp + ); assert!(msection.get_connection().is_some()); assert!(msection.get_bandwidth().is_empty()); assert!(!msection.get_attributes().is_empty()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Sendrecv).is_some()); - assert!(!msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Extmap).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::IcePwd).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::IceUfrag).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::EndOfCandidates).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Mid).is_some()); - assert!(!msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Msid).is_some()); - assert!(!msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Rtcpfb).is_some()); - assert!(!msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::RtcpMux).is_some()); - assert!(!msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Rtpmap).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Sctpmap).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Setup).is_some()); - assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Ssrc).is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Sendrecv) + .is_some()); + assert!(!msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Extmap) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::IcePwd) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::IceUfrag) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::EndOfCandidates) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Mid) + .is_some()); + assert!(!msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Msid) + .is_some()); + assert!(!msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Rtcpfb) + .is_some()); + assert!(!msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::RtcpMux) + .is_some()); + assert!(!msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Rtpmap) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Sctpmap) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Setup) + .is_some()); + assert!(msection + .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Ssrc) + .is_some()); + + check_parse_and_serialize(sdp_str); } #[test] @@ -373,7 +485,7 @@ a=ssrc:2673335628 cname:qPTZ+BI+42mgbOi+\r\n a=ssrc:2673335628 msid:HWpbmTmXleVSnlssQd80bPuw9cxQFroDkkBP b6ec5178-c611-403f-bbec-3833ed547c09\r\n a=ssrc:2673335628 mslabel:HWpbmTmXleVSnlssQd80bPuw9cxQFroDkkBP\r\n a=ssrc:2673335628 label:b6ec5178-c611-403f-bbec-3833ed547c09\r\n"; - let sdp_res = rsdparsa::parse_sdp(sdp, true); + let sdp_res = webrtc_sdp::parse_sdp(sdp, true); assert!(sdp_res.is_ok()); let sdp_opt = sdp_res.ok(); assert!(sdp_opt.is_some()); @@ -382,21 +494,29 @@ a=ssrc:2673335628 label:b6ec5178-c611-403f-bbec-3833ed547c09\r\n"; assert_eq!(sdp.media.len(), 2); let msection1 = &(sdp.media[0]); - assert_eq!(*msection1.get_type(), - rsdparsa::media_type::SdpMediaValue::Audio); + assert_eq!( + *msection1.get_type(), + webrtc_sdp::media_type::SdpMediaValue::Audio + ); assert_eq!(msection1.get_port(), 9); - assert_eq!(*msection1.get_proto(), - rsdparsa::media_type::SdpProtocolValue::UdpTlsRtpSavpf); + assert_eq!( + *msection1.get_proto(), + webrtc_sdp::media_type::SdpProtocolValue::UdpTlsRtpSavpf + ); assert!(!msection1.get_attributes().is_empty()); assert!(msection1.get_connection().is_some()); assert!(msection1.get_bandwidth().is_empty()); let msection2 = &(sdp.media[1]); - assert_eq!(*msection2.get_type(), - rsdparsa::media_type::SdpMediaValue::Video); + assert_eq!( + *msection2.get_type(), + webrtc_sdp::media_type::SdpMediaValue::Video + ); assert_eq!(msection2.get_port(), 9); - assert_eq!(*msection2.get_proto(), - rsdparsa::media_type::SdpProtocolValue::UdpTlsRtpSavpf); + assert_eq!( + *msection2.get_proto(), + webrtc_sdp::media_type::SdpProtocolValue::UdpTlsRtpSavpf + ); assert!(!msection2.get_attributes().is_empty()); assert!(msection2.get_connection().is_some()); assert!(msection2.get_bandwidth().is_empty()); @@ -452,7 +572,7 @@ a=setup:actpass\r\n a=simulcast: send rid=foo;bar\r\n a=ssrc:2988475468 cname:{77067f00-2e8d-8b4c-8992-cfe338f56851}\r\n a=ssrc:1649784806 cname:{77067f00-2e8d-8b4c-8992-cfe338f56851}\r\n"; - let sdp_res = rsdparsa::parse_sdp(sdp, true); + let sdp_res = webrtc_sdp::parse_sdp(sdp, true); assert!(sdp_res.is_ok()); let sdp_opt = sdp_res.ok(); assert!(sdp_opt.is_some()); @@ -463,35 +583,35 @@ a=ssrc:1649784806 cname:{77067f00-2e8d-8b4c-8992-cfe338f56851}\r\n"; #[test] fn parse_firefox_simulcast_answer() { - let sdp = "v=0\r\n -o=mozilla...THIS_IS_SDPARTA-55.0a1 7548296603161351381 0 IN IP4 0.0.0.0\r\n -s=-\r\n -t=0 0\r\n -a=fingerprint:sha-256 B1:47:49:4F:7D:83:03:BE:E9:FC:73:A3:FB:33:38:40:0B:3B:6A:56:78:EB:EE:D5:6D:2D:D5:3A:B6:13:97:E7\r\n -a=ice-options:trickle\r\n -a=msid-semantic:WMS *\r\n -m=video 9 UDP/TLS/RTP/SAVPF 120\r\n -c=IN IP4 0.0.0.0\r\n -a=recvonly\r\n -a=extmap:1 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n -a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n -a=fmtp:120 max-fs=12288;max-fr=60\r\n -a=ice-pwd:c886e2caf2ae397446312930cd1afe51\r\n -a=ice-ufrag:f57396c0\r\n -a=mid:sdparta_0\r\n -a=rtcp-fb:120 nack\r\n -a=rtcp-fb:120 nack pli\r\n -a=rtcp-fb:120 ccm fir\r\n -a=rtcp-fb:120 goog-remb\r\n -a=rtcp-mux\r\n -a=rtpmap:120 VP8/90000\r\n -a=setup:active\r\n -a=ssrc:2564157021 cname:{cae1cd32-7433-5b48-8dc8-8e3f8b2f96cd}\r\n -a=simulcast: recv rid=foo;bar\r\n -a=rid:foo recv\r\n -a=rid:bar recv\r\n -a=extmap:3/recvonly urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n"; - let sdp_res = rsdparsa::parse_sdp(sdp, true); + let sdp_str = "v=0\r\n\ + o=mozilla...THIS_IS_SDPARTA-55.0a1 7548296603161351381 0 IN IP4 0.0.0.0\r\n\ + s=-\r\n\ + t=0 0\r\n\ + a=fingerprint:sha-256 B1:47:49:4F:7D:83:03:BE:E9:FC:73:A3:FB:33:38:40:0B:3B:6A:56:78:EB:EE:D5:6D:2D:D5:3A:B6:13:97:E7\r\n\ + a=ice-options:trickle\r\n\ + a=msid-semantic:WMS *\r\n\ + m=video 9 UDP/TLS/RTP/SAVPF 120\r\n\ + c=IN IP4 0.0.0.0\r\n + a=recvonly\r\n\ + a=extmap:1 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n\ + a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n\ + a=fmtp:120 max-fs=12288;max-fr=60\r\n\ + a=ice-pwd:c886e2caf2ae397446312930cd1afe51\r\n\ + a=ice-ufrag:f57396c0\r\n\ + a=mid:sdparta_0\r\n\ + a=rtcp-fb:120 nack\r\n\ + a=rtcp-fb:120 nack pli\r\n\ + a=rtcp-fb:120 ccm fir\r\n\ + a=rtcp-fb:120 goog-remb\r\n\ + a=rtcp-mux\r\n\ + a=rtpmap:120 VP8/90000\r\n\ + a=setup:active\r\n\ + a=ssrc:2564157021 cname:{cae1cd32-7433-5b48-8dc8-8e3f8b2f96cd}\r\n\ + a=simulcast: recv rid=foo;bar\r\n\ + a=rid:foo recv\r\n\ + a=rid:bar recv\r\n\ + a=extmap:3/recvonly urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n"; + let sdp_res = webrtc_sdp::parse_sdp(sdp_str, true); assert!(sdp_res.is_ok()); let sdp_opt = sdp_res.ok(); assert!(sdp_opt.is_some()); diff --git a/media/webrtc/signaling/src/sdp/rsdparsa_capi/Cargo.toml b/media/webrtc/signaling/src/sdp/rsdparsa_capi/Cargo.toml index 49a651b4346f..4c9a8319922a 100644 --- a/media/webrtc/signaling/src/sdp/rsdparsa_capi/Cargo.toml +++ b/media/webrtc/signaling/src/sdp/rsdparsa_capi/Cargo.toml @@ -7,5 +7,5 @@ authors = ["Paul Ellenbogen ", [dependencies] libc = "^0.2.0" log = "0.4" -rsdparsa = {version = "0.1.0", path = "../rsdparsa"} +rsdparsa = {package = "webrtc-sdp", version = "0.1.0", path = "../rsdparsa"} nserror = { path = "../../../../../../xpcom/rust/nserror" } diff --git a/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/attribute.rs b/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/attribute.rs index 936dfb6d179e..fac2bfff36fb 100644 --- a/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/attribute.rs +++ b/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/attribute.rs @@ -1076,7 +1076,7 @@ pub unsafe extern "C" fn sdp_get_candidates(attributes: *const Vec let attr_strings: Vec = (*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()) + Some(attr.to_string()) } else { None } diff --git a/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/lib.rs b/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/lib.rs index aa668d735dd8..b69acae27a8a 100644 --- a/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/lib.rs +++ b/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/lib.rs @@ -45,10 +45,10 @@ pub unsafe extern "C" fn parse_sdp(sdp: StringView, let parser_result = rsdparsa::parse_sdp(&sdp_str, fail_on_warning); match parser_result { - Ok(parsed) => { + Ok(mut parsed) => { *error = match parsed.warnings.len(){ 0 => ptr::null(), - _ => Box::into_raw(Box::new(parsed.warnings[0].clone())), + _ => Box::into_raw(Box::new(parsed.warnings.remove(0))), }; *session = Rc::into_raw(Rc::new(parsed)); NS_OK diff --git a/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/media_section.rs b/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/media_section.rs index d8a95dd42c01..5ce8079b14f1 100644 --- a/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/media_section.rs +++ b/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/media_section.rs @@ -56,6 +56,9 @@ pub enum RustSdpProtocolValue { DtlsSctp, UdpDtlsSctp, TcpDtlsSctp, + RtpAvp, + RtpAvpf, + RtpSavp, } impl<'a> From<&'a SdpProtocolValue> for RustSdpProtocolValue { @@ -70,6 +73,9 @@ impl<'a> From<&'a SdpProtocolValue> for RustSdpProtocolValue { SdpProtocolValue::DtlsSctp => RustSdpProtocolValue::DtlsSctp, SdpProtocolValue::UdpDtlsSctp => RustSdpProtocolValue::UdpDtlsSctp, SdpProtocolValue::TcpDtlsSctp => RustSdpProtocolValue::TcpDtlsSctp, + SdpProtocolValue::RtpAvp => RustSdpProtocolValue::RtpAvp, + SdpProtocolValue::RtpAvpf => RustSdpProtocolValue::RtpAvpf, + SdpProtocolValue::RtpSavp => RustSdpProtocolValue::RtpSavp, } } }