UNPKG

sdp-jingle-json

Version:

A parser/serializer for SDP to JSON. Useful for converting SDP to other formats like Jingle for WebRTC signalling

248 lines (210 loc) 7.75 kB
var SENDERS = require('./senders'); exports.toSessionSDP = function (session, opts) { var role = opts.role || 'initiator'; var direction = opts.direction || 'outgoing'; var sid = opts.sid || session.sid || Date.now(); var time = opts.time || Date.now(); var sdp = [ 'v=0', 'o=- ' + sid + ' ' + time + ' IN IP4 0.0.0.0', 's=-', 't=0 0' ]; var contents = session.contents || []; var hasSources = false; contents.forEach(function (content) { if (content.application.sources && content.application.sources.length) { hasSources = true; } }); if (hasSources) { sdp.push('a=msid-semantic: WMS *'); } var groups = session.groups || []; groups.forEach(function (group) { sdp.push('a=group:' + group.semantics + ' ' + group.contents.join(' ')); }); contents.forEach(function (content) { sdp.push(exports.toMediaSDP(content, opts)); }); return sdp.join('\r\n') + '\r\n'; }; exports.toMediaSDP = function (content, opts) { var sdp = []; var role = opts.role || 'initiator'; var direction = opts.direction || 'outgoing'; var desc = content.application; var transport = content.transport; var payloads = desc.payloads || []; var fingerprints = (transport && transport.fingerprints) || []; var mline = []; if (desc.applicationType == 'datachannel') { mline.push('application'); mline.push('1'); mline.push('DTLS/SCTP'); if (transport.sctp) { transport.sctp.forEach(function (map) { mline.push(map.number); }); } } else { mline.push(desc.media); mline.push('1'); if (fingerprints.length > 0) { mline.push('UDP/TLS/RTP/SAVPF'); } else if (desc.encryption && desc.encryption.length > 0) { mline.push('RTP/SAVPF'); } else { mline.push('RTP/AVPF'); } payloads.forEach(function (payload) { mline.push(payload.id); }); } sdp.push('m=' + mline.join(' ')); sdp.push('c=IN IP4 0.0.0.0'); if (desc.bandwidth && desc.bandwidth.type && desc.bandwidth.bandwidth) { sdp.push('b=' + desc.bandwidth.type + ':' + desc.bandwidth.bandwidth); } if (desc.applicationType == 'rtp') { sdp.push('a=rtcp:1 IN IP4 0.0.0.0'); } if (transport) { if (transport.ufrag) { sdp.push('a=ice-ufrag:' + transport.ufrag); } if (transport.pwd) { sdp.push('a=ice-pwd:' + transport.pwd); } var pushedSetup = false; fingerprints.forEach(function (fingerprint) { sdp.push('a=fingerprint:' + fingerprint.hash + ' ' + fingerprint.value); if (fingerprint.setup && !pushedSetup) { sdp.push('a=setup:' + fingerprint.setup); } }); if (transport.sctp) { transport.sctp.forEach(function (map) { sdp.push('a=sctpmap:' + map.number + ' ' + map.protocol + ' ' + map.streams); }); } } if (desc.applicationType == 'rtp') { sdp.push('a=' + (SENDERS[role][direction][content.senders] || 'sendrecv')); } sdp.push('a=mid:' + content.name); if (desc.sources && desc.sources.length) { var streams = {}; desc.sources.forEach(function (source) { (source.parameters || []).forEach(function (param) { if (param.key === 'msid') { streams[param.value] = 1; } }); }); streams = Object.keys(streams); if (streams.length === 1) { sdp.push('a=msid:' + streams[0]); } } if (desc.mux) { sdp.push('a=rtcp-mux'); } if (desc.rsize) { sdp.push('a=rtcp-rsize'); } var encryption = desc.encryption || []; encryption.forEach(function (crypto) { sdp.push('a=crypto:' + crypto.tag + ' ' + crypto.cipherSuite + ' ' + crypto.keyParams + (crypto.sessionParams ? ' ' + crypto.sessionParams : '')); }); if (desc.googConferenceFlag) { sdp.push('a=x-google-flag:conference'); } payloads.forEach(function (payload) { var rtpmap = 'a=rtpmap:' + payload.id + ' ' + payload.name + '/' + payload.clockrate; if (payload.channels && payload.channels != '1') { rtpmap += '/' + payload.channels; } sdp.push(rtpmap); if (payload.parameters && payload.parameters.length) { var fmtp = ['a=fmtp:' + payload.id]; var parameters = []; payload.parameters.forEach(function (param) { parameters.push((param.key ? param.key + '=' : '') + param.value); }); fmtp.push(parameters.join(';')); sdp.push(fmtp.join(' ')); } if (payload.feedback) { payload.feedback.forEach(function (fb) { if (fb.type === 'trr-int') { sdp.push('a=rtcp-fb:' + payload.id + ' trr-int ' + (fb.value ? fb.value : '0')); } else { sdp.push('a=rtcp-fb:' + payload.id + ' ' + fb.type + (fb.subtype ? ' ' + fb.subtype : '')); } }); } }); if (desc.feedback) { desc.feedback.forEach(function (fb) { if (fb.type === 'trr-int') { sdp.push('a=rtcp-fb:* trr-int ' + (fb.value ? fb.value : '0')); } else { sdp.push('a=rtcp-fb:* ' + fb.type + (fb.subtype ? ' ' + fb.subtype : '')); } }); } var hdrExts = desc.headerExtensions || []; hdrExts.forEach(function (hdr) { sdp.push('a=extmap:' + hdr.id + (hdr.senders ? '/' + SENDERS[role][direction][hdr.senders] : '') + ' ' + hdr.uri); }); var ssrcGroups = desc.sourceGroups || []; ssrcGroups.forEach(function (ssrcGroup) { sdp.push('a=ssrc-group:' + ssrcGroup.semantics + ' ' + ssrcGroup.sources.join(' ')); }); var ssrcs = desc.sources || []; ssrcs.forEach(function (ssrc) { for (var i = 0; i < ssrc.parameters.length; i++) { var param = ssrc.parameters[i]; sdp.push('a=ssrc:' + (ssrc.ssrc || desc.ssrc) + ' ' + param.key + (param.value ? (':' + param.value) : '')); } }); var candidates = transport.candidates || []; candidates.forEach(function (candidate) { sdp.push(exports.toCandidateSDP(candidate)); }); return sdp.join('\r\n'); }; exports.toCandidateSDP = function (candidate) { var sdp = []; sdp.push(candidate.foundation); sdp.push(candidate.component); sdp.push(candidate.protocol.toUpperCase()); sdp.push(candidate.priority); sdp.push(candidate.ip); sdp.push(candidate.port); var type = candidate.type; sdp.push('typ'); sdp.push(type); if (type === 'srflx' || type === 'prflx' || type === 'relay') { if (candidate.relAddr && candidate.relPort) { sdp.push('raddr'); sdp.push(candidate.relAddr); sdp.push('rport'); sdp.push(candidate.relPort); } } if (candidate.tcpType && candidate.protocol.toUpperCase() == 'TCP') { sdp.push('tcptype'); sdp.push(candidate.tcpType); } sdp.push('generation'); sdp.push(candidate.generation || '0'); // FIXME: apparently this is wrong per spec // but then, we need this when actually putting this into // SDP so it's going to stay. // decision needs to be revisited when browsers dont // accept this any longer return 'a=candidate:' + sdp.join(' '); };