From ad14285e30f269fe34d69e9875f217b5e0a38cef Mon Sep 17 00:00:00 2001 From: Dan Minor Date: Thu, 11 Jan 2018 10:42:16 -0500 Subject: [PATCH] Bug 1379265 - Import rsdparsa; r=rillian Imported from git commit 0fd5ac915a134d4ffd5dd5949247632a1b92f7bc. MozReview-Commit-ID: 8njzZT5YTTD --HG-- extra : rebase_source : eed1aaf0fefa879ed345054fb3a2b67e6ab6d2ba --- .../signaling/src/sdp/rsdparsa/.gitignore | 2 + .../signaling/src/sdp/rsdparsa/.travis.yml | 68 + .../signaling/src/sdp/rsdparsa/Cargo.toml | 10 + .../webrtc/signaling/src/sdp/rsdparsa/LICENSE | 373 +++++ .../signaling/src/sdp/rsdparsa/README.md | 13 + .../src/sdp/rsdparsa/src/attribute_type.rs | 1457 +++++++++++++++++ .../src/sdp/rsdparsa/src/bin/file_parser.rs | 35 + .../src/sdp/rsdparsa/src/bin/sdps/10.sdp | 13 + .../src/sdp/rsdparsa/src/bin/sdps/11.sdp | 63 + .../src/sdp/rsdparsa/src/bin/sdps/12.sdp | 58 + .../src/sdp/rsdparsa/src/bin/sdps/13.sdp | 12 + .../src/sdp/rsdparsa/src/bin/sdps/14.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/15.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/16.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/17.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/18.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/19.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/2.sdp | 7 + .../src/sdp/rsdparsa/src/bin/sdps/20.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/21.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/22.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/23.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/24.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/25.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/26.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/27.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/28.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/29.sdp | 8 + .../src/sdp/rsdparsa/src/bin/sdps/3.sdp | 34 + .../src/sdp/rsdparsa/src/bin/sdps/30.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/31.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/32.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/33.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/34.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/35.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/36.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/37.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/38.sdp | 9 + .../src/sdp/rsdparsa/src/bin/sdps/39.sdp | 8 + .../src/sdp/rsdparsa/src/bin/sdps/4.sdp | 7 + .../src/sdp/rsdparsa/src/bin/sdps/40.sdp | 8 + .../src/sdp/rsdparsa/src/bin/sdps/41.sdp | 91 + .../src/sdp/rsdparsa/src/bin/sdps/5.sdp | 5 + .../src/sdp/rsdparsa/src/bin/sdps/6.sdp | 12 + .../src/sdp/rsdparsa/src/bin/sdps/7.sdp | 7 + .../src/sdp/rsdparsa/src/bin/sdps/8.sdp | 84 + .../src/sdp/rsdparsa/src/bin/sdps/9.sdp | 34 + .../src/sdp/rsdparsa/src/bin/sdps/extract.sh | 3 + .../signaling/src/sdp/rsdparsa/src/error.rs | 215 +++ .../signaling/src/sdp/rsdparsa/src/lib.rs | 974 +++++++++++ .../src/sdp/rsdparsa/src/media_type.rs | 414 +++++ .../signaling/src/sdp/rsdparsa/src/network.rs | 80 + .../src/sdp/rsdparsa/src/unsupported_types.rs | 74 + .../src/sdp/rsdparsa/tests/unit_tests.rs | 423 +++++ 54 files changed, 4808 insertions(+) create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/.gitignore create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/.travis.yml create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/Cargo.toml create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/LICENSE create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/README.md create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/attribute_type.rs create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/file_parser.rs create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/10.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/11.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/12.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/13.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/14.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/15.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/16.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/17.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/18.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/19.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/2.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/20.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/21.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/22.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/23.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/24.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/25.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/26.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/27.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/28.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/29.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/3.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/30.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/31.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/32.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/33.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/34.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/35.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/36.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/37.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/38.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/39.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/4.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/40.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/41.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/5.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/6.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/7.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/8.sdp create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/9.sdp create mode 100755 media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/extract.sh create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/error.rs create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/lib.rs create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/media_type.rs create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/network.rs create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/src/unsupported_types.rs create mode 100644 media/webrtc/signaling/src/sdp/rsdparsa/tests/unit_tests.rs diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/.gitignore b/media/webrtc/signaling/src/sdp/rsdparsa/.gitignore new file mode 100644 index 000000000000..a9d37c560c6a --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/.travis.yml b/media/webrtc/signaling/src/sdp/rsdparsa/.travis.yml new file mode 100644 index 000000000000..e301aa4b5803 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/.travis.yml @@ -0,0 +1,68 @@ +language: rust +cache: cargo +sudo: true +os: + - linux +# Taken out temporarily because it's to slow +# - osx + +rust: + - nightly + - beta + - stable + # mimimum stable version because we use init shorthand + - 1.17.0 + +matrix: + allow_failures: + - rust: nightly + +before_install: + - sudo apt-get update + +addons: + apt: + packages: + - libcurl4-openssl-dev + - libelf-dev + - libdw-dev + - cmake + - gcc + - binutils-dev + +# Add clippy +before_script: + - export PATH=$PATH:~/.cargo/bin + - | + if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then + cargo install --force clippy; + fi + +script: + - cargo build --verbose --all + - | + if [[ "$TRAVIS_RUST_VERSION" == "nightly" && + -f ~/.cargo/bin/cargo-clippy ]]; then + cargo clippy; + fi + - cargo test --verbose --all + +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 && + mkdir build && + cd build && + cmake .. && + make && + sudo make install && + cd ../.. && + rm -rf kcov-master && + 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 && + 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" + fi diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/Cargo.toml b/media/webrtc/signaling/src/sdp/rsdparsa/Cargo.toml new file mode 100644 index 000000000000..6520a0bbfec9 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "rsdparsa" +version = "0.1.0" +authors = ["Nils Ohlmeier "] + +[dependencies] +clippy = {version = "*", optional = true} + +[features] +default = [] diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/LICENSE b/media/webrtc/signaling/src/sdp/rsdparsa/LICENSE new file mode 100644 index 000000000000..a612ad9813b0 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/README.md b/media/webrtc/signaling/src/sdp/rsdparsa/README.md new file mode 100644 index 000000000000..824da17b6345 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/README.md @@ -0,0 +1,13 @@ +# rsdparsa + +[![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) + +A SDP parser written in Rust specifically aimed for WebRTC + +Requires minimum Rust 1.17 + +## License + +Licensed under [MPL](https://www.mozilla.org/MPL/2.0/). diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/attribute_type.rs b/media/webrtc/signaling/src/sdp/rsdparsa/src/attribute_type.rs new file mode 100644 index 000000000000..2cb24098d133 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/attribute_type.rs @@ -0,0 +1,1457 @@ +use std::net::IpAddr; +use std::str::FromStr; +use std::fmt; + +use SdpType; +use error::SdpParserInternalError; +use network::{parse_nettype, parse_addrtype, parse_unicast_addr}; + +#[derive(Clone)] +pub enum SdpAttributeCandidateTransport { + Udp, + Tcp, +} + +#[derive(Clone)] +pub enum SdpAttributeCandidateType { + Host, + Srflx, + Prflx, + Relay, +} + +#[derive(Clone)] +pub enum SdpAttributeCandidateTcpType { + Active, + Passive, + Simultaneous, +} + +#[derive(Clone)] +pub struct SdpAttributeCandidate { + pub foundation: String, + pub component: u32, + pub transport: SdpAttributeCandidateTransport, + pub priority: u64, + pub address: IpAddr, + pub port: u32, + pub c_type: SdpAttributeCandidateType, + pub raddr: Option, + pub rport: Option, + pub tcp_type: Option, + pub generation: Option, + pub ufrag: Option, + pub networkcost: Option, +} + +impl SdpAttributeCandidate { + pub fn new(foundation: String, + component: u32, + transport: SdpAttributeCandidateTransport, + priority: u64, + address: IpAddr, + port: u32, + c_type: SdpAttributeCandidateType) + -> SdpAttributeCandidate { + SdpAttributeCandidate { + foundation, + component, + transport, + priority, + address, + port, + c_type, + raddr: None, + rport: None, + tcp_type: None, + generation: None, + ufrag: None, + networkcost: None, + } + } + + fn set_remote_address(&mut self, ip: IpAddr) { + self.raddr = Some(ip) + } + + fn set_remote_port(&mut self, p: u32) { + self.rport = Some(p) + } + + fn set_tcp_type(&mut self, t: SdpAttributeCandidateTcpType) { + self.tcp_type = Some(t) + } + + fn set_generation(&mut self, g: u32) { + self.generation = Some(g) + } + + fn set_ufrag(&mut self, u: String) { + self.ufrag = Some(u) + } + + fn set_network_cost(&mut self, n: u32) { + self.networkcost = Some(n) + } +} + +#[derive(Clone)] +pub struct SdpAttributeRemoteCandidate { + pub component: u32, + pub address: IpAddr, + pub port: u32, +} + +#[derive(Clone)] +pub struct SdpAttributeSimulcastId { + pub id: String, + pub paused: bool, +} + +impl SdpAttributeSimulcastId { + pub fn new(idstr: String) -> SdpAttributeSimulcastId { + if idstr.starts_with('~') { + SdpAttributeSimulcastId { + id: idstr[1..].to_string(), + paused: true, + } + } else { + SdpAttributeSimulcastId { + id: idstr, + paused: false, + } + } + } +} + +#[derive(Clone)] +pub struct SdpAttributeSimulcastAlternatives { + pub ids: Vec, +} + +impl SdpAttributeSimulcastAlternatives { + pub fn new(idlist: String) -> SdpAttributeSimulcastAlternatives { + SdpAttributeSimulcastAlternatives { + ids: idlist + .split(',') + .map(|x| x.to_string()) + .map(SdpAttributeSimulcastId::new) + .collect(), + } + } +} + +#[derive(Clone)] +pub struct SdpAttributeSimulcast { + pub send: Vec, + pub receive: Vec, +} + +impl SdpAttributeSimulcast { + fn parse_ids(&mut self, direction: SdpAttributeDirection, idlist: String) { + let list = idlist + .split(';') + .map(|x| x.to_string()) + .map(SdpAttributeSimulcastAlternatives::new) + .collect(); + // TODO prevent over-writing existing values + match direction { + SdpAttributeDirection::Recvonly => self.receive = list, + SdpAttributeDirection::Sendonly => self.send = list, + _ => (), + } + } +} + +#[derive(Clone)] +pub struct SdpAttributeRtcp { + pub port: u16, + pub unicast_addr: Option, +} + +impl SdpAttributeRtcp { + pub fn new(port: u16) -> SdpAttributeRtcp { + SdpAttributeRtcp { + port, + unicast_addr: None, + } + } + + fn set_addr(&mut self, addr: IpAddr) { + self.unicast_addr = Some(addr) + } +} + +#[derive(Clone)] +pub struct SdpAttributeRtcpFb { + pub payload_type: u32, + // TODO parse this and use an enum instead? + pub feedback_type: String, +} + +#[derive(Clone)] +pub enum SdpAttributeDirection { + Recvonly, + Sendonly, + Sendrecv, +} + +#[derive(Clone)] +pub struct SdpAttributeExtmap { + pub id: u16, + pub direction: Option, + pub url: String, + pub extension_attributes: Option, +} + +#[derive(Clone)] +pub struct SdpAttributeFmtp { + pub payload_type: u8, + pub tokens: Vec, +} + +#[derive(Clone)] +pub struct SdpAttributeFingerprint { + // TODO turn the supported hash algorithms into an enum? + pub hash_algorithm: String, + pub fingerprint: String, +} + +#[derive(Clone)] +pub struct SdpAttributeSctpmap { + pub port: u16, + pub channels: u32, +} + +#[derive(Clone)] +pub enum SdpAttributeGroupSemantic { + LipSynchronization, + FlowIdentification, + SingleReservationFlow, + AlternateNetworkAddressType, + ForwardErrorCorrection, + DecodingDependency, + Bundle, +} + +#[derive(Clone)] +pub struct SdpAttributeGroup { + pub semantics: SdpAttributeGroupSemantic, + pub tags: Vec, +} + +#[derive(Clone)] +pub struct SdpAttributeMsid { + pub id: String, + pub appdata: Option, +} + +#[derive(Clone, Debug)] +pub struct SdpAttributeMsidSemantic { + pub semantic: String, + pub msids: Vec, +} + +#[derive(Clone)] +pub struct SdpAttributeRtpmap { + pub payload_type: u8, + pub codec_name: String, + pub frequency: u32, + pub channels: Option, +} + +impl SdpAttributeRtpmap { + pub fn new(payload_type: u8, codec_name: String, frequency: u32) -> SdpAttributeRtpmap { + SdpAttributeRtpmap { + payload_type, + codec_name, + frequency, + channels: None, + } + } + + fn set_channels(&mut self, c: u32) { + self.channels = Some(c) + } +} + +#[derive(Clone)] +pub enum SdpAttributeSetup { + Active, + Actpass, + Holdconn, + Passive, +} + +#[derive(Clone)] +pub struct SdpAttributeSsrc { + pub id: u32, + pub attribute: Option, + pub value: Option, +} + +impl SdpAttributeSsrc { + pub fn new(id: u32) -> SdpAttributeSsrc { + SdpAttributeSsrc { + id, + attribute: None, + value: None, + } + } + + fn set_attribute(&mut self, a: &str) { + if a.find(':') == None { + self.attribute = Some(a.to_string()); + } else { + let v: Vec<&str> = a.splitn(2, ':').collect(); + self.attribute = Some(v[0].to_string()); + self.value = Some(v[1].to_string()); + } + } +} + +#[derive(Clone)] +pub enum SdpAttribute { + BundleOnly, + Candidate(SdpAttributeCandidate), + EndOfCandidates, + Extmap(SdpAttributeExtmap), + Fingerprint(SdpAttributeFingerprint), + Fmtp(SdpAttributeFmtp), + Group(SdpAttributeGroup), + IceLite, + IceMismatch, + IceOptions(Vec), + IcePwd(String), + IceUfrag(String), + Identity(String), + ImageAttr(String), + Inactive, + Label(String), + MaxMessageSize(u64), + MaxPtime(u64), + Mid(String), + Msid(SdpAttributeMsid), + MsidSemantic(SdpAttributeMsidSemantic), + Ptime(u64), + Rid(String), + Recvonly, + RemoteCandidate(SdpAttributeRemoteCandidate), + Rtpmap(SdpAttributeRtpmap), + Rtcp(SdpAttributeRtcp), + Rtcpfb(SdpAttributeRtcpFb), + RtcpMux, + RtcpRsize, + Sctpmap(SdpAttributeSctpmap), + SctpPort(u64), + Sendonly, + Sendrecv, + Setup(SdpAttributeSetup), + Simulcast(SdpAttributeSimulcast), + Ssrc(SdpAttributeSsrc), + SsrcGroup(String), +} + +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::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::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, + } + } +} + +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::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; + + fn from_str(line: &str) -> Result { + let tokens: Vec<_> = line.splitn(2, ':').collect(); + let name = tokens[0].to_lowercase(); + let val = match tokens.get(1) { + Some(x) => x.trim(), + None => "", + }; + 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))); + } + _ => (), + } + } + match name.as_str() { + "bundle-only" => Ok(SdpAttribute::BundleOnly), + "end-of-candidates" => Ok(SdpAttribute::EndOfCandidates), + "ice-lite" => Ok(SdpAttribute::IceLite), + "ice-mismatch" => Ok(SdpAttribute::IceMismatch), + "ice-pwd" => Ok(SdpAttribute::IcePwd(string_or_empty(val)?)), + "ice-ufrag" => Ok(SdpAttribute::IceUfrag(string_or_empty(val)?)), + "identity" => Ok(SdpAttribute::Identity(string_or_empty(val)?)), + "imageattr" => Ok(SdpAttribute::ImageAttr(string_or_empty(val)?)), + "inactive" => Ok(SdpAttribute::Inactive), + "label" => Ok(SdpAttribute::Label(string_or_empty(val)?)), + "max-message-size" => Ok(SdpAttribute::MaxMessageSize(val.parse()?)), + "maxptime" => Ok(SdpAttribute::MaxPtime(val.parse()?)), + "mid" => Ok(SdpAttribute::Mid(string_or_empty(val)?)), + "msid-semantic" => parse_msid_semantic(val), + "ptime" => Ok(SdpAttribute::Ptime(val.parse()?)), + "rid" => Ok(SdpAttribute::Rid(string_or_empty(val)?)), + "recvonly" => Ok(SdpAttribute::Recvonly), + "rtcp-mux" => Ok(SdpAttribute::RtcpMux), + "rtcp-rsize" => Ok(SdpAttribute::RtcpRsize), + "sendonly" => Ok(SdpAttribute::Sendonly), + "sendrecv" => Ok(SdpAttribute::Sendrecv), + "ssrc-group" => Ok(SdpAttribute::SsrcGroup(string_or_empty(val)?)), + "sctp-port" => parse_sctp_port(val), + "candidate" => parse_candidate(val), + "extmap" => parse_extmap(val), + "fingerprint" => parse_fingerprint(val), + "fmtp" => parse_fmtp(val), + "group" => parse_group(val), + "ice-options" => parse_ice_options(val), + "msid" => parse_msid(val), + "remote-candidates" => parse_remote_candidates(val), + "rtpmap" => parse_rtpmap(val), + "rtcp" => parse_rtcp(val), + "rtcp-fb" => parse_rtcp_fb(val), + "sctpmap" => parse_sctpmap(val), + "setup" => parse_setup(val), + "simulcast" => parse_simulcast(val), + "ssrc" => parse_ssrc(val), + _ => { + Err(SdpParserInternalError::Unsupported(format!("Unknown attribute type {}", name))) + } + } + } +} + +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())) + } else { + Ok(to_parse.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))); + } + Ok(SdpAttribute::SctpPort(port)) +} + +fn parse_candidate(to_parse: &str) -> 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())); + } + 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())) + } + }; + 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())); + } + match tokens[6].to_lowercase().as_ref() { + "typ" => (), + _ => { + return Err(SdpParserInternalError::Generic("Candidate attribute token must be 'typ'" + .to_string())) + } + }; + let cand_type = match tokens[7].to_lowercase().as_ref() { + "host" => SdpAttributeCandidateType::Host, + "srflx" => SdpAttributeCandidateType::Srflx, + "prflx" => SdpAttributeCandidateType::Prflx, + "relay" => SdpAttributeCandidateType::Relay, + _ => 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); + if tokens.len() > 8 { + let mut index = 8; + while tokens.len() > index + 1 { + match tokens[index].to_lowercase().as_ref() { + "generation" => { + let generation = tokens[index + 1].parse::()?; + cand.set_generation(generation); + index += 2; + } + "network-cost" => { + let cost = tokens[index + 1].parse::()?; + cand.set_network_cost(cost); + index += 2; + } + "raddr" => { + let addr = parse_unicast_addr(tokens[index + 1])?; + cand.set_remote_address(addr); + index += 2; + } + "rport" => { + 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())); + } + 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())) + } + }); + index += 2; + } + "ufrag" => { + let ufrag = tokens[index + 1]; + cand.set_ufrag(ufrag.to_string()); + index += 2; + } + _ => { + return Err(SdpParserInternalError::Unsupported("Uknown candidate extension name" + .to_string())) + } + }; + } + } + Ok(SdpAttribute::Candidate(cand)) +} + +// ABNF for extmap is defined in RFC 5285 +// https://tools.ietf.org/html/rfc5285#section-7 +fn parse_extmap(to_parse: &str) -> 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())); + } + let id: u16; + let mut direction: Option = None; + if tokens[0].find('/') == None { + id = tokens[0].parse::()?; + } else { + 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())) + } + }) + } + // 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 { + None + } 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())); + } + Some(ext_string) + }; + Ok(SdpAttribute::Extmap(SdpAttributeExtmap { + id, + direction, + url: tokens[1].to_string(), + extension_attributes: 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())); + } + Ok(SdpAttribute::Fingerprint(SdpAttributeFingerprint { + hash_algorithm: tokens[0].to_string(), + fingerprint: tokens[1].to_string(), + })) +} + +fn parse_fmtp(to_parse: &str) -> Result { + let tokens: Vec<&str> = to_parse.split_whitespace().collect(); + if tokens.len() != 2 { + return Err(SdpParserInternalError::Generic("Fmtp needs to have two tokens".to_string())); + } + Ok(SdpAttribute::Fmtp(SdpAttributeFmtp { + // TODO check for dynamic PT range + payload_type: tokens[0].parse::()?, + // TODO this should probably be slit into known tokens + // plus a list of unknown tokens + tokens: to_parse.split(';').map(|x| x.to_string()).collect(), + })) +} + +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())) + } + 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, + _ => { + return Err(SdpParserInternalError::Generic("Unsupported group semantics" + .to_string())) + } + } + } + }; + Ok(SdpAttribute::Group(SdpAttributeGroup { + semantics, + tags: tokens.map(|x| x.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())); + } + Ok(SdpAttribute::IceOptions(to_parse.split_whitespace().map(|x| x.to_string()).collect())) +} + +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())) + } + Some(x) => x.to_string(), + }; + let appdata = match tokens.next() { + None => None, + 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())); + } + // 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(), + }; + Ok(SdpAttribute::MsidSemantic(semantic)) +} + +fn parse_remote_candidates(to_parse: &str) -> Result { + let mut tokens = to_parse.split_whitespace(); + let component = match tokens.next() { + None => { + return Err(SdpParserInternalError::Generic( + "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(), + )) + } + 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(), + )) + } + Some(x) => x.parse::()?, + }; + if port > 65535 { + return Err(SdpParserInternalError::Generic( + "Remote-candidate port can only be a bit 16bit number".to_string(), + )); + }; + Ok(SdpAttribute::RemoteCandidate(SdpAttributeRemoteCandidate { + 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())) + } + Some(x) => { + let pt = x.parse::()?; + if pt > 127 { + 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())) + } + Some(x) => x.split('/'), + }; + let name = match parameters.next() { + None => { + 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())) + } + Some(x) => x.parse::()?, + }; + let mut rtpmap = SdpAttributeRtpmap::new(payload_type, name, frequency); + match parameters.next() { + Some(x) => rtpmap.set_channels(x.parse::()?), + None => (), + }; + Ok(SdpAttribute::Rtpmap(rtpmap)) +} + +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())) + } + Some(x) => x.parse::()?, + }; + let mut rtcp = SdpAttributeRtcp::new(port); + match tokens.next() { + None => (), + Some(x) => { + parse_nettype(x)?; + match tokens.next() { + None => { + return Err(SdpParserInternalError::Generic( + "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(), + )) + } + 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(), + )); + } + addr + } + }; + rtcp.set_addr(addr); + } + }; + } + }; + Ok(SdpAttribute::Rtcp(rtcp)) +} + +fn parse_rtcp_fb(to_parse: &str) -> Result { + let tokens: Vec<&str> = to_parse.splitn(2, ' ').collect(); + Ok(SdpAttribute::Rtcpfb(SdpAttributeRtcpFb { + // TODO limit this to dymaic PTs + payload_type: tokens[0].parse::()?, + feedback_type: match tokens.get(1) { + Some(x) => x.to_string(), + None => { + return Err(SdpParserInternalError::Generic( + "Error parsing rtcpfb".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())); + } + let port = tokens[0].parse::()?; + if tokens[1].to_lowercase() != "webrtc-datachannel" { + return Err(SdpParserInternalError::Generic("Unsupported sctpmap type token".to_string())); + } + Ok(SdpAttribute::Sctpmap(SdpAttributeSctpmap { + 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(), + )) + } + })) +} + +fn parse_simulcast(to_parse: &str) -> Result { + let mut tokens = to_parse.split_whitespace(); + let mut token = match tokens.next() { + None => { + return Err(SdpParserInternalError::Generic( + "Simulcast attribute is missing send/recv value".to_string(), + )) + } + Some(x) => x, + }; + let mut sc = SdpAttributeSimulcast { + send: Vec::new(), + receive: Vec::new(), + }; + loop { + let sendrecv = match token.to_lowercase().as_ref() { + "send" => SdpAttributeDirection::Sendonly, + "recv" => SdpAttributeDirection::Recvonly, + _ => { + return Err(SdpParserInternalError::Generic( + "Unsupported send/recv value in simulcast attribute" + .to_string(), + )) + } + }; + match tokens.next() { + None => { + return Err(SdpParserInternalError::Generic("Simulcast attribute is missing id list" + .to_string())) + } + Some(x) => sc.parse_ids(sendrecv, x.to_string()), + }; + token = match tokens.next() { + None => { + break; + } + Some(x) => x, + }; + } + Ok(SdpAttribute::Simulcast(sc)) +} + +fn parse_ssrc(to_parse: &str) -> Result { + let mut tokens = to_parse.split_whitespace(); + let ssrc_id = match tokens.next() { + None => { + return Err(SdpParserInternalError::Generic("Ssrc attribute is missing ssrc-id value" + .to_string())) + } + Some(x) => x.parse::()?, + }; + let mut ssrc = SdpAttributeSsrc::new(ssrc_id); + match tokens.next() { + None => (), + Some(x) => ssrc.set_attribute(x), + }; + Ok(SdpAttribute::Ssrc(ssrc)) +} + +pub fn parse_attribute(value: &str) -> Result { + Ok(SdpType::Attribute(value.trim().parse()?)) +} + +#[test] +fn test_parse_attribute_candidate() { + assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ host").is_ok()); + assert!(parse_attribute("candidate:foo 1 UDP 2122252543 172.16.156.106 49760 typ host") + .is_ok()); + assert!(parse_attribute("candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host").is_ok()); + assert!(parse_attribute("candidate:0 1 TCP 2122252543 ::1 49760 typ host").is_ok()); + assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ srflx").is_ok()); + assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ prflx").is_ok()); + assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ relay").is_ok()); + assert!( + parse_attribute( + "candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host tcptype active" + ).is_ok() + ); + assert!( + parse_attribute( + "candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host tcptype passive" + ).is_ok() + ); + assert!( + parse_attribute("candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host tcptype so") + .is_ok() + ); + assert!( + parse_attribute("candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host ufrag foobar") + .is_ok() + ); + assert!( + parse_attribute( + "candidate:0 1 TCP 2122252543 172.16.156.106 49760 typ host network-cost 50" + ).is_ok() + ); + assert!(parse_attribute("candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 generation 0").is_ok()); + assert!(parse_attribute("candidate:1 1 UDP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665").is_ok()); + assert!(parse_attribute("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive").is_ok()); + assert!(parse_attribute("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive generation 1").is_ok()); + assert!(parse_attribute("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive generation 1 ufrag +DGd").is_ok()); + assert!(parse_attribute("candidate:1 1 TCP 1685987071 24.23.204.141 54609 typ srflx raddr 192.168.1.4 rport 61665 tcptype passive generation 1 ufrag +DGd network-cost 1").is_ok()); + + assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ").is_err()); + assert!(parse_attribute("candidate:0 foo UDP 2122252543 172.16.156.106 49760 typ host") + .is_err()); + assert!(parse_attribute("candidate:0 1 FOO 2122252543 172.16.156.106 49760 typ host").is_err()); + assert!(parse_attribute("candidate:0 1 UDP foo 172.16.156.106 49760 typ host").is_err()); + assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156 49760 typ host").is_err()); + assert!(parse_attribute("candidate:0 1 UDP 2122252543 172.16.156.106 70000 typ host").is_err()); + 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: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()); +} + +#[test] +fn test_parse_attribute_end_of_candidates() { + assert!(parse_attribute("end-of-candidates").is_ok()); + 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()); + + assert!(parse_attribute("extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time ext_attributes") + .is_ok()); + + 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 ",); + bad_char.push(0x00 as char); + assert!(parse_attribute(&bad_char).is_err()); +} + +#[test] +fn test_parse_attribute_fingerprint() { + 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:BF:2F:E3:91:CB:57:A9:9D:4A:A2:0B:40").is_ok()) +} + +#[test] +fn test_parse_attribute_fmtp() { + assert!(parse_attribute("fmtp:109 maxplaybackrate=48000;stereo=1;useinbandfec=1").is_ok()) +} + +#[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()); + + assert!(parse_attribute("group:").is_err()); + assert!(parse_attribute("group:NEVER_SUPPORTED_SEMANTICS").is_err()); +} + +#[test] +fn test_parse_attribute_bundle_only() { + assert!(parse_attribute("bundle-only").is_ok()); + assert!(parse_attribute("bundle-only foobar").is_err()); +} + +#[test] +fn test_parse_attribute_ice_lite() { + assert!(parse_attribute("ice-lite").is_ok()); + + assert!(parse_attribute("ice-lite foobar").is_err()); +} + +#[test] +fn test_parse_attribute_ice_mismatch() { + assert!(parse_attribute("ice-mismatch").is_ok()); + + assert!(parse_attribute("ice-mismatch foobar").is_err()); +} + +#[test] +fn test_parse_attribute_ice_options() { + assert!(parse_attribute("ice-options:trickle").is_ok()); + + assert!(parse_attribute("ice-options:").is_err()); +} + +#[test] +fn test_parse_attribute_ice_pwd() { + assert!(parse_attribute("ice-pwd:e3baa26dd2fa5030d881d385f1e36cce").is_ok()); + + assert!(parse_attribute("ice-pwd:").is_err()); +} + +#[test] +fn test_parse_attribute_ice_ufrag() { + assert!(parse_attribute("ice-ufrag:58b99ead").is_ok()); + + assert!(parse_attribute("ice-ufrag:").is_err()); +} + +#[test] +fn test_parse_attribute_identity() { + assert!(parse_attribute("identity:eyJpZHAiOnsiZG9tYWluIjoiZXhhbXBsZS5vcmciLCJwcm90b2NvbCI6ImJvZ3VzIn0sImFzc2VydGlvbiI6IntcImlkZW50aXR5XCI6XCJib2JAZXhhbXBsZS5vcmdcIixcImNvbnRlbnRzXCI6XCJhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3l6XCIsXCJzaWduYXR1cmVcIjpcIjAxMDIwMzA0MDUwNlwifSJ9").is_ok()); + + assert!(parse_attribute("identity:").is_err()); +} + +#[test] +fn test_parse_attribute_imageattr() { + assert!(parse_attribute("imageattr:120 send * recv *").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() + ); + 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()); + + assert!(parse_attribute("imageattr:").is_err()); +} + +#[test] +fn test_parse_attribute_inactive() { + assert!(parse_attribute("inactive").is_ok()); + 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()); + + assert!(parse_attribute("label:").is_err()); +} + +#[test] +fn test_parse_attribute_maxptime() { + assert!(parse_attribute("maxptime:60").is_ok()); + + 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()); + + 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() + ); + + assert!(parse_attribute("msid:").is_err()); +} + +#[test] +fn test_parse_attribute_msid_semantics() { + assert!(parse_attribute("msid-semantic:WMS *").is_ok()) +} + +#[test] +fn test_parse_attribute_ptime() { + assert!(parse_attribute("ptime:30").is_ok()); + + assert!(parse_attribute("ptime:").is_err()); +} + +#[test] +fn test_parse_attribute_rid() { + assert!(parse_attribute("rid:foo send").is_ok()); + assert!(parse_attribute("rid:foo").is_ok()); + + assert!(parse_attribute("rid:").is_err()); +} + +#[test] +fn test_parse_attribute_recvonly() { + assert!(parse_attribute("recvonly").is_ok()); + 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()); + + 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()); + assert!(parse_attribute("remote-candidates:0 10.0.0.1 70000").is_err()); + assert!(parse_attribute("remote-candidates:0 10.0.0.1").is_err()); + assert!(parse_attribute("remote-candidates:0").is_err()); + assert!(parse_attribute("remote-candidates:").is_err()); +} + +#[test] +fn test_parse_attribute_sendonly() { + assert!(parse_attribute("sendonly").is_ok()); + assert!(parse_attribute("sendonly foobar").is_err()); +} + +#[test] +fn test_parse_attribute_sendrecv() { + assert!(parse_attribute("sendrecv").is_ok()); + 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()); + + assert!(parse_attribute("setup:").is_err()); + assert!(parse_attribute("setup:foobar").is_err()); +} + +#[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()); + + assert!(parse_attribute("rtcp:").is_err()); + assert!(parse_attribute("rtcp:70000").is_err()); + assert!(parse_attribute("rtcp:9 IN").is_err()); + assert!(parse_attribute("rtcp:9 IN IP4").is_err()); + assert!(parse_attribute("rtcp:9 IN IP4 ::1").is_err()); +} + +#[test] +fn test_parse_attribute_rtcp_fb() { + assert!(parse_attribute("rtcp-fb:101 ccm fir").is_ok()) +} + +#[test] +fn test_parse_attribute_rtcp_mux() { + assert!(parse_attribute("rtcp-mux").is_ok()); + assert!(parse_attribute("rtcp-mux foobar").is_err()); +} + +#[test] +fn test_parse_attribute_rtcp_rsize() { + assert!(parse_attribute("rtcp-rsize").is_ok()); + 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()); + + assert!(parse_attribute("rtpmap:109 ").is_err()); + assert!(parse_attribute("rtpmap:109 opus").is_err()); + assert!(parse_attribute("rtpmap:128 opus/48000").is_err()); +} + +#[test] +fn test_parse_attribute_sctpmap() { + assert!(parse_attribute("sctpmap:5000 webrtc-datachannel 256").is_ok()); + + assert!(parse_attribute("sctpmap:70000 webrtc-datachannel 256").is_err()); + assert!(parse_attribute("sctpmap:5000 unsupported 256").is_err()); + assert!(parse_attribute("sctpmap:5000 webrtc-datachannel 2a").is_err()); +} + +#[test] +fn test_parse_attribute_sctp_port() { + assert!(parse_attribute("sctp-port:5000").is_ok()); + + assert!(parse_attribute("sctp-port:").is_err()); + assert!(parse_attribute("sctp-port:70000").is_err()); +} + +#[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()); + + assert!(parse_attribute("max-message-size:").is_err()); + assert!(parse_attribute("max-message-size:abc").is_err()); +} + +#[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()); + // old draft 03 notation used by Firefox 55 + assert!(parse_attribute("simulcast: send rid=foo;bar").is_ok()); + + assert!(parse_attribute("simulcast:").is_err()); + assert!(parse_attribute("simulcast:send").is_err()); + assert!(parse_attribute("simulcast:foobar 1").is_err()); + assert!(parse_attribute("simulcast:send 1 foobar 2").is_err()); +} + +#[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:").is_err()); + assert!(parse_attribute("ssrc:foo").is_err()); +} + +#[test] +fn test_parse_attribute_ssrc_group() { + assert!(parse_attribute("ssrc-group:FID 3156517279 2673335628").is_ok()) +} + +#[test] +fn test_parse_unknown_attribute() { + assert!(parse_attribute("unknown").is_err()) +} + +// Returns true if valid byte-string as defined by RFC 4566 +// https://tools.ietf.org/html/rfc4566 +fn valid_byte_string(input: &str) -> bool { + !(input.contains(0x00 as char) || input.contains(0x0A as char) || input.contains(0x0D as char)) +} diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/file_parser.rs b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/file_parser.rs new file mode 100644 index 000000000000..04ba61c81c9d --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/file_parser.rs @@ -0,0 +1,35 @@ +use std::error::Error; +use std::io::prelude::*; +use std::fs::File; +use std::path::Path; +use std::env; +extern crate rsdparsa; + +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 + }; + + let mut s = String::new(); + match file.read_to_string(&mut s) { + Err(why) => panic!("couldn't read {}: {}", + display, + why.description()), + Ok(s) => s + }; + + rsdparsa::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/src/bin/sdps/10.sdp new file mode 100644 index 000000000000..4b5cadc98667 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/10.sdp @@ -0,0 +1,13 @@ +v=0 +o=- 4294967296 2 IN IP4 127.0.0.1 +s=SIP Call +c=IN IP4 198.51.100.7 +t=0 0 +m=video 9 RTP/SAVPF 97 120 121 122 123 +c=IN IP6 ::1 +a=fingerprint:sha-1 DF:FA:FB:08:3B:3C:54:1D:D7:D4:05:77:A0:72:9B:14:08:6D:0F:4C:2E:AC:8A:FD:0A:8E:99:BF:5D:E8:3C:E7 +a=rtpmap:97 H264/90000 +a=rtpmap:120 VP8/90000 +a=rtpmap:121 VP9/90000 +a=rtpmap:122 red/90000 +a=rtpmap:123 ulpfec/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/11.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/11.sdp new file mode 100644 index 000000000000..652f613728da --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/11.sdp @@ -0,0 +1,63 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=ice-ufrag:4a799b2e +a=ice-pwd:e4cc12a910f106a0a744719425510e17 +a=ice-lite +a=msid-semantic:WMS stream streama +a=fingerprint:sha-256 DF:2E:AC:8A:FD:0A:8E:99:BF:5D:E8:3C:E7:FA:FB:08:3B:3C:54:1D:D7:D4:05:77:A0:72:9B:14:08:6D:0F:4C +a=group:BUNDLE first second +a=group:BUNDLE third +a=group:LS first third +m=audio 9 RTP/SAVPF 109 9 0 8 101 +c=IN IP4 0.0.0.0 +a=mid:first +a=rtpmap:109 opus/48000/2 +a=ptime:20 +a=maxptime:20 +a=rtpmap:9 G722/8000 +a=rtpmap:0 PCMU/8000 +a=rtpmap:8 PCMA/8000 +a=rtpmap:101 telephone-event/8000 +a=fmtp:101 0-15,66,32-34,67 +a=ice-ufrag:00000000 +a=ice-pwd:0000000000000000000000000000000 +a=sendonly +a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level +a=setup:actpass +a=rtcp-mux +a=msid:stream track +a=candidate:0 1 UDP 2130379007 10.0.0.36 62453 typ host +a=candidate:2 1 UDP 1694236671 24.6.134.204 62453 typ srflx raddr 10.0.0.36 rport 62453 +a=candidate:3 1 UDP 100401151 162.222.183.171 49761 typ relay raddr 162.222.183.171 rport 49761 +a=candidate:6 1 UDP 16515071 162.222.183.171 51858 typ relay raddr 162.222.183.171 rport 51858 +a=candidate:3 2 UDP 100401150 162.222.183.171 62454 typ relay raddr 162.222.183.171 rport 62454 +a=candidate:2 2 UDP 1694236670 24.6.134.204 55428 typ srflx raddr 10.0.0.36 rport 55428 +a=candidate:6 2 UDP 16515070 162.222.183.171 50340 typ relay raddr 162.222.183.171 rport 50340 +a=candidate:0 2 UDP 2130379006 10.0.0.36 55428 typ host +m=video 9 RTP/SAVPF 97 98 120 +c=IN IP6 ::1 +a=mid:second +a=rtpmap:97 H264/90000 +a=rtpmap:98 H264/90000 +a=rtpmap:120 VP8/90000 +a=recvonly +a=setup:active +a=rtcp-mux +a=msid:streama tracka +a=msid:streamb trackb +a=candidate:0 1 UDP 2130379007 10.0.0.36 59530 typ host +a=candidate:0 2 UDP 2130379006 10.0.0.36 64378 typ host +a=candidate:2 2 UDP 1694236670 24.6.134.204 64378 typ srflx raddr 10.0.0.36 rport 64378 +a=candidate:6 2 UDP 16515070 162.222.183.171 64941 typ relay raddr 162.222.183.171 rport 64941 +a=candidate:6 1 UDP 16515071 162.222.183.171 64800 typ relay raddr 162.222.183.171 rport 64800 +a=candidate:2 1 UDP 1694236671 24.6.134.204 59530 typ srflx raddr 10.0.0.36 rport 59530 +a=candidate:3 1 UDP 100401151 162.222.183.171 62935 typ relay raddr 162.222.183.171 rport 62935 +a=candidate:3 2 UDP 100401150 162.222.183.171 61026 typ relay raddr 162.222.183.171 rport 61026 +m=audio 9 RTP/SAVPF 0 +a=mid:third +a=rtpmap:0 PCMU/8000 +a=ice-lite +a=msid:noappdata diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/12.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/12.sdp new file mode 100644 index 000000000000..647de5dca83f --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/12.sdp @@ -0,0 +1,58 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 27987 0 IN IP4 0.0.0.0 +s=SIP Call +t=0 0 +a=ice-ufrag:8a39d2ae +a=ice-pwd:601d53aba51a318351b3ecf5ee00048f +a=fingerprint:sha-256 30:FF:8E:2B:AC:9D:ED:70:18:10:67:C8:AE:9E:68:F3:86:53:51:B0:AC:31:B7:BE:6D:CF:A4:2E:D3:6E:B4:28 +m=audio 9 RTP/SAVPF 109 9 0 8 101 +c=IN IP4 0.0.0.0 +a=rtpmap:109 opus/48000/2 +a=ptime:20 +a=rtpmap:9 G722/8000 +a=rtpmap:0 PCMU/8000 +a=rtpmap:8 PCMA/8000 +a=rtpmap:101 telephone-event/8000 +a=fmtp:101 0-15 +a=sendrecv +a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level +a=extmap:2/sendonly some_extension +a=extmap:3 some_other_extension some_params some more params +a=setup:actpass +a=rtcp-mux +m=video 9 RTP/SAVPF 120 126 97 +c=IN IP4 0.0.0.0 +a=rtpmap:120 VP8/90000 +a=rtpmap:126 H264/90000 +a=rtpmap:97 H264/90000 +a=sendrecv +a=rtcp-fb:120 ack rpsi +a=rtcp-fb:120 ack app foo +a=rtcp-fb:120 ack foo +a=rtcp-fb:120 nack +a=rtcp-fb:120 nack sli +a=rtcp-fb:120 nack pli +a=rtcp-fb:120 nack rpsi +a=rtcp-fb:120 nack app foo +a=rtcp-fb:120 nack foo +a=rtcp-fb:120 ccm fir +a=rtcp-fb:120 ccm tmmbr +a=rtcp-fb:120 ccm tstr +a=rtcp-fb:120 ccm vbcm +a=rtcp-fb:120 ccm foo +a=rtcp-fb:120 trr-int 10 +a=rtcp-fb:120 goog-remb +a=rtcp-fb:120 foo +a=rtcp-fb:126 nack +a=rtcp-fb:126 nack pli +a=rtcp-fb:126 ccm fir +a=rtcp-fb:97 nack +a=rtcp-fb:97 nack pli +a=rtcp-fb:97 ccm fir +a=rtcp-fb:* ccm tmmbr +a=setup:actpass +a=rtcp-mux +m=application 9 DTLS/SCTP 5000 +c=IN IP4 0.0.0.0 +a=sctpmap:5000 webrtc-datachannel 16 +a=setup:actpass diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/13.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/13.sdp new file mode 100644 index 000000000000..31c3d8d86bfc --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/13.sdp @@ -0,0 +1,12 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 27987 0 IN IP4 0.0.0.0 +s=SIP Call +t=0 0 +a=ice-ufrag:8a39d2ae +a=ice-pwd:601d53aba51a318351b3ecf5ee00048f +a=fingerprint:sha-256 30:FF:8E:2B:AC:9D:ED:70:18:10:67:C8:AE:9E:68:F3:86:53:51:B0:AC:31:B7:BE:6D:CF:A4:2E:D3:6E:B4:28 +m=application 9 UDP/DTLS/SCTP webrtc-datachannel +c=IN IP4 0.0.0.0 +a=sctp-port:5000 +a=max-message-size:10000 +a=setup:actpass diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/14.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/14.sdp new file mode 100644 index 000000000000..cad2a9a1997d --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/14.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=candidate:0 1 UDP 2130379007 10.0.0.36 62453 typ host +m=audio 9 RTP/SAVPF 109 9 0 8 101 +c=IN IP4 0.0.0.0 +a=rtpmap:109 opus/48000/2 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/15.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/15.sdp new file mode 100644 index 000000000000..d356ecaf1feb --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/15.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=bundle-only +m=audio 9 RTP/SAVPF 109 9 0 8 101 +c=IN IP4 0.0.0.0 +a=rtpmap:109 opus/48000/2 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/16.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/16.sdp new file mode 100644 index 000000000000..67326a6e9a27 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/16.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=fmtp:109 0-15 +m=audio 9 RTP/SAVPF 109 9 0 8 101 +c=IN IP4 0.0.0.0 +a=rtpmap:109 opus/48000/2 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/17.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/17.sdp new file mode 100644 index 000000000000..002322219102 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/17.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=ice-mismatch +m=audio 9 RTP/SAVPF 109 9 0 8 101 +c=IN IP4 0.0.0.0 +a=rtpmap:109 opus/48000/2 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/18.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/18.sdp new file mode 100644 index 000000000000..3287af64134b --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/18.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=imageattr:120 send * recv * +m=video 9 RTP/SAVPF 120 +c=IN IP4 0.0.0.0 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/19.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/19.sdp new file mode 100644 index 000000000000..20bb0a8058b7 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/19.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=label:foobar +m=video 9 RTP/SAVPF 120 +c=IN IP4 0.0.0.0 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/2.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/2.sdp new file mode 100644 index 000000000000..21d383d2f255 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/2.sdp @@ -0,0 +1,7 @@ +v=0 +o=- 4294967296 2 IN IP4 127.0.0.1 +s=SIP Call +c=IN IP4 198.51.100.7 +t=0 0 +m=video 56436 RTP/SAVPF 120 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/20.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/20.sdp new file mode 100644 index 000000000000..4d28e12a9f8c --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/20.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=maxptime:100 +m=video 9 RTP/SAVPF 120 +c=IN IP4 0.0.0.0 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/21.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/21.sdp new file mode 100644 index 000000000000..7a2d6fa9dce4 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/21.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=mid:foobar +m=video 9 RTP/SAVPF 120 +c=IN IP4 0.0.0.0 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/22.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/22.sdp new file mode 100644 index 000000000000..95e5518123a6 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/22.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=msid:foobar +m=video 9 RTP/SAVPF 120 +c=IN IP4 0.0.0.0 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/23.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/23.sdp new file mode 100644 index 000000000000..7722360f3214 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/23.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=ptime:50 +m=video 9 RTP/SAVPF 120 +c=IN IP4 0.0.0.0 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/24.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/24.sdp new file mode 100644 index 000000000000..dd13f1e143f8 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/24.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=remote-candidates:0 10.0.0.1 5555 +m=video 9 RTP/SAVPF 120 +c=IN IP4 0.0.0.0 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/25.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/25.sdp new file mode 100644 index 000000000000..7fd8a006fb71 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/25.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=rtcp:5555 +m=video 9 RTP/SAVPF 120 +c=IN IP4 0.0.0.0 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/26.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/26.sdp new file mode 100644 index 000000000000..789e0627668b --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/26.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=rtcp-fb:120 nack +m=video 9 RTP/SAVPF 120 +c=IN IP4 0.0.0.0 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/27.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/27.sdp new file mode 100644 index 000000000000..f8201916e99f --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/27.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=rtcp-mux +m=video 9 RTP/SAVPF 120 +c=IN IP4 0.0.0.0 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/28.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/28.sdp new file mode 100644 index 000000000000..11d9331b1085 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/28.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=rtcp-rsize +m=video 9 RTP/SAVPF 120 +c=IN IP4 0.0.0.0 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/29.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/29.sdp new file mode 100644 index 000000000000..f34b97fe5860 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/29.sdp @@ -0,0 +1,8 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=rtpmap:120 VP8/90000 +m=video 9 RTP/SAVPF 120 +c=IN IP4 0.0.0.0 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/3.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/3.sdp new file mode 100644 index 000000000000..f60f31f2da35 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/3.sdp @@ -0,0 +1,34 @@ +v=0 +o=- 4294967296 2 IN IP4 127.0.0.1 +s=SIP Call +c=IN IP4 198.51.100.7 +t=0 0 +m=video 56436 RTP/SAVPF 120 +a=rtpmap:120 VP8/90000 +a=rtpmap:122 red/90000 +a=rtcp-fb:120 ack rpsi +a=rtcp-fb:120 ack app +a=rtcp-fb:120 ack app foo +a=rtcp-fb:120 ack foo bar +a=rtcp-fb:120 ack foo bar baz +a=rtcp-fb:120 nack +a=rtcp-fb:120 nack pli +a=rtcp-fb:120 nack sli +a=rtcp-fb:120 nack rpsi +a=rtcp-fb:120 nack app +a=rtcp-fb:120 nack app foo +a=rtcp-fb:120 nack app foo bar +a=rtcp-fb:120 nack foo bar baz +a=rtcp-fb:120 trr-int 0 +a=rtcp-fb:120 trr-int 123 +a=rtcp-fb:120 goog-remb +a=rtcp-fb:120 ccm fir +a=rtcp-fb:120 ccm tmmbr +a=rtcp-fb:120 ccm tstr +a=rtcp-fb:120 ccm vbcm 123 456 789 +a=rtcp-fb:120 ccm foo +a=rtcp-fb:120 ccm foo bar baz +a=rtcp-fb:120 foo +a=rtcp-fb:120 foo bar +a=rtcp-fb:120 foo bar baz +a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/30.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/30.sdp new file mode 100644 index 000000000000..202ea88c0846 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/30.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=sctpmap:5000 +m=video 9 RTP/SAVPF 120 +c=IN IP4 0.0.0.0 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/31.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/31.sdp new file mode 100644 index 000000000000..cd510e1d4670 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/31.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=ssrc:5000 +m=video 9 RTP/SAVPF 120 +c=IN IP4 0.0.0.0 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/32.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/32.sdp new file mode 100644 index 000000000000..45696947686d --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/32.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=ssrc-group:FID 5000 +m=video 9 RTP/SAVPF 120 +c=IN IP4 0.0.0.0 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/33.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/33.sdp new file mode 100644 index 000000000000..179e048da8e6 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/33.sdp @@ -0,0 +1,9 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +m=video 9 RTP/SAVPF 120 +c=IN IP4 0.0.0.0 +a=rtpmap:120 VP8/90000 +a=imageattr:flob diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/34.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/34.sdp new file mode 100644 index 000000000000..39ef01206ded --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/34.sdp @@ -0,0 +1,9 @@ +v=0 +o=- 4294967296 2 IN IP4 127.0.0.1 +s=SIP Call +c=IN IP4 198.51.100.7 +b=CT:5000 +t=0 0 +m=video 56436 RTP/SAVPF 120 +a=rtpmap:120 VP8/90000 +a=sendrecv diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/35.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/35.sdp new file mode 100644 index 000000000000..39ef01206ded --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/35.sdp @@ -0,0 +1,9 @@ +v=0 +o=- 4294967296 2 IN IP4 127.0.0.1 +s=SIP Call +c=IN IP4 198.51.100.7 +b=CT:5000 +t=0 0 +m=video 56436 RTP/SAVPF 120 +a=rtpmap:120 VP8/90000 +a=sendrecv diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/36.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/36.sdp new file mode 100644 index 000000000000..39ef01206ded --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/36.sdp @@ -0,0 +1,9 @@ +v=0 +o=- 4294967296 2 IN IP4 127.0.0.1 +s=SIP Call +c=IN IP4 198.51.100.7 +b=CT:5000 +t=0 0 +m=video 56436 RTP/SAVPF 120 +a=rtpmap:120 VP8/90000 +a=sendrecv diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/37.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/37.sdp new file mode 100644 index 000000000000..f03bbaa8efde --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/37.sdp @@ -0,0 +1,9 @@ +v=0 +o=- 4294967296 2 IN IP4 127.0.0.1 +s=SIP Call +c=IN IP4 198.51.100.7 +b=CT:5000 +t=0 0 +m=video 56436 RTP/SAVPF 120 +a=rtpmap:120 VP8/90000 +a=recvonly diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/38.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/38.sdp new file mode 100644 index 000000000000..bbe385d9842e --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/38.sdp @@ -0,0 +1,9 @@ +v=0 +o=- 4294967296 2 IN IP4 127.0.0.1 +s=SIP Call +c=IN IP4 198.51.100.7 +b=CT:5000 +t=0 0 +m=video 56436 RTP/SAVPF 120 +a=rtpmap:120 VP8/90000 +a=sendonly diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/39.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/39.sdp new file mode 100644 index 000000000000..723d2abf7f03 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/39.sdp @@ -0,0 +1,8 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +m=video 9 RTP/SAVPF 120 +c=IN IP4 0.0.0.0 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/4.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/4.sdp new file mode 100644 index 000000000000..0682623c93a4 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/4.sdp @@ -0,0 +1,7 @@ +v=0 +o=- 4294967296 2 IN IP4 127.0.0.1 +s=SIP Call +t=0 0 +m=video 56436 RTP/SAVPF 120 +c=IN IP4 198.51.100.7 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/40.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/40.sdp new file mode 100644 index 000000000000..723d2abf7f03 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/40.sdp @@ -0,0 +1,8 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +m=video 9 RTP/SAVPF 120 +c=IN IP4 0.0.0.0 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/41.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/41.sdp new file mode 100644 index 000000000000..753ca5ccb40b --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/41.sdp @@ -0,0 +1,91 @@ +v=0 +o=- 1109973417102828257 2 IN IP4 127.0.0.1 +s=- +t=0 0 +a=group:BUNDLE audio video +a=msid-semantic: WMS 1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIP +m=audio 32952 UDP/TLS/RTP/SAVPF 111 103 104 0 8 107 106 105 13 126 +c=IN IP4 128.64.32.16 +a=rtcp:32952 IN IP4 128.64.32.16 +a=candidate:77142221 1 udp 2113937151 192.168.137.1 54081 typ host generation 0 +a=candidate:77142221 2 udp 2113937151 192.168.137.1 54081 typ host generation 0 +a=candidate:983072742 1 udp 2113937151 172.22.0.56 54082 typ host generation 0 +a=candidate:983072742 2 udp 2113937151 172.22.0.56 54082 typ host generation 0 +a=candidate:2245074553 1 udp 1845501695 32.64.128.1 62397 typ srflx raddr 192.168.137.1 rport 54081 generation 0 +a=candidate:2245074553 2 udp 1845501695 32.64.128.1 62397 typ srflx raddr 192.168.137.1 rport 54081 generation 0 +a=candidate:2479353907 1 udp 1845501695 32.64.128.1 54082 typ srflx raddr 172.22.0.56 rport 54082 generation 0 +a=candidate:2479353907 2 udp 1845501695 32.64.128.1 54082 typ srflx raddr 172.22.0.56 rport 54082 generation 0 +a=candidate:1243276349 1 tcp 1509957375 192.168.137.1 0 typ host generation 0 +a=candidate:1243276349 2 tcp 1509957375 192.168.137.1 0 typ host generation 0 +a=candidate:1947960086 1 tcp 1509957375 172.22.0.56 0 typ host generation 0 +a=candidate:1947960086 2 tcp 1509957375 172.22.0.56 0 typ host generation 0 +a=candidate:1808221584 1 udp 33562367 128.64.32.16 32952 typ relay raddr 32.64.128.1 rport 62398 generation 0 +a=candidate:1808221584 2 udp 33562367 128.64.32.16 32952 typ relay raddr 32.64.128.1 rport 62398 generation 0 +a=candidate:507872740 1 udp 33562367 128.64.32.16 40975 typ relay raddr 32.64.128.1 rport 54085 generation 0 +a=candidate:507872740 2 udp 33562367 128.64.32.16 40975 typ relay raddr 32.64.128.1 rport 54085 generation 0 +a=ice-ufrag:xQuJwjX3V3eMA81k +a=ice-pwd:ZUiRmjS2GDhG140p73dAsSVP +a=ice-options:google-ice +a=fingerprint:sha-256 59:4A:8B:73:A7:73:53:71:88:D7:4D:58:28:0C:79:72:31:29:9B:05:37:DD:58:43:C2:D4:85:A2:B3:66:38:7A +a=setup:active +a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level +a=sendrecv +a=mid:audio +a=rtcp-mux +a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:/U44g3ULdtapeiSg+T3n6dDLBKIjpOhb/NXAL/2b +a=rtpmap:111 opus/48000/2 +a=rtpmap:103 ISAC/16000 +a=rtpmap:104 ISAC/32000 +a=rtpmap:0 PCMU/8000 +a=rtpmap:8 PCMA/8000 +a=rtpmap:107 CN/48000 +a=rtpmap:106 CN/32000 +a=rtpmap:105 CN/16000 +a=rtpmap:13 CN/8000 +a=rtpmap:126 telephone-event/8000 +a=maxptime:60 +a=ssrc:2271517329 cname:mKDNt7SQf6pwDlIn +a=ssrc:2271517329 msid:1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIP 1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIPa0 +a=ssrc:2271517329 mslabel:1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIP +a=ssrc:2271517329 label:1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIPa0 +m=video 32952 UDP/TLS/RTP/SAVPF 100 116 117 +c=IN IP4 128.64.32.16 +a=rtcp:32952 IN IP4 128.64.32.16 +a=candidate:77142221 1 udp 2113937151 192.168.137.1 54081 typ host generation 0 +a=candidate:77142221 2 udp 2113937151 192.168.137.1 54081 typ host generation 0 +a=candidate:983072742 1 udp 2113937151 172.22.0.56 54082 typ host generation 0 +a=candidate:983072742 2 udp 2113937151 172.22.0.56 54082 typ host generation 0 +a=candidate:2245074553 1 udp 1845501695 32.64.128.1 62397 typ srflx raddr 192.168.137.1 rport 54081 generation 0 +a=candidate:2245074553 2 udp 1845501695 32.64.128.1 62397 typ srflx raddr 192.168.137.1 rport 54081 generation 0 +a=candidate:2479353907 1 udp 1845501695 32.64.128.1 54082 typ srflx raddr 172.22.0.56 rport 54082 generation 0 +a=candidate:2479353907 2 udp 1845501695 32.64.128.1 54082 typ srflx raddr 172.22.0.56 rport 54082 generation 0 +a=candidate:1243276349 1 tcp 1509957375 192.168.137.1 0 typ host generation 0 +a=candidate:1243276349 2 tcp 1509957375 192.168.137.1 0 typ host generation 0 +a=candidate:1947960086 1 tcp 1509957375 172.22.0.56 0 typ host generation 0 +a=candidate:1947960086 2 tcp 1509957375 172.22.0.56 0 typ host generation 0 +a=candidate:1808221584 1 udp 33562367 128.64.32.16 32952 typ relay raddr 32.64.128.1 rport 62398 generation 0 +a=candidate:1808221584 2 udp 33562367 128.64.32.16 32952 typ relay raddr 32.64.128.1 rport 62398 generation 0 +a=candidate:507872740 1 udp 33562367 128.64.32.16 40975 typ relay raddr 32.64.128.1 rport 54085 generation 0 +a=candidate:507872740 2 udp 33562367 128.64.32.16 40975 typ relay raddr 32.64.128.1 rport 54085 generation 0 +a=ice-ufrag:xQuJwjX3V3eMA81k +a=ice-pwd:ZUiRmjS2GDhG140p73dAsSVP +a=ice-options:google-ice +a=fingerprint:sha-256 59:4A:8B:73:A7:73:53:71:88:D7:4D:58:28:0C:79:72:31:29:9B:05:37:DD:58:43:C2:D4:85:A2:B3:66:38:7A +a=setup:active +a=extmap:2 urn:ietf:params:rtp-hdrext:toffset +a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time +a=sendrecv +a=mid:video +a=rtcp-mux +a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:/U44g3ULdtapeiSg+T3n6dDLBKIjpOhb/NXAL/2b +a=rtpmap:100 VP8/90000 +a=rtcp-fb:100 ccm fir +a=rtcp-fb:100 nack +a=rtcp-fb:100 goog-remb +a=rtpmap:116 red/90000 +a=rtpmap:117 ulpfec/90000 +a=ssrc:54724160 cname:mKDNt7SQf6pwDlIn +a=ssrc:54724160 msid:1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIP 1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIPv0 +a=ssrc:54724160 mslabel:1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIP +a=ssrc:54724160 label:1PBxet5BYh0oYodwsvNM4k6KiO2eWCX40VIPv0 + diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/5.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/5.sdp new file mode 100644 index 000000000000..f575af373085 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/5.sdp @@ -0,0 +1,5 @@ +v=0 +o=- 4294967296 2 IN IP4 127.0.0.1 +s=SIP Call +t=0 0 +a=ice-lite diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/6.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/6.sdp new file mode 100644 index 000000000000..d20cb3fc70b7 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/6.sdp @@ -0,0 +1,12 @@ +v=0 +o=- 4294967296 2 IN IP4 127.0.0.1 +s=SIP Call +c=IN IP4 198.51.100.7 +b=CT:5000 +b=FOOBAR:10 +b=AS:4 +t=0 0 +m=video 56436 RTP/SAVPF 120 +a=rtpmap:120 VP8/90000 +m=audio 12345/2 RTP/SAVPF 0 +a=rtpmap:0 PCMU/8000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/7.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/7.sdp new file mode 100644 index 000000000000..1fe28e7253c4 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/7.sdp @@ -0,0 +1,7 @@ +v=0 +o=- 4294967296 2 IN IP4 127.0.0.1 +c=IN IP4 198.51.100.7 +t=0 0 +m=video 56436 RTP/SAVPF 120 +b=CT:1000 +a=rtpmap:120 VP8/90000 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/8.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/8.sdp new file mode 100644 index 000000000000..6eda93cdbf13 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/8.sdp @@ -0,0 +1,84 @@ +v=0 +o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0 +s=SIP Call +c=IN IP4 224.0.0.1/100/12 +t=0 0 +a=ice-ufrag:4a799b2e +a=ice-pwd:e4cc12a910f106a0a744719425510e17 +a=ice-lite +a=ice-options:trickle foo +a=msid-semantic:WMS stream streama +a=msid-semantic:foo stream +a=fingerprint:sha-256 DF:2E:AC:8A:FD:0A:8E:99:BF:5D:E8:3C:E7:FA:FB:08:3B:3C:54:1D:D7:D4:05:77:A0:72:9B:14:08:6D:0F:4C +a=identity:eyJpZHAiOnsiZG9tYWluIjoiZXhhbXBsZS5vcmciLCJwcm90b2NvbCI6ImJvZ3VzIn0sImFzc2VydGlvbiI6IntcImlkZW50aXR5XCI6XCJib2JAZXhhbXBsZS5vcmdcIixcImNvbnRlbnRzXCI6XCJhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3l6XCIsXCJzaWduYXR1cmVcIjpcIjAxMDIwMzA0MDUwNlwifSJ9 +a=group:BUNDLE first second +a=group:BUNDLE third +a=group:LS first third +m=audio 9 RTP/SAVPF 109 9 0 8 101 +c=IN IP4 0.0.0.0 +a=mid:first +a=rtpmap:109 opus/48000/2 +a=ptime:20 +a=maxptime:20 +a=rtpmap:9 G722/8000 +a=rtpmap:0 PCMU/8000 +a=rtpmap:8 PCMA/8000 +a=rtpmap:101 telephone-event/8000 +a=fmtp:101 0-15,66,32-34,67 +a=ice-ufrag:00000000 +a=ice-pwd:0000000000000000000000000000000 +a=sendonly +a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level +a=setup:actpass +a=rtcp-mux +a=msid:stream track +a=candidate:0 1 UDP 2130379007 10.0.0.36 62453 typ host +a=candidate:2 1 UDP 1694236671 24.6.134.204 62453 typ srflx raddr 10.0.0.36 rport 62453 +a=candidate:3 1 UDP 100401151 162.222.183.171 49761 typ relay raddr 162.222.183.171 rport 49761 +a=candidate:6 1 UDP 16515071 162.222.183.171 51858 typ relay raddr 162.222.183.171 rport 51858 +a=candidate:3 2 UDP 100401150 162.222.183.171 62454 typ relay raddr 162.222.183.171 rport 62454 +a=candidate:2 2 UDP 1694236670 24.6.134.204 55428 typ srflx raddr 10.0.0.36 rport 55428 +a=candidate:6 2 UDP 16515070 162.222.183.171 50340 typ relay raddr 162.222.183.171 rport 50340 +a=candidate:0 2 UDP 2130379006 10.0.0.36 55428 typ host +a=rtcp:62454 IN IP4 162.222.183.171 +a=end-of-candidates +a=ssrc:5150 +m=video 9 RTP/SAVPF 120 121 122 123 +c=IN IP6 ::1 +a=fingerprint:sha-1 DF:FA:FB:08:3B:3C:54:1D:D7:D4:05:77:A0:72:9B:14:08:6D:0F:4C:2E:AC:8A:FD:0A:8E:99:BF:5D:E8:3C:E7 +a=mid:second +a=rtpmap:120 VP8/90000 +a=rtpmap:121 VP9/90000 +a=rtpmap:122 red/90000 +a=rtpmap:123 ulpfec/90000 +a=recvonly +a=rtcp-fb:120 nack +a=rtcp-fb:120 nack pli +a=rtcp-fb:120 ccm fir +a=rtcp-fb:121 nack +a=rtcp-fb:121 nack pli +a=rtcp-fb:121 ccm fir +a=setup:active +a=rtcp-mux +a=msid:streama tracka +a=msid:streamb trackb +a=candidate:0 1 UDP 2130379007 10.0.0.36 59530 typ host +a=candidate:0 2 UDP 2130379006 10.0.0.36 64378 typ host +a=candidate:2 2 UDP 1694236670 24.6.134.204 64378 typ srflx raddr 10.0.0.36 rport 64378 +a=candidate:6 2 UDP 16515070 162.222.183.171 64941 typ relay raddr 162.222.183.171 rport 64941 +a=candidate:6 1 UDP 16515071 162.222.183.171 64800 typ relay raddr 162.222.183.171 rport 64800 +a=candidate:2 1 UDP 1694236671 24.6.134.204 59530 typ srflx raddr 10.0.0.36 rport 59530 +a=candidate:3 1 UDP 100401151 162.222.183.171 62935 typ relay raddr 162.222.183.171 rport 62935 +a=candidate:3 2 UDP 100401150 162.222.183.171 61026 typ relay raddr 162.222.183.171 rport 61026 +a=rtcp:61026 +a=end-of-candidates +a=ssrc:1111 foo +a=ssrc:1111 foo:bar +a=imageattr:120 send * recv * +m=audio 9 RTP/SAVPF 0 +a=mid:third +a=rtpmap:0 PCMU/8000 +a=ice-lite +a=ice-options:foo bar +a=msid:noappdata +a=bundle-only diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/9.sdp b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/9.sdp new file mode 100644 index 000000000000..f5c4acdbd8c6 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/9.sdp @@ -0,0 +1,34 @@ +v=0 +o=- 4294967296 2 IN IP4 127.0.0.1 +s=SIP Call +c=IN IP4 198.51.100.7 +t=0 0 +m=audio 9 RTP/SAVPF 109 9 0 8 101 +c=IN IP4 0.0.0.0 +a=mid:first +a=rtpmap:109 opus/48000/2 +a=ptime:20 +a=maxptime:20 +a=rtpmap:9 G722/8000 +a=rtpmap:0 PCMU/8000 +a=rtpmap:8 PCMA/8000 +a=rtpmap:101 telephone-event/8000 +a=fmtp:101 0-15 +a=fmtp:101 0-5. +a=fmtp:101 0-15,66,67 +a=fmtp:101 0,1,2-4,5-15,66,67 +a=fmtp:101 5,6,7 +a=fmtp:101 0 +a=fmtp:101 1 +a=fmtp:101 123 +a=fmtp:101 0-123 +a=fmtp:101 -12 +a=fmtp:101 12- +a=fmtp:101 1,12-,4 +a=fmtp:101 ,2,3 +a=fmtp:101 ,,,2,3 +a=fmtp:101 1,,,,,,,,3 +a=fmtp:101 1,2,3, +a=fmtp:101 1-2-3 +a=fmtp:101 112233 +a=fmtp:101 33-2 diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/extract.sh b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/extract.sh new file mode 100755 index 000000000000..60f1b1c763d3 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/bin/sdps/extract.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +grep '\"[ a-z]=[^=]*$' sdp_unittests.cpp | grep -v 'ParseSdp(kVideoSdp' | grep -v 'kVideoWithRedAndUlpfec' | grep -v 'ASSERT_NE' | grep -v 'BASE64_DTLS_HELLO' | grep -v '^\/\/' | sed 's/ParseSdp(//g' | sed 's/^[[:space:]]*//' | sed 's/+ //' | sed 's/ \/\/.*$//' | sed 's/\;$//' | sed 's/)$//' | sed 's/, false//' | sed 's/" CRLF//' | sed 's/^\"//' | sed 's/\"$//' | sed 's/\\r\\n//' | gawk -v RS='(^|\n)v=' '/./ { print "v="$0 > NR".sdp" }' diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/error.rs b/media/webrtc/signaling/src/sdp/rsdparsa/src/error.rs new file mode 100644 index 000000000000..5db493375775 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/error.rs @@ -0,0 +1,215 @@ +use std::num::ParseIntError; +use std::net::AddrParseError; +use std::fmt; +use std::error; +use std::error::Error; + +#[derive(Debug)] +pub enum SdpParserInternalError { + Generic(String), + Unsupported(String), + Integer(ParseIntError), + Address(AddrParseError), +} + +impl fmt::Display for SdpParserInternalError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + SdpParserInternalError::Generic(ref message) => { + write!(f, "Generic parsing error: {}", message) + } + SdpParserInternalError::Unsupported(ref message) => { + write!(f, "Unsupported parsing error: {}", message) + } + SdpParserInternalError::Integer(ref error) => { + write!(f, "Integer parsing error: {}", error.description()) + } + SdpParserInternalError::Address(ref error) => { + write!(f, "IP address parsing error: {}", error.description()) + } + } + } +} + +impl error::Error for SdpParserInternalError { + fn description(&self) -> &str { + match *self { + SdpParserInternalError::Generic(ref message) | + SdpParserInternalError::Unsupported(ref message) => message, + SdpParserInternalError::Integer(ref error) => error.description(), + SdpParserInternalError::Address(ref error) => error.description(), + } + } + + fn cause(&self) -> Option<&error::Error> { + match *self { + SdpParserInternalError::Integer(ref error) => Some(error), + SdpParserInternalError::Address(ref error) => Some(error), + // Can't tell much more about our internal errors + _ => None, + } + } +} + +#[test] +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!(generic.description(), "generic message"); + assert!(generic.cause().is_none()); +} + +#[test] +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"); + assert_eq!(unsupported.description(), "unsupported internal message"); + assert!(unsupported.cause().is_none()); +} + +#[test] +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!(int_err.description(), "invalid digit found in string"); + assert!(!int_err.cause().is_none()); +} + +#[test] +fn test_sdp_parser_internal_error_address() { + let v = "127.0.0.a"; + use std::str::FromStr; + use std::net::IpAddr; + 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!(addr_err.description(), "invalid IP address syntax"); + assert!(!addr_err.cause().is_none()); +} + +#[derive(Debug)] +pub enum SdpParserError { + Line { + error: SdpParserInternalError, + line: String, + line_number: usize, + }, + Unsupported { + error: SdpParserInternalError, + line: String, + line_number: usize, + }, + Sequence { message: String, line_number: usize }, +} + +impl fmt::Display for SdpParserError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + SdpParserError::Line { + ref error, + ref line, + ref line_number, + } => { + 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) + } + SdpParserError::Sequence { + ref message, + ref line_number, + } => write!(f, "Sequence error in line({}): {}", line_number, message), + } + } +} + + +impl error::Error for SdpParserError { + fn description(&self) -> &str { + match *self { + 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), + // Can't tell much more about our internal errors + _ => None, + } + } +} + +impl From for SdpParserInternalError { + fn from(err: ParseIntError) -> SdpParserInternalError { + SdpParserInternalError::Integer(err) + } +} + +impl From for SdpParserInternalError { + fn from(err: AddrParseError) -> SdpParserInternalError { + SdpParserInternalError::Address(err) + } +} + +#[test] +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!(line1.description(), "test message"); + assert!(line1.cause().is_some()); +} + +#[test] +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!(unsupported1.description(), "unsupported value"); + assert!(unsupported1.cause().is_some()); +} + +#[test] +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!(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 new file mode 100644 index 000000000000..e3fb24359e58 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/lib.rs @@ -0,0 +1,974 @@ +#![cfg_attr(feature="clippy", feature(plugin))] + +use std::net::IpAddr; +use std::fmt; + +pub mod attribute_type; +pub mod error; +pub mod media_type; +pub mod network; +pub mod unsupported_types; + +use attribute_type::{SdpAttribute, parse_attribute}; +use error::{SdpParserInternalError, SdpParserError}; +use media_type::{SdpMedia, SdpMediaLine, parse_media, parse_media_vector}; +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)] +pub enum SdpBandwidth { + As(u32), + Ct(u32), + Tias(u32), + Unknown(String, u32), +} + +#[derive(Clone)] +pub struct SdpConnection { + pub addr: IpAddr, + pub ttl: Option, + pub amount: Option, +} + +#[derive(Clone)] +pub struct SdpOrigin { + pub username: String, + pub session_id: u64, + pub session_version: u64, + 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) + } +} + +#[derive(Clone)] +pub struct SdpTiming { + pub start: u64, + pub stop: u64, +} + +pub enum SdpType { + 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), +} + +pub struct SdpLine { + pub line_number: usize, + pub sdp_type: SdpType, +} + +pub struct SdpSession { + pub version: u64, + pub origin: SdpOrigin, + pub session: String, + pub connection: Option, + pub bandwidth: Vec, + pub timing: Option, + pub attribute: Vec, + pub media: Vec, + // unsupported values: + // information: Option, + // uri: Option, + // email: Option, + // phone: Option, + // repeat: Option, + // zone: Option, + // key: Option, +} + +impl SdpSession { + pub fn new(version: u64, origin: SdpOrigin, session: String) -> SdpSession { + SdpSession { + version, + origin, + session, + connection: None, + bandwidth: Vec::new(), + timing: None, + attribute: Vec::new(), + media: Vec::new(), + } + } + + pub fn get_version(&self) -> u64 { + self.version + } + + pub fn get_origin(&self) -> &SdpOrigin { + &self.origin + } + + pub fn get_session(&self) -> &String { + &self.session + } + + pub fn get_connection(&self) -> &Option { + &self.connection + } + + pub fn set_connection(&mut self, c: &SdpConnection) { + self.connection = Some(c.clone()) + } + + pub fn add_bandwidth(&mut self, b: &SdpBandwidth) { + self.bandwidth.push(b.clone()) + } + + pub fn set_timing(&mut self, t: &SdpTiming) { + self.timing = Some(t.clone()) + } + + 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))); + }; + Ok(self.attribute.push(a.clone())) + } + + pub fn extend_media(&mut self, v: Vec) { + self.media.extend(v) + } + + pub fn has_timing(&self) -> bool { + self.timing.is_some() + } + + pub fn has_attributes(&self) -> bool { + !self.attribute.is_empty() + } + + // FIXME this is a temporary hack until we re-oranize the SdpAttribute enum + // so that we can build a generic has_attribute(X) function + fn has_extmap_attribute(&self) -> bool { + for attribute in &self.attribute { + if let &SdpAttribute::Extmap(_) = attribute { + return true; + } + } + false + } + + pub fn has_media(&self) -> bool { + !self.media.is_empty() + } +} + +fn parse_session(value: &str) -> Result { + println!("session: {}", value); + Ok(SdpType::Session(String::from(value))) +} + +#[test] +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))); + }; + println!("version: {}", ver); + Ok(SdpType::Version(ver)) +} + +#[test] +fn test_version_works() { + assert!(parse_version("0").is_ok()); +} + +#[test] +fn test_version_unsupported_input() { + assert!(parse_version("1").is_err()); + assert!(parse_version("11").is_err()); + assert!(parse_version("a").is_err()); +} + +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())) + } + Some(x) => x, + }; + let session_id = match tokens.next() { + None => { + 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())) + } + Some(x) => x.parse::()?, + }; + match tokens.next() { + None => { + return Err(SdpParserInternalError::Generic( + "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())) + } + 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())) + } + Some(x) => parse_unicast_addr(x)?, + }; + if !addrtype.same_protocol(&unicast_addr) { + return Err(SdpParserInternalError::Generic("Origin addrtype does not match address." + .to_string())); + } + let o = SdpOrigin { + username: String::from(username), + session_id, + session_version, + unicast_addr, + }; + println!("{}", o); + Ok(SdpType::Origin(o)) +} + +#[test] +fn test_origin_works() { + assert!(parse_origin("mozilla 506705521068071134 0 IN IP4 0.0.0.0").is_ok()); + assert!(parse_origin("mozilla 506705521068071134 0 IN IP6 ::1").is_ok()); +} + +#[test] +fn test_origin_wrong_amount_of_tokens() { + assert!(parse_origin("a b c d e").is_err()); + assert!(parse_origin("a b c d e f g").is_err()); +} + +#[test] +fn test_origin_unsupported_nettype() { + assert!(parse_origin("mozilla 506705521068071134 0 UNSUPPORTED IP4 0.0.0.0").is_err()); +} + +#[test] +fn test_origin_unsupported_addrtpe() { + assert!(parse_origin("mozilla 506705521068071134 0 IN IP1 0.0.0.0").is_err()); +} + +#[test] +fn test_origin_broken_ip_addr() { + assert!(parse_origin("mozilla 506705521068071134 0 IN IP4 1.1.1.256").is_err()); + assert!(parse_origin("mozilla 506705521068071134 0 IN IP6 ::g").is_err()); +} + +#[test] +fn test_origin_addr_type_mismatch() { + assert!(parse_origin("mozilla 506705521068071134 0 IN IP4 ::1").is_err()); +} + +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())); + } + parse_nettype(cv[0])?; + let addrtype = parse_addrtype(cv[1])?; + let mut ttl = None; + let mut amount = None; + let mut addr_token = cv[2]; + if addr_token.find('/') != None { + let addr_tokens: Vec<&str> = addr_token.split('/').collect(); + if addr_tokens.len() >= 3 { + amount = Some(addr_tokens[2].parse::()?); + } + ttl = Some(addr_tokens[1].parse::()?); + addr_token = addr_tokens[0]; + } + let addr = parse_unicast_addr(addr_token)?; + if !addrtype.same_protocol(&addr) { + return Err(SdpParserInternalError::Generic("connection addrtype does not match address." + .to_string())); + } + let c = SdpConnection { addr, ttl, amount }; + println!("connection: {}", c.addr); + Ok(SdpType::Connection(c)) +} + +#[test] +fn connection_works() { + assert!(parse_connection("IN IP4 127.0.0.1").is_ok()); + assert!(parse_connection("IN IP4 127.0.0.1/10/10").is_ok()); +} + +#[test] +fn connection_lots_of_whitespace() { + assert!(parse_connection("IN IP4 127.0.0.1").is_ok()); +} + +#[test] +fn connection_wrong_amount_of_tokens() { + assert!(parse_connection("IN IP4").is_err()); + assert!(parse_connection("IN IP4 0.0.0.0 foobar").is_err()); +} + +#[test] +fn connection_unsupported_nettype() { + assert!(parse_connection("UNSUPPORTED IP4 0.0.0.0").is_err()); +} + +#[test] +fn connection_unsupported_addrtpe() { + assert!(parse_connection("IN IP1 0.0.0.0").is_err()); +} + +#[test] +fn connection_broken_ip_addr() { + assert!(parse_connection("IN IP4 1.1.1.256").is_err()); + assert!(parse_connection("IN IP6 ::g").is_err()); +} + +#[test] +fn connection_addr_type_mismatch() { + assert!(parse_connection("IN IP4 ::1").is_err()); +} + +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())); + } + let bandwidth = bv[1].parse::()?; + let bw = match bv[0].to_uppercase().as_ref() { + "AS" => SdpBandwidth::As(bandwidth), + "CT" => SdpBandwidth::Ct(bandwidth), + "TIAS" => SdpBandwidth::Tias(bandwidth), + _ => SdpBandwidth::Unknown(String::from(bv[0]), bandwidth), + }; + println!("bandwidth: {}, {}", bv[0], bandwidth); + Ok(SdpType::Bandwidth(bw)) +} + +#[test] +fn bandwidth_works() { + assert!(parse_bandwidth("AS:1").is_ok()); + assert!(parse_bandwidth("CT:123").is_ok()); + assert!(parse_bandwidth("TIAS:12345").is_ok()); +} + +#[test] +fn bandwidth_wrong_amount_of_tokens() { + assert!(parse_bandwidth("TIAS").is_err()); + assert!(parse_bandwidth("TIAS:12345:xyz").is_err()); +} + +#[test] +fn bandwidth_unsupported_type() { + assert!(parse_bandwidth("UNSUPPORTED:12345").is_ok()); +} + +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())); + } + let start = tv[0].parse::()?; + let stop = tv[1].parse::()?; + let t = SdpTiming { start, stop }; + println!("timing: {}, {}", t.start, t.stop); + Ok(SdpType::Timing(t)) +} + +#[test] +fn test_timing_works() { + assert!(parse_timing("0 0").is_ok()); +} + +#[test] +fn test_timing_non_numeric_tokens() { + assert!(parse_timing("a 0").is_err()); + assert!(parse_timing("0 a").is_err()); +} + +#[test] +fn test_timing_wrong_amount_of_tokens() { + assert!(parse_timing("0").is_err()); + assert!(parse_timing("0 0 0").is_err()); +} + +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, + }); + } + 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, + }) + } + 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, + }); + } + if trimmed.is_empty() { + return Err(SdpParserError::Line { + error: SdpParserInternalError::Generic("type is empty".to_string()), + line: line.to_string(), + line_number: line_number, + }); + } + trimmed + } + }; + let line_value = match splitted_line.next() { + None => { + return Err(SdpParserError::Line { + error: SdpParserInternalError::Generic("missing value".to_string()), + line: line.to_string(), + line_number: 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, + }); + } + 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::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, + } + } + }) +} + +#[test] +fn test_parse_sdp_line_works() { + assert!(parse_sdp_line("v=0", 0).is_ok()); + assert!(parse_sdp_line("s=somesession", 0).is_ok()); +} + +#[test] +fn test_parse_sdp_line_empty_line() { + assert!(parse_sdp_line("", 0).is_err()); +} + +#[test] +fn test_parse_sdp_line_unknown_key() { + assert!(parse_sdp_line("y=foobar", 0).is_err()); +} + +#[test] +fn test_parse_sdp_line_too_long_type() { + assert!(parse_sdp_line("ab=foobar", 0).is_err()); +} + +#[test] +fn test_parse_sdp_line_without_equal() { + assert!(parse_sdp_line("abcd", 0).is_err()); + assert!(parse_sdp_line("ab cd", 0).is_err()); +} + +#[test] +fn test_parse_sdp_line_empty_value() { + assert!(parse_sdp_line("v=", 0).is_err()); + assert!(parse_sdp_line("o=", 0).is_err()); + assert!(parse_sdp_line("s=", 0).is_err()); +} + +#[test] +fn test_parse_sdp_line_empty_name() { + assert!(parse_sdp_line("=abc", 0).is_err()); +} + +#[test] +fn test_parse_sdp_line_valid_a_line() { + assert!(parse_sdp_line("a=rtpmap:8 PCMA/8000", 0).is_ok()); +} + +#[test] +fn test_parse_sdp_line_invalid_a_line() { + assert!(parse_sdp_line("a=rtpmap:200 PCMA/8000", 0).is_err()); +} + +fn sanity_check_sdp_session(session: &SdpSession) -> Result<(), SdpParserError> { + if !session.has_timing() { + return Err(SdpParserError::Sequence { + message: "Missing timing type".to_string(), + line_number: 0, + }); + } + + if !session.has_media() { + return Err(SdpParserError::Sequence { + message: "Missing media setion".to_string(), + line_number: 0, + }); + } + + // Check that extmaps are not defined on session and media level + if session.has_extmap_attribute() { + for msection in &session.media { + if msection.has_extmap_attribute() { + return Err(SdpParserError::Sequence { + message: "Extmap can't be define at session and media level" + .to_string(), + line_number: 0, + }); + } + } + } + + Ok(()) +} + +#[cfg(test)] +fn create_dummy_sdp_session() -> SdpSession { + let origin = parse_origin("mozilla 506705521068071134 0 IN IP4 0.0.0.0"); + assert!(origin.is_ok()); + let sdp_session; + if let SdpType::Origin(o) = origin.unwrap() { + sdp_session = SdpSession::new(0, o, "-".to_string()); + } else { + panic!("SdpType is not Origin"); + } + sdp_session +} + +#[cfg(test)] +use media_type::create_dummy_media_section; + +#[test] +fn test_sanity_check_sdp_session_timing() { + let mut sdp_session = create_dummy_sdp_session(); + sdp_session.extend_media(vec![create_dummy_media_section()]); + + assert!(sanity_check_sdp_session(&sdp_session).is_err()); + + let t = SdpTiming { start: 0, stop: 0 }; + sdp_session.set_timing(&t); + + assert!(sanity_check_sdp_session(&sdp_session).is_ok()); +} + +#[test] +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); + + assert!(sanity_check_sdp_session(&sdp_session).is_err()); + + sdp_session.extend_media(vec![create_dummy_media_section()]); + + assert!(sanity_check_sdp_session(&sdp_session).is_ok()); +} + +#[test] +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.extend_media(vec![create_dummy_media_section()]); + + 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() { + extmap = a; + } else { + panic!("SdpType is not Attribute"); + } + let ret = sdp_session.add_attribute(&extmap); + assert!(ret.is_ok()); + assert!(sdp_session.has_extmap_attribute()); + + assert!(sanity_check_sdp_session(&sdp_session).is_ok()); + + 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() { + mextmap = ma; + } else { + panic!("SdpType is not Attribute"); + } + let mut second_media = create_dummy_media_section(); + assert!(second_media.add_attribute(&mextmap).is_ok()); + assert!(second_media.has_extmap_attribute()); + + sdp_session.extend_media(vec![second_media]); + assert!(sdp_session.media.len() == 2); + + assert!(sanity_check_sdp_session(&sdp_session).is_err()); + + sdp_session.attribute = Vec::new(); + + assert!(sanity_check_sdp_session(&sdp_session).is_ok()); +} + +#[test] +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.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 { + return Err(SdpParserError::Sequence { + message: "SDP neeeds at least 5 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 { + SdpType::Version(v) => v, + _ => { + return Err(SdpParserError::Sequence { + message: "first line needs to be version number".to_string(), + line_number: lines[0].line_number, + }) + } + }; + let origin: SdpOrigin = match lines[1].sdp_type { + SdpType::Origin(ref v) => v.clone(), + _ => { + return Err(SdpParserError::Sequence { + message: "second line needs to be origin".to_string(), + line_number: lines[1].line_number, + }) + } + }; + let session: String = match lines[2].sdp_type { + SdpType::Session(ref v) => v.clone(), + _ => { + return Err(SdpParserError::Sequence { + message: "third line needs to be session".to_string(), + line_number: lines[2].line_number, + }) + } + }; + 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.has_media() { + break; + }; + } + sanity_check_sdp_session(&sdp_session)?; + Ok(sdp_session) +} + +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, + }); + } + if sdp.len() < 62 { + return Err(SdpParserError::Line { + error: SdpParserInternalError::Generic("string to short to be valid SDP" + .to_string()), + line: sdp.to_string(), + line_number: 0, + }); + } + let lines = sdp.lines(); + let mut errors: Vec = Vec::new(); + let mut warnings: Vec = Vec::new(); + let mut sdp_lines: Vec = Vec::new(); + for (line_number, line) in lines.enumerate() { + let stripped_line = line.trim(); + if stripped_line.is_empty() { + continue; + } + match parse_sdp_line(stripped_line, line_number) { + Ok(n) => { + sdp_lines.push(n); + } + Err(e) => { + match e { + // FIXME is this really a good way to accomplish this? + 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, + }); + } + SdpParserError::Sequence { + message, + line_number, + } => { + errors.push(SdpParserError::Sequence { + message, + line_number, + }) + } + } + } + }; + } + for warning in warnings { + if fail_on_warning { + return Err(warning); + } else { + println!("Warning: {}", warning); + }; + } + // We just return the last of the errors here + if let Some(e) = errors.pop() { + return Err(e); + }; + let session = parse_sdp_vector(&sdp_lines)?; + Ok(session) +} + +#[test] +fn test_parse_sdp_zero_length_string_fails() { + assert!(parse_sdp("", true).is_err()); +} + +#[test] +fn test_parse_sdp_to_short_string() { + assert!(parse_sdp("fooooobarrrr", true).is_err()); +} + +#[test] +fn test_parse_sdp_line_error() { + assert!(parse_sdp("v=0\r\n +o=- 0 0 IN IP4 0.0.0.0\r\n +s=-\r\n +t=0 foobar\r\n +m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n", + true) + .is_err()); +} + +#[test] +fn test_parse_sdp_unsupported_error() { + assert!(parse_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=foobar 0 UDP/TLS/RTP/SAVPF 0\r\n", + true) + .is_err()); +} + +#[test] +fn test_parse_sdp_unsupported_warning() { + assert!(parse_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=audio 0 UDP/TLS/RTP/SAVPF 0\r\n +a=unsupported\r\n", + false) + .is_ok()); +} + +#[test] +fn test_parse_sdp_sequence_error() { + assert!(parse_sdp("v=0\r\n +o=- 0 0 IN IP4 0.0.0.0\r\n +t=0 0\r\n +s=-\r\n +m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n", + true) + .is_err()); +} + +#[test] +fn test_parse_sdp_integer_error() { + assert!(parse_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=audio 0 UDP/TLS/RTP/SAVPF 0\r\n +a=rtcp:34er21\r\n", + true) + .is_err()); +} + +#[test] +fn test_parse_sdp_ipaddr_error() { + assert!(parse_sdp("v=0\r\n +o=- 0 0 IN IP4 0.a.b.0\r\n +s=-\r\n +t=0 0\r\n +m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n", + true) + .is_err()); +} + +#[test] +fn test_parse_sdp_invalid_session_attribute() { + assert!(parse_sdp("v=0\r\n +o=- 0 0 IN IP4 0.a.b.0\r\n +s=-\r\n +t=0 0\r\n +a=bundle-only\r\n +m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n", + true) + .is_err()); +} + +#[test] +fn test_parse_sdp_invalid_media_attribute() { + assert!(parse_sdp("v=0\r\n +o=- 0 0 IN IP4 0.a.b.0\r\n +s=-\r\n +t=0 0\r\n +m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n +a=ice-lite\r\n", + true) + .is_err()); +} diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/media_type.rs b/media/webrtc/signaling/src/sdp/rsdparsa/src/media_type.rs new file mode 100644 index 000000000000..a9b116aabe52 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/media_type.rs @@ -0,0 +1,414 @@ +use std::fmt; +use {SdpType, SdpLine, SdpBandwidth, SdpConnection}; +use attribute_type::SdpAttribute; +use error::{SdpParserError, SdpParserInternalError}; + +#[derive(Clone)] +pub struct SdpMediaLine { + pub media: SdpMediaValue, + pub port: u32, + pub port_count: u32, + pub proto: SdpProtocolValue, + pub formats: SdpFormatList, +} + +#[derive(Clone,Debug,PartialEq)] +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) + } +} + +#[derive(Clone,Debug,PartialEq)] +pub enum SdpProtocolValue { + RtpSavpf, + UdpTlsRtpSavpf, + TcpTlsRtpSavpf, + DtlsSctp, + UdpDtlsSctp, + TcpDtlsSctp, +} + +impl fmt::Display for SdpProtocolValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let printable = match *self { + SdpProtocolValue::RtpSavpf => "Rtp/Savpf", + SdpProtocolValue::UdpTlsRtpSavpf => "Udp/Tls/Rtp/Savpf", + SdpProtocolValue::TcpTlsRtpSavpf => "Tcp/Tls/Rtp/Savpf", + SdpProtocolValue::DtlsSctp => "Dtls/Sctp", + SdpProtocolValue::UdpDtlsSctp => "Udp/Dtls/Sctp", + SdpProtocolValue::TcpDtlsSctp => "Tcp/Dtls/Sctp", + }; + write!(f, "{}", printable) + } +} + +#[derive(Clone)] +pub enum SdpFormatList { + Integers(Vec), + Strings(Vec), +} + +impl fmt::Display for SdpFormatList { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + SdpFormatList::Integers(ref x) => write!(f, "{:?}", x), + SdpFormatList::Strings(ref x) => write!(f, "{:?}", x), + } + } +} + +pub struct SdpMedia { + media: SdpMediaLine, + connection: Option, + bandwidth: Vec, + attribute: Vec, + // unsupported values: + // information: Option, + // key: Option, +} + +impl SdpMedia { + pub fn new(media: SdpMediaLine) -> SdpMedia { + SdpMedia { + media, + connection: None, + bandwidth: Vec::new(), + attribute: Vec::new(), + } + } + + pub fn get_type(&self) -> &SdpMediaValue { + &self.media.media + } + + pub fn get_port(&self) -> u32 { + self.media.port + } + + pub fn get_port_count(&self) -> u32 { + self.media.port_count + } + + pub fn get_proto(&self) -> &SdpProtocolValue { + &self.media.proto + } + + pub fn get_formats(&self) -> &SdpFormatList { + &self.media.formats + } + + pub fn has_bandwidth(&self) -> bool { + !self.bandwidth.is_empty() + } + + pub fn get_bandwidth(&self) -> &Vec { + &self.bandwidth + } + + pub fn add_bandwidth(&mut self, bw: &SdpBandwidth) { + self.bandwidth.push(bw.clone()) + } + + pub fn has_attributes(&self) -> bool { + !self.attribute.is_empty() + } + + pub fn get_attributes(&self) -> &Vec { + &self.attribute + } + + 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))); + } + Ok(self.attribute.push(attr.clone())) + } + + // FIXME this is a temporary hack until we re-oranize the SdpAttribute enum + // so that we can build a generic has_attribute(X) function + pub fn has_extmap_attribute(&self) -> bool { + for attribute in &self.attribute { + if let &SdpAttribute::Extmap(_) = attribute { + return true; + } + } + false + } + + pub fn has_connection(&self) -> bool { + self.connection.is_some() + } + + pub fn get_connection(&self) -> &Option { + &self.connection + } + + 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(), + )); + } + Ok(self.connection = Some(c.clone())) + } +} + +#[cfg(test)] +pub fn create_dummy_media_section() -> SdpMedia { + let media_line = SdpMediaLine { + media: SdpMediaValue::Audio, + port: 9, + port_count: 0, + proto: SdpProtocolValue::RtpSavpf, + formats: SdpFormatList::Integers(Vec::new()), + }; + SdpMedia::new(media_line) +} + +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))) + } + }) +} + +#[test] +fn test_parse_media_token() { + let audio = parse_media_token("audio"); + assert!(audio.is_ok()); + assert_eq!(audio.unwrap(), SdpMediaValue::Audio); + let video = parse_media_token("VIDEO"); + assert!(video.is_ok()); + assert_eq!(video.unwrap(), SdpMediaValue::Video); + let app = parse_media_token("aPplIcatIOn"); + assert!(app.is_ok()); + assert_eq!(app.unwrap(), SdpMediaValue::Application); + + assert!(parse_media_token("").is_err()); + 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/SAVPF" => SdpProtocolValue::UdpTlsRtpSavpf, + "TCP/TLS/RTP/SAVPF" => SdpProtocolValue::TcpTlsRtpSavpf, + "DTLS/SCTP" => SdpProtocolValue::DtlsSctp, + "UDP/DTLS/SCTP" => SdpProtocolValue::UdpDtlsSctp, + "TCP/DTLS/SCTP" => SdpProtocolValue::TcpDtlsSctp, + _ => { + return Err(SdpParserInternalError::Unsupported(format!("unsupported protocol value: {}", + value))) + } + }) +} + +#[test] +fn test_parse_protocol_token() { + 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/savpf"); + assert!(udps.is_ok()); + assert_eq!(udps.unwrap(), SdpProtocolValue::UdpTlsRtpSavpf); + let tcps = parse_protocol_token("TCP/tls/rtp/savpf"); + assert!(tcps.is_ok()); + assert_eq!(tcps.unwrap(), SdpProtocolValue::TcpTlsRtpSavpf); + let dtls = parse_protocol_token("dtLs/ScTP"); + assert!(dtls.is_ok()); + assert_eq!(dtls.unwrap(), SdpProtocolValue::DtlsSctp); + let usctp = parse_protocol_token("udp/DTLS/sctp"); + assert!(usctp.is_ok()); + assert_eq!(usctp.unwrap(), SdpProtocolValue::UdpDtlsSctp); + 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())); + } + 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())), + Some(p) => p.parse::()?, + }; + if port > 65535 { + return Err(SdpParserInternalError::Generic("media port token is too big".to_string())); + } + let port_count = match ptokens.next() { + None => 0, + Some(c) => c.parse::()?, + }; + let proto = parse_protocol_token(mv[2])?; + let fmt_slice: &[&str] = &mv[3..]; + let formats = match media { + SdpMediaValue::Audio | SdpMediaValue::Video => { + let mut fmt_vec: Vec = vec![]; + for num in fmt_slice { + let fmt_num = num.parse::()?; + match fmt_num { + 0 | // PCMU + 8 | // PCMA + 9 | // G722 + 13 | // Comfort Noise + 96 ... 127 => (), // dynamic range + _ => return Err(SdpParserInternalError::Generic( + "format number in media line is out of range".to_string())) + }; + fmt_vec.push(fmt_num); + } + SdpFormatList::Integers(fmt_vec) + } + SdpMediaValue::Application => { + let mut fmt_vec: Vec = vec![]; + // TODO enforce length == 1 and content 'webrtc-datachannel' only? + for token in fmt_slice { + fmt_vec.push(String::from(*token)); + } + SdpFormatList::Strings(fmt_vec) + } + }; + let m = SdpMediaLine { + media, + port, + port_count, + proto, + formats, + }; + println!("media: {}, {}, {}, {}", m.media, m.port, m.proto, m.formats); + Ok(SdpType::Media(m)) +} + +#[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()); + + 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()); +} + +#[test] +fn test_media_missing_token() { + assert!(parse_media("video 9 UDP/TLS/RTP/SAVPF").is_err()); +} + +#[test] +fn test_media_invalid_port_number() { + assert!(parse_media("video 75123 UDP/TLS/RTP/SAVPF 8").is_err()); +} + +#[test] +fn test_media_invalid_type() { + assert!(parse_media("invalid 9 UDP/TLS/RTP/SAVPF 8").is_err()); +} + +#[test] +fn test_media_invalid_port() { + assert!(parse_media("audio / UDP/TLS/RTP/SAVPF 8").is_err()); +} + +#[test] +fn test_media_invalid_transport() { + assert!(parse_media("audio 9 invalid/invalid 8").is_err()); +} + +#[test] +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> { + let mut media_sections: Vec = Vec::new(); + let mut sdp_media = match lines[0].sdp_type { + SdpType::Media(ref v) => SdpMedia::new(v.clone()), + _ => { + return Err(SdpParserError::Sequence { + message: "first line in media section needs to be a media line" + .to_string(), + line_number: lines[0].line_number, + }) + } + }; + for line in lines.iter().skip(1) { + match line.sdp_type { + SdpType::Connection(ref c) => { + sdp_media + .set_connection(c) + .map_err(|e: SdpParserInternalError| { + SdpParserError::Sequence { + message: format!("{}", e), + line_number: line.line_number, + } + })? + } + SdpType::Bandwidth(ref b) => sdp_media.add_bandwidth(b), + SdpType::Attribute(ref a) => { + sdp_media + .add_attribute(a) + .map_err(|e: SdpParserInternalError| { + SdpParserError::Sequence { + message: format!("{}", e), + line_number: line.line_number, + } + })? + } + SdpType::Media(ref v) => { + media_sections.push(sdp_media); + sdp_media = SdpMedia::new(v.clone()); + } + + SdpType::Email(_) | + SdpType::Phone(_) | + SdpType::Origin(_) | + SdpType::Repeat(_) | + SdpType::Session(_) | + SdpType::Timing(_) | + SdpType::Uri(_) | + SdpType::Version(_) | + SdpType::Zone(_) => { + return Err(SdpParserError::Sequence { + 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(_) => (), + }; + } + media_sections.push(sdp_media); + Ok(media_sections) +} +// TODO add unit tests for parse_media_vector diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/network.rs b/media/webrtc/signaling/src/sdp/rsdparsa/src/network.rs new file mode 100644 index 000000000000..2d768bfc995e --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/network.rs @@ -0,0 +1,80 @@ +use std::str::FromStr; +use std::fmt; +use std::net::IpAddr; + +use error::SdpParserInternalError; + +#[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) + } +} + +impl fmt::Display for SdpAddrType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let printable = match *self { + SdpAddrType::IP4 => "Ip4", + SdpAddrType::IP6 => "Ip6", + }; + write!(f, "{}", printable) + } +} + +pub fn parse_nettype(value: &str) -> Result<(), SdpParserInternalError> { + if value.to_uppercase() != "IN" { + return Err(SdpParserInternalError::Generic("nettype needs to be IN".to_string())); + }; + Ok(()) +} + +#[test] +fn test_parse_nettype() { + let internet = parse_nettype("iN"); + assert!(internet.is_ok()); + + assert!(parse_nettype("").is_err()); + assert!(parse_nettype("FOO").is_err()); +} + +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())) + } + }) +} + +#[test] +fn test_parse_addrtype() { + let ip4 = parse_addrtype("iP4"); + assert!(ip4.is_ok()); + assert_eq!(ip4.unwrap(), SdpAddrType::IP4); + let ip6 = parse_addrtype("Ip6"); + assert!(ip6.is_ok()); + assert_eq!(ip6.unwrap(), SdpAddrType::IP6); + + assert!(parse_addrtype("").is_err()); + assert!(parse_addrtype("IP5").is_err()); +} + +pub fn parse_unicast_addr(value: &str) -> Result { + Ok(IpAddr::from_str(value)?) +} + +#[test] +fn test_parse_unicast_addr() { + let ip4 = parse_unicast_addr("127.0.0.1"); + assert!(ip4.is_ok()); + let ip6 = parse_unicast_addr("::1"); + assert!(ip6.is_ok()); +} diff --git a/media/webrtc/signaling/src/sdp/rsdparsa/src/unsupported_types.rs b/media/webrtc/signaling/src/sdp/rsdparsa/src/unsupported_types.rs new file mode 100644 index 000000000000..272224b6af0c --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/unsupported_types.rs @@ -0,0 +1,74 @@ +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 new file mode 100644 index 000000000000..b1a6d65e1941 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/rsdparsa/tests/unit_tests.rs @@ -0,0 +1,423 @@ +extern crate rsdparsa; + +#[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 +t=0 0\r\n +m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n"; + let sdp_res = rsdparsa::parse_sdp(sdp, true); + assert!(sdp_res.is_ok()); + let sdp_opt = sdp_res.ok(); + assert!(sdp_opt.is_some()); + let sdp = sdp_opt.unwrap(); + assert_eq!(sdp.version, 0); + assert_eq!(sdp.session, "-"); + assert!(sdp.connection.is_none()); + assert_eq!(sdp.attribute.len(), 0); + 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_port(), 0); + assert_eq!(*msection.get_proto(), + rsdparsa::media_type::SdpProtocolValue::UdpTlsRtpSavpf); + assert!(!msection.has_attributes()); + assert!(!msection.has_bandwidth()); + assert!(!msection.has_connection()); + assert!(msection.get_connection().is_none()); +} + +#[test] +fn parse_minimal_sdp_with_emtpy_lines() { + let sdp = "v=0\r\n +\r\n +o=- 0 0 IN IP4 0.0.0.0\r\n + \r\n +s=-\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); + assert!(sdp_res.is_ok()); + let sdp_opt = sdp_res.ok(); + assert!(sdp_opt.is_some()); + let sdp = sdp_opt.unwrap(); + assert_eq!(sdp.version, 0); + assert_eq!(sdp.session, "-"); +} + +#[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); + assert!(sdp_res.is_ok()); + let sdp_opt = sdp_res.ok(); + assert!(sdp_opt.is_some()); + let sdp = sdp_opt.unwrap(); + assert_eq!(sdp.version, 0); + assert_eq!(sdp.session, "-"); + assert!(sdp.get_connection().is_some()); +} + +#[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 +a=ssrc:2655508255 cname:{735484ea-4f6c-f74a-bd66-7425f8476c2e}\r\n"; + let sdp_res = rsdparsa::parse_sdp(sdp, true); + assert!(sdp_res.is_ok()); + let sdp_opt = sdp_res.ok(); + assert!(sdp_opt.is_some()); + let sdp = sdp_opt.unwrap(); + assert_eq!(sdp.version, 0); + 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_port(), 9); + assert_eq!(*msection.get_proto(), + rsdparsa::media_type::SdpProtocolValue::UdpTlsRtpSavpf); + assert!(msection.has_attributes()); + assert!(msection.has_connection()); + assert!(msection.get_connection().is_some()); + assert!(!msection.has_bandwidth()); +} + +#[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 +a=ssrc:2709871439 cname:{735484ea-4f6c-f74a-bd66-7425f8476c2e}"; + let sdp_res = rsdparsa::parse_sdp(sdp, true); + assert!(sdp_res.is_ok()); + let sdp_opt = sdp_res.ok(); + assert!(sdp_opt.is_some()); + let sdp = sdp_opt.unwrap(); + assert_eq!(sdp.version, 0); + 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_port(), 9); + assert_eq!(*msection.get_proto(), + rsdparsa::media_type::SdpProtocolValue::UdpTlsRtpSavpf); +} + +#[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); + assert!(sdp_res.is_ok()); + let sdp_opt = sdp_res.ok(); + assert!(sdp_opt.is_some()); + let sdp = sdp_opt.unwrap(); + assert_eq!(sdp.version, 0); + 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_port(), 49760); + assert_eq!(*msection.get_proto(), + rsdparsa::media_type::SdpProtocolValue::DtlsSctp); +} + +#[test] +fn parse_chrome_audio_video_offer() { + let sdp = "v=0\r\n +o=- 3836772544440436510 2 IN IP4 127.0.0.1\r\n +s=-\r\n +t=0 0\r\n +a=group:BUNDLE audio video\r\n +a=msid-semantic: WMS HWpbmTmXleVSnlssQd80bPuw9cxQFroDkkBP\r\n +m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 126\r\n +c=IN IP4 0.0.0.0\r\n +a=rtcp:9 IN IP4 0.0.0.0\r\n +a=ice-ufrag:A4by\r\n +a=ice-pwd:Gfvb2rbYMiW0dZz8ZkEsXICs\r\n +a=fingerprint:sha-256 15:B0:92:1F:C7:40:EE:22:A6:AF:26:EF:EA:FF:37:1D:B3:EF:11:0B:8B:73:4F:01:7D:C9:AE:26:4F:87:E0:95\r\n +a=setup:actpass\r\n +a=mid:audio\r\n +a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n +a=sendrecv\r\n +a=rtcp-mux\r\n +a=rtpmap:111 opus/48000/2\r\n +a=rtcp-fb:111 transport-cc\r\n +a=fmtp:111 minptime=10;useinbandfec=1\r\n +a=rtpmap:103 ISAC/16000\r\n +a=rtpmap:104 ISAC/32000\r\n +a=rtpmap:9 G722/8000\r\n +a=rtpmap:0 PCMU/8000\r\n +a=rtpmap:8 PCMA/8000\r\n +a=rtpmap:106 CN/32000\r\n +a=rtpmap:105 CN/16000\r\n +a=rtpmap:13 CN/8000\r\n +a=rtpmap:126 telephone-event/8000\r\n +a=ssrc:162559313 cname:qPTZ+BI+42mgbOi+\r\n +a=ssrc:162559313 msid:HWpbmTmXleVSnlssQd80bPuw9cxQFroDkkBP f6188af5-d8d6-462c-9c75-f12bc41fe322\r\n +a=ssrc:162559313 mslabel:HWpbmTmXleVSnlssQd80bPuw9cxQFroDkkBP\r\n +a=ssrc:162559313 label:f6188af5-d8d6-462c-9c75-f12bc41fe322\r\n +m=video 9 UDP/TLS/RTP/SAVPF 100 101 107 116 117 96 97 99 98\r\n +c=IN IP4 0.0.0.0\r\n +a=rtcp:9 IN IP4 0.0.0.0\r\n +a=ice-ufrag:A4by\r\n +a=ice-pwd:Gfvb2rbYMiW0dZz8ZkEsXICs\r\n +a=fingerprint:sha-256 15:B0:92:1F:C7:40:EE:22:A6:AF:26:EF:EA:FF:37:1D:B3:EF:11:0B:8B:73:4F:01:7D:C9:AE:26:4F:87:E0:95\r\n +a=setup:actpass\r\n +a=mid:video\r\n +a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n +a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n +a=extmap:4 urn:3gpp:video-orientation\r\n +a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n +a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\n +a=sendrecv\r\n +a=rtcp-mux\r\n +a=rtcp-rsize\r\n +a=rtpmap:100 VP8/90000\r\n +a=rtcp-fb:100 ccm fir\r\n +a=rtcp-fb:100 nack\r\n +a=rtcp-fb:100 nack pli\r\n +a=rtcp-fb:100 goog-remb\r\n +a=rtcp-fb:100 transport-cc\r\n +a=rtpmap:101 VP9/90000\r\n +a=rtcp-fb:101 ccm fir\r\n +a=rtcp-fb:101 nack\r\n +a=rtcp-fb:101 nack pli\r\n +a=rtcp-fb:101 goog-remb\r\n +a=rtcp-fb:101 transport-cc\r\n +a=rtpmap:107 H264/90000\r\n +a=rtcp-fb:107 ccm fir\r\n +a=rtcp-fb:107 nack\r\n +a=rtcp-fb:107 nack pli\r\n +a=rtcp-fb:107 goog-remb\r\n +a=rtcp-fb:107 transport-cc\r\n +a=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\n +a=rtpmap:116 red/90000\r\n +a=rtpmap:117 ulpfec/90000\r\n +a=rtpmap:96 rtx/90000\r\n +a=fmtp:96 apt=100\r\n +a=rtpmap:97 rtx/90000\r\n +a=fmtp:97 apt=101\r\n +a=rtpmap:99 rtx/90000\r\n +a=fmtp:99 apt=107\r\n +a=rtpmap:98 rtx/90000\r\n +a=fmtp:98 apt=116\r\n +a=ssrc-group:FID 3156517279 2673335628\r\n +a=ssrc:3156517279 cname:qPTZ+BI+42mgbOi+\r\n +a=ssrc:3156517279 msid:HWpbmTmXleVSnlssQd80bPuw9cxQFroDkkBP b6ec5178-c611-403f-bbec-3833ed547c09\r\n +a=ssrc:3156517279 mslabel:HWpbmTmXleVSnlssQd80bPuw9cxQFroDkkBP\r\n +a=ssrc:3156517279 label:b6ec5178-c611-403f-bbec-3833ed547c09\r\n +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); + assert!(sdp_res.is_ok()); + let sdp_opt = sdp_res.ok(); + assert!(sdp_opt.is_some()); + let sdp = sdp_opt.unwrap(); + assert_eq!(sdp.version, 0); + 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_port(), 9); + assert_eq!(*msection1.get_proto(), + rsdparsa::media_type::SdpProtocolValue::UdpTlsRtpSavpf); + assert!(msection1.has_attributes()); + assert!(msection1.has_connection()); + assert!(!msection1.has_bandwidth()); + + let msection2 = &(sdp.media[1]); + assert_eq!(*msection2.get_type(), + rsdparsa::media_type::SdpMediaValue::Video); + assert_eq!(msection2.get_port(), 9); + assert_eq!(*msection2.get_proto(), + rsdparsa::media_type::SdpProtocolValue::UdpTlsRtpSavpf); + assert!(msection2.has_attributes()); + assert!(msection2.has_connection()); + assert!(!msection2.has_bandwidth()); +} + +#[test] +fn parse_firefox_simulcast_offer() { + let sdp = "v=0\r\n +o=mozilla...THIS_IS_SDPARTA-55.0a1 983028567300715536 0 IN IP4 0.0.0.0\r\n +s=-\r\n +t=0 0\r\n +a=fingerprint:sha-256 68:42:13:88:B6:C1:7D:18:79:07:8A:C6:DC:28:D6:DC:DD:E3:C9:41:E7:80:A7:FE:02:65:FB:76:A0:CD:58:ED\r\n +a=ice-options:trickle\r\n +a=msid-semantic:WMS *\r\n +m=video 9 UDP/TLS/RTP/SAVPF 120 121 126 97\r\n +c=IN IP4 0.0.0.0\r\n +a=sendrecv\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=extmap:3/sendonly urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n +a=fmtp:126 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1\r\n +a=fmtp:97 profile-level-id=42e01f;level-asymmetry-allowed=1\r\n +a=fmtp:120 max-fs=12288;max-fr=60\r\n +a=fmtp:121 max-fs=12288;max-fr=60\r\n +a=ice-pwd:4af388405d558b91f5ba6c2c48f161bf\r\n +a=ice-ufrag:ce1ac488\r\n +a=mid:sdparta_0\r\n +a=msid:{fb6d1fa3-d993-f244-a0fe-d9fb99214c23} {8be9a0f7-9272-6c42-90f3-985d55bd8de5}\r\n +a=rid:foo send\r\n +a=rid:bar send\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:121 nack\r\n +a=rtcp-fb:121 nack pli\r\n +a=rtcp-fb:121 ccm fir\r\n +a=rtcp-fb:121 goog-remb\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: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:120 VP8/90000\r\n +a=rtpmap:121 VP9/90000\r\n +a=rtpmap:126 H264/90000\r\n +a=rtpmap:97 H264/90000\r\n +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); + assert!(sdp_res.is_ok()); + let sdp_opt = sdp_res.ok(); + assert!(sdp_opt.is_some()); + let sdp = sdp_opt.unwrap(); + assert_eq!(sdp.version, 0); + assert_eq!(sdp.media.len(), 1); +} + +#[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); + assert!(sdp_res.is_ok()); + let sdp_opt = sdp_res.ok(); + assert!(sdp_opt.is_some()); + let sdp = sdp_opt.unwrap(); + assert_eq!(sdp.version, 0); + assert_eq!(sdp.media.len(), 1); +}