pax_global_header00006660000000000000000000000064137616677020014531gustar00rootroot0000000000000052 comment=2462ab1111481d1e1038892915e961047d5be7b9 sdp-transform-2.14.1/000077500000000000000000000000001376166770200144155ustar00rootroot00000000000000sdp-transform-2.14.1/.eslintrc.js000066400000000000000000000005661376166770200166630ustar00rootroot00000000000000module.exports = { 'env': { 'browser': true, 'commonjs': true, 'es6' : true }, 'extends': 'eslint:recommended', 'globals': { 'process': false, '__dirname': false }, 'rules': { 'indent': ['error', 2], 'linebreak-style': ['error', 'unix'], 'quotes': ['error', 'single'], 'semi': ['error', 'always'], 'no-console': 0 } }; sdp-transform-2.14.1/.gitignore000066400000000000000000000000251376166770200164020ustar00rootroot00000000000000node_modules lib-cov sdp-transform-2.14.1/.npmignore000066400000000000000000000000601376166770200164100ustar00rootroot00000000000000test examples .gitignore .travis.yml .npmignore sdp-transform-2.14.1/.npmrc000066400000000000000000000000231376166770200155300ustar00rootroot00000000000000package-lock=false sdp-transform-2.14.1/.travis.yml000066400000000000000000000003611376166770200165260ustar00rootroot00000000000000language: node_js sudo: false node_js: - 6 - 8 - 10 notifications: email: on_success: change on_failure: always script: - npm run lint - npm test after_script: - npm install coveralls - 'npm run coverage | coveralls' sdp-transform-2.14.1/CHANGELOG.md000066400000000000000000000160021376166770200162250ustar00rootroot000000000000002.14.1 / 2020-12-02 =================== * Fix `a=rtcp-fb` bug where trr-int is `*` via #91 2.14.0 / 2020-01-22 =================== * Add `a=ptime` now support float values for sub-ms values via #89 2.13.0 / 2019-09-29 =================== * Add `a=ts-refclk` and `a=mediaclk` for RFC7273 support via #78 2.12.0 / 2019-08-05 =================== * a=extmap-allow-mixed (RFC 8285) #87 2.11.0 / 2019-07-28 =================== * BFCP RFC4583 support via #86 2.10.0 / 2019-07-12 =================== * `a=connection` support for RFC4145 via #85 2.9.0 / 2019-07-11 ================== * `a=keywds` support via #82 2.8.0 / 2019-05-29 ================== * `a=extmap` `encrypt-uri` improvements in #81 * `parsePayloads` safe parsing bugfix for integer equivalent m-lines #80 2.7.0 / 2018-11-21 ================== * `a=sctp-port` + `a=max-message-size` support added in #76 via @egzonzeneli 2.6.0 / 2018-11-14 ================== * `a=label` support added in #75 via @jeremy-j-ackso 2.5.0 / 2018-11-02 ================== * `a=bundle-only` support added in #73 via @ibc 2.4.1 / 2018-04-02 ================== * `parseParams` now doesn't break on name only params #70 via @manuc66 2.4.0 / 2018-01-24 ================== * `a=source-filter` support added in #69 via @thosil 2.3.1 / 2018-01-05 ================== * `a=ssrc` bug attributes including dashes fixed in #68 via @MichelSimonot 2.3.0 / 2017-03-06 ================== * `a=framerate` from rfc4566 now parseable - #63 via @gregpabian 2.2.0 / 2017-03-05 ================== * `a=rid` now parseable - #59 from @ibc * `parseFmtpConfig` now aliased as `parseParams` - works on a more general level - #60 * `parseFmtpConfig` deprecated - will be removed in 3.0.0 * `a=imageattr` now parseable - #61 from @ibc * `parseImageattrParams` for extended image attr parsing RFC6236 - #61 * `a=simulcast` now parseable (both draft version 3 and draft v7) - #62 from @ibc - `parseSimulcastStreamList` for more detailed simulcast parsing - #62 2.1.0 / 2017-03-02 ================== * `a=x-google-flag:%s` now parseable - #58 via @ibc 2.0.1 / 2017-02-20 ================== * a=ssrc-group parsing now doesn't break on dash-separation #54 via @murillo128 2.0.0 / 2017-02-16 ================== * a=extmap lines now parsed into a 4 object struct rather than a broken 3 object compound struct - #51 via @ibc * this is unlikely to be breaking, but we major bumped just to be sure 1.7.0 / 2016-12-09 ================== * a=ssrc lines now properly handle attributes without values - #40 via @zxcpoiu * a=candidate now supports network-id and network-cost values - #49 via @zxcpoiu 1.6.2 / 2016-03-23 ================== * Fix `a=rtpmap` parsing when codec included dots - #44 via @alexanderklintstrom 1.6.1 / 2016-03-18 ================== * Fix parsing of fmtp parameters with base64 in `parseFmtpConfig` - #42 via @lmoj 1.6.0 / 2016-03-02 ================== * Add support for `a=sctpmap` - #41 via @6uliver 1.5.3 / 2015-11-25 ================== * Parse tcp ice-candidates with raddr + rport correctly - #37 via @damencho 1.5.2 / 2015-11-17 ================== * Parse tcp ice-candidates lines correctly - #35 via @virtuacoplenny 1.5.1 / 2015-11-15 ================== * Added `.npmignore` 1.5.0 / 2015-09-05 ================== * Suport AirTunes a=rtpmap lines without clockrate #30 - via @DuBistKomisch 1.4.1 / 2015-08-14 ================== * Proper handling of whitespaces in a=fmtp: lines #29 - via @bgrozev * `parseFmtpConfig` helper also handles whitespaces properly 1.4.0 / 2015-03-18 ================== * Add support for `a=rtcp-rsize` 1.3.0 / 2015-03-16 ================== * Add support for `a=end-of-candidates` trickle ice attribute 1.2.1 / 2015-03-15 ================== * Add parsing for a=ssrc-group 1.2.0 / 2015-03-05 ================== * a=msid attributes support and msid-semantic improvements * writer now ignores `undefined` or `null` values 1.1.0 / 2014-10-20 ================== * Add support for parsing session level `a=ice-lite` 1.0.0 / 2014-09-30 ================== * Be more lenient with nelines. Allow \r\n, \r or \n. 0.6.1 / 2014-07-25 ================== * Documentation and test coverage release 0.6.0 / 2014-02-18 ================== * invalid a= lines are now parsed verbatim in `media[i].invalid` (#19) * everything in `media[i].invalid` is written out verbatim (#19) * add basic RTSP support (a=control lines) (#20) 0.5.3 / 2014-01-17 ================== * ICE candidates now parsed fully (no longer ignoring optional attrs) (#13) 0.5.2 / 2014-01-17 ================== * Remove `util` dependency to help browserify users * Better parsing of `a=extmap`, `a=crypto` and `a=rtcp-fb` lines * `sdp-verify` bin file included to help discover effects of `write ∘ parse` 0.5.1 / 2014-01-16 ================== * Correctly parse a=rtpmap with telephone-event codec #16 * Correctly parse a=rtcp lines that conditionally include the IP #16 0.5.0 / 2014-01-14 ================== * Enforce spec mandated \r\n line endings over \n (#15) * Parsing of opus rtpmap wrong because encoding parameters were discarded (#12) 0.4.1 / 2013-12-19 ================== * Changed 'sendrecv' key on media streams to be called 'direction' to match SDP related RFCs (thanks to @saghul) 0.3.3 / 2013-12-10 ================== * Fixed a bug that caused time description lines ("t=" and "z=") to be in the wrong place 0.3.2 / 2013-10-21 ================== * Fixed a bug where large sessionId values where being rounded (#8) * Optionally specify the `outerOrder` and `innerOrder` for the writer (allows working around Chrome not following the RFC specified order in #7) 0.3.1 / 2013-10-19 ================== * Fixed a bug that meant the writer didn't write the last newline (#6) 0.3.0 / 2013-10-18 ================== * Changed ext grammar to parse id and direction as one (fixes writing bug) * Allow mid to be a string (fixes bug) * Add support for maxptime value * Add support for ice-options * Add support for grouping frameworks * Add support for msid-semantic * Add support for ssrc * Add support for rtcp-mux * Writer improvements: add support for session level push attributes 0.2.1 / 2013-07-31 ================== * Support release thanks to @legastero, following was pulled from his fork: * Add support for rtcp-fb attributes. * Add support for header extension (extmap) attributes. * Add support for crypto attributes. * Add remote-candidates attribute support and parser. 0.2.0 / 2013-07-27 ================== * parse most normal lines sensibly * factored out grammar properly * added a writer that uses common grammar * stop preprocessing parse object explicitly (so that parser ∘ writer == Id) these parser helpers are instead exposed (may in the future be extended) 0.1.0 / 2013-07-21 ================== * rewrite parsing mechanism * parse origin lines more efficiently * parsing output now significantly different 0.0.2 / 2012-07-18 ================== * ice properties parsed 0.0.1 / 2012-07-17 ================== * Original release sdp-transform-2.14.1/LICENSE000066400000000000000000000020671376166770200154270ustar00rootroot00000000000000(The MIT License) Copyright (c) 2013 Eirik Albrigtsen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. sdp-transform-2.14.1/README.md000066400000000000000000000157041376166770200157030ustar00rootroot00000000000000# SDP Transform [![npm status](http://img.shields.io/npm/v/sdp-transform.svg)](https://www.npmjs.org/package/sdp-transform) [![build status](https://secure.travis-ci.org/clux/sdp-transform.svg)](http://travis-ci.org/clux/sdp-transform) [![dependency status](https://david-dm.org/clux/sdp-transform.svg)](https://david-dm.org/clux/sdp-transform) [![coverage status](http://img.shields.io/coveralls/clux/sdp-transform.svg)](https://coveralls.io/r/clux/sdp-transform) A simple parser and writer of SDP. Defines internal grammar based on [RFC4566 - SDP](http://tools.ietf.org/html/rfc4566), [RFC5245 - ICE](http://tools.ietf.org/html/rfc5245), and many more. For simplicity it will force values that are integers to integers and leave everything else as strings when parsing. The module should be simple to extend or build upon, and is constructed rigorously. ## Installation ```bash $ npm install sdp-transform ``` ## TypeScript Definitions Available in the [@types/sdp-transform](https://www.npmjs.com/package/@types/sdp-transform) package: ```bash $ npm install -D @types/sdp-transform ``` ## Usage Load using CommonJS syntax or ES6 syntax: ```js // CommonJS const sdpTransform = require('sdp-transform'); // ES6 import * as sdpTransform from 'sdp-transform'; ``` ## Usage - Parser Pass it an unprocessed SDP string. ```js const sdpStr = "v=0\r\n\ o=- 20518 0 IN IP4 203.0.113.1\r\n\ s= \r\n\ t=0 0\r\n\ c=IN IP4 203.0.113.1\r\n\ a=ice-ufrag:F7gI\r\n\ a=ice-pwd:x9cml/YzichV2+XlhiMu8g\r\n\ a=fingerprint:sha-1 42:89:c5:c6:55:9d:6e:c8:e8:83:55:2a:39:f9:b6:eb:e9:a3:a9:e7\r\n\ m=audio 54400 RTP/SAVPF 0 96\r\n\ a=rtpmap:0 PCMU/8000\r\n\ a=rtpmap:96 opus/48000\r\n\ a=ptime:20\r\n\ a=sendrecv\r\n\ a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host\r\n\ a=candidate:1 2 UDP 2113667326 203.0.113.1 54401 typ host\r\n\ m=video 55400 RTP/SAVPF 97 98\r\n\ a=rtpmap:97 H264/90000\r\n\ a=fmtp:97 profile-level-id=4d0028;packetization-mode=1\r\n\ a=rtpmap:98 VP8/90000\r\n\ a=sendrecv\r\n\ a=candidate:0 1 UDP 2113667327 203.0.113.1 55400 typ host\r\n\ a=candidate:1 2 UDP 2113667326 203.0.113.1 55401 typ host\r\n\ "; const res = sdpTransform.parse(sdpStr); // => { version: 0, origin: { username: '-', sessionId: 20518, sessionVersion: 0, netType: 'IN', ipVer: 4, address: '203.0.113.1' }, name: '', timing: { start: 0, stop: 0 }, connection: { version: 4, ip: '203.0.113.1' }, iceUfrag: 'F7gI', icePwd: 'x9cml/YzichV2+XlhiMu8g', fingerprint: { type: 'sha-1', hash: '42:89:c5:c6:55:9d:6e:c8:e8:83:55:2a:39:f9:b6:eb:e9:a3:a9:e7' }, media: [ { rtp: [Object], fmtp: [], type: 'audio', port: 54400, protocol: 'RTP/SAVPF', payloads: '0 96', ptime: 20, direction: 'sendrecv', candidates: [Object] }, { rtp: [Object], fmtp: [Object], type: 'video', port: 55400, protocol: 'RTP/SAVPF', payloads: '97 98', direction: 'sendrecv', candidates: [Object] } ] } // each media line is parsed into the following format res.media[1]; // => { rtp: [ { payload: 97, codec: 'H264', rate: 90000 }, { payload: 98, codec: 'VP8', rate: 90000 } ], fmtp: [ { payload: 97, config: 'profile-level-id=4d0028;packetization-mode=1' } ], type: 'video', port: 55400, protocol: 'RTP/SAVPF', payloads: '97 98', direction: 'sendrecv', candidates: [ { foundation: 0, component: 1, transport: 'UDP', priority: 2113667327, ip: '203.0.113.1', port: 55400, type: 'host' }, { foundation: 1, component: 2, transport: 'UDP', priority: 2113667326, ip: '203.0.113.1', port: 55401, type: 'host' } ] } ``` In this example, only slightly dodgy string coercion case here is for `candidates[i].foundation`, which can be a string, but in this case can be equally parsed as an integer. ### Parser Postprocessing No excess parsing is done to the raw strings apart from maybe coercing to ints, because the writer is built to be the inverse of the parser. That said, a few helpers have been built in: #### parseParams() Parses `fmtp.config` and others such as `rid.params` and returns an object with all the params in a key/value fashion. ```js // to parse the fmtp.config from the previous example sdpTransform.parseParams(res.media[1].fmtp[0].config); // => { 'profile-level-id': '4d0028', 'packetization-mode': 1 } ``` #### parsePayloads() Returns an array with all the payload advertised in the main m-line. ```js // what payloads where actually advertised in the main m-line ? sdpTransform.parsePayloads(res.media[1].payloads); // => [97, 98] ``` #### parseImageAttributes() Parses [Generic Image Attributes](https://tools.ietf.org/html/rfc6236). Must be provided with the `attrs1` or `attrs2` string of a `a=imageattr` line. Returns an array of key/value objects. ```js // a=imageattr:97 send [x=1280,y=720] recv [x=1280,y=720] [x=320,y=180] sdpTransform.parseImageAttributes(res.media[1].imageattrs[0].attrs2) // => [ {'x': 1280, 'y': 720}, {'x': 320, 'y': 180} ] ``` #### parseSimulcastStreamList() Parses [simulcast](https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast) streams/formats. Must be provided with the `attrs1` or `attrs2` string of the `a=simulcast` line. Returns an array of simulcast streams. Each entry is an array of alternative simulcast formats, which are objects with two keys: * `scid`: Simulcast identifier * `paused`: Whether the simulcast format is paused ```js // a=simulcast:send 1,~4;2;3 recv c sdpTransform.parseSimulcastStreamList(res.media[1].simulcast.attrs1); // => [ // First simulcast stream (two alternative formats) [ {scid: 1, paused: false}, {scid: 4, paused: true} ], // Second simulcast stream [ {scid: 2, paused: false} ], // Third simulcast stream [ {scid: 3, paused: false} ] ] ``` ## Usage - Writer The writer is the inverse of the parser, and will need a struct equivalent to the one returned by it. ```js sdpTransform.write(res).split('\r\n'); // res parsed above // => [ 'v=0', 'o=- 20518 0 IN IP4 203.0.113.1', 's= ', 'c=IN IP4 203.0.113.1', 't=0 0', 'a=ice-ufrag:F7gI', 'a=ice-pwd:x9cml/YzichV2+XlhiMu8g', 'a=fingerprint:sha-1 42:89:c5:c6:55:9d:6e:c8:e8:83:55:2a:39:f9:b6:eb:e9:a3:a9:e7', 'm=audio 54400 RTP/SAVPF 0 96', 'a=rtpmap:0 PCMU/8000', 'a=rtpmap:96 opus/48000', 'a=ptime:20', 'a=sendrecv', 'a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host', 'a=candidate:1 2 UDP 2113667326 203.0.113.1 54401 typ host', 'm=video 55400 RTP/SAVPF 97 98', 'a=rtpmap:97 H264/90000', 'a=rtpmap:98 VP8/90000', 'a=fmtp:97 profile-level-id=4d0028;packetization-mode=1', 'a=sendrecv', 'a=candidate:0 1 UDP 2113667327 203.0.113.1 55400 typ host', 'a=candidate:1 2 UDP 2113667326 203.0.113.1 55401 typ host' ] ``` The only thing different from the original input is we follow the order specified by the SDP RFC, and we will always do so. ## License MIT-Licensed. See LICENSE file for details. sdp-transform-2.14.1/checker.js000077500000000000000000000024651376166770200163710ustar00rootroot00000000000000#!/usr/bin/env node var transform = require('./') , file = require('path').join(process.cwd(), process.argv[2]) , sdp = require('fs').readFileSync(file).toString() , parsed = transform.parse(sdp) , written = transform.write(parsed) , writtenLines = written.split('\r\n') , origLines = sdp.split('\r\n') , numMissing = 0 , numNew = 0 ; var parseFails = 0; parsed.media.forEach(function (media) { (media.invalid || []).forEach(function (inv) { console.warn('unrecognized a=' + inv.value + ' belonging to m=' + media.type); parseFails += 1; }); }); var parseStr = parseFails + ' unrecognized line(s) copied blindly'; origLines.forEach(function (line, i) { if (writtenLines.indexOf(line) < 0) { console.error('l' + i + ' lost (' + line + ')'); numMissing += 1; } }); writtenLines.forEach(function (line, i) { if (origLines.indexOf(line) < 0) { console.error('l' + i + ' new (' + line + ')'); numNew += 1; } }); var failed = (numMissing > 0 || numNew > 0); if (failed) { console.log('\n' + file + ' changes during transform:'); console.log(numMissing + ' missing line(s), ' + numNew + ' new line(s)%s', parseFails > 0 ? ', ' + parseStr : '' ); } else { console.log(file + ' verified%s', parseFails > 0 ? ', but had ' + parseStr : ''); } process.exit(failed ? 1 : 0); sdp-transform-2.14.1/lib/000077500000000000000000000000001376166770200151635ustar00rootroot00000000000000sdp-transform-2.14.1/lib/grammar.js000066400000000000000000000346271376166770200171630ustar00rootroot00000000000000var grammar = module.exports = { v: [{ name: 'version', reg: /^(\d*)$/ }], o: [{ // o=- 20518 0 IN IP4 203.0.113.1 // NB: sessionId will be a String in most cases because it is huge name: 'origin', reg: /^(\S*) (\d*) (\d*) (\S*) IP(\d) (\S*)/, names: ['username', 'sessionId', 'sessionVersion', 'netType', 'ipVer', 'address'], format: '%s %s %d %s IP%d %s' }], // default parsing of these only (though some of these feel outdated) s: [{ name: 'name' }], i: [{ name: 'description' }], u: [{ name: 'uri' }], e: [{ name: 'email' }], p: [{ name: 'phone' }], z: [{ name: 'timezones' }], // TODO: this one can actually be parsed properly... r: [{ name: 'repeats' }], // TODO: this one can also be parsed properly // k: [{}], // outdated thing ignored t: [{ // t=0 0 name: 'timing', reg: /^(\d*) (\d*)/, names: ['start', 'stop'], format: '%d %d' }], c: [{ // c=IN IP4 10.47.197.26 name: 'connection', reg: /^IN IP(\d) (\S*)/, names: ['version', 'ip'], format: 'IN IP%d %s' }], b: [{ // b=AS:4000 push: 'bandwidth', reg: /^(TIAS|AS|CT|RR|RS):(\d*)/, names: ['type', 'limit'], format: '%s:%s' }], m: [{ // m=video 51744 RTP/AVP 126 97 98 34 31 // NB: special - pushes to session // TODO: rtp/fmtp should be filtered by the payloads found here? reg: /^(\w*) (\d*) ([\w/]*)(?: (.*))?/, names: ['type', 'port', 'protocol', 'payloads'], format: '%s %d %s %s' }], a: [ { // a=rtpmap:110 opus/48000/2 push: 'rtp', reg: /^rtpmap:(\d*) ([\w\-.]*)(?:\s*\/(\d*)(?:\s*\/(\S*))?)?/, names: ['payload', 'codec', 'rate', 'encoding'], format: function (o) { return (o.encoding) ? 'rtpmap:%d %s/%s/%s' : o.rate ? 'rtpmap:%d %s/%s' : 'rtpmap:%d %s'; } }, { // a=fmtp:108 profile-level-id=24;object=23;bitrate=64000 // a=fmtp:111 minptime=10; useinbandfec=1 push: 'fmtp', reg: /^fmtp:(\d*) ([\S| ]*)/, names: ['payload', 'config'], format: 'fmtp:%d %s' }, { // a=control:streamid=0 name: 'control', reg: /^control:(.*)/, format: 'control:%s' }, { // a=rtcp:65179 IN IP4 193.84.77.194 name: 'rtcp', reg: /^rtcp:(\d*)(?: (\S*) IP(\d) (\S*))?/, names: ['port', 'netType', 'ipVer', 'address'], format: function (o) { return (o.address != null) ? 'rtcp:%d %s IP%d %s' : 'rtcp:%d'; } }, { // a=rtcp-fb:98 trr-int 100 push: 'rtcpFbTrrInt', reg: /^rtcp-fb:(\*|\d*) trr-int (\d*)/, names: ['payload', 'value'], format: 'rtcp-fb:%s trr-int %d' }, { // a=rtcp-fb:98 nack rpsi push: 'rtcpFb', reg: /^rtcp-fb:(\*|\d*) ([\w-_]*)(?: ([\w-_]*))?/, names: ['payload', 'type', 'subtype'], format: function (o) { return (o.subtype != null) ? 'rtcp-fb:%s %s %s' : 'rtcp-fb:%s %s'; } }, { // a=extmap:2 urn:ietf:params:rtp-hdrext:toffset // a=extmap:1/recvonly URI-gps-string // a=extmap:3 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:smpte-tc 25@600/24 push: 'ext', reg: /^extmap:(\d+)(?:\/(\w+))?(?: (urn:ietf:params:rtp-hdrext:encrypt))? (\S*)(?: (\S*))?/, names: ['value', 'direction', 'encrypt-uri', 'uri', 'config'], format: function (o) { return ( 'extmap:%d' + (o.direction ? '/%s' : '%v') + (o['encrypt-uri'] ? ' %s' : '%v') + ' %s' + (o.config ? ' %s' : '') ); } }, { // a=extmap-allow-mixed name: 'extmapAllowMixed', reg: /^(extmap-allow-mixed)/ }, { // a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR|2^20|1:32 push: 'crypto', reg: /^crypto:(\d*) ([\w_]*) (\S*)(?: (\S*))?/, names: ['id', 'suite', 'config', 'sessionConfig'], format: function (o) { return (o.sessionConfig != null) ? 'crypto:%d %s %s %s' : 'crypto:%d %s %s'; } }, { // a=setup:actpass name: 'setup', reg: /^setup:(\w*)/, format: 'setup:%s' }, { // a=connection:new name: 'connectionType', reg: /^connection:(new|existing)/, format: 'connection:%s' }, { // a=mid:1 name: 'mid', reg: /^mid:([^\s]*)/, format: 'mid:%s' }, { // a=msid:0c8b064d-d807-43b4-b434-f92a889d8587 98178685-d409-46e0-8e16-7ef0db0db64a name: 'msid', reg: /^msid:(.*)/, format: 'msid:%s' }, { // a=ptime:20 name: 'ptime', reg: /^ptime:(\d*(?:\.\d*)*)/, format: 'ptime:%d' }, { // a=maxptime:60 name: 'maxptime', reg: /^maxptime:(\d*(?:\.\d*)*)/, format: 'maxptime:%d' }, { // a=sendrecv name: 'direction', reg: /^(sendrecv|recvonly|sendonly|inactive)/ }, { // a=ice-lite name: 'icelite', reg: /^(ice-lite)/ }, { // a=ice-ufrag:F7gI name: 'iceUfrag', reg: /^ice-ufrag:(\S*)/, format: 'ice-ufrag:%s' }, { // a=ice-pwd:x9cml/YzichV2+XlhiMu8g name: 'icePwd', reg: /^ice-pwd:(\S*)/, format: 'ice-pwd:%s' }, { // a=fingerprint:SHA-1 00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33 name: 'fingerprint', reg: /^fingerprint:(\S*) (\S*)/, names: ['type', 'hash'], format: 'fingerprint:%s %s' }, { // a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host // a=candidate:1162875081 1 udp 2113937151 192.168.34.75 60017 typ host generation 0 network-id 3 network-cost 10 // a=candidate:3289912957 2 udp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 generation 0 network-id 3 network-cost 10 // a=candidate:229815620 1 tcp 1518280447 192.168.150.19 60017 typ host tcptype active generation 0 network-id 3 network-cost 10 // a=candidate:3289912957 2 tcp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 tcptype passive generation 0 network-id 3 network-cost 10 push:'candidates', reg: /^candidate:(\S*) (\d*) (\S*) (\d*) (\S*) (\d*) typ (\S*)(?: raddr (\S*) rport (\d*))?(?: tcptype (\S*))?(?: generation (\d*))?(?: network-id (\d*))?(?: network-cost (\d*))?/, names: ['foundation', 'component', 'transport', 'priority', 'ip', 'port', 'type', 'raddr', 'rport', 'tcptype', 'generation', 'network-id', 'network-cost'], format: function (o) { var str = 'candidate:%s %d %s %d %s %d typ %s'; str += (o.raddr != null) ? ' raddr %s rport %d' : '%v%v'; // NB: candidate has three optional chunks, so %void middles one if it's missing str += (o.tcptype != null) ? ' tcptype %s' : '%v'; if (o.generation != null) { str += ' generation %d'; } str += (o['network-id'] != null) ? ' network-id %d' : '%v'; str += (o['network-cost'] != null) ? ' network-cost %d' : '%v'; return str; } }, { // a=end-of-candidates (keep after the candidates line for readability) name: 'endOfCandidates', reg: /^(end-of-candidates)/ }, { // a=remote-candidates:1 203.0.113.1 54400 2 203.0.113.1 54401 ... name: 'remoteCandidates', reg: /^remote-candidates:(.*)/, format: 'remote-candidates:%s' }, { // a=ice-options:google-ice name: 'iceOptions', reg: /^ice-options:(\S*)/, format: 'ice-options:%s' }, { // a=ssrc:2566107569 cname:t9YU8M1UxTF8Y1A1 push: 'ssrcs', reg: /^ssrc:(\d*) ([\w_-]*)(?::(.*))?/, names: ['id', 'attribute', 'value'], format: function (o) { var str = 'ssrc:%d'; if (o.attribute != null) { str += ' %s'; if (o.value != null) { str += ':%s'; } } return str; } }, { // a=ssrc-group:FEC 1 2 // a=ssrc-group:FEC-FR 3004364195 1080772241 push: 'ssrcGroups', // token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 / %x41-5A / %x5E-7E reg: /^ssrc-group:([\x21\x23\x24\x25\x26\x27\x2A\x2B\x2D\x2E\w]*) (.*)/, names: ['semantics', 'ssrcs'], format: 'ssrc-group:%s %s' }, { // a=msid-semantic: WMS Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV name: 'msidSemantic', reg: /^msid-semantic:\s?(\w*) (\S*)/, names: ['semantic', 'token'], format: 'msid-semantic: %s %s' // space after ':' is not accidental }, { // a=group:BUNDLE audio video push: 'groups', reg: /^group:(\w*) (.*)/, names: ['type', 'mids'], format: 'group:%s %s' }, { // a=rtcp-mux name: 'rtcpMux', reg: /^(rtcp-mux)/ }, { // a=rtcp-rsize name: 'rtcpRsize', reg: /^(rtcp-rsize)/ }, { // a=sctpmap:5000 webrtc-datachannel 1024 name: 'sctpmap', reg: /^sctpmap:([\w_/]*) (\S*)(?: (\S*))?/, names: ['sctpmapNumber', 'app', 'maxMessageSize'], format: function (o) { return (o.maxMessageSize != null) ? 'sctpmap:%s %s %s' : 'sctpmap:%s %s'; } }, { // a=x-google-flag:conference name: 'xGoogleFlag', reg: /^x-google-flag:([^\s]*)/, format: 'x-google-flag:%s' }, { // a=rid:1 send max-width=1280;max-height=720;max-fps=30;depend=0 push: 'rids', reg: /^rid:([\d\w]+) (\w+)(?: ([\S| ]*))?/, names: ['id', 'direction', 'params'], format: function (o) { return (o.params) ? 'rid:%s %s %s' : 'rid:%s %s'; } }, { // a=imageattr:97 send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320] recv [x=330,y=250] // a=imageattr:* send [x=800,y=640] recv * // a=imageattr:100 recv [x=320,y=240] push: 'imageattrs', reg: new RegExp( // a=imageattr:97 '^imageattr:(\\d+|\\*)' + // send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320] '[\\s\\t]+(send|recv)[\\s\\t]+(\\*|\\[\\S+\\](?:[\\s\\t]+\\[\\S+\\])*)' + // recv [x=330,y=250] '(?:[\\s\\t]+(recv|send)[\\s\\t]+(\\*|\\[\\S+\\](?:[\\s\\t]+\\[\\S+\\])*))?' ), names: ['pt', 'dir1', 'attrs1', 'dir2', 'attrs2'], format: function (o) { return 'imageattr:%s %s %s' + (o.dir2 ? ' %s %s' : ''); } }, { // a=simulcast:send 1,2,3;~4,~5 recv 6;~7,~8 // a=simulcast:recv 1;4,5 send 6;7 name: 'simulcast', reg: new RegExp( // a=simulcast: '^simulcast:' + // send 1,2,3;~4,~5 '(send|recv) ([a-zA-Z0-9\\-_~;,]+)' + // space + recv 6;~7,~8 '(?:\\s?(send|recv) ([a-zA-Z0-9\\-_~;,]+))?' + // end '$' ), names: ['dir1', 'list1', 'dir2', 'list2'], format: function (o) { return 'simulcast:%s %s' + (o.dir2 ? ' %s %s' : ''); } }, { // old simulcast draft 03 (implemented by Firefox) // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-03 // a=simulcast: recv pt=97;98 send pt=97 // a=simulcast: send rid=5;6;7 paused=6,7 name: 'simulcast_03', reg: /^simulcast:[\s\t]+([\S+\s\t]+)$/, names: ['value'], format: 'simulcast: %s' }, { // a=framerate:25 // a=framerate:29.97 name: 'framerate', reg: /^framerate:(\d+(?:$|\.\d+))/, format: 'framerate:%s' }, { // RFC4570 // a=source-filter: incl IN IP4 239.5.2.31 10.1.15.5 name: 'sourceFilter', reg: /^source-filter: *(excl|incl) (\S*) (IP4|IP6|\*) (\S*) (.*)/, names: ['filterMode', 'netType', 'addressTypes', 'destAddress', 'srcList'], format: 'source-filter: %s %s %s %s %s' }, { // a=bundle-only name: 'bundleOnly', reg: /^(bundle-only)/ }, { // a=label:1 name: 'label', reg: /^label:(.+)/, format: 'label:%s' }, { // RFC version 26 for SCTP over DTLS // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-26#section-5 name: 'sctpPort', reg: /^sctp-port:(\d+)$/, format: 'sctp-port:%s' }, { // RFC version 26 for SCTP over DTLS // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-26#section-6 name: 'maxMessageSize', reg: /^max-message-size:(\d+)$/, format: 'max-message-size:%s' }, { // RFC7273 // a=ts-refclk:ptp=IEEE1588-2008:39-A7-94-FF-FE-07-CB-D0:37 push:'tsRefClocks', reg: /^ts-refclk:([^\s=]*)(?:=(\S*))?/, names: ['clksrc', 'clksrcExt'], format: function (o) { return 'ts-refclk:%s' + (o.clksrcExt != null ? '=%s' : ''); } }, { // RFC7273 // a=mediaclk:direct=963214424 name:'mediaClk', reg: /^mediaclk:(?:id=(\S*))? *([^\s=]*)(?:=(\S*))?(?: *rate=(\d+)\/(\d+))?/, names: ['id', 'mediaClockName', 'mediaClockValue', 'rateNumerator', 'rateDenominator'], format: function (o) { var str = 'mediaclk:'; str += (o.id != null ? 'id=%s %s' : '%v%s'); str += (o.mediaClockValue != null ? '=%s' : ''); str += (o.rateNumerator != null ? ' rate=%s' : ''); str += (o.rateDenominator != null ? '/%s' : ''); return str; } }, { // a=keywds:keywords name: 'keywords', reg: /^keywds:(.+)$/, format: 'keywds:%s' }, { // a=content:main name: 'content', reg: /^content:(.+)/, format: 'content:%s' }, // BFCP https://tools.ietf.org/html/rfc4583 { // a=floorctrl:c-s name: 'bfcpFloorCtrl', reg: /^floorctrl:(c-only|s-only|c-s)/, format: 'floorctrl:%s' }, { // a=confid:1 name: 'bfcpConfId', reg: /^confid:(\d+)/, format: 'confid:%s' }, { // a=userid:1 name: 'bfcpUserId', reg: /^userid:(\d+)/, format: 'userid:%s' }, { // a=floorid:1 name: 'bfcpFloorId', reg: /^floorid:(.+) (?:m-stream|mstrm):(.+)/, names: ['id', 'mStream'], format: 'floorid:%s mstrm:%s' }, { // any a= that we don't understand is kept verbatim on media.invalid push: 'invalid', names: ['value'] } ] }; // set sensible defaults to avoid polluting the grammar with boring details Object.keys(grammar).forEach(function (key) { var objs = grammar[key]; objs.forEach(function (obj) { if (!obj.reg) { obj.reg = /(.*)/; } if (!obj.format) { obj.format = '%s'; } }); }); sdp-transform-2.14.1/lib/index.js000066400000000000000000000007361376166770200166360ustar00rootroot00000000000000var parser = require('./parser'); var writer = require('./writer'); exports.write = writer; exports.parse = parser.parse; exports.parseParams = parser.parseParams; exports.parseFmtpConfig = parser.parseFmtpConfig; // Alias of parseParams(). exports.parsePayloads = parser.parsePayloads; exports.parseRemoteCandidates = parser.parseRemoteCandidates; exports.parseImageAttributes = parser.parseImageAttributes; exports.parseSimulcastStreamList = parser.parseSimulcastStreamList; sdp-transform-2.14.1/lib/parser.js000066400000000000000000000062271376166770200170240ustar00rootroot00000000000000var toIntIfInt = function (v) { return String(Number(v)) === v ? Number(v) : v; }; var attachProperties = function (match, location, names, rawName) { if (rawName && !names) { location[rawName] = toIntIfInt(match[1]); } else { for (var i = 0; i < names.length; i += 1) { if (match[i+1] != null) { location[names[i]] = toIntIfInt(match[i+1]); } } } }; var parseReg = function (obj, location, content) { var needsBlank = obj.name && obj.names; if (obj.push && !location[obj.push]) { location[obj.push] = []; } else if (needsBlank && !location[obj.name]) { location[obj.name] = {}; } var keyLocation = obj.push ? {} : // blank object that will be pushed needsBlank ? location[obj.name] : location; // otherwise, named location or root attachProperties(content.match(obj.reg), keyLocation, obj.names, obj.name); if (obj.push) { location[obj.push].push(keyLocation); } }; var grammar = require('./grammar'); var validLine = RegExp.prototype.test.bind(/^([a-z])=(.*)/); exports.parse = function (sdp) { var session = {} , media = [] , location = session; // points at where properties go under (one of the above) // parse lines we understand sdp.split(/(\r\n|\r|\n)/).filter(validLine).forEach(function (l) { var type = l[0]; var content = l.slice(2); if (type === 'm') { media.push({rtp: [], fmtp: []}); location = media[media.length-1]; // point at latest media line } for (var j = 0; j < (grammar[type] || []).length; j += 1) { var obj = grammar[type][j]; if (obj.reg.test(content)) { return parseReg(obj, location, content); } } }); session.media = media; // link it up return session; }; var paramReducer = function (acc, expr) { var s = expr.split(/=(.+)/, 2); if (s.length === 2) { acc[s[0]] = toIntIfInt(s[1]); } else if (s.length === 1 && expr.length > 1) { acc[s[0]] = undefined; } return acc; }; exports.parseParams = function (str) { return str.split(/;\s?/).reduce(paramReducer, {}); }; // For backward compatibility - alias will be removed in 3.0.0 exports.parseFmtpConfig = exports.parseParams; exports.parsePayloads = function (str) { return str.toString().split(' ').map(Number); }; exports.parseRemoteCandidates = function (str) { var candidates = []; var parts = str.split(' ').map(toIntIfInt); for (var i = 0; i < parts.length; i += 3) { candidates.push({ component: parts[i], ip: parts[i + 1], port: parts[i + 2] }); } return candidates; }; exports.parseImageAttributes = function (str) { return str.split(' ').map(function (item) { return item.substring(1, item.length-1).split(',').reduce(paramReducer, {}); }); }; exports.parseSimulcastStreamList = function (str) { return str.split(';').map(function (stream) { return stream.split(',').map(function (format) { var scid, paused = false; if (format[0] !== '~') { scid = toIntIfInt(format); } else { scid = toIntIfInt(format.substring(1, format.length)); paused = true; } return { scid: scid, paused: paused }; }); }); }; sdp-transform-2.14.1/lib/writer.js000066400000000000000000000057751376166770200170530ustar00rootroot00000000000000var grammar = require('./grammar'); // customized util.format - discards excess arguments and can void middle ones var formatRegExp = /%[sdv%]/g; var format = function (formatStr) { var i = 1; var args = arguments; var len = args.length; return formatStr.replace(formatRegExp, function (x) { if (i >= len) { return x; // missing argument } var arg = args[i]; i += 1; switch (x) { case '%%': return '%'; case '%s': return String(arg); case '%d': return Number(arg); case '%v': return ''; } }); // NB: we discard excess arguments - they are typically undefined from makeLine }; var makeLine = function (type, obj, location) { var str = obj.format instanceof Function ? (obj.format(obj.push ? location : location[obj.name])) : obj.format; var args = [type + '=' + str]; if (obj.names) { for (var i = 0; i < obj.names.length; i += 1) { var n = obj.names[i]; if (obj.name) { args.push(location[obj.name][n]); } else { // for mLine and push attributes args.push(location[obj.names[i]]); } } } else { args.push(location[obj.name]); } return format.apply(null, args); }; // RFC specified order // TODO: extend this with all the rest var defaultOuterOrder = [ 'v', 'o', 's', 'i', 'u', 'e', 'p', 'c', 'b', 't', 'r', 'z', 'a' ]; var defaultInnerOrder = ['i', 'c', 'b', 'a']; module.exports = function (session, opts) { opts = opts || {}; // ensure certain properties exist if (session.version == null) { session.version = 0; // 'v=0' must be there (only defined version atm) } if (session.name == null) { session.name = ' '; // 's= ' must be there if no meaningful name set } session.media.forEach(function (mLine) { if (mLine.payloads == null) { mLine.payloads = ''; } }); var outerOrder = opts.outerOrder || defaultOuterOrder; var innerOrder = opts.innerOrder || defaultInnerOrder; var sdp = []; // loop through outerOrder for matching properties on session outerOrder.forEach(function (type) { grammar[type].forEach(function (obj) { if (obj.name in session && session[obj.name] != null) { sdp.push(makeLine(type, obj, session)); } else if (obj.push in session && session[obj.push] != null) { session[obj.push].forEach(function (el) { sdp.push(makeLine(type, obj, el)); }); } }); }); // then for each media line, follow the innerOrder session.media.forEach(function (mLine) { sdp.push(makeLine('m', grammar.m[0], mLine)); innerOrder.forEach(function (type) { grammar[type].forEach(function (obj) { if (obj.name in mLine && mLine[obj.name] != null) { sdp.push(makeLine(type, obj, mLine)); } else if (obj.push in mLine && mLine[obj.push] != null) { mLine[obj.push].forEach(function (el) { sdp.push(makeLine(type, obj, el)); }); } }); }); }); return sdp.join('\r\n') + '\r\n'; }; sdp-transform-2.14.1/package.json000066400000000000000000000014771376166770200167140ustar00rootroot00000000000000{ "name": "sdp-transform", "description": "A simple parser/writer for the Session Description Protocol", "author": "Eirik Albrigtsen ", "version": "2.14.1", "repository": { "type": "git", "url": "clux/sdp-transform" }, "keywords": [ "sdp", "webrtc", "serializer" ], "main": "./lib/", "bin": { "sdp-verify": "./checker.js" }, "scripts": { "lint": "eslint .", "test": "bndg test/*.test.js", "precoverage": "istanbul cover bndg test/*.test.js", "coverage": "cat coverage/lcov.info && rm -rf coverage" }, "dependencies": {}, "devDependencies": { "bandage": "^0.5.0", "co-fs": "^1.2.0", "eslint": "^5.10.0", "istanbul": "^0.4.5" }, "bugs": { "url": "https://github.com/clux/sdp-transform/issues" }, "license": "MIT" } sdp-transform-2.14.1/test/000077500000000000000000000000001376166770200153745ustar00rootroot00000000000000sdp-transform-2.14.1/test/alac.sdp000066400000000000000000000005221376166770200170030ustar00rootroot00000000000000v=0 o=iTunes 3413821438 0 IN IP4 fe80::217:f2ff:fe0f:e0f6 s=iTunes c=IN IP4 fe80::5a55:caff:fe1a:e187 t=0 0 m=audio 0 RTP/AVP 96 a=rtpmap:96 AppleLossless a=fmtp:96 352 0 16 40 10 14 2 255 0 0 44100 a=fpaeskey:RlBMWQECAQAAAAA8AAAAAPFOnNe+zWb5/n4L5KZkE2AAAAAQlDx69reTdwHF9LaNmhiRURTAbcL4brYAceAkZ49YirXm62N4 a=aesiv:5b+YZi9Ikb845BmNhaVo+Q sdp-transform-2.14.1/test/bfcp.sdp000066400000000000000000000012251376166770200170160ustar00rootroot00000000000000v=0 o=- 1556822052 1556822053 IN IP4 192.0.0.0 s= c=IN IP4 192.0.0.0 b=AS:1024 t=0 0 a=sendrecv m=audio 3230 RTP/AVP 9 a=sendrecv a=rtpmap:9 G722/8000 a=fmtp:9 bitrate=64000 m=video 3232 RTP/AVP 111 a=sendrecv a=content:main a=label:1 a=rtpmap:111 H264/90000 a=fmtp:111 profile-level-id=64001f; packetization-mode=1; max-br=20010; sar=13 m=application 3238 UDP/BFCP * a=floorctrl:s-only a=confid:1 a=userid:1 a=floorid:1 m-stream:3 a=setup:passive a=connection:new m=video 3234 RTP/AVP 111 a=sendrecv a=content:slides a=label:3 a=rtpmap:111 H264/90000 a=fmtp:111 profile-level-id=64001f; packetization-mode=1; max-mbps=122500; max-fs=8192; max-br=20010; sar=13 sdp-transform-2.14.1/test/compose.test.js000066400000000000000000000026461376166770200203650ustar00rootroot00000000000000var fs = require('co-fs') , test = require('bandage') , main = require('..') , parse = main.parse , write = main.write ; var verifyCompose = function *(file, t) { var sdp = yield fs.readFile(__dirname + '/' + file, 'utf8'); var obj = parse(sdp); var sdp2 = write(obj); var obj2 = parse(sdp2); t.deepEqual(obj, obj2, 'parse ∘ write ∘ parse === parse | ' + file); // This only tests that (parse ∘ write) == Id on the image of the parse. // It also doesn't test if (write ∘ parse) is the identity: which it isnt. // Properties may get reordered slightly (up to RFC legality). // However: (write ∘ parse) should be the identity on the image of write // because our own ordering is deterministic. t.equal(sdp2, write(obj2), 'write ∘ parse === Id on Im(write) for ' + file); }; var sdps = [ 'normal.sdp', 'hacky.sdp', 'icelite.sdp', 'invalid.sdp', 'jssip.sdp', 'jsep.sdp', 'alac.sdp', 'onvif.sdp', 'ssrc.sdp', 'simulcast.sdp', 'st2022-6.sdp', 'st2110-20.sdp', 'sctp-dtls-26.sdp', 'extmap-encrypt.sdp', 'dante-aes67.sdp', 'bfcp.sdp', 'tcp-active.sdp', 'tcp-passive.sdp', 'mediaclk-avbtp.sdp', 'mediaclk-ptp-v2-w-rate.sdp', 'mediaclk-ptp-v2.sdp', 'mediaclk-rtp.sdp', 'ts-refclk-media.sdp', 'ts-refclk-sess.sdp', 'rtcp-fb.sdp' ]; sdps.forEach((name) => { test(name.split('.')[0] + 'Compose', function *(t) { yield verifyCompose(name, t); }); }); sdp-transform-2.14.1/test/coverage.test.js000066400000000000000000000021571376166770200205100ustar00rootroot00000000000000var fs = require('co-fs') , test = require('bandage') , main = require('..') , parse = main.parse ; var verifyCoverage = function *(file, t) { var sdp = yield fs.readFile(__dirname + '/' + file, 'utf8'); var obj = parse(sdp); obj.media.forEach(function (m) { t.ok(!m.invalid, 'no invalids in ' + file + ' at m=' + m.type); if (Array.isArray(m.invalid)) { t.deepEqual(m.invalid, [], 'no invalids in ' + file + ' at m=' + m.type); } }); }; var sdps = [ 'normal.sdp', 'hacky.sdp', 'icelite.sdp', 'jssip.sdp', 'jsep.sdp', // 'alac.sdp', // deliberate invalids 'onvif.sdp', 'ssrc.sdp', 'simulcast.sdp', 'st2022-6.sdp', // 'st2110-20.sdp', // deliberate invalids 'sctp-dtls-26.sdp', 'extmap-encrypt.sdp', 'dante-aes67.sdp', 'bfcp.sdp', 'tcp-active.sdp', 'tcp-passive.sdp', 'mediaclk-avbtp.sdp', 'mediaclk-ptp-v2-w-rate.sdp', 'mediaclk-ptp-v2.sdp', 'mediaclk-rtp.sdp', 'ts-refclk-media.sdp', 'ts-refclk-sess.sdp', 'rtcp-fb.sdp' ]; sdps.forEach((name) => { test(name.split('.')[0] + 'Coverage', function *(t) { yield verifyCoverage(name, t); }); }); sdp-transform-2.14.1/test/dante-aes67.sdp000066400000000000000000000003461376166770200201250ustar00rootroot00000000000000v=0 o=- 1423986 1423994 IN IP4 169.254.98.63 s=AOIP44-serial-1614 : 2 c=IN IP4 239.65.125.63/32 t=0 0 a=keywds:Dante m=audio 5004 RTP/AVP 97 i=2 channels: TxChan 0, TxChan 1 a=recvonly a=rtpmap:97 L24/48000/2 a=ptime:1 sdp-transform-2.14.1/test/extmap-encrypt.sdp000066400000000000000000000005561376166770200210720ustar00rootroot00000000000000v=0 o=- 20518 0 IN IP4 203.0.113.1 s= t=0 0 c=IN IP4 203.0.113.1 m=audio 54400 RTP/SAVPF 96 a=rtpmap:96 opus/48000 a=extmap:1/sendonly URI-toffset a=extmap:2 urn:ietf:params:rtp-hdrext:toffset a=extmap:3 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:smpte-tc 25@600/24 a=extmap:4/recvonly urn:ietf:params:rtp-hdrext:encrypt URI-gps-string sdp-transform-2.14.1/test/hacky.sdp000066400000000000000000000054361376166770200172130ustar00rootroot00000000000000v=0 o=- 3710604898417546434 2 IN IP4 127.0.0.1 s=- t=0 0 a=group:BUNDLE audio video a=msid-semantic: WMS Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV m=audio 1 RTP/SAVPF 111 103 104 0 8 107 106 105 13 126 c=IN IP4 0.0.0.0 a=rtcp:1 IN IP4 0.0.0.0 a=candidate:1127303604 1 udp 2122260223 0.0.0.0 60672 typ host generation 0 a=candidate:229815620 1 tcp 1518280447 0.0.0.0 0 typ host tcptype active generation 0 a=candidate:1 1 TCP 2128609279 10.0.1.1 9 typ host tcptype active a=candidate:2 1 TCP 2124414975 10.0.1.1 8998 typ host tcptype passive a=candidate:3 1 TCP 2120220671 10.0.1.1 8999 typ host tcptype so a=candidate:4 1 TCP 1688207359 192.0.2.3 9 typ srflx raddr 10.0.1.1 rport 9 tcptype active a=candidate:5 1 TCP 1684013055 192.0.2.3 45664 typ srflx raddr 10.0.1.1 rport 8998 tcptype passive generation 5 a=candidate:6 1 TCP 1692401663 192.0.2.3 45687 typ srflx raddr 10.0.1.1 rport 8999 tcptype so a=ice-ufrag:lat6xwB1/flm+VwG a=ice-pwd:L5+HonleGeFHa8jPZLc/kr0E a=ice-options:google-ice 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:8QVQSHJ2AM8gIumHpYRRdWHyZ5NkLhaTD1AENOWx a=rtpmap:111 opus/48000/2 a=fmtp:111 minptime=10 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=ptime:0.125 a=maxptime:60 a=ssrc:2754920552 cname:t9YU8M1UxTF8Y1A1 a=ssrc:2754920552 msid:Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlVa0 a=ssrc:2754920552 mslabel:Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV a=ssrc:2754920552 label:Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlVa0 a=label:1 m=video 1 RTP/SAVPF 100 116 117 c=IN IP4 0.0.0.0 a=rtcp:12312 a=ice-ufrag:lat6xwB1/flm+VwG a=ice-pwd:L5+HonleGeFHa8jPZLc/kr0E a=ice-options:google-ice a=extmap:2 urn:ietf:params:rtp-hdrext:toffset a=sendrecv a=mid:video a=rtcp-mux a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:8QVQSHJ2AM8gIumHpYRRdWHyZ5NkLhaTD1AENOWx 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:2566107569 cname:t9YU8M1UxTF8Y1A1 a=ssrc:2566107569 msid:Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlVv0 a=ssrc:2566107569 mslabel:Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV a=ssrc:2566107569 label:Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlVv0 m=application 9 DTLS/SCTP 5000 c=IN IP4 0.0.0.0 b=AS:30 a=framerate:29.97 a=ice-ufrag:pDUB98Lc+2dc5+JF a=ice-pwd:G/CIMBOa9RQINDL4Y8NjpotH a=fingerprint:sha-256 F0:37:78:FE:3D:13:E9:10:B5:0C:4C:9E:48:37:E7:A0:F8:16:DC:1A:2C:69:67:B0:DF:E6:CB:73:F8:EF:BA:02 a=setup:active a=mid:33db2c4da91d73fd a=sctpmap:5000 webrtc-datachannel 1024 sdp-transform-2.14.1/test/icelite.sdp000066400000000000000000000010411376166770200175160ustar00rootroot00000000000000v=0 o=- 3622532974 3622532974 IN IP4 192.168.100.100 s=- c=IN IP4 192.168.100.100 t=0 0 a=ice-lite m=audio 10018 RTP/SAVPF 8 0 101 a=rtpmap:8 PCMA/8000 a=rtpmap:0 PCMU/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-15 a=sendrecv a=rtcp-mux a=setup:actpass a=fingerprint:sha-256 CE:17:02:86:E2:E8:B0:EF:F9:F3:3F:82:8A:A6:F0:EF:30:73:1D:5D:B3:5A:60:D7:AC:FE:F0:E3:DF:D5:D9:7B a=ice-ufrag:nXET a=ice-pwd:d0iwx/Qam8JnuvL+wkcXee a=candidate:X 1 UDP 659136 192.168.100.100 10018 typ host a=candidate:X 2 UDP 659134 192.168.100.100 10019 typ host sdp-transform-2.14.1/test/invalid.sdp000066400000000000000000000002531376166770200175320ustar00rootroot00000000000000v=0 o=- 3710604898417546434 2 IN IP4 127.0.0.1 s=- t=0 0 m=audio 1 RTP/AVP 0 c=IN IP4 0.0.0.0 a=rtcp:1 IN IP7 X a=rtpmap:0 PCMU/8000 a=goo:hithere f=invalid:yes sdp-transform-2.14.1/test/jsep.sdp000066400000000000000000000033121376166770200170440ustar00rootroot00000000000000v=0 o=- 4962303333179871722 1 IN IP4 0.0.0.0 s=- t=0 0 a=msid-semantic:WMS a=group:BUNDLE a1 v1 m=audio 56500 UDP/TLS/RTP/SAVPF 96 0 8 97 98 c=IN IP4 192.0.2.1 a=mid:a1 a=rtcp:56501 IN IP4 192.0.2.1 a=msid:47017fee-b6c1-4162-929c-a25110252400 f83006c5-a0ff-4e0a-9ed9-d3e6747be7d9 a=sendrecv a=rtpmap:96 opus/48000/2 a=rtpmap:0 PCMU/8000 a=rtpmap:8 PCMA/8000 a=rtpmap:97 telephone-event/8000 a=rtpmap:98 telephone-event/48000 a=maxptime:120 a=ice-ufrag:ETEn1v9DoTMB9J4r a=ice-pwd:OtSK0WpNtpUjkY4+86js7ZQl a=ice-options:trickle a=fingerprint:sha-256 19:E2:1C:3B:4B:9F:81:E6:B8:5C:F4:A5:A8:D8:73:04:BB:05:2F:70:9F:04:A9:0E:05:E9:26:33:E8:70:88:A2 a=setup:actpass a=rtcp-mux a=rtcp-rsize a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level a=extmap:2 urn:ietf:params:rtp-hdrext:sdes:mid a=ssrc:1732846380 cname:EocUG1f0fcg/yvY7 a=candidate:3348148302 1 udp 2113937151 192.0.2.1 56500 typ host a=candidate:3348148302 2 udp 2113937151 192.0.2.1 56501 typ host a=end-of-candidates m=video 0 UDP/TLS/RTP/SAVPF 100 101 c=IN IP4 192.0.2.1 a=rtcp:56503 IN IP4 192.0.2.1 a=mid:v1 a=bundle-only a=msid:61317484-2ed4-49d7-9eb7-1414322a7aae f30bdb4a-5db8-49b5-bcdc-e0c9a23172e0 a=sendrecv a=rtpmap:100 VP8/90000 a=rtpmap:101 rtx/90000 a=fmtp:101 apt=100 a=ice-ufrag:BGKkWnG5GmiUpdIV a=ice-pwd:mqyWsAjvtKwTGnvhPztQ9mIf a=ice-options:trickle a=fingerprint:sha-256 19:E2:1C:3B:4B:9F:81:E6:B8:5C:F4:A5:A8:D8:73:04:BB:05:2F:70:9F:04:A9:0E:05:E9:26:33:E8:70:88:A2 a=setup:actpass a=rtcp-mux a=rtcp-rsize a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid a=rtcp-fb:100 ccm fir a=rtcp-fb:100 nack a=rtcp-fb:100 nack pli a=ssrc:1366781083 cname:EocUG1f0fcg/yvY7 a=ssrc:1366781084 cname:EocUG1f0fcg/yvY7 a=ssrc-group:FID 1366781083 1366781084 a=end-of-candidates sdp-transform-2.14.1/test/jssip.sdp000066400000000000000000000034441376166770200172410ustar00rootroot00000000000000v=0 o=- 1334496563563564720 2 IN IP4 127.0.0.1 s=- t=0 0 a=group:BUNDLE audio a=msid-semantic: WMS KOaPIn6F0Qm9PuOA6WHfjdfqWMt9sGl6uOqg m=audio 60017 RTP/SAVPF 111 103 104 0 8 106 105 13 126 c=IN IP4 193.84.77.194 a=rtcp:60017 IN IP4 193.84.77.194 a=candidate:1162875081 1 udp 2113937151 192.168.34.75 60017 typ host generation 0 a=candidate:1162875081 2 udp 2113937151 192.168.34.75 60017 typ host generation 0 a=candidate:3289912957 1 udp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 generation 0 a=candidate:3289912957 2 udp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 generation 0 a=candidate:198437945 1 tcp 1509957375 192.168.34.75 0 typ host generation 0 a=candidate:198437945 2 tcp 1509957375 192.168.34.75 0 typ host generation 0 a=ice-ufrag:5I2uVefP13X1wzOY a=ice-pwd:e46UjXntt0K/xTncQcDBQePn a=ice-options:google-ice a=fingerprint:sha-256 79:14:AB:AB:93:7F:07:E8:91:1A:11:16:36:D0:11:66:C4:4F:31:A0:74:46:65:58:70:E5:09:95:48:F4:4B:D9 a=setup:actpass a=mid:audio a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level a=sendrecv a=rtcp-mux a=crypto:0 AES_CM_128_HMAC_SHA1_32 inline:6JYKxLF+o2nhouDHr5J0oNb3CEGK3I/HHv9idGTY a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:ayId2M5kCitGTEEI9OjgEqatTA0IXGpQhFjmKOGk a=rtpmap:111 opus/48000/2 a=fmtp:111 minptime=10 a=rtpmap:103 ISAC/16000 a=rtpmap:104 ISAC/32000 a=rtpmap:0 PCMU/8000 a=rtpmap:8 PCMA/8000 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:1399694169 cname:w7AkLB30C7pk/PFE a=ssrc:1399694169 msid:KOaPIn6F0Qm9PuOA6WHfjdfqWMt9sGl6uOqg 775dca64-4698-455b-8a02-89833bd24773 a=ssrc:1399694169 mslabel:KOaPIn6F0Qm9PuOA6WHfjdfqWMt9sGl6uOqg a=ssrc:1399694169 label:775dca64-4698-455b-8a02-89833bd24773 sdp-transform-2.14.1/test/mediaclk-avbtp.sdp000066400000000000000000000003561376166770200207730ustar00rootroot00000000000000v=0 o=- 1311738121 1311738121 IN IP4 192.0.2.1 c=IN IP4 233.252.0.1/64 s= t=0 0 m=audio 5004 RTP/AVP 96 a=rtpmap:96 L24/48000/2 a=sendonly a=ts-refclk:ptp=IEEE1588-2008:39-A7-94-FF-FE-07-CB-D0:0 a=mediaclk:IEEE1722=38-D6-6D-8E-D2-78-13-2Fsdp-transform-2.14.1/test/mediaclk-ptp-v2-w-rate.sdp000066400000000000000000000003551376166770200222030ustar00rootroot00000000000000v=0 o=- 1311738121 1311738121 IN IP4 192.0.2.1 c=IN IP4 233.252.0.1/64 s= t=0 0 m=audio 5004 RTP/AVP 96 a=rtpmap:96 L24/44100/2 a=sendonly a=ts-refclk:ptp=IEEE1588-2008:39-A7-94-FF-FE-07-CB-D0:0 a=mediaclk:direct=963214424 rate=1000/1001sdp-transform-2.14.1/test/mediaclk-ptp-v2.sdp000066400000000000000000000003361376166770200210050ustar00rootroot00000000000000v=0 o=- 1311738121 1311738121 IN IP4 192.0.2.1 c=IN IP4 233.252.0.1/64 s= t=0 0 m=audio 5004 RTP/AVP 96 a=rtpmap:96 L24/48000/8 a=sendonly a=ts-refclk:ptp=IEEE1588-2008:39-A7-94-FF-FE-07-CB-D0:0 a=mediaclk:direct=963214424sdp-transform-2.14.1/test/mediaclk-rtp.sdp000066400000000000000000000003601376166770200204570ustar00rootroot00000000000000v=0 o=- 1311738121 1311738121 IN IP4 192.0.2.1 c=IN IP4 233.252.0.1/64 s= t=0 0 m=audio 5004 RTP/AVP 96 a=rtpmap:96 L24/48000/2 a=sendonly a=ts-refclk:ptp=IEEE1588-2008:39-A7-94-FF-FE-07-CB-D0:0 a=mediaclk:id=MDA6NjA6MmI6MjA6MTI6MWY= sendersdp-transform-2.14.1/test/normal.sdp000066400000000000000000000031001376166770200173660ustar00rootroot00000000000000v=0 o=- 20518 0 IN IP4 203.0.113.1 s= t=0 0 c=IN IP4 203.0.113.1 a=ice-ufrag:F7gI a=ice-pwd:x9cml/YzichV2+XlhiMu8g a=fingerprint:sha-1 42:89:c5:c6:55:9d:6e:c8:e8:83:55:2a:39:f9:b6:eb:e9:a3:a9:e7 m=audio 54400 RTP/SAVPF 0 96 a=rtpmap:0 PCMU/8000 a=rtpmap:96 opus/48000 a=extmap:1 URI-toffset a=extmap:2/recvonly URI-gps-string a=extmap-allow-mixed a=ptime:20 a=sendrecv a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host a=candidate:1 2 UDP 2113667326 203.0.113.1 54401 typ host a=candidate:2 1 UDP 1686052607 203.0.113.1 54402 typ srflx raddr 192.168.1.145 rport 54402 generation 0 network-id 3 network-cost 10 a=candidate:3 2 UDP 1686052606 203.0.113.1 54403 typ srflx raddr 192.168.1.145 rport 54403 generation 0 network-id 3 network-cost 10 m=video 55400 RTP/SAVPF 97 98 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=4d0028;packetization-mode=1;sprop-parameter-sets=Z0IAH5WoFAFuQA==,aM48gA== a=fmtp:98 minptime=10; useinbandfec=1 a=rtpmap:98 VP8/90000 a=rtcp-fb:* nack a=rtcp-fb:98 nack rpsi a=rtcp-fb:98 trr-int 100 a=crypto:1 AES_CM_128_HMAC_SHA1_32 inline:keNcG3HezSNID7LmfDa9J4lfdUL8W1F7TNJKcbuy|2^20|1:32 a=sendrecv a=candidate:0 1 UDP 2113667327 203.0.113.1 55400 typ host a=candidate:1 2 UDP 2113667326 203.0.113.1 55401 typ host a=candidate:2 1 UDP 1686052607 203.0.113.1 55402 typ srflx raddr 192.168.1.145 rport 55402 generation 0 network-id 3 a=candidate:3 2 UDP 1686052606 203.0.113.1 55403 typ srflx raddr 192.168.1.145 rport 55403 generation 0 network-id 3 a=ssrc:1399694169 foo:bar a=ssrc:1399694169 baz a=ssrc:1399694169 foo-bar:baz sdp-transform-2.14.1/test/onvif.sdp000066400000000000000000000005111376166770200172220ustar00rootroot00000000000000v=0 o=- 2890844256 2890842807 IN IP4 172.16.2.93 s=RTSP Session m=audio 0 RTP/AVP 0 a=control:rtsp://example.com/onvif_camera/audio m=video 0 RTP/AVP 26 a=control:rtsp://example.com/onvif_camera/video m=application 0 RTP/AVP 107 a=control:rtsp://example.com/onvif_camera/metadata a=recvonly a=rtpmap:107 vnd.onvif.metadata/90000 sdp-transform-2.14.1/test/parse.test.js000066400000000000000000001111751376166770200200300ustar00rootroot00000000000000var fs = require('co-fs') , test = require('bandage') , main = require('..') , parse = main.parse , write = main.write , parseFmtpConfig = main.parseFmtpConfig , parseParams = main.parseParams , parseImageAttributes = main.parseImageAttributes , parseSimulcastStreamList = main.parseSimulcastStreamList ; test('normalSdp', function *(t) { var sdp = yield fs.readFile(__dirname + '/normal.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length > 0, 'got media'); t.equal(session.origin.username, '-', 'origin username'); t.equal(session.origin.sessionId, 20518, 'origin sessionId'); t.equal(session.origin.sessionVersion, 0, 'origin sessionVersion'); t.equal(session.origin.netType, 'IN', 'origin netType'); t.equal(session.origin.ipVer, 4, 'origin ipVer'); t.equal(session.origin.address, '203.0.113.1', 'origin address'); t.equal(session.connection.ip, '203.0.113.1', 'session connect ip'); t.equal(session.connection.version, 4, 'session connect ip ver'); // global ICE and fingerprint t.equal(session.iceUfrag, 'F7gI', 'global ufrag'); t.equal(session.icePwd, 'x9cml/YzichV2+XlhiMu8g', 'global pwd'); var audio = media[0]; t.equal(audio.type, 'audio', 'audio type'); t.equal(audio.port, 54400, 'audio port'); t.equal(audio.protocol, 'RTP/SAVPF', 'audio protocol'); t.equal(audio.direction, 'sendrecv', 'audio direction'); t.equal(audio.rtp[0].payload, 0, 'audio rtp 0 payload'); t.equal(audio.rtp[0].codec, 'PCMU', 'audio rtp 0 codec'); t.equal(audio.rtp[0].rate, 8000, 'audio rtp 0 rate'); t.equal(audio.rtp[1].payload, 96, 'audio rtp 1 payload'); t.equal(audio.rtp[1].codec, 'opus', 'audio rtp 1 codec'); t.equal(audio.rtp[1].rate, 48000, 'audio rtp 1 rate'); t.deepEqual(audio.ext[0], { value: 1, uri: 'URI-toffset' }, 'audio extension 0'); t.deepEqual(audio.ext[1], { value: 2, direction: 'recvonly', uri: 'URI-gps-string' }, 'audio extension 1'); t.equal(audio.extmapAllowMixed, 'extmap-allow-mixed', 'extmap-allow-mixed present'); var video = media[1]; t.equal(video.type, 'video', 'video type'); t.equal(video.port, 55400, 'video port'); t.equal(video.protocol, 'RTP/SAVPF', 'video protocol'); t.equal(video.direction, 'sendrecv', 'video direction'); t.equal(video.rtp[0].payload, 97, 'video rtp 0 payload'); t.equal(video.rtp[0].codec, 'H264', 'video rtp 0 codec'); t.equal(video.rtp[0].rate, 90000, 'video rtp 0 rate'); t.equal(video.fmtp[0].payload, 97, 'video fmtp 0 payload'); var vidFmtp = parseFmtpConfig(video.fmtp[0].config); t.equal(vidFmtp['profile-level-id'], '4d0028', 'video fmtp 0 profile-level-id'); t.equal(vidFmtp['packetization-mode'], 1, 'video fmtp 0 packetization-mode'); t.equal(vidFmtp['sprop-parameter-sets'], 'Z0IAH5WoFAFuQA==,aM48gA==', 'video fmtp 0 sprop-parameter-sets'); t.equal(video.fmtp[1].payload, 98, 'video fmtp 1 payload'); var vidFmtp2 = parseFmtpConfig(video.fmtp[1].config); t.equal(vidFmtp2.minptime, 10, 'video fmtp 1 minptime'); t.equal(vidFmtp2.useinbandfec, 1, 'video fmtp 1 useinbandfec'); t.equal(video.rtp[1].payload, 98, 'video rtp 1 payload'); t.equal(video.rtp[1].codec, 'VP8', 'video rtp 1 codec'); t.equal(video.rtp[1].rate, 90000, 'video rtp 1 rate'); t.equal(video.rtcpFb[0].payload, '*', 'video rtcp-fb 0 payload'); t.equal(video.rtcpFb[0].type, 'nack', 'video rtcp-fb 0 type'); t.equal(video.rtcpFb[1].payload, 98, 'video rtcp-fb 0 payload'); t.equal(video.rtcpFb[1].type, 'nack', 'video rtcp-fb 0 type'); t.equal(video.rtcpFb[1].subtype, 'rpsi', 'video rtcp-fb 0 subtype'); t.equal(video.rtcpFbTrrInt[0].payload, 98, 'video rtcp-fb trr-int 0 payload'); t.equal(video.rtcpFbTrrInt[0].value, 100, 'video rtcp-fb trr-int 0 value'); t.equal(video.crypto[0].id, 1, 'video crypto 0 id'); t.equal(video.crypto[0].suite, 'AES_CM_128_HMAC_SHA1_32', 'video crypto 0 suite'); t.equal(video.crypto[0].config, 'inline:keNcG3HezSNID7LmfDa9J4lfdUL8W1F7TNJKcbuy|2^20|1:32', 'video crypto 0 config'); t.equal(video.ssrcs.length, 3, 'video got 3 ssrc lines'); // test ssrc with attr:value t.deepEqual(video.ssrcs[0], { id: 1399694169, attribute: 'foo', value: 'bar' }, 'video 1st ssrc line attr:value'); // test ssrc with attr only t.deepEqual(video.ssrcs[1], { id: 1399694169, attribute: 'baz', }, 'video 2nd ssrc line attr only'); // test ssrc with at-tr:value t.deepEqual(video.ssrcs[2], { id: 1399694169, attribute: 'foo-bar', value: 'baz' }, 'video 3rd ssrc line attr with dash'); // ICE candidates (same for both audio and video in this case) [audio.candidates, video.candidates].forEach(function (cs, i) { var str = (i === 0) ? 'audio ' : 'video '; var port = (i === 0) ? 54400 : 55400; t.equal(cs.length, 4, str + 'got 4 candidates'); t.equal(cs[0].foundation, 0, str + 'ice candidate 0 foundation'); t.equal(cs[0].component, 1, str + 'ice candidate 0 component'); t.equal(cs[0].transport, 'UDP', str + 'ice candidate 0 transport'); t.equal(cs[0].priority, 2113667327, str + 'ice candidate 0 priority'); t.equal(cs[0].ip, '203.0.113.1', str + 'ice candidate 0 ip'); t.equal(cs[0].port, port, str + 'ice candidate 0 port'); t.equal(cs[0].type, 'host', str + 'ice candidate 0 type'); t.equal(cs[1].foundation, 1, str + 'ice candidate 1 foundation'); t.equal(cs[1].component, 2, str + 'ice candidate 1 component'); t.equal(cs[1].transport, 'UDP', str + 'ice candidate 1 transport'); t.equal(cs[1].priority, 2113667326, str + 'ice candidate 1 priority'); t.equal(cs[1].ip, '203.0.113.1', str + 'ice candidate 1 ip'); t.equal(cs[1].port, port+1, str + 'ice candidate 1 port'); t.equal(cs[1].type, 'host', str + 'ice candidate 1 type'); t.equal(cs[2].foundation, 2, str + 'ice candidate 2 foundation'); t.equal(cs[2].component, 1, str + 'ice candidate 2 component'); t.equal(cs[2].transport, 'UDP', str + 'ice candidate 2 transport'); t.equal(cs[2].priority, 1686052607, str + 'ice candidate 2 priority'); t.equal(cs[2].ip, '203.0.113.1', str + 'ice candidate 2 ip'); t.equal(cs[2].port, port+2, str + 'ice candidate 2 port'); t.equal(cs[2].type, 'srflx', str + 'ice candidate 2 type'); t.equal(cs[2].raddr, '192.168.1.145', str + 'ice candidate 2 raddr'); t.equal(cs[2].rport, port+2, str + 'ice candidate 2 rport'); t.equal(cs[2].generation, 0, str + 'ice candidate 2 generation'); t.equal(cs[2]['network-id'], 3, str + 'ice candidate 2 network-id'); t.equal(cs[2]['network-cost'], (i === 0 ? 10 : undefined), str + 'ice candidate 2 network-cost'); t.equal(cs[3].foundation, 3, str + 'ice candidate 3 foundation'); t.equal(cs[3].component, 2, str + 'ice candidate 3 component'); t.equal(cs[3].transport, 'UDP', str + 'ice candidate 3 transport'); t.equal(cs[3].priority, 1686052606, str + 'ice candidate 3 priority'); t.equal(cs[3].ip, '203.0.113.1', str + 'ice candidate 3 ip'); t.equal(cs[3].port, port+3, str + 'ice candidate 3 port'); t.equal(cs[3].type, 'srflx', str + 'ice candidate 3 type'); t.equal(cs[3].raddr, '192.168.1.145', str + 'ice candidate 3 raddr'); t.equal(cs[3].rport, port+3, str + 'ice candidate 3 rport'); t.equal(cs[3].generation, 0, str + 'ice candidate 3 generation'); t.equal(cs[3]['network-id'], 3, str + 'ice candidate 3 network-id'); t.equal(cs[3]['network-cost'], (i === 0 ? 10 : undefined), str + 'ice candidate 3 network-cost'); }); t.equal(media.length, 2, 'got 2 m-lines'); }); /* * Test for an sdp that started out as something from chrome * it's since been hacked to include tests for other stuff * ignore the name */ test('hackySdp', function *(t) { var sdp = yield fs.readFile(__dirname + '/hacky.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length > 0, 'got media'); t.equal(session.origin.sessionId, '3710604898417546434', 'origin sessionId'); t.ok(session.groups, 'parsing session groups'); t.equal(session.groups.length, 1, 'one grouping'); t.equal(session.groups[0].type, 'BUNDLE', 'grouping is BUNDLE'); t.equal(session.groups[0].mids, 'audio video', 'bundling audio video'); t.ok(session.msidSemantic, 'have an msid semantic'); t.equal(session.msidSemantic.semantic, 'WMS', 'webrtc semantic'); t.equal(session.msidSemantic.token, 'Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV', 'semantic token'); // verify a=rtcp:65179 IN IP4 193.84.77.194 t.equal(media[0].rtcp.port, 1, 'rtcp port'); t.equal(media[0].rtcp.netType, 'IN', 'rtcp netType'); t.equal(media[0].rtcp.ipVer, 4, 'rtcp ipVer'); t.equal(media[0].rtcp.address, '0.0.0.0', 'rtcp address'); // verify ice tcp types t.equal(media[0].candidates[0].tcptype, undefined, 'no tcptype'); t.equal(media[0].candidates[1].tcptype, 'active', 'active tcptype'); t.equal(media[0].candidates[1].transport, 'tcp', 'tcp transport'); t.equal(media[0].candidates[1].generation, 0, 'generation 0'); t.equal(media[0].candidates[1].type, 'host', 'tcp host'); t.equal(media[0].candidates[2].generation, undefined, 'no generation'); t.equal(media[0].candidates[2].type, 'host', 'tcp host'); t.equal(media[0].candidates[2].tcptype, 'active', 'active tcptype'); t.equal(media[0].candidates[3].tcptype, 'passive', 'passive tcptype'); t.equal(media[0].candidates[4].tcptype, 'so', 'so tcptype'); // raddr + rport + tcptype + generation t.equal(media[0].candidates[5].type, 'srflx', 'tcp srflx'); t.equal(media[0].candidates[5].rport, 9, 'tcp rport'); t.equal(media[0].candidates[5].raddr, '10.0.1.1', 'tcp raddr'); t.equal(media[0].candidates[5].tcptype, 'active', 'active tcptype'); t.equal(media[0].candidates[6].tcptype, 'passive', 'passive tcptype'); t.equal(media[0].candidates[6].rport, 8998, 'tcp rport'); t.equal(media[0].candidates[6].raddr, '10.0.1.1', 'tcp raddr'); t.equal(media[0].candidates[6].generation, 5, 'tcp generation'); // and verify it works without specifying the ip t.equal(media[1].rtcp.port, 12312, 'rtcp port'); t.equal(media[1].rtcp.netType, undefined, 'rtcp netType'); t.equal(media[1].rtcp.ipVer, undefined, 'rtcp ipVer'); t.equal(media[1].rtcp.address, undefined, 'rtcp address'); // verify a=rtpmap:126 telephone-event/8000 var lastRtp = media[0].rtp.length-1; t.equal(media[0].rtp[lastRtp].codec, 'telephone-event', 'dtmf codec'); t.equal(media[0].rtp[lastRtp].rate, 8000, 'dtmf rate'); t.equal(media[0].iceOptions, 'google-ice', 'ice options parsed'); t.equal(media[0].ptime, 0.125, 'audio packet duration'); t.equal(media[0].maxptime, 60, 'maxptime parsed'); t.equal(media[0].rtcpMux, 'rtcp-mux', 'rtcp-mux present'); t.equal(media[0].rtp[0].codec, 'opus', 'audio rtp 0 codec'); t.equal(media[0].rtp[0].encoding, 2, 'audio rtp 0 encoding'); t.ok(media[0].ssrcs, 'have ssrc lines'); t.equal(media[0].ssrcs.length, 4, 'got 4 ssrc lines'); var ssrcs = media[0].ssrcs; t.deepEqual(ssrcs[0], { id: 2754920552, attribute: 'cname', value: 't9YU8M1UxTF8Y1A1' }, '1st ssrc line'); t.deepEqual(ssrcs[1], { id: 2754920552, attribute: 'msid', value: 'Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlVa0' }, '2nd ssrc line'); t.deepEqual(ssrcs[2], { id: 2754920552, attribute: 'mslabel', value: 'Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV' }, '3rd ssrc line'); t.deepEqual(ssrcs[3], { id: 2754920552, attribute: 'label', value: 'Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlVa0' }, '4th ssrc line'); // verify a=sctpmap:5000 webrtc-datachannel 1024 t.ok(media[2].sctpmap, 'we have sctpmap'); t.equal(media[2].sctpmap.sctpmapNumber, 5000, 'sctpmap number is 5000'); t.equal(media[2].sctpmap.app, 'webrtc-datachannel', 'sctpmap app is webrtc-datachannel'); t.equal(media[2].sctpmap.maxMessageSize, 1024, 'sctpmap maxMessageSize is 1024'); // verify a=framerate:29.97 t.ok(media[2].framerate, 'we have framerate'); t.equal(media[2].framerate, 29.97, 'framerate is 29.97'); // verify a=label:1 t.ok(media[0].label, 'we have label'); t.equal(media[0].label, 1, 'label is 1'); }); test('iceliteSdp', function *(t) { var sdp = yield fs.readFile(__dirname + '/icelite.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); t.equal(session.icelite, 'ice-lite', 'icelite parsed'); var rew = write(session); t.ok(rew.indexOf('a=ice-lite\r\n') >= 0, 'got ice-lite'); t.ok(rew.indexOf('m=') > rew.indexOf('a=ice-lite'), 'session level icelite'); }); test('invalidSdp', function *(t) { var sdp = yield fs.readFile(__dirname + '/invalid.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length > 0, 'got media'); // verify a=rtcp:65179 IN IP4 193.84.77.194 t.equal(media[0].rtcp.port, 1, 'rtcp port'); t.equal(media[0].rtcp.netType, 'IN', 'rtcp netType'); t.equal(media[0].rtcp.ipVer, 7, 'rtcp ipVer'); t.equal(media[0].rtcp.address, 'X', 'rtcp address'); t.equal(media[0].invalid.length, 1, 'found exactly 1 invalid line'); // f= lost t.equal(media[0].invalid[0].value, 'goo:hithere', 'copied verbatim'); }); test('jssipSdp', function *(t) { var sdp = yield fs.readFile(__dirname + '/jssip.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length > 0, 'got media'); var audio = media[0]; var audCands = audio.candidates; t.equal(audCands.length, 6, '6 candidates'); // testing ice optionals: t.deepEqual(audCands[0], { foundation: 1162875081, component: 1, transport: 'udp', priority: 2113937151, ip: '192.168.34.75', port: 60017, type: 'host', generation: 0, }, 'audio candidate 0' ); t.deepEqual(audCands[2], { foundation: 3289912957, component: 1, transport: 'udp', priority: 1845501695, ip: '193.84.77.194', port: 60017, type: 'srflx', raddr: '192.168.34.75', rport: 60017, generation: 0, }, 'audio candidate 2 (raddr rport)' ); t.deepEqual(audCands[4], { foundation: 198437945, component: 1, transport: 'tcp', priority: 1509957375, ip: '192.168.34.75', port: 0, type: 'host', generation: 0 }, 'audio candidate 4 (tcp)' ); }); test('jsepSdp', function *(t) { var sdp = yield fs.readFile(__dirname + '/jsep.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length === 2, 'got media'); var video = media[1]; t.equal(video.ssrcGroups.length, 1, '1 ssrc grouping'); t.deepEqual(video.ssrcGroups[0], { semantics: 'FID', ssrcs: '1366781083 1366781084' }, 'ssrc-group' ); t.equal(video.msid, '61317484-2ed4-49d7-9eb7-1414322a7aae f30bdb4a-5db8-49b5-bcdc-e0c9a23172e0' , 'msid' ); t.ok(video.rtcpRsize, 'rtcp-rsize present'); t.ok(video.bundleOnly, 'bundle-only present'); // video contains 'a=end-of-candidates' // we want to ensure this comes after the candidate lines // so this is the only place we actually test the writer in here t.ok(video.endOfCandidates, 'have end of candidates marker'); var rewritten = write(session).split('\r\n'); var idx = rewritten.indexOf('a=end-of-candidates'); t.equal(rewritten[idx-1].slice(0, 11), 'a=candidate', 'marker after candidate'); }); test('alacSdp', function *(t) { var sdp = yield fs.readFile(__dirname + '/alac.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length > 0, 'got media'); var audio = media[0]; t.equal(audio.type, 'audio', 'audio type'); t.equal(audio.protocol, 'RTP/AVP', 'audio protocol'); t.equal(audio.fmtp[0].payload, 96, 'audio fmtp 0 payload'); t.equal(audio.fmtp[0].config, '352 0 16 40 10 14 2 255 0 0 44100', 'audio fmtp 0 config'); t.equal(audio.rtp[0].payload, 96, 'audio rtp 0 payload'); t.equal(audio.rtp[0].codec, 'AppleLossless', 'audio rtp 0 codec'); t.equal(audio.rtp[0].rate, undefined, 'audio rtp 0 rate'); t.equal(audio.rtp[0].encoding, undefined, 'audio rtp 0 encoding'); }); test('onvifSdp', function *(t) { var sdp = yield fs.readFile(__dirname + '/onvif.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length > 0, 'got media'); var audio = media[0]; t.equal(audio.type, 'audio', 'audio type'); t.equal(audio.port, 0, 'audio port'); t.equal(audio.protocol, 'RTP/AVP', 'audio protocol'); t.equal(audio.control, 'rtsp://example.com/onvif_camera/audio', 'audio control'); t.equal(audio.payloads, 0, 'audio payloads'); var video = media[1]; t.equal(video.type, 'video', 'video type'); t.equal(video.port, 0, 'video port'); t.equal(video.protocol, 'RTP/AVP', 'video protocol'); t.equal(video.control, 'rtsp://example.com/onvif_camera/video', 'video control'); t.equal(video.payloads, 26, 'video payloads'); var application = media[2]; t.equal(application.type, 'application', 'application type'); t.equal(application.port, 0, 'application port'); t.equal(application.protocol, 'RTP/AVP', 'application protocol'); t.equal(application.control, 'rtsp://example.com/onvif_camera/metadata', 'application control'); t.equal(application.payloads, 107, 'application payloads'); t.equal(application.direction, 'recvonly', 'application direction'); t.equal(application.rtp[0].payload, 107, 'application rtp 0 payload'); t.equal(application.rtp[0].codec, 'vnd.onvif.metadata', 'application rtp 0 codec'); t.equal(application.rtp[0].rate, 90000, 'application rtp 0 rate'); t.equal(application.rtp[0].encoding, undefined, 'application rtp 0 encoding'); }); test('ssrcSdp', function *(t) { var sdp = yield fs.readFile(__dirname + '/ssrc.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length > 0, 'got media'); var video = media[1]; t.equal(video.ssrcGroups.length, 2, 'video got 2 ssrc-group lines'); var expectedSsrc = [ { semantics: 'FID', ssrcs: '3004364195 1126032854' }, { semantics: 'FEC-FR', ssrcs: '3004364195 1080772241' } ]; t.deepEqual(video.ssrcGroups, expectedSsrc, 'video ssrc-group obj'); }); test('simulcastSdp', function *(t) { var sdp = yield fs.readFile(__dirname + '/simulcast.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length > 0, 'got media'); var video = media[1]; t.equal(video.type, 'video', 'video type'); // test rid lines t.equal(video.rids.length, 5, 'video got 5 rid lines'); // test rid 1 t.deepEqual(video.rids[0], { id: 1, direction: 'send', params: 'pt=97;max-width=1280;max-height=720;max-fps=30' }, 'video 1st rid line'); // test rid 2 t.deepEqual(video.rids[1], { id: 2, direction: 'send', params: 'pt=98' }, 'video 2nd rid line'); // test rid 3 t.deepEqual(video.rids[2], { id: 3, direction: 'send', params: 'pt=99' }, 'video 3rd rid line'); // test rid 4 t.deepEqual(video.rids[3], { id: 4, direction: 'send', params: 'pt=100' }, 'video 4th rid line'); // test rid 5 t.deepEqual(video.rids[4], { id: 'c', direction: 'recv', params: 'pt=97' }, 'video 5th rid line'); // test rid 1 params var rid1Params = parseParams(video.rids[0].params); t.deepEqual(rid1Params, { 'pt': 97, 'max-width': 1280, 'max-height': 720, 'max-fps': 30 }, 'video 1st rid params'); // test rid 2 params var rid2Params = parseParams(video.rids[1].params); t.deepEqual(rid2Params, { 'pt': 98 }, 'video 2nd rid params'); // test rid 3 params var rid3Params = parseParams(video.rids[2].params); t.deepEqual(rid3Params, { 'pt': 99 }, 'video 3rd rid params'); // test rid 4 params var rid4Params = parseParams(video.rids[3].params); t.deepEqual(rid4Params, { 'pt': 100 }, 'video 4th rid params'); // test rid 5 params var rid5Params = parseParams(video.rids[4].params); t.deepEqual(rid5Params, { 'pt': 97 }, 'video 5th rid params'); // test imageattr lines t.equal(video.imageattrs.length, 5, 'video got 5 imageattr lines'); // test imageattr 1 t.deepEqual(video.imageattrs[0], { pt: 97, dir1: 'send', attrs1: '[x=1280,y=720]', dir2: 'recv', attrs2: '[x=1280,y=720] [x=320,y=180] [x=160,y=90]' }, 'video 1st imageattr line'); // test imageattr 2 t.deepEqual(video.imageattrs[1], { pt: 98, dir1: 'send', attrs1: '[x=320,y=180]' }, 'video 2nd imageattr line'); // test imageattr 3 t.deepEqual(video.imageattrs[2], { pt: 99, dir1: 'send', attrs1: '[x=160,y=90]' }, 'video 3rd imageattr line'); // test imageattr 4 t.deepEqual(video.imageattrs[3], { pt: 100, dir1: 'recv', attrs1: '[x=1280,y=720] [x=320,y=180]', dir2: 'send', attrs2: '[x=1280,y=720]' }, 'video 4th imageattr line'); // test imageattr 5 t.deepEqual(video.imageattrs[4], { pt: '*', dir1: 'recv', attrs1: '*' }, 'video 5th imageattr line'); // test imageattr 1 send params var imageattr1SendParams = parseImageAttributes(video.imageattrs[0].attrs1); t.deepEqual(imageattr1SendParams, [ {'x': 1280, 'y': 720} ], 'video 1st imageattr send params'); // test imageattr 1 recv params var imageattr1RecvParams = parseImageAttributes(video.imageattrs[0].attrs2); t.deepEqual(imageattr1RecvParams, [ {'x': 1280, 'y': 720}, {'x': 320, 'y': 180}, {'x': 160, 'y': 90}, ], 'video 1st imageattr recv params'); // test imageattr 2 send params var imageattr2SendParams = parseImageAttributes(video.imageattrs[1].attrs1); t.deepEqual(imageattr2SendParams, [ {'x': 320, 'y': 180} ], 'video 2nd imageattr send params'); // test imageattr 3 send params var imageattr3SendParams = parseImageAttributes(video.imageattrs[2].attrs1); t.deepEqual(imageattr3SendParams, [ {'x': 160, 'y': 90} ], 'video 3rd imageattr send params'); // test imageattr 4 recv params var imageattr4RecvParams = parseImageAttributes(video.imageattrs[3].attrs1); t.deepEqual(imageattr4RecvParams, [ {'x': 1280, 'y': 720}, {'x': 320, 'y': 180}, ], 'video 4th imageattr recv params'); // test imageattr 4 send params var imageattr4SendParams = parseImageAttributes(video.imageattrs[3].attrs2); t.deepEqual(imageattr4SendParams, [ {'x': 1280, 'y': 720} ], 'video 4th imageattr send params'); // test imageattr 5 recv params t.equal(video.imageattrs[4].attrs1, '*', 'video 5th imageattr recv params'); // test simulcast line t.deepEqual(video.simulcast, { dir1: 'send', list1: '1,~4;2;3', dir2: 'recv', list2: 'c' }, 'video simulcast line'); // test simulcast send streams var simulcastSendStreams = parseSimulcastStreamList(video.simulcast.list1); t.deepEqual(simulcastSendStreams, [ [ {scid: 1, paused: false}, {scid: 4, paused: true} ], [ {scid: 2, paused: false} ], [ {scid: 3, paused: false} ] ], 'video simulcast send streams'); // test simulcast recv streams var simulcastRecvStreams = parseSimulcastStreamList(video.simulcast.list2); t.deepEqual(simulcastRecvStreams, [ [ {scid: 'c', paused: false} ] ], 'video simulcast recv streams'); // test simulcast version 03 line // test simulcast line t.deepEqual(video.simulcast_03, { value: 'send rid=1,4;2;3 paused=4 recv rid=c' }, 'video simulcast draft 03 line'); }); test('ST2022-6', function *(t) { var sdp = yield fs.readFile(__dirname + '/st2022-6.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length > 0, 'got media'); var video = media[0]; var sourceFilter = video.sourceFilter; t.equal(sourceFilter.filterMode, 'incl', 'filter-mode is "incl"'); t.equal(sourceFilter.netType, 'IN', 'nettype is "IN"'); t.equal(sourceFilter.addressTypes, 'IP4', 'address-type is "IP4"'); t.equal(sourceFilter.destAddress, '239.0.0.1', 'dest-address is "239.0.0.1"'); t.equal(sourceFilter.srcList, '192.168.20.20', 'src-list is "192.168.20.20"'); }); test('ST2110-20', function* (t) { var sdp = yield fs.readFile(__dirname + '/st2110-20.sdp', 'utf8'); var session = parse(sdp + ''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length > 0, 'got media'); var video = media[0]; var sourceFilter = video.sourceFilter; t.equal(sourceFilter.filterMode, 'incl', 'filter-mode is "incl"'); t.equal(sourceFilter.netType, 'IN', 'nettype is "IN"'); t.equal(sourceFilter.addressTypes, 'IP4', 'address-type is "IP4"'); t.equal(sourceFilter.destAddress, '239.100.9.10', 'dest-address is "239.100.9.10"'); t.equal(sourceFilter.srcList, '192.168.100.2', 'src-list is "192.168.100.2"'); t.equal(video.type, 'video', 'video type'); var fmtp0Params = parseParams(video.fmtp[0].config); t.deepEqual(fmtp0Params, { sampling: 'YCbCr-4:2:2', width: 1280, height: 720, interlace: undefined, exactframerate: '60000/1001', depth: 10, TCS: 'SDR', colorimetry: 'BT709', PM: '2110GPM', SSN: 'ST2110-20:2017' }, 'video 5th rid params'); }); test('SCTP-DTLS-26', function* (t) { var sdp = yield fs.readFile(__dirname + '/sctp-dtls-26.sdp', 'utf8'); var session = parse(sdp + ''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length > 0, 'got media'); t.equal(session.origin.sessionId, '5636137646675714991', 'origin sessionId'); t.ok(session.groups, 'parsing session groups'); t.equal(session.groups.length, 1, 'one grouping'); t.equal(session.groups[0].type, 'BUNDLE', 'grouping is BUNDLE'); t.equal(session.groups[0].mids, 'data', 'bundling data'); t.ok(session.msidSemantic, 'have an msid semantic'); t.equal(session.msidSemantic.semantic, 'WMS', 'webrtc semantic'); // verify media is data application t.equal(media[0].type, 'application', 'media type application'); t.equal(media[0].mid, 'data', 'media id pplication'); // verify protocol and ports t.equal(media[0].protocol, 'UDP/DTLS/SCTP', 'protocol is UDP/DTLS/SCTP'); t.equal(media[0].port, 9, 'the UDP port value is 9'); t.equal(media[0].sctpPort, 5000, 'the offerer/answer SCTP port value is 5000'); // verify maxMessageSize t.equal(media[0].maxMessageSize, 10000, 'maximum message size is 10000'); }); test('extmapEncryptSdp', function *(t) { var sdp = yield fs.readFile(__dirname + '/extmap-encrypt.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length > 0, 'got media'); t.equal(session.origin.username, '-', 'origin username'); t.equal(session.origin.sessionId, 20518, 'origin sessionId'); t.equal(session.origin.sessionVersion, 0, 'origin sessionVersion'); t.equal(session.origin.netType, 'IN', 'origin netType'); t.equal(session.origin.ipVer, 4, 'origin ipVer'); t.equal(session.origin.address, '203.0.113.1', 'origin address'); t.equal(session.connection.ip, '203.0.113.1', 'session connect ip'); t.equal(session.connection.version, 4, 'session connect ip ver'); var audio = media[0]; t.equal(audio.type, 'audio', 'audio type'); t.equal(audio.port, 54400, 'audio port'); t.equal(audio.protocol, 'RTP/SAVPF', 'audio protocol'); t.equal(audio.rtp[0].payload, 96, 'audio rtp 0 payload'); t.equal(audio.rtp[0].codec, 'opus', 'audio rtp 0 codec'); t.equal(audio.rtp[0].rate, 48000, 'audio rtp 0 rate'); // extmap and encrypted extmap t.deepEqual(audio.ext[0], { value: 1, direction: 'sendonly', uri: 'URI-toffset' }, 'audio extension 0'); t.deepEqual(audio.ext[1], { value: 2, uri: 'urn:ietf:params:rtp-hdrext:toffset' }, 'audio extension 1'); t.deepEqual(audio.ext[2], { value: 3, 'encrypt-uri': 'urn:ietf:params:rtp-hdrext:encrypt', uri: 'urn:ietf:params:rtp-hdrext:smpte-tc', config: '25@600/24' }, 'audio extension 2'); t.deepEqual(audio.ext[3], { value: 4, direction: 'recvonly', 'encrypt-uri': 'urn:ietf:params:rtp-hdrext:encrypt', uri: 'URI-gps-string' }, 'audio extension 3'); t.equal(media.length, 1, 'got 1 m-lines'); }); test('dante-aes67', function *(t) { var sdp = yield fs.readFile(__dirname + '/dante-aes67.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length == 1, 'got single media'); t.equal(session.origin.username, '-', 'origin username'); t.equal(session.origin.sessionId, 1423986, 'origin sessionId'); t.equal(session.origin.sessionVersion, 1423994, 'origin sessionVersion'); t.equal(session.origin.netType, 'IN', 'origin netType'); t.equal(session.origin.ipVer, 4, 'origin ipVer'); t.equal(session.origin.address, '169.254.98.63', 'origin address'); t.equal(session.name, 'AOIP44-serial-1614 : 2', 'Session Name'); t.equal(session.keywords, 'Dante', 'Keywords'); t.equal(session.connection.ip, '239.65.125.63/32', 'session connect ip'); t.equal(session.connection.version, 4, 'session connect ip ver'); var audio = media[0]; t.equal(audio.type, 'audio', 'audio type'); t.equal(audio.port, 5004, 'audio port'); t.equal(audio.protocol, 'RTP/AVP', 'audio protocol'); t.equal(audio.direction, 'recvonly', 'audio direction'); t.equal(audio.description, '2 channels: TxChan 0, TxChan 1', 'audio description'); t.equal(audio.ptime, 1, 'audio packet duration'); t.equal(audio.rtp[0].payload, 97, 'audio rtp payload type'); t.equal(audio.rtp[0].codec, 'L24', 'audio rtp codec'); t.equal(audio.rtp[0].rate, 48000, 'audio sample rate'); t.equal(audio.rtp[0].encoding, 2, 'audio channels'); }); test('bfcp', function *(t) { var sdp = yield fs.readFile(__dirname + '/bfcp.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length == 4, 'got 4 media'); t.equal(session.origin.username, '-', 'origin username'); var audio = media[0]; t.equal(audio.type, 'audio', 'audio type'); var video = media[1]; t.equal(video.type, 'video', 'main video type'); t.equal(video.direction, 'sendrecv', 'main video direction'); t.equal(video.content, 'main', 'main video content'); t.equal(video.label, 1, 'main video label'); var app = media[2]; t.equal(app.type, 'application', 'application type'); t.equal(app.port, 3238, 'application port'); t.equal(app.protocol, 'UDP/BFCP', 'bfcp protocol'); t.equal(app.payloads, '*', 'bfcp payloads'); t.equal(app.connectionType, 'new', 'connection type'); t.equal(app.bfcpFloorCtrl, 's-only', 'bfcp Floor Control'); t.equal(app.bfcpConfId, 1, 'bfcp ConfId'); t.equal(app.bfcpUserId, 1, 'bfcp UserId'); t.equal(app.bfcpFloorId.id, 1, 'bfcp FloorId'); t.equal(app.bfcpFloorId.mStream, 3, 'bfcp Floor Stream'); var video2 = media[3]; t.equal(video2.type, 'video', '2nd video type'); t.equal(video2.direction, 'sendrecv', '2nd video direction'); t.equal(video2.content, 'slides', '2nd video content'); t.equal(video2.label, 3, '2nd video label'); }); test('tcp-active', function *(t) { var sdp = yield fs.readFile(__dirname + '/tcp-active.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length == 1, 'got single media'); t.equal(session.origin.username, '-', 'origin username'); t.equal(session.origin.sessionId, 1562876543, 'origin sessionId'); t.equal(session.origin.sessionVersion, 11, 'origin sessionVersion'); t.equal(session.origin.netType, 'IN', 'origin netType'); t.equal(session.origin.ipVer, 4, 'origin ipVer'); t.equal(session.origin.address, '192.0.2.3', 'origin address'); var image = media[0]; t.equal(image.type, 'image', 'image type'); t.equal(image.port, 9, 'port'); t.equal(image.connection.version, 4, 'Connection is IPv4'); t.equal(image.connection.ip, '192.0.2.3', 'Connection address'); t.equal(image.protocol, 'TCP', 'TCP protocol'); t.equal(image.payloads, 't38', 'TCP payload'); t.equal(image.setup, 'active', 'setup active'); t.equal(image.connectionType, 'new', 'new connection'); }); test('tcp-passive', function *(t) { var sdp = yield fs.readFile(__dirname + '/tcp-passive.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length == 1, 'got single media'); t.equal(session.origin.username, '-', 'origin username'); t.equal(session.origin.sessionId, 1562876543, 'origin sessionId'); t.equal(session.origin.sessionVersion, 11, 'origin sessionVersion'); t.equal(session.origin.netType, 'IN', 'origin netType'); t.equal(session.origin.ipVer, 4, 'origin ipVer'); t.equal(session.origin.address, '192.0.2.2', 'origin address'); var image = media[0]; t.equal(image.type, 'image', 'image type'); t.equal(image.port, 54111, 'port'); t.equal(image.connection.version, 4, 'Connection is IPv4'); t.equal(image.connection.ip, '192.0.2.2', 'Connection address'); t.equal(image.protocol, 'TCP', 'TCP protocol'); t.equal(image.payloads, 't38', 'TCP payload'); t.equal(image.setup, 'passive', 'setup passive'); t.equal(image.connectionType, 'existing', 'existing connection'); }); test('mediaclk-avbtp', function *(t) { var sdp = yield fs.readFile(__dirname + '/mediaclk-avbtp.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length == 1, 'got single media'); var audio = media[0]; t.equal(audio.mediaClk.mediaClockName, 'IEEE1722', 'IEEE1722 Media Clock'); t.equal(audio.mediaClk.mediaClockValue, '38-D6-6D-8E-D2-78-13-2F', 'AVB stream ID'); }); test('mediaclk-ptp-v2-w-rate', function *(t) { var sdp = yield fs.readFile(__dirname + '/mediaclk-ptp-v2-w-rate.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length == 1, 'got single media'); var audio = media[0]; t.equal(audio.mediaClk.mediaClockName, 'direct', 'Direct Media Clock'); t.equal(audio.mediaClk.mediaClockValue, 963214424, 'offset'); t.equal(audio.mediaClk.rateNumerator, 1000, 'rate numerator'); t.equal(audio.mediaClk.rateDenominator, 1001, 'rate denominator'); }); test('mediaclk-ptp-v2', function *(t) { var sdp = yield fs.readFile(__dirname + '/mediaclk-ptp-v2.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length == 1, 'got single media'); var audio = media[0]; t.equal(audio.mediaClk.mediaClockName, 'direct', 'Direct Media Clock'); t.equal(audio.mediaClk.mediaClockValue, 963214424, 'offset'); }); test('mediaclk-rtp', function *(t) { var sdp = yield fs.readFile(__dirname + '/mediaclk-rtp.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var media = session.media; t.ok(media && media.length == 1, 'got single media'); var audio = media[0]; t.equal(audio.mediaClk.id, 'MDA6NjA6MmI6MjA6MTI6MWY=', 'Media Clock ID'); t.equal(audio.mediaClk.mediaClockName, 'sender', 'sender type'); }); test('ts-refclk-media', function *(t) { var sdp = yield fs.readFile(__dirname + '/ts-refclk-media.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var sessTsRefClocks = session.tsRefClocks; t.ok(sessTsRefClocks && sessTsRefClocks.length == 1, 'got one TS Ref Clock'); t.equal(sessTsRefClocks[0].clksrc, 'local', 'local Clock Source at Session Level'); var media = session.media; t.ok(media && media.length == 2, 'got two media'); var audio = media[0]; var audTsRefClocks = audio.tsRefClocks; t.ok(audTsRefClocks && audTsRefClocks.length == 2, 'got two audio TS Ref Clocks'); var audTsRefClock1 = audTsRefClocks[0]; t.equal(audTsRefClock1.clksrc, 'ntp', 'NTP Clock Source'); t.equal(audTsRefClock1.clksrcExt, '203.0.113.10', 'IPv4 address'); var audTsRefClock2 = audTsRefClocks[1]; t.equal(audTsRefClock2.clksrc, 'ntp', 'NTP Clock Source'); t.equal(audTsRefClock2.clksrcExt, '198.51.100.22', 'IPv4 address'); var video = media[1]; var vidTsRefClocks = video.tsRefClocks; t.ok(vidTsRefClocks && vidTsRefClocks.length == 1, 'got one video TS Ref Clocks'); t.equal(vidTsRefClocks[0].clksrc, 'ptp', 'PTP Clock Source'); t.equal(vidTsRefClocks[0].clksrcExt, 'IEEE802.1AS-2011:39-A7-94-FF-FE-07-CB-D0', 'PTP config'); }); test('ts-refclk-sess', function *(t) { var sdp = yield fs.readFile(__dirname + '/ts-refclk-sess.sdp', 'utf8'); var session = parse(sdp+''); t.ok(session, 'got session info'); var sessTsRefClocks = session.tsRefClocks; t.ok(sessTsRefClocks && sessTsRefClocks.length == 1, 'got one TS Ref Clock at Session Level'); t.equal(sessTsRefClocks[0].clksrc, 'ntp', 'NTP Clock Source'); t.equal(sessTsRefClocks[0].clksrcExt, '/traceable/', 'traceable Clock Source'); });sdp-transform-2.14.1/test/rtcp-fb.sdp000066400000000000000000000007351376166770200174460ustar00rootroot00000000000000v=0 o=alice 1957 513 IN IP4 127.0.0.1 s=Talk c=IN IP4 127.0.0.1 t=0 0 a=rtcp-xr:rcvr-rtt=all:10000 stat-summary=loss,dup,jitt,TTL voip-metrics m=audio 7777 RTP/AVP 96 101 a=rtpmap:96 opus/48000/2 a=fmtp:96 useinbandfec=1 a=rtpmap:101 telephone-event/48000 a=rtcp-fb:* trr-int 5 a=rtcp-fb:* ccm tmmbr m=video 8888 RTP/AVP 96 a=rtpmap:96 VP8/90000 a=rtcp-fb:* trr-int 5 a=rtcp-fb:* ccm tmmbr a=rtcp-fb:96 nack pli a=rtcp-fb:96 nack sli a=rtcp-fb:96 ack rpsi a=rtcp-fb:96 ccm fir sdp-transform-2.14.1/test/sctp-dtls-26.sdp000066400000000000000000000006511376166770200202500ustar00rootroot00000000000000v=0 o=- 5636137646675714991 2 IN IP4 127.0.0.1 s=- t=0 0 a=group:BUNDLE data a=msid-semantic: WMS m=application 9 UDP/DTLS/SCTP webrtc-datachannel c=IN IP4 0.0.0.0 a=sctp-port:5000 a=ice-ufrag:8qF7 a=ice-pwd:zjQd1U0/CufgXINHcPcdd0Bd a=ice-options:trickle a=fingerprint:sha-256 10:8E:F5:D7:A2:B3:63:EF:BD:64:8C:5F:56:A0:66:05:9F:B1:5C:1A:C5:79:BD:EE:90:92:C4:1A:C4:B7:1F:58 a=setup:actpass a=mid:data a=max-message-size:10000sdp-transform-2.14.1/test/simulcast.sdp000066400000000000000000000017241376166770200201140ustar00rootroot00000000000000v=0 o=alice 2362969037 2362969040 IN IP4 192.0.2.156 s=Simulcast Enabled Client t=0 0 c=IN IP4 192.0.2.156 m=audio 49200 RTP/AVP 0 a=rtpmap:0 PCMU/8000 m=video 49300 RTP/AVP 97 98 99 100 a=rtpmap:97 H264/90000 a=rtpmap:98 H264/90000 a=rtpmap:99 H264/90000 a=rtpmap:100 VP8/90000 a=fmtp:97 profile-level-id=42c01f; max-fs=3600; max-mbps=108000 a=fmtp:98 profile-level-id=42c00b; max-fs=240; max-mbps=3600 a=fmtp:99 profile-level-id=42c00b; max-fs=120; max-mbps=1800 a=extmap:1 urn:ietf:params:rtp-hdrext:sdes:RtpStreamId a=imageattr:97 send [x=1280,y=720] recv [x=1280,y=720] [x=320,y=180] [x=160,y=90] a=imageattr:98 send [x=320,y=180] a=imageattr:99 send [x=160,y=90] a=imageattr:100 recv [x=1280,y=720] [x=320,y=180] send [x=1280,y=720] a=imageattr:* recv * a=rid:1 send pt=97;max-width=1280;max-height=720;max-fps=30 a=rid:2 send pt=98 a=rid:3 send pt=99 a=rid:4 send pt=100 a=rid:c recv pt=97 a=simulcast:send 1,~4;2;3 recv c a=simulcast: send rid=1,4;2;3 paused=4 recv rid=c sdp-transform-2.14.1/test/ssrc.sdp000066400000000000000000000070031376166770200170560ustar00rootroot00000000000000v=0 o=- 4327261771880257373 2 IN IP4 127.0.0.1 s=- t=0 0 a=group:BUNDLE audio video a=msid-semantic: WMS xIKmAwWv4ft4ULxNJGhkHzvPaCkc8EKo4SGj m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126 c=IN IP4 0.0.0.0 a=rtcp:9 IN IP4 0.0.0.0 a=ice-ufrag:ez5G a=ice-pwd:1F1qS++jzWLSQi0qQDZkX/QV a=fingerprint:sha-256 D2:FA:0E:C3:22:59:5E:14:95:69:92:3D:13:B4:84:24:2C:C2:A2:C0:3E:FD:34:8E:5E:EA:6F:AF:52:CE:E6:0F a=setup:actpass a=mid:audio a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level a=sendrecv a=rtcp-mux a=rtpmap:111 opus/48000/2 a=rtcp-fb:111 transport-cc a=fmtp:111 minptime=10;useinbandfec=1 a=rtpmap:103 ISAC/16000 a=rtpmap:104 ISAC/32000 a=rtpmap:9 G722/8000 a=rtpmap:0 PCMU/8000 a=rtpmap:8 PCMA/8000 a=rtpmap:106 CN/32000 a=rtpmap:105 CN/16000 a=rtpmap:13 CN/8000 a=rtpmap:110 telephone-event/48000 a=rtpmap:112 telephone-event/32000 a=rtpmap:113 telephone-event/16000 a=rtpmap:126 telephone-event/8000 a=ssrc:3510681183 cname:loqPWNg7JMmrFUnr a=ssrc:3510681183 msid:xIKmAwWv4ft4ULxNJGhkHzvPaCkc8EKo4SGj 7ea47500-22eb-4815-a899-c74ef321b6ee a=ssrc:3510681183 mslabel:xIKmAwWv4ft4ULxNJGhkHzvPaCkc8EKo4SGj a=ssrc:3510681183 label:7ea47500-22eb-4815-a899-c74ef321b6ee m=video 9 UDP/TLS/RTP/SAVPF 96 98 100 102 127 125 97 99 101 124 c=IN IP4 0.0.0.0 a=rtcp:9 IN IP4 0.0.0.0 a=ice-ufrag:ez5G a=ice-pwd:1F1qS++jzWLSQi0qQDZkX/QV a=fingerprint:sha-256 D2:FA:0E:C3:22:59:5E:14:95:69:92:3D:13:B4:84:24:2C:C2:A2:C0:3E:FD:34:8E:5E:EA:6F:AF:52:CE:E6:0F a=setup:actpass a=mid:video a=extmap:2 urn:ietf:params:rtp-hdrext:toffset a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time a=extmap:4 urn:3gpp:video-orientation a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay a=sendrecv a=rtcp-mux a=rtcp-rsize a=rtpmap:96 VP8/90000 a=rtcp-fb:96 ccm fir a=rtcp-fb:96 nack a=rtcp-fb:96 nack pli a=rtcp-fb:96 goog-remb a=rtcp-fb:96 transport-cc a=rtpmap:98 VP9/90000 a=rtcp-fb:98 ccm fir a=rtcp-fb:98 nack a=rtcp-fb:98 nack pli a=rtcp-fb:98 goog-remb a=rtcp-fb:98 transport-cc a=rtpmap:100 H264/90000 a=rtcp-fb:100 ccm fir a=rtcp-fb:100 nack a=rtcp-fb:100 nack pli a=rtcp-fb:100 goog-remb a=rtcp-fb:100 transport-cc a=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f a=rtpmap:102 red/90000 a=rtpmap:127 ulpfec/90000 a=rtpmap:125 flexfec-03/90000 a=rtcp-fb:125 ccm fir a=rtcp-fb:125 nack a=rtcp-fb:125 nack pli a=rtcp-fb:125 goog-remb a=rtcp-fb:125 transport-cc a=fmtp:125 repair-window=10000000 a=rtpmap:97 rtx/90000 a=fmtp:97 apt=96 a=rtpmap:99 rtx/90000 a=fmtp:99 apt=98 a=rtpmap:101 rtx/90000 a=fmtp:101 apt=100 a=rtpmap:124 rtx/90000 a=fmtp:124 apt=102 a=ssrc-group:FID 3004364195 1126032854 a=ssrc-group:FEC-FR 3004364195 1080772241 a=ssrc:3004364195 cname:loqPWNg7JMmrFUnr a=ssrc:3004364195 msid:xIKmAwWv4ft4ULxNJGhkHzvPaCkc8EKo4SGj cf093ab0-0b28-4930-8fe1-7ca8d529be25 a=ssrc:3004364195 mslabel:xIKmAwWv4ft4ULxNJGhkHzvPaCkc8EKo4SGj a=ssrc:3004364195 label:cf093ab0-0b28-4930-8fe1-7ca8d529be25 a=ssrc:1126032854 cname:loqPWNg7JMmrFUnr a=ssrc:1126032854 msid:xIKmAwWv4ft4ULxNJGhkHzvPaCkc8EKo4SGj cf093ab0-0b28-4930-8fe1-7ca8d529be25 a=ssrc:1126032854 mslabel:xIKmAwWv4ft4ULxNJGhkHzvPaCkc8EKo4SGj a=ssrc:1126032854 label:cf093ab0-0b28-4930-8fe1-7ca8d529be25 a=ssrc:1080772241 cname:loqPWNg7JMmrFUnr a=ssrc:1080772241 msid:xIKmAwWv4ft4ULxNJGhkHzvPaCkc8EKo4SGj cf093ab0-0b28-4930-8fe1-7ca8d529be25 a=ssrc:1080772241 mslabel:xIKmAwWv4ft4ULxNJGhkHzvPaCkc8EKo4SGj a=ssrc:1080772241 label:cf093ab0-0b28-4930-8fe1-7ca8d529be25 sdp-transform-2.14.1/test/st2022-6.sdp000066400000000000000000000003031376166770200171770ustar00rootroot00000000000000v=0 o=- 198403 11 IN IP4 192.168.20.20 s=st2022-6 source t=0 0 m=video 2004 RTP/AVP 98 c=IN IP4 239.0.0.1/32 a=rtpmap:98 SMPTE2022-6/27000000 a=source-filter: incl IN IP4 239.0.0.1 192.168.20.20 sdp-transform-2.14.1/test/st2110-20.sdp000066400000000000000000000016551376166770200172640ustar00rootroot00000000000000v=0 o=- 123456 11 IN IP4 192.168.100.2 s=Example of a SMPTE ST2110-20 signal i=this example is for 720p interlaced video t=0 0 a=recvonly a=group:DUP primary secondary m=video 50000 RTP/AVP 112 c=IN IP4 239.100.9.10/32 a=source-filter:incl IN IP4 239.100.9.10 192.168.100.2 a=rtpmap:112 raw/90000 a=fmtp:112 sampling=YCbCr-4:2:2; width=1280; height=720; interlace; exactframerate=60000/1001; depth=10; TCS=SDR; colorimetry=BT709; PM=2110GPM; SSN=ST2110-20:2017; a=ts-refclk:ptp=IEEE1588-2008:39-A7-94-FF-FE-07-CB-D0:37 a=mediaclk:direct=0 a=mid:primary m=video 50020 RTP/AVP 112 c=IN IP4 239.101.9.10/32 a=source-filter:incl IN IP4 239.101.9.10 192.168.101.2 a=rtpmap:112 raw/90000 a=fmtp:112 sampling=YCbCr-4:2:2; width=1280; height=720; interlace; exactframerate=60000/1001; depth=10; TCS=SDR; colorimetry=BT709; PM=2110GPM; SSN=ST2110-20:2017; a=ts-refclk:ptp=IEEE1588-2008:39-A7-94-FF-FE-07-CB-D0:37 a=mediaclk:direct=0 a=mid:secondary; sdp-transform-2.14.1/test/tcp-active.sdp000066400000000000000000000002041376166770200201370ustar00rootroot00000000000000v=0 o=- 1562876543 11 IN IP4 192.0.2.3 s=RFC4145 Example 7.4.2 m=image 9 TCP t38 c=IN IP4 192.0.2.3 a=setup:active a=connection:new sdp-transform-2.14.1/test/tcp-passive.sdp000066400000000000000000000002161376166770200203410ustar00rootroot00000000000000v=0 o=- 1562876543 11 IN IP4 192.0.2.2 s=RFC4145 Example 7.3.1 m=image 54111 TCP t38 c=IN IP4 192.0.2.2 a=setup:passive a=connection:existing sdp-transform-2.14.1/test/ts-refclk-media.sdp000066400000000000000000000007061376166770200210560ustar00rootroot00000000000000v=0 o=jdoe 2890844526 2890842807 IN IP4 192.0.2.1 s=SDP Seminar i=A Seminar on the session description protocol u=http://www.example.com/seminars/sdp.pdf e=j.doe@example.com (Jane Doe) c=IN IP4 233.252.0.1/64 t=2873397496 2873404696 a=recvonly a=ts-refclk:local m=audio 49170 RTP/AVP 0 a=ts-refclk:ntp=203.0.113.10 a=ts-refclk:ntp=198.51.100.22 m=video 51372 RTP/AVP 99 a=rtpmap:99 h263-1998/90000 a=ts-refclk:ptp=IEEE802.1AS-2011:39-A7-94-FF-FE-07-CB-D0sdp-transform-2.14.1/test/ts-refclk-sess.sdp000066400000000000000000000005341376166770200207530ustar00rootroot00000000000000v=0 o=jdoe 2890844526 2890842807 IN IP4 192.0.2.1 s=SDP Seminar i=A Seminar on the session description protocol u=http://www.example.com/seminars/sdp.pdf e=j.doe@example.com (Jane Doe) c=IN IP4 233.252.0.1/64 t=2873397496 2873404696 a=recvonly a=ts-refclk:ntp=/traceable/ m=audio 49170 RTP/AVP 0 m=video 51372 RTP/AVP 99 a=rtpmap:99 h263-1998/90000