@irbisadm/simple-sdp 1.2.0 → 1.2.1-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/types/index.d.ts +1 -1
- package/wasm/dist/simple-sdp.wasm +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The project
|
|
4
4
|
follows [Semantic Versioning](https://semver.org/).
|
|
5
5
|
|
|
6
|
+
## [1.2.1-rc.1] — 2026-11-01
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- Releases are published to npm by CI (`release.yml`) with npm trusted publishing, so
|
|
10
|
+
every version from now on carries an npm provenance attestation.
|
|
11
|
+
|
|
6
12
|
## [1.2.0] — 2026-11-01
|
|
7
13
|
|
|
8
14
|
### Added
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
class e extends Error{constructor(e,t,r){super(t>0?`SDP line ${t} "${r}": ${e}`:`SDP: ${e}`),this.name="SdpParseError",this.reason=e,this.lineNumber=t,this.line=r}}const t=/^\d+$/,r=/^\d+$/,i=(e,t="value")=>{if(!r.test(e))throw new Error(`${t} "${e}" is not a non-negative integer`);return parseInt(e,10)},s={d:86400,h:3600,m:60,s:1},n=(e,t=!1)=>{if(t&&e.startsWith("-"))return-n(e.slice(1));const r=e.slice(-1);return r in s?s[r]*i(e.slice(0,-1),"time"):i(e,"time")},o=e=>{const t=e.indexOf(":"),r=-1===t?e:e.slice(0,t);if(0===r.length)throw new Error("empty key method");return-1===t?{method:r}:{method:r,encryptionKey:e.slice(t+1)}},a=e=>/(^|\/)RTP\//.test(e),d=e=>{const t=e.indexOf(":");if(t<=0)throw new Error('expected "<bwtype>:<bandwidth>"');return{bwtype:e.slice(0,t),bandwidth:i(e.slice(t+1),"bandwidth")}},c=e=>{const t=e.split(" ");if(3!==t.length)throw new Error('expected "<nettype> <addrtype> <connection-address>"');const[r,i,s]=t;return{netType:r,addrType:i,connectionAddress:s}},p=(e,t)=>Object.prototype.hasOwnProperty.call(e,t)?e[t]:void 0,l=(e,t,r)=>{Object.defineProperty(e,t,{value:r,writable:!0,enumerable:!0,configurable:!0})},m=(e,t)=>{if(void 0===e||0===e.length)throw new Error(`expected a value: ${t}`);return e},u=e=>{if(void 0!==e)throw new Error("flag attribute must not have a value");return!0},f=e=>{const t=e.indexOf(" ");return-1===t?[e,void 0]:[e.slice(0,t),e.slice(t+1)]},v=(e,t)=>{var r;const s=i(t,"payload type");return null===(r=e.codecs)||void 0===r?void 0:r.find((e=>e.payloadType===s))},h=(e,t,r,i)=>{if(void 0!==e[t])throw new Error(`duplicate a=${i}`);e[t]=r},g=(e,t,r)=>{void 0===e[t]&&(e[t]=[]),e[t].push(r)},$=["sendrecv","sendonly","recvonly","inactive"],w=e=>{const t={};for(const r of e.split(";")){const e=r.trim();if(0===e.length)continue;const i=e.indexOf("=");-1===i?l(t,e,null):l(t,e.slice(0,i),e.slice(i+1))}return t},y='"<foundation> <component> <transport> <priority> <address> <port> typ <type> [raddr <addr>] [rport <port>] *(<ext-name> <ext-value>)"',b=e=>{const t=i(e,"ssrc-id");if(t>4294967295)throw new Error(`ssrc-id ${t} does not fit into 32 bits`);return t},x=/^[A-Za-z0-9_-]+$/,k=["session","media"],E=["media"],T=["session"],j=["active","passive","actpass","holdconn"],P=e=>({scopes:k,parse:t=>h(t,"direction",e,e)}),F={sendrecv:P("sendrecv"),sendonly:P("sendonly"),recvonly:P("recvonly"),inactive:P("inactive"),"ice-ufrag":{scopes:k,parse:(e,t)=>h(e,"iceUfrag",m(t,"a=ice-ufrag:<ufrag>"),"ice-ufrag")},"ice-pwd":{scopes:k,parse:(e,t)=>h(e,"icePwd",m(t,"a=ice-pwd:<password>"),"ice-pwd")},"ice-options":{scopes:k,parse:(e,t)=>h(e,"iceOptions",m(t,"a=ice-options:<option> ...").split(" "),"ice-options")},"ice-lite":{scopes:T,parse:(e,t)=>h(e,"iceLite",u(t),"ice-lite")},candidate:{scopes:E,parse:(e,t)=>g(e,"candidates",(e=>{var t;const r=e.split(" ");if(r.length<8||"typ"!==r[6]||r.length%2!=0)throw new Error(`expected ${y}`);const[s,n,o,a,d,c,,p]=r,m={foundation:s,component:i(n,"component id"),transport:o,priority:i(a,"priority"),address:d,port:i(c,"port"),type:p};for(let e=8;e<r.length;e+=2){const[s,n]=[r[e],r[e+1]];if(0===s.length||0===n.length)throw new Error(`expected ${y}`);switch(s){case"raddr":m.relatedAddress=n;break;case"rport":m.relatedPort=i(n,"rport");break;case"tcptype":m.tcpType=n;break;default:l(null!==(t=m.extensions)&&void 0!==t?t:m.extensions={},s,n)}}return m})(m(t,"a=candidate:<candidate>")))},"end-of-candidates":{scopes:k,parse:(e,t)=>h(e,"endOfCandidates",u(t),"end-of-candidates")},fingerprint:{scopes:k,parse:(e,t)=>{const[r,i]=f(m(t,"a=fingerprint:<hash-function> <fingerprint>"));if(!i||i.includes(" "))throw new Error('expected "a=fingerprint:<hash-function> <fingerprint>"');g(e,"fingerprints",{hashFunction:r,fingerprint:i})}},setup:{scopes:k,parse:(e,t)=>{if(!j.includes(t))throw new Error(`a=setup must be one of ${j.join(", ")}`);h(e,"setup",t,"setup")}},extmap:{scopes:k,parse:(e,t)=>g(e,"extmap",(e=>{const[t,r]=f(e);if(!r)throw new Error('expected "<id>[/<direction>] <uri> [<attributes>]"');const[s,n,...o]=t.split("/");if(o.length||void 0!==n&&!$.includes(n))throw new Error(`invalid extmap id "${t}"`);const[a,d]=f(r),c={id:i(s,"extmap id"),uri:a};return void 0!==n&&(c.direction=n),void 0!==d&&(c.attributes=d),c})(m(t,"a=extmap:<id> <uri>")))},group:{scopes:T,parse:(e,t)=>g(e,"group",(e=>{const[t,...r]=e.split(" ");if(0===t.length||r.some((e=>0===e.length)))throw new Error('expected "<semantics> <mid> ..."');return{semantics:t,mids:r}})(m(t,"a=group:<semantics> <mid> ...")))},mid:{scopes:E,parse:(e,t)=>h(e,"mid",m(t,"a=mid:<identification-tag>"),"mid")},msid:{scopes:E,parse:(e,t)=>g(e,"msid",(e=>{const[t,r,...i]=e.split(" ");if(0===t.length||""===r||i.length)throw new Error('expected "<stream id> [<track id>]"');return void 0===r?{streamId:t}:{streamId:t,trackId:r}})(m(t,"a=msid:<stream id> [<track id>]")))},ssrc:{scopes:E,parse:(e,t)=>((e,t)=>{var r;const[i,s]=f(t);if(!s)throw new Error('expected "<ssrc-id> <attribute>[:<value>]"');const n=b(i),o=s.indexOf(":"),a=-1===o?s:s.slice(0,o),d=-1===o?null:s.slice(o+1),c=null!==(r=e.ssrcs)&&void 0!==r?r:e.ssrcs=[];let m=c.find((e=>e.id===n));return m||(m={id:n,attributes:{}},c.push(m)),void 0===p(m.attributes,a)&&(l(m.attributes,a,d),!0)})(e,m(t,"a=ssrc:<ssrc-id> <attribute>[:<value>]"))},"ssrc-group":{scopes:E,parse:(e,t)=>g(e,"ssrcGroups",(e=>{const[t,...r]=e.split(" ");if(0===t.length)throw new Error('expected "<semantics> <ssrc-id> ..."');return{semantics:t,ssrcs:r.map(b)}})(m(t,"a=ssrc-group:<semantics> <ssrc-id> ...")))},rid:{scopes:E,parse:(e,t)=>g(e,"rids",(e=>{const[t,r]=f(e),[s,n]=r?f(r):[void 0,void 0];if(!x.test(t)||"send"!==s&&"recv"!==s||""===n)throw new Error('expected "<rid-id> <send|recv> [<params>]"');const o={id:t,direction:s};if(void 0!==n){const e=w(n);if("pt"in e){if(!n.startsWith("pt=")||null===e.pt)throw new Error('"pt=" must be the first rid parameter');o.payloadTypes=e.pt.split(",").map((e=>i(e,"payload type"))),delete e.pt}Object.keys(e).length&&(o.restrictions=e)}return o})(m(t,"a=rid:<rid-id> <send|recv>")))},simulcast:{scopes:E,parse:(e,t)=>h(e,"simulcast",(e=>{const t=e.split(" ");if(2!==t.length&&4!==t.length)throw new Error('expected "send|recv <streams> [send|recv <streams>]"');const r={};for(let e=0;e<t.length;e+=2){const i=t[e];if("send"!==i&&"recv"!==i||r[i])throw new Error('expected "send|recv <streams> [send|recv <streams>]"');r[i]=t[e+1].split(";").map((e=>e.split(",").map((e=>{const t=e.startsWith("~"),r=t?e.slice(1):e;if(!x.test(r))throw new Error(`invalid simulcast rid "${e}"`);return t?{rid:r,paused:t}:{rid:r}}))))}return r})(m(t,"a=simulcast:<send|recv> <streams>")),"simulcast")},"extmap-allow-mixed":{scopes:k,parse:(e,t)=>h(e,"extmapAllowMixed",u(t),"extmap-allow-mixed")},"rtcp-mux":{scopes:E,parse:(e,t)=>h(e,"rtcpMux",u(t),"rtcp-mux")},"rtcp-rsize":{scopes:E,parse:(e,t)=>h(e,"rtcpRsize",u(t),"rtcp-rsize")},rtcp:{scopes:E,parse:(e,t)=>{const r=m(t,"a=rtcp:<port> [<nettype> <addrtype> <address>]").split(" ");if(1!==r.length&&4!==r.length)throw new Error('expected "a=rtcp:<port> [<nettype> <addrtype> <address>]"');const[s,n,o,a]=r;h(e,"rtcp",1===r.length?{port:i(s,"port")}:{port:i(s,"port"),netType:n,addrType:o,address:a},"rtcp")}},"sctp-port":{scopes:E,parse:(e,t)=>h(e,"sctpPort",i(m(t,"a=sctp-port:<port>"),"sctp-port"),"sctp-port")},"max-message-size":{scopes:E,parse:(e,t)=>h(e,"maxMessageSize",i(m(t,"a=max-message-size:<size>"),"max-message-size"),"max-message-size")},rtpmap:{scopes:E,parse:(e,t)=>{const[r,s]=f(m(t,"a=rtpmap:<payload type> <encoding>")),n=e.codecs?v(e,r):void 0;if(!n||void 0!==n.encodingName)return!1;((e,t)=>{const[r,s,n,...o]=t.split("/");if(!r||void 0===s||o.length)throw new Error('expected "<payload type> <encoding name>/<clock rate>[/<encoding parameters>]"');e.encodingName=r,e.clockRate=i(s,"clock rate"),void 0!==n&&(e.encodingParameters=i(n,"encoding parameters"))})(n,null!=s?s:"")}},fmtp:{scopes:E,parse:(e,t)=>{const[r,i]=f(m(t,"a=fmtp:<format> <parameters>")),s=e.codecs?v(e,r):void 0;if(!s||void 0!==s.parameters||void 0===i)return!1;s.parameters=w(i)}},"rtcp-fb":{scopes:E,parse:(e,t)=>{const[r,i]=f(m(t,"a=rtcp-fb:<payload type> <feedback>"));if(!i||!e.codecs)return!1;if("*"===r)return void g(e,"rtcpFeedbackWildcard",i);const s=v(e,r);if(!s)return!1;g(s,"rtcpFeedback",i)}}},z=(e,t,r)=>{const i=r.indexOf(":"),s=-1===i?r:r.slice(0,i),n=-1===i?void 0:r.slice(i+1);if(0===s.length)throw new Error("empty attribute name");const o=p(F,s);o&&o.scopes.includes(t)&&!1!==o.parse(e,n)||g(e,"attributes",void 0===n?{name:s}:{name:s,value:n})},I={v:0,o:1,s:2,i:3,u:4,e:5,p:6,c:7,b:8,t:9,r:10,z:11,k:12,a:13},O=new Set(["e","p","b","a"]),C={m:0,i:1,c:2,b:3,k:4,a:5},R=new Set(["b","a"]),A={session:"v o s i u e p c b t r z k a",media:"m i c b k a"},N=r=>{const s=r.split(/\r?\n/);""===s[s.length-1]&&s.pop();const p={media:[]};let l=null,m=null,u=-1;s.forEach(((r,s)=>{var f,v,h,g,$,w,y;const b=t=>{throw new e(t,s+1,r)};0===r.length&&b("empty line"),(r.length<2||"="!==r[1])&&b('expected "<type>=<value>"');const x=r[0],k=r.slice(2);if(0===s&&"v"!==x&&b('a session description must start with "v="'),"m"===x)u=0;else{const e=l?"media":"session",t=l?R:O,r=(l?C:I)[x];void 0===r&&b(x in I?`"${x}=" is not allowed in a media description`:`unknown line type "${x}"`),l||"t"!==x?l||"r"!==x&&"z"!==x?r<u?b(`line is out of order (${e} order: ${A[e]})`):r!==u||t.has(x)||b(l&&"c"===x?'multiple "c=" lines in one media description are not supported':`duplicate "${x}=" line`):u!==I.t&&u!==I.r&&b(`"${x}=" must follow "t=" or "r="`):u>I.z&&b(`"t=" must precede "k=" and "a=" (order: ${A.session})`),u=r}try{if(l){switch(x){case"i":l.information=k;break;case"c":l.connectionInformation=c(k);break;case"b":(null!==(f=l.bandwidths)&&void 0!==f?f:l.bandwidths=[]).push(d(k));break;case"k":l.encryptionKey=o(k);break;case"a":z(l,"media",k)}if("m"!==x)return}switch(x){case"v":"0"!==k&&b(`unsupported protocol version "${k}"`),p.protocolVersion=0;break;case"o":p.origin=(e=>{const r=e.split(" ");if(6!==r.length)throw new Error('expected "<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>"');const[i,s,n,o,a,d]=r;if(!t.test(s))throw new Error(`sess-id "${s}" is not numeric`);if(!t.test(n))throw new Error(`sess-version "${n}" is not numeric`);return{username:i,sessId:s,sessVersion:n,netType:o,addrType:a,unicastAddress:d}})(k);break;case"s":p.sessionName=k;break;case"i":p.sessionInformation=k;break;case"u":p.uri=k;break;case"e":(null!==(v=p.emailAddresses)&&void 0!==v?v:p.emailAddresses=[]).push(k);break;case"p":(null!==(h=p.phoneNumbers)&&void 0!==h?h:p.phoneNumbers=[]).push(k);break;case"c":p.connectionInformation=c(k);break;case"b":(null!==(g=p.bandwidths)&&void 0!==g?g:p.bandwidths=[]).push(d(k));break;case"t":m=(e=>{const t=e.split(" ");if(2!==t.length)throw new Error('expected "<start-time> <stop-time>"');return{startTime:i(t[0],"start time"),stopTime:i(t[1],"stop time")}})(k),(null!==($=p.timing)&&void 0!==$?$:p.timing=[]).push(m);break;case"r":(null!==(w=(y=m).repeat)&&void 0!==w?w:y.repeat=[]).push((e=>{const t=e.split(" ");if(t.length<3)throw new Error('expected "<repeat interval> <active duration> <offset>..."');const[r,i,...s]=t.map((e=>n(e)));return{repeatInterval:r,activeDuration:i,offsets:s}})(k));break;case"z":m.zoneAdjustments=(e=>{const t=e.split(" ");if(t.length<2||t.length%2!=0)throw new Error('expected pairs of "<adjustment time> <offset>"');const r=[];for(let e=0;e<t.length;e+=2)r.push({adjustmentTime:i(t[e],"adjustment time"),offset:n(t[e+1],!0)});return r})(k);break;case"k":p.encryptionKey=o(k);break;case"a":z(p,"session",k);break;case"m":l=(e=>{const t=e.split(" ");if(t.length<4)throw new Error('expected "<media> <port> <proto> <fmt> ..."');const[r,s,n,...o]=t,[d,c,...p]=s.split("/");if(p.length)throw new Error(`invalid port "${s}"`);const l={type:r,port:i(d,"port"),proto:n};return void 0!==c&&(l.numberOfPorts=i(c,"number of ports")),a(n)?l.codecs=o.map((e=>{const t=i(e,"payload type");if(t>127)throw new Error(`payload type ${t} is out of range 0-127`);return{payloadType:t}})):l.formats=o,l})(k),p.media.push(l)}}catch(t){if(t instanceof e)throw t;b(t.message)}}));for(const[t,r]of[["protocolVersion","v"],["origin","o"],["sessionName","s"],["timing","t"]])if(void 0===p[t])throw new e(`missing mandatory "${r}=" line`,0,"");return p},S={parse:N,isValid:(e,t=!1)=>{try{return N(e),!0}catch(e){return t&&console.error(e),!1}}},U=e=>`c=${e.netType} ${e.addrType} ${e.connectionAddress}`,M=e=>`b=${e.bwtype}:${e.bandwidth}`,q=e=>`k=${e.method}${void 0!==e.encryptionKey?`:${e.encryptionKey}`:""}`,K=e=>Object.entries(e).map((([e,t])=>null===t?e:`${e}=${t}`)).join(";"),V=e=>{var t,r;return[`t=${e.startTime} ${e.stopTime}`,...(null!==(t=e.repeat)&&void 0!==t?t:[]).map((e=>`r=${[e.repeatInterval,e.activeDuration,...e.offsets].join(" ")}`)),...(null===(r=e.zoneAdjustments)||void 0===r?void 0:r.length)?[`z=${e.zoneAdjustments.map((e=>`${e.adjustmentTime} ${e.offset}`)).join(" ")}`]:[]]},D=e=>{var t;return[...void 0!==e.encodingName?[`a=rtpmap:${e.payloadType} ${e.encodingName}/${e.clockRate}${void 0!==e.encodingParameters?`/${e.encodingParameters}`:""}`]:[],...(null!==(t=e.rtcpFeedback)&&void 0!==t?t:[]).map((t=>`a=rtcp-fb:${e.payloadType} ${t}`)),...void 0!==e.parameters?[`a=fmtp:${e.payloadType} ${K(e.parameters)}`]:[]]},L=e=>{const t=[...e.payloadTypes?[`pt=${e.payloadTypes.join(",")}`]:[],...e.restrictions&&Object.keys(e.restrictions).length?[K(e.restrictions)]:[]].join(";");return`a=rid:${e.id} ${e.direction}${t?` ${t}`:""}`},W=(e,t)=>e?[`a=${t}`]:[],G=e=>{var t;return[...void 0!==e.iceUfrag?[`a=ice-ufrag:${e.iceUfrag}`]:[],...void 0!==e.icePwd?[`a=ice-pwd:${e.icePwd}`]:[],...e.iceOptions?[`a=ice-options:${e.iceOptions.join(" ")}`]:[],...(null!==(t=e.fingerprints)&&void 0!==t?t:[]).map((e=>`a=fingerprint:${e.hashFunction} ${e.fingerprint}`)),...void 0!==e.setup?[`a=setup:${e.setup}`]:[]]},B=e=>{var t;return[...(null!==(t=e.extmap)&&void 0!==t?t:[]).map((e=>`a=extmap:${e.id}${e.direction?`/${e.direction}`:""} ${e.uri}${void 0!==e.attributes?` ${e.attributes}`:""}`)),...W(e.extmapAllowMixed,"extmap-allow-mixed")]},Z=e=>{var t;return(null!==(t=e.attributes)&&void 0!==t?t:[]).map((e=>void 0===e.value?`a=${e.name}`:`a=${e.name}:${e.value}`))},_=e=>{var t,r,i,s,n,o,a,d;const c=e.codecs?e.codecs.map((e=>String(e.payloadType))):e.formats;if(!c||0===c.length)throw new Error(`media section "${e.type}" has neither codecs nor formats`);return[`m=${e.type} ${e.port}${void 0!==e.numberOfPorts?`/${e.numberOfPorts}`:""} ${e.proto} ${c.join(" ")}`,...void 0!==e.information?[`i=${e.information}`]:[],...e.connectionInformation?[U(e.connectionInformation)]:[],...(null!==(t=e.bandwidths)&&void 0!==t?t:[]).map(M),...e.encryptionKey?[q(e.encryptionKey)]:[],...e.rtcp?[`a=rtcp:${e.rtcp.port}${void 0!==e.rtcp.netType?` ${e.rtcp.netType} ${e.rtcp.addrType} ${e.rtcp.address}`:""}`]:[],...G(e),...void 0!==e.mid?[`a=mid:${e.mid}`]:[],...B(e),...e.direction?[`a=${e.direction}`]:[],...(null!==(r=e.msid)&&void 0!==r?r:[]).map((e=>`a=msid:${e.streamId}${void 0!==e.trackId?` ${e.trackId}`:""}`)),...W(e.rtcpMux,"rtcp-mux"),...W(e.rtcpRsize,"rtcp-rsize"),...(null!==(i=e.codecs)&&void 0!==i?i:[]).flatMap(D),...(null!==(s=e.rtcpFeedbackWildcard)&&void 0!==s?s:[]).map((e=>`a=rtcp-fb:* ${e}`)),...void 0!==e.sctpPort?[`a=sctp-port:${e.sctpPort}`]:[],...void 0!==e.maxMessageSize?[`a=max-message-size:${e.maxMessageSize}`]:[],...(null!==(n=e.rids)&&void 0!==n?n:[]).map(L),...e.simulcast?[(p=e.simulcast,`a=simulcast:${["send","recv"].filter((e=>p[e])).map((e=>`${e} ${p[e].map((e=>e.map((e=>`${e.paused?"~":""}${e.rid}`)).join(","))).join(";")}`)).join(" ")}`)]:[],...(null!==(o=e.ssrcGroups)&&void 0!==o?o:[]).map((e=>`a=ssrc-group:${[e.semantics,...e.ssrcs].join(" ")}`)),...(null!==(a=e.ssrcs)&&void 0!==a?a:[]).flatMap((e=>Object.entries(e.attributes).map((([t,r])=>`a=ssrc:${e.id} ${t}${null===r?"":`:${r}`}`)))),...(null!==(d=e.candidates)&&void 0!==d?d:[]).map((e=>`a=candidate:${(e=>{var t;return[e.foundation,e.component,e.transport,e.priority,e.address,e.port,"typ",e.type,...void 0!==e.relatedAddress?["raddr",e.relatedAddress]:[],...void 0!==e.relatedPort?["rport",e.relatedPort]:[],...void 0!==e.tcpType?["tcptype",e.tcpType]:[],...Object.entries(null!==(t=e.extensions)&&void 0!==t?t:{}).flat()].join(" ")})(e)}`)),...W(e.endOfCandidates,"end-of-candidates"),...Z(e)];var p},H={write:e=>{var t,r,i,s,n,o,a,d;const{origin:c}=e;if(!c)throw new Error("origin (o=) is required");if(!(null===(t=e.timing)||void 0===t?void 0:t.length))throw new Error("at least one timing (t=) entry is required");return[`v=${null!==(r=e.protocolVersion)&&void 0!==r?r:0}`,`o=${c.username} ${c.sessId} ${c.sessVersion} ${c.netType} ${c.addrType} ${c.unicastAddress}`,`s=${null!==(i=e.sessionName)&&void 0!==i?i:"-"}`,...void 0!==e.sessionInformation?[`i=${e.sessionInformation}`]:[],...void 0!==e.uri?[`u=${e.uri}`]:[],...(null!==(s=e.emailAddresses)&&void 0!==s?s:[]).map((e=>`e=${e}`)),...(null!==(n=e.phoneNumbers)&&void 0!==n?n:[]).map((e=>`p=${e}`)),...e.connectionInformation?[U(e.connectionInformation)]:[],...(null!==(o=e.bandwidths)&&void 0!==o?o:[]).map(M),...e.timing.flatMap(V),...e.encryptionKey?[q(e.encryptionKey)]:[],...(null!==(a=e.group)&&void 0!==a?a:[]).map((e=>`a=group:${[e.semantics,...e.mids].join(" ")}`)),...W(e.iceLite,"ice-lite"),...G(e),...B(e),...e.direction?[`a=${e.direction}`]:[],...W(e.endOfCandidates,"end-of-candidates"),...Z(e),...(null!==(d=e.media)&&void 0!==d?d:[]).flatMap(_)].join("\r\n")+"\r\n"}},J={md2:16,md5:16,"sha-1":20,"sha-224":28,"sha-256":32,"sha-384":48,"sha-512":64},Q=/^[0-9A-Fa-f]{2}(:[0-9A-Fa-f]{2})*$/,X={validate:(t,r={})=>{var i,s,n,o;let d;if("string"==typeof t)try{d=S.parse(t)}catch(t){if(t instanceof e)return[{severity:"error",code:"parse-error",message:t.message,lineNumber:t.lineNumber}];throw t}else d=t;const c=[],l=(e,t,r)=>c.push(void 0===r?{severity:"error",code:e,message:t}:{severity:"error",code:e,message:t,mediaIndex:r}),m=(e,t,r)=>c.push(void 0===r?{severity:"warning",code:e,message:t}:{severity:"warning",code:e,message:t,mediaIndex:r}),u=null!==(i=d.media)&&void 0!==i?i:[];0!==d.protocolVersion&&l("invalid-version",`v= must be 0, got ${d.protocolVersion}`),d.origin||l("missing-origin","o= is required"),void 0===d.sessionName?l("missing-session-name","s= is required"):""===d.sessionName&&m("empty-session-name",'s= must not be empty; use "s= " or "s=-" (RFC 8866 §5.3)'),(null===(s=d.timing)||void 0===s?void 0:s.length)||l("missing-timing","at least one t= line is required"),d.connectionInformation||u.forEach(((e,t)=>{e.connectionInformation||l("missing-connection","c= is required at session level or in every media description (RFC 8866 §5.7)",t)}));const f=new Set;u.forEach(((e,t)=>{void 0!==e.mid&&(f.has(e.mid)&&l("duplicate-mid",`a=mid:${e.mid} is used by more than one media description`,t),f.add(e.mid))}));for(const e of null!==(n=d.group)&&void 0!==n?n:[])for(const t of e.mids)f.has(t)||l("group-unknown-mid",`a=group:${e.semantics} references unknown mid "${t}"`);(null===(o=d.group)||void 0===o?void 0:o.some((e=>"BUNDLE"===e.semantics)))&&u.forEach(((e,t)=>{void 0===e.mid&&l("bundle-missing-mid","every media description needs a=mid when BUNDLE is used (RFC 9143)",t)}));const v=void 0!==d.iceUfrag||void 0!==d.icePwd||d.iceLite||u.some((e=>{var t;return void 0!==e.iceUfrag||void 0!==e.icePwd||(null===(t=e.candidates)||void 0===t?void 0:t.length)})),h=(e,t,r)=>{void 0!==e&&(e.length<4||e.length>256)&&l("ice-ufrag-length",`ice-ufrag must be 4-256 characters, got ${e.length} (RFC 8839 §5.4)`,r),void 0!==t&&(t.length<22||t.length>256)&&l("ice-pwd-length",`ice-pwd must be 22-256 characters, got ${t.length} (RFC 8839 §5.4)`,r)};h(d.iceUfrag,d.icePwd);const g=(e,t)=>{var r;for(const{hashFunction:i,fingerprint:s}of null!==(r=e.fingerprints)&&void 0!==r?r:[]){const e=p(J,i.toLowerCase());Q.test(s)?void 0!==e&&s.split(":").length!==e?l("invalid-fingerprint",`a=fingerprint:${i} has ${s.split(":").length} bytes, expected ${e} (RFC 8122 §5)`,t):s!==s.toUpperCase()&&m("fingerprint-not-uppercase",`a=fingerprint:${i} must use upper-case hex (RFC 8122 §5)`,t):l("invalid-fingerprint",`a=fingerprint:${i} must be colon-separated hex pairs (RFC 8122 §5)`,t)}};return g(d),u.forEach(((e,t)=>{var i,s,n,o,c,p,u,f,$,w,y,b,x,k,E,T;const j=0!==e.port;if(h(e.iceUfrag,e.icePwd,t),g(e,t),v&&j&&(void 0===(null!==(i=e.iceUfrag)&&void 0!==i?i:d.iceUfrag)&&l("missing-ice-ufrag","a=ice-ufrag is required (RFC 8839 §5.4)",t),void 0===(null!==(s=e.icePwd)&&void 0!==s?s:d.icePwd)&&l("missing-ice-pwd","a=ice-pwd is required (RFC 8839 §5.4)",t)),(e=>/(^|\/)(D?TLS)(\/|$)/.test(e.proto))(e)&&j){(null===(n=e.fingerprints)||void 0===n?void 0:n.length)||(null===(o=d.fingerprints)||void 0===o?void 0:o.length)||l("missing-fingerprint",`a=fingerprint is required for ${e.proto} (RFC 8842 §5.2, §5.3)`,t);const i=null!==(c=e.setup)&&void 0!==c?c:d.setup;void 0===i?l("missing-setup",`a=setup is required for ${e.proto} (RFC 8842 §5.2, §5.3)`,t):"answer"===r.type&&"actpass"===i&&l("setup-actpass-in-answer","an answer must use a=setup:active or a=setup:passive (RFC 5763 §5)",t)}if(a(e.proto)){for(const r of null!==(p=e.codecs)&&void 0!==p?p:[])r.payloadType>34&&void 0===r.encodingName&&l("missing-rtpmap",`payload type ${r.payloadType} is dynamic and needs a=rtpmap (RFC 8866 §6.6)`,t);for(const r of null!==(u=e.attributes)&&void 0!==u?u:[])"rtpmap"!==r.name&&"fmtp"!==r.name&&"rtcp-fb"!==r.name||m("unknown-payload-type",`a=${r.name}:${r.value} refers to a payload type that is not in the m= line, or repeats`,t)}const P=new Set;for(const r of null!==(f=e.extmap)&&void 0!==f?f:[])(r.id<1||15===r.id||r.id>255)&&l("extmap-invalid-id",`extmap id ${r.id} must be 1-14 or 16-255 (RFC 8285)`,t),P.has(r.id)&&l("extmap-duplicate-id",`extmap id ${r.id} is used more than once`,t),P.add(r.id);for(const r of["send","recv"])for(const i of(null!==(w=null===($=e.simulcast)||void 0===$?void 0:$[r])&&void 0!==w?w:[]).flat())(null===(y=e.rids)||void 0===y?void 0:y.some((e=>e.id===i.rid&&e.direction===r)))||l("simulcast-unknown-rid",`a=simulcast ${r} references "${i.rid}" without a matching a=rid:${i.rid} ${r} (RFC 8853 §5.1)`,t);for(const r of null!==(b=e.rids)&&void 0!==b?b:[])for(const i of null!==(x=r.payloadTypes)&&void 0!==x?x:[])(null===(k=e.codecs)||void 0===k?void 0:k.some((e=>e.payloadType===i)))||l("rid-unknown-payload-type",`a=rid:${r.id} references payload type ${i} that is not in the m= line (RFC 8851)`,t);for(const r of null!==(E=e.ssrcGroups)&&void 0!==E?E:[])for(const i of r.ssrcs)(null===(T=e.ssrcs)||void 0===T?void 0:T.some((e=>e.id===i)))||m("ssrc-group-unknown-ssrc",`a=ssrc-group:${r.semantics} references ssrc ${i} without an a=ssrc line`,t)})),c}},Y="1.2.0";export{e as SdpParseError,S as sdpReader,X as sdpValidator,H as sdpWriter,Y as version};
|
|
1
|
+
class e extends Error{constructor(e,t,r){super(t>0?`SDP line ${t} "${r}": ${e}`:`SDP: ${e}`),this.name="SdpParseError",this.reason=e,this.lineNumber=t,this.line=r}}const t=/^\d+$/,r=/^\d+$/,i=(e,t="value")=>{if(!r.test(e))throw new Error(`${t} "${e}" is not a non-negative integer`);return parseInt(e,10)},s={d:86400,h:3600,m:60,s:1},n=(e,t=!1)=>{if(t&&e.startsWith("-"))return-n(e.slice(1));const r=e.slice(-1);return r in s?s[r]*i(e.slice(0,-1),"time"):i(e,"time")},o=e=>{const t=e.indexOf(":"),r=-1===t?e:e.slice(0,t);if(0===r.length)throw new Error("empty key method");return-1===t?{method:r}:{method:r,encryptionKey:e.slice(t+1)}},a=e=>/(^|\/)RTP\//.test(e),d=e=>{const t=e.indexOf(":");if(t<=0)throw new Error('expected "<bwtype>:<bandwidth>"');return{bwtype:e.slice(0,t),bandwidth:i(e.slice(t+1),"bandwidth")}},c=e=>{const t=e.split(" ");if(3!==t.length)throw new Error('expected "<nettype> <addrtype> <connection-address>"');const[r,i,s]=t;return{netType:r,addrType:i,connectionAddress:s}},p=(e,t)=>Object.prototype.hasOwnProperty.call(e,t)?e[t]:void 0,l=(e,t,r)=>{Object.defineProperty(e,t,{value:r,writable:!0,enumerable:!0,configurable:!0})},m=(e,t)=>{if(void 0===e||0===e.length)throw new Error(`expected a value: ${t}`);return e},u=e=>{if(void 0!==e)throw new Error("flag attribute must not have a value");return!0},f=e=>{const t=e.indexOf(" ");return-1===t?[e,void 0]:[e.slice(0,t),e.slice(t+1)]},v=(e,t)=>{var r;const s=i(t,"payload type");return null===(r=e.codecs)||void 0===r?void 0:r.find((e=>e.payloadType===s))},h=(e,t,r,i)=>{if(void 0!==e[t])throw new Error(`duplicate a=${i}`);e[t]=r},g=(e,t,r)=>{void 0===e[t]&&(e[t]=[]),e[t].push(r)},$=["sendrecv","sendonly","recvonly","inactive"],w=e=>{const t={};for(const r of e.split(";")){const e=r.trim();if(0===e.length)continue;const i=e.indexOf("=");-1===i?l(t,e,null):l(t,e.slice(0,i),e.slice(i+1))}return t},y='"<foundation> <component> <transport> <priority> <address> <port> typ <type> [raddr <addr>] [rport <port>] *(<ext-name> <ext-value>)"',b=e=>{const t=i(e,"ssrc-id");if(t>4294967295)throw new Error(`ssrc-id ${t} does not fit into 32 bits`);return t},x=/^[A-Za-z0-9_-]+$/,k=["session","media"],E=["media"],T=["session"],j=["active","passive","actpass","holdconn"],P=e=>({scopes:k,parse:t=>h(t,"direction",e,e)}),F={sendrecv:P("sendrecv"),sendonly:P("sendonly"),recvonly:P("recvonly"),inactive:P("inactive"),"ice-ufrag":{scopes:k,parse:(e,t)=>h(e,"iceUfrag",m(t,"a=ice-ufrag:<ufrag>"),"ice-ufrag")},"ice-pwd":{scopes:k,parse:(e,t)=>h(e,"icePwd",m(t,"a=ice-pwd:<password>"),"ice-pwd")},"ice-options":{scopes:k,parse:(e,t)=>h(e,"iceOptions",m(t,"a=ice-options:<option> ...").split(" "),"ice-options")},"ice-lite":{scopes:T,parse:(e,t)=>h(e,"iceLite",u(t),"ice-lite")},candidate:{scopes:E,parse:(e,t)=>g(e,"candidates",(e=>{var t;const r=e.split(" ");if(r.length<8||"typ"!==r[6]||r.length%2!=0)throw new Error(`expected ${y}`);const[s,n,o,a,d,c,,p]=r,m={foundation:s,component:i(n,"component id"),transport:o,priority:i(a,"priority"),address:d,port:i(c,"port"),type:p};for(let e=8;e<r.length;e+=2){const[s,n]=[r[e],r[e+1]];if(0===s.length||0===n.length)throw new Error(`expected ${y}`);switch(s){case"raddr":m.relatedAddress=n;break;case"rport":m.relatedPort=i(n,"rport");break;case"tcptype":m.tcpType=n;break;default:l(null!==(t=m.extensions)&&void 0!==t?t:m.extensions={},s,n)}}return m})(m(t,"a=candidate:<candidate>")))},"end-of-candidates":{scopes:k,parse:(e,t)=>h(e,"endOfCandidates",u(t),"end-of-candidates")},fingerprint:{scopes:k,parse:(e,t)=>{const[r,i]=f(m(t,"a=fingerprint:<hash-function> <fingerprint>"));if(!i||i.includes(" "))throw new Error('expected "a=fingerprint:<hash-function> <fingerprint>"');g(e,"fingerprints",{hashFunction:r,fingerprint:i})}},setup:{scopes:k,parse:(e,t)=>{if(!j.includes(t))throw new Error(`a=setup must be one of ${j.join(", ")}`);h(e,"setup",t,"setup")}},extmap:{scopes:k,parse:(e,t)=>g(e,"extmap",(e=>{const[t,r]=f(e);if(!r)throw new Error('expected "<id>[/<direction>] <uri> [<attributes>]"');const[s,n,...o]=t.split("/");if(o.length||void 0!==n&&!$.includes(n))throw new Error(`invalid extmap id "${t}"`);const[a,d]=f(r),c={id:i(s,"extmap id"),uri:a};return void 0!==n&&(c.direction=n),void 0!==d&&(c.attributes=d),c})(m(t,"a=extmap:<id> <uri>")))},group:{scopes:T,parse:(e,t)=>g(e,"group",(e=>{const[t,...r]=e.split(" ");if(0===t.length||r.some((e=>0===e.length)))throw new Error('expected "<semantics> <mid> ..."');return{semantics:t,mids:r}})(m(t,"a=group:<semantics> <mid> ...")))},mid:{scopes:E,parse:(e,t)=>h(e,"mid",m(t,"a=mid:<identification-tag>"),"mid")},msid:{scopes:E,parse:(e,t)=>g(e,"msid",(e=>{const[t,r,...i]=e.split(" ");if(0===t.length||""===r||i.length)throw new Error('expected "<stream id> [<track id>]"');return void 0===r?{streamId:t}:{streamId:t,trackId:r}})(m(t,"a=msid:<stream id> [<track id>]")))},ssrc:{scopes:E,parse:(e,t)=>((e,t)=>{var r;const[i,s]=f(t);if(!s)throw new Error('expected "<ssrc-id> <attribute>[:<value>]"');const n=b(i),o=s.indexOf(":"),a=-1===o?s:s.slice(0,o),d=-1===o?null:s.slice(o+1),c=null!==(r=e.ssrcs)&&void 0!==r?r:e.ssrcs=[];let m=c.find((e=>e.id===n));return m||(m={id:n,attributes:{}},c.push(m)),void 0===p(m.attributes,a)&&(l(m.attributes,a,d),!0)})(e,m(t,"a=ssrc:<ssrc-id> <attribute>[:<value>]"))},"ssrc-group":{scopes:E,parse:(e,t)=>g(e,"ssrcGroups",(e=>{const[t,...r]=e.split(" ");if(0===t.length)throw new Error('expected "<semantics> <ssrc-id> ..."');return{semantics:t,ssrcs:r.map(b)}})(m(t,"a=ssrc-group:<semantics> <ssrc-id> ...")))},rid:{scopes:E,parse:(e,t)=>g(e,"rids",(e=>{const[t,r]=f(e),[s,n]=r?f(r):[void 0,void 0];if(!x.test(t)||"send"!==s&&"recv"!==s||""===n)throw new Error('expected "<rid-id> <send|recv> [<params>]"');const o={id:t,direction:s};if(void 0!==n){const e=w(n);if("pt"in e){if(!n.startsWith("pt=")||null===e.pt)throw new Error('"pt=" must be the first rid parameter');o.payloadTypes=e.pt.split(",").map((e=>i(e,"payload type"))),delete e.pt}Object.keys(e).length&&(o.restrictions=e)}return o})(m(t,"a=rid:<rid-id> <send|recv>")))},simulcast:{scopes:E,parse:(e,t)=>h(e,"simulcast",(e=>{const t=e.split(" ");if(2!==t.length&&4!==t.length)throw new Error('expected "send|recv <streams> [send|recv <streams>]"');const r={};for(let e=0;e<t.length;e+=2){const i=t[e];if("send"!==i&&"recv"!==i||r[i])throw new Error('expected "send|recv <streams> [send|recv <streams>]"');r[i]=t[e+1].split(";").map((e=>e.split(",").map((e=>{const t=e.startsWith("~"),r=t?e.slice(1):e;if(!x.test(r))throw new Error(`invalid simulcast rid "${e}"`);return t?{rid:r,paused:t}:{rid:r}}))))}return r})(m(t,"a=simulcast:<send|recv> <streams>")),"simulcast")},"extmap-allow-mixed":{scopes:k,parse:(e,t)=>h(e,"extmapAllowMixed",u(t),"extmap-allow-mixed")},"rtcp-mux":{scopes:E,parse:(e,t)=>h(e,"rtcpMux",u(t),"rtcp-mux")},"rtcp-rsize":{scopes:E,parse:(e,t)=>h(e,"rtcpRsize",u(t),"rtcp-rsize")},rtcp:{scopes:E,parse:(e,t)=>{const r=m(t,"a=rtcp:<port> [<nettype> <addrtype> <address>]").split(" ");if(1!==r.length&&4!==r.length)throw new Error('expected "a=rtcp:<port> [<nettype> <addrtype> <address>]"');const[s,n,o,a]=r;h(e,"rtcp",1===r.length?{port:i(s,"port")}:{port:i(s,"port"),netType:n,addrType:o,address:a},"rtcp")}},"sctp-port":{scopes:E,parse:(e,t)=>h(e,"sctpPort",i(m(t,"a=sctp-port:<port>"),"sctp-port"),"sctp-port")},"max-message-size":{scopes:E,parse:(e,t)=>h(e,"maxMessageSize",i(m(t,"a=max-message-size:<size>"),"max-message-size"),"max-message-size")},rtpmap:{scopes:E,parse:(e,t)=>{const[r,s]=f(m(t,"a=rtpmap:<payload type> <encoding>")),n=e.codecs?v(e,r):void 0;if(!n||void 0!==n.encodingName)return!1;((e,t)=>{const[r,s,n,...o]=t.split("/");if(!r||void 0===s||o.length)throw new Error('expected "<payload type> <encoding name>/<clock rate>[/<encoding parameters>]"');e.encodingName=r,e.clockRate=i(s,"clock rate"),void 0!==n&&(e.encodingParameters=i(n,"encoding parameters"))})(n,null!=s?s:"")}},fmtp:{scopes:E,parse:(e,t)=>{const[r,i]=f(m(t,"a=fmtp:<format> <parameters>")),s=e.codecs?v(e,r):void 0;if(!s||void 0!==s.parameters||void 0===i)return!1;s.parameters=w(i)}},"rtcp-fb":{scopes:E,parse:(e,t)=>{const[r,i]=f(m(t,"a=rtcp-fb:<payload type> <feedback>"));if(!i||!e.codecs)return!1;if("*"===r)return void g(e,"rtcpFeedbackWildcard",i);const s=v(e,r);if(!s)return!1;g(s,"rtcpFeedback",i)}}},z=(e,t,r)=>{const i=r.indexOf(":"),s=-1===i?r:r.slice(0,i),n=-1===i?void 0:r.slice(i+1);if(0===s.length)throw new Error("empty attribute name");const o=p(F,s);o&&o.scopes.includes(t)&&!1!==o.parse(e,n)||g(e,"attributes",void 0===n?{name:s}:{name:s,value:n})},I={v:0,o:1,s:2,i:3,u:4,e:5,p:6,c:7,b:8,t:9,r:10,z:11,k:12,a:13},O=new Set(["e","p","b","a"]),C={m:0,i:1,c:2,b:3,k:4,a:5},R=new Set(["b","a"]),A={session:"v o s i u e p c b t r z k a",media:"m i c b k a"},N=r=>{const s=r.split(/\r?\n/);""===s[s.length-1]&&s.pop();const p={media:[]};let l=null,m=null,u=-1;s.forEach(((r,s)=>{var f,v,h,g,$,w,y;const b=t=>{throw new e(t,s+1,r)};0===r.length&&b("empty line"),(r.length<2||"="!==r[1])&&b('expected "<type>=<value>"');const x=r[0],k=r.slice(2);if(0===s&&"v"!==x&&b('a session description must start with "v="'),"m"===x)u=0;else{const e=l?"media":"session",t=l?R:O,r=(l?C:I)[x];void 0===r&&b(x in I?`"${x}=" is not allowed in a media description`:`unknown line type "${x}"`),l||"t"!==x?l||"r"!==x&&"z"!==x?r<u?b(`line is out of order (${e} order: ${A[e]})`):r!==u||t.has(x)||b(l&&"c"===x?'multiple "c=" lines in one media description are not supported':`duplicate "${x}=" line`):u!==I.t&&u!==I.r&&b(`"${x}=" must follow "t=" or "r="`):u>I.z&&b(`"t=" must precede "k=" and "a=" (order: ${A.session})`),u=r}try{if(l){switch(x){case"i":l.information=k;break;case"c":l.connectionInformation=c(k);break;case"b":(null!==(f=l.bandwidths)&&void 0!==f?f:l.bandwidths=[]).push(d(k));break;case"k":l.encryptionKey=o(k);break;case"a":z(l,"media",k)}if("m"!==x)return}switch(x){case"v":"0"!==k&&b(`unsupported protocol version "${k}"`),p.protocolVersion=0;break;case"o":p.origin=(e=>{const r=e.split(" ");if(6!==r.length)throw new Error('expected "<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>"');const[i,s,n,o,a,d]=r;if(!t.test(s))throw new Error(`sess-id "${s}" is not numeric`);if(!t.test(n))throw new Error(`sess-version "${n}" is not numeric`);return{username:i,sessId:s,sessVersion:n,netType:o,addrType:a,unicastAddress:d}})(k);break;case"s":p.sessionName=k;break;case"i":p.sessionInformation=k;break;case"u":p.uri=k;break;case"e":(null!==(v=p.emailAddresses)&&void 0!==v?v:p.emailAddresses=[]).push(k);break;case"p":(null!==(h=p.phoneNumbers)&&void 0!==h?h:p.phoneNumbers=[]).push(k);break;case"c":p.connectionInformation=c(k);break;case"b":(null!==(g=p.bandwidths)&&void 0!==g?g:p.bandwidths=[]).push(d(k));break;case"t":m=(e=>{const t=e.split(" ");if(2!==t.length)throw new Error('expected "<start-time> <stop-time>"');return{startTime:i(t[0],"start time"),stopTime:i(t[1],"stop time")}})(k),(null!==($=p.timing)&&void 0!==$?$:p.timing=[]).push(m);break;case"r":(null!==(w=(y=m).repeat)&&void 0!==w?w:y.repeat=[]).push((e=>{const t=e.split(" ");if(t.length<3)throw new Error('expected "<repeat interval> <active duration> <offset>..."');const[r,i,...s]=t.map((e=>n(e)));return{repeatInterval:r,activeDuration:i,offsets:s}})(k));break;case"z":m.zoneAdjustments=(e=>{const t=e.split(" ");if(t.length<2||t.length%2!=0)throw new Error('expected pairs of "<adjustment time> <offset>"');const r=[];for(let e=0;e<t.length;e+=2)r.push({adjustmentTime:i(t[e],"adjustment time"),offset:n(t[e+1],!0)});return r})(k);break;case"k":p.encryptionKey=o(k);break;case"a":z(p,"session",k);break;case"m":l=(e=>{const t=e.split(" ");if(t.length<4)throw new Error('expected "<media> <port> <proto> <fmt> ..."');const[r,s,n,...o]=t,[d,c,...p]=s.split("/");if(p.length)throw new Error(`invalid port "${s}"`);const l={type:r,port:i(d,"port"),proto:n};return void 0!==c&&(l.numberOfPorts=i(c,"number of ports")),a(n)?l.codecs=o.map((e=>{const t=i(e,"payload type");if(t>127)throw new Error(`payload type ${t} is out of range 0-127`);return{payloadType:t}})):l.formats=o,l})(k),p.media.push(l)}}catch(t){if(t instanceof e)throw t;b(t.message)}}));for(const[t,r]of[["protocolVersion","v"],["origin","o"],["sessionName","s"],["timing","t"]])if(void 0===p[t])throw new e(`missing mandatory "${r}=" line`,0,"");return p},S={parse:N,isValid:(e,t=!1)=>{try{return N(e),!0}catch(e){return t&&console.error(e),!1}}},U=e=>`c=${e.netType} ${e.addrType} ${e.connectionAddress}`,M=e=>`b=${e.bwtype}:${e.bandwidth}`,q=e=>`k=${e.method}${void 0!==e.encryptionKey?`:${e.encryptionKey}`:""}`,K=e=>Object.entries(e).map((([e,t])=>null===t?e:`${e}=${t}`)).join(";"),V=e=>{var t,r;return[`t=${e.startTime} ${e.stopTime}`,...(null!==(t=e.repeat)&&void 0!==t?t:[]).map((e=>`r=${[e.repeatInterval,e.activeDuration,...e.offsets].join(" ")}`)),...(null===(r=e.zoneAdjustments)||void 0===r?void 0:r.length)?[`z=${e.zoneAdjustments.map((e=>`${e.adjustmentTime} ${e.offset}`)).join(" ")}`]:[]]},D=e=>{var t;return[...void 0!==e.encodingName?[`a=rtpmap:${e.payloadType} ${e.encodingName}/${e.clockRate}${void 0!==e.encodingParameters?`/${e.encodingParameters}`:""}`]:[],...(null!==(t=e.rtcpFeedback)&&void 0!==t?t:[]).map((t=>`a=rtcp-fb:${e.payloadType} ${t}`)),...void 0!==e.parameters?[`a=fmtp:${e.payloadType} ${K(e.parameters)}`]:[]]},L=e=>{const t=[...e.payloadTypes?[`pt=${e.payloadTypes.join(",")}`]:[],...e.restrictions&&Object.keys(e.restrictions).length?[K(e.restrictions)]:[]].join(";");return`a=rid:${e.id} ${e.direction}${t?` ${t}`:""}`},W=(e,t)=>e?[`a=${t}`]:[],G=e=>{var t;return[...void 0!==e.iceUfrag?[`a=ice-ufrag:${e.iceUfrag}`]:[],...void 0!==e.icePwd?[`a=ice-pwd:${e.icePwd}`]:[],...e.iceOptions?[`a=ice-options:${e.iceOptions.join(" ")}`]:[],...(null!==(t=e.fingerprints)&&void 0!==t?t:[]).map((e=>`a=fingerprint:${e.hashFunction} ${e.fingerprint}`)),...void 0!==e.setup?[`a=setup:${e.setup}`]:[]]},B=e=>{var t;return[...(null!==(t=e.extmap)&&void 0!==t?t:[]).map((e=>`a=extmap:${e.id}${e.direction?`/${e.direction}`:""} ${e.uri}${void 0!==e.attributes?` ${e.attributes}`:""}`)),...W(e.extmapAllowMixed,"extmap-allow-mixed")]},Z=e=>{var t;return(null!==(t=e.attributes)&&void 0!==t?t:[]).map((e=>void 0===e.value?`a=${e.name}`:`a=${e.name}:${e.value}`))},_=e=>{var t,r,i,s,n,o,a,d;const c=e.codecs?e.codecs.map((e=>String(e.payloadType))):e.formats;if(!c||0===c.length)throw new Error(`media section "${e.type}" has neither codecs nor formats`);return[`m=${e.type} ${e.port}${void 0!==e.numberOfPorts?`/${e.numberOfPorts}`:""} ${e.proto} ${c.join(" ")}`,...void 0!==e.information?[`i=${e.information}`]:[],...e.connectionInformation?[U(e.connectionInformation)]:[],...(null!==(t=e.bandwidths)&&void 0!==t?t:[]).map(M),...e.encryptionKey?[q(e.encryptionKey)]:[],...e.rtcp?[`a=rtcp:${e.rtcp.port}${void 0!==e.rtcp.netType?` ${e.rtcp.netType} ${e.rtcp.addrType} ${e.rtcp.address}`:""}`]:[],...G(e),...void 0!==e.mid?[`a=mid:${e.mid}`]:[],...B(e),...e.direction?[`a=${e.direction}`]:[],...(null!==(r=e.msid)&&void 0!==r?r:[]).map((e=>`a=msid:${e.streamId}${void 0!==e.trackId?` ${e.trackId}`:""}`)),...W(e.rtcpMux,"rtcp-mux"),...W(e.rtcpRsize,"rtcp-rsize"),...(null!==(i=e.codecs)&&void 0!==i?i:[]).flatMap(D),...(null!==(s=e.rtcpFeedbackWildcard)&&void 0!==s?s:[]).map((e=>`a=rtcp-fb:* ${e}`)),...void 0!==e.sctpPort?[`a=sctp-port:${e.sctpPort}`]:[],...void 0!==e.maxMessageSize?[`a=max-message-size:${e.maxMessageSize}`]:[],...(null!==(n=e.rids)&&void 0!==n?n:[]).map(L),...e.simulcast?[(p=e.simulcast,`a=simulcast:${["send","recv"].filter((e=>p[e])).map((e=>`${e} ${p[e].map((e=>e.map((e=>`${e.paused?"~":""}${e.rid}`)).join(","))).join(";")}`)).join(" ")}`)]:[],...(null!==(o=e.ssrcGroups)&&void 0!==o?o:[]).map((e=>`a=ssrc-group:${[e.semantics,...e.ssrcs].join(" ")}`)),...(null!==(a=e.ssrcs)&&void 0!==a?a:[]).flatMap((e=>Object.entries(e.attributes).map((([t,r])=>`a=ssrc:${e.id} ${t}${null===r?"":`:${r}`}`)))),...(null!==(d=e.candidates)&&void 0!==d?d:[]).map((e=>`a=candidate:${(e=>{var t;return[e.foundation,e.component,e.transport,e.priority,e.address,e.port,"typ",e.type,...void 0!==e.relatedAddress?["raddr",e.relatedAddress]:[],...void 0!==e.relatedPort?["rport",e.relatedPort]:[],...void 0!==e.tcpType?["tcptype",e.tcpType]:[],...Object.entries(null!==(t=e.extensions)&&void 0!==t?t:{}).flat()].join(" ")})(e)}`)),...W(e.endOfCandidates,"end-of-candidates"),...Z(e)];var p},H={write:e=>{var t,r,i,s,n,o,a,d;const{origin:c}=e;if(!c)throw new Error("origin (o=) is required");if(!(null===(t=e.timing)||void 0===t?void 0:t.length))throw new Error("at least one timing (t=) entry is required");return[`v=${null!==(r=e.protocolVersion)&&void 0!==r?r:0}`,`o=${c.username} ${c.sessId} ${c.sessVersion} ${c.netType} ${c.addrType} ${c.unicastAddress}`,`s=${null!==(i=e.sessionName)&&void 0!==i?i:"-"}`,...void 0!==e.sessionInformation?[`i=${e.sessionInformation}`]:[],...void 0!==e.uri?[`u=${e.uri}`]:[],...(null!==(s=e.emailAddresses)&&void 0!==s?s:[]).map((e=>`e=${e}`)),...(null!==(n=e.phoneNumbers)&&void 0!==n?n:[]).map((e=>`p=${e}`)),...e.connectionInformation?[U(e.connectionInformation)]:[],...(null!==(o=e.bandwidths)&&void 0!==o?o:[]).map(M),...e.timing.flatMap(V),...e.encryptionKey?[q(e.encryptionKey)]:[],...(null!==(a=e.group)&&void 0!==a?a:[]).map((e=>`a=group:${[e.semantics,...e.mids].join(" ")}`)),...W(e.iceLite,"ice-lite"),...G(e),...B(e),...e.direction?[`a=${e.direction}`]:[],...W(e.endOfCandidates,"end-of-candidates"),...Z(e),...(null!==(d=e.media)&&void 0!==d?d:[]).flatMap(_)].join("\r\n")+"\r\n"}},J={md2:16,md5:16,"sha-1":20,"sha-224":28,"sha-256":32,"sha-384":48,"sha-512":64},Q=/^[0-9A-Fa-f]{2}(:[0-9A-Fa-f]{2})*$/,X={validate:(t,r={})=>{var i,s,n,o;let d;if("string"==typeof t)try{d=S.parse(t)}catch(t){if(t instanceof e)return[{severity:"error",code:"parse-error",message:t.message,lineNumber:t.lineNumber}];throw t}else d=t;const c=[],l=(e,t,r)=>c.push(void 0===r?{severity:"error",code:e,message:t}:{severity:"error",code:e,message:t,mediaIndex:r}),m=(e,t,r)=>c.push(void 0===r?{severity:"warning",code:e,message:t}:{severity:"warning",code:e,message:t,mediaIndex:r}),u=null!==(i=d.media)&&void 0!==i?i:[];0!==d.protocolVersion&&l("invalid-version",`v= must be 0, got ${d.protocolVersion}`),d.origin||l("missing-origin","o= is required"),void 0===d.sessionName?l("missing-session-name","s= is required"):""===d.sessionName&&m("empty-session-name",'s= must not be empty; use "s= " or "s=-" (RFC 8866 §5.3)'),(null===(s=d.timing)||void 0===s?void 0:s.length)||l("missing-timing","at least one t= line is required"),d.connectionInformation||u.forEach(((e,t)=>{e.connectionInformation||l("missing-connection","c= is required at session level or in every media description (RFC 8866 §5.7)",t)}));const f=new Set;u.forEach(((e,t)=>{void 0!==e.mid&&(f.has(e.mid)&&l("duplicate-mid",`a=mid:${e.mid} is used by more than one media description`,t),f.add(e.mid))}));for(const e of null!==(n=d.group)&&void 0!==n?n:[])for(const t of e.mids)f.has(t)||l("group-unknown-mid",`a=group:${e.semantics} references unknown mid "${t}"`);(null===(o=d.group)||void 0===o?void 0:o.some((e=>"BUNDLE"===e.semantics)))&&u.forEach(((e,t)=>{void 0===e.mid&&l("bundle-missing-mid","every media description needs a=mid when BUNDLE is used (RFC 9143)",t)}));const v=void 0!==d.iceUfrag||void 0!==d.icePwd||d.iceLite||u.some((e=>{var t;return void 0!==e.iceUfrag||void 0!==e.icePwd||(null===(t=e.candidates)||void 0===t?void 0:t.length)})),h=(e,t,r)=>{void 0!==e&&(e.length<4||e.length>256)&&l("ice-ufrag-length",`ice-ufrag must be 4-256 characters, got ${e.length} (RFC 8839 §5.4)`,r),void 0!==t&&(t.length<22||t.length>256)&&l("ice-pwd-length",`ice-pwd must be 22-256 characters, got ${t.length} (RFC 8839 §5.4)`,r)};h(d.iceUfrag,d.icePwd);const g=(e,t)=>{var r;for(const{hashFunction:i,fingerprint:s}of null!==(r=e.fingerprints)&&void 0!==r?r:[]){const e=p(J,i.toLowerCase());Q.test(s)?void 0!==e&&s.split(":").length!==e?l("invalid-fingerprint",`a=fingerprint:${i} has ${s.split(":").length} bytes, expected ${e} (RFC 8122 §5)`,t):s!==s.toUpperCase()&&m("fingerprint-not-uppercase",`a=fingerprint:${i} must use upper-case hex (RFC 8122 §5)`,t):l("invalid-fingerprint",`a=fingerprint:${i} must be colon-separated hex pairs (RFC 8122 §5)`,t)}};return g(d),u.forEach(((e,t)=>{var i,s,n,o,c,p,u,f,$,w,y,b,x,k,E,T;const j=0!==e.port;if(h(e.iceUfrag,e.icePwd,t),g(e,t),v&&j&&(void 0===(null!==(i=e.iceUfrag)&&void 0!==i?i:d.iceUfrag)&&l("missing-ice-ufrag","a=ice-ufrag is required (RFC 8839 §5.4)",t),void 0===(null!==(s=e.icePwd)&&void 0!==s?s:d.icePwd)&&l("missing-ice-pwd","a=ice-pwd is required (RFC 8839 §5.4)",t)),(e=>/(^|\/)(D?TLS)(\/|$)/.test(e.proto))(e)&&j){(null===(n=e.fingerprints)||void 0===n?void 0:n.length)||(null===(o=d.fingerprints)||void 0===o?void 0:o.length)||l("missing-fingerprint",`a=fingerprint is required for ${e.proto} (RFC 8842 §5.2, §5.3)`,t);const i=null!==(c=e.setup)&&void 0!==c?c:d.setup;void 0===i?l("missing-setup",`a=setup is required for ${e.proto} (RFC 8842 §5.2, §5.3)`,t):"answer"===r.type&&"actpass"===i&&l("setup-actpass-in-answer","an answer must use a=setup:active or a=setup:passive (RFC 5763 §5)",t)}if(a(e.proto)){for(const r of null!==(p=e.codecs)&&void 0!==p?p:[])r.payloadType>34&&void 0===r.encodingName&&l("missing-rtpmap",`payload type ${r.payloadType} is dynamic and needs a=rtpmap (RFC 8866 §6.6)`,t);for(const r of null!==(u=e.attributes)&&void 0!==u?u:[])"rtpmap"!==r.name&&"fmtp"!==r.name&&"rtcp-fb"!==r.name||m("unknown-payload-type",`a=${r.name}:${r.value} refers to a payload type that is not in the m= line, or repeats`,t)}const P=new Set;for(const r of null!==(f=e.extmap)&&void 0!==f?f:[])(r.id<1||15===r.id||r.id>255)&&l("extmap-invalid-id",`extmap id ${r.id} must be 1-14 or 16-255 (RFC 8285)`,t),P.has(r.id)&&l("extmap-duplicate-id",`extmap id ${r.id} is used more than once`,t),P.add(r.id);for(const r of["send","recv"])for(const i of(null!==(w=null===($=e.simulcast)||void 0===$?void 0:$[r])&&void 0!==w?w:[]).flat())(null===(y=e.rids)||void 0===y?void 0:y.some((e=>e.id===i.rid&&e.direction===r)))||l("simulcast-unknown-rid",`a=simulcast ${r} references "${i.rid}" without a matching a=rid:${i.rid} ${r} (RFC 8853 §5.1)`,t);for(const r of null!==(b=e.rids)&&void 0!==b?b:[])for(const i of null!==(x=r.payloadTypes)&&void 0!==x?x:[])(null===(k=e.codecs)||void 0===k?void 0:k.some((e=>e.payloadType===i)))||l("rid-unknown-payload-type",`a=rid:${r.id} references payload type ${i} that is not in the m= line (RFC 8851)`,t);for(const r of null!==(E=e.ssrcGroups)&&void 0!==E?E:[])for(const i of r.ssrcs)(null===(T=e.ssrcs)||void 0===T?void 0:T.some((e=>e.id===i)))||m("ssrc-group-unknown-ssrc",`a=ssrc-group:${r.semantics} references ssrc ${i} without an a=ssrc line`,t)})),c}},Y="1.2.1-rc.1";export{e as SdpParseError,S as sdpReader,X as sdpValidator,H as sdpWriter,Y as version};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@irbisadm/simple-sdp",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1-rc.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Parse, modify, write and validate SDP (RFC 8866) for WebRTC and SIP. Typed ICE, DTLS, BUNDLE, simulcast, codecs. Zero dependencies.",
|
|
6
6
|
"author": {
|
package/types/index.d.ts
CHANGED
|
@@ -332,6 +332,6 @@ declare class SdpParseError extends Error {
|
|
|
332
332
|
constructor(reason: string, lineNumber: number, line: string);
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
-
declare const version = "1.2.
|
|
335
|
+
declare const version = "1.2.1-rc.1";
|
|
336
336
|
|
|
337
337
|
export { Sdp, SdpBandwidthInformation, SdpCandidate, SdpCodec, SdpCommonAttributes, SdpConnectionInformation, SdpDirection, SdpEncryptionKey, SdpExtmap, SdpFingerprint, SdpGenericAttribute, SdpGroup, SdpIssue, SdpIssueCode, SdpMediaSection, SdpMsid, SdpOrigin, SdpPair, SdpParseError, SdpRepeatTimes, SdpRid, SdpRtcp, SdpSetup, SdpSimulcast, SdpSimulcastRid, SdpSimulcastStreams, SdpSsrc, SdpSsrcGroup, SdpTimeZoneAdjustment, SdpTiming, SdpValidateOptions, sdpReader, sdpValidator, sdpWriter, version };
|
|
Binary file
|