UNPKG

sdp-jingle-json

Version:

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

226 lines (190 loc) 7.41 kB
var SENDERS = require('./senders'); var parsers = require('./parsers'); var idCounter = Math.random(); exports._setIdCounter = function (counter) { idCounter = counter; }; exports.toSessionJSON = function (sdp, opts) { var i; var creators = opts.creators || []; var role = opts.role || 'initiator'; var direction = opts.direction || 'outgoing'; // Divide the SDP into session and media sections. var media = sdp.split(/\r?\nm=/); for (i = 1; i < media.length; i++) { media[i] = 'm=' + media[i]; if (i !== media.length - 1) { media[i] += '\r\n'; } } var session = media.shift() + '\r\n'; var sessionLines = parsers.lines(session); var parsed = {}; var contents = []; for (i = 0; i < media.length; i++) { contents.push(exports.toMediaJSON(media[i], session, { role: role, direction: direction, creator: creators[i] || 'initiator' })); } parsed.contents = contents; var groupLines = parsers.findLines('a=group:', sessionLines); if (groupLines.length) { parsed.groups = parsers.groups(groupLines); } return parsed; }; exports.toMediaJSON = function (media, session, opts) { var creator = opts.creator || 'initiator'; var role = opts.role || 'initiator'; var direction = opts.direction || 'outgoing'; var lines = parsers.lines(media); var sessionLines = parsers.lines(session); var mline = parsers.mline(lines[0]); var content = { creator: creator, name: mline.media, application: { applicationType: 'rtp', media: mline.media, payloads: [], encryption: [], feedback: [], headerExtensions: [] }, transport: { transportType: 'iceUdp', candidates: [], fingerprints: [] } }; if (mline.media == 'application') { // FIXME: the description is most likely to be independent // of the SDP and should be processed by other parts of the library content.application = { applicationType: 'datachannel' }; content.transport.sctp = []; } var desc = content.application; var trans = content.transport; // If we have a mid, use that for the content name instead. var mid = parsers.findLine('a=mid:', lines); if (mid) { content.name = mid.substr(6); } if (parsers.findLine('a=sendrecv', lines, sessionLines)) { content.senders = 'both'; } else if (parsers.findLine('a=sendonly', lines, sessionLines)) { content.senders = SENDERS[role][direction].sendonly; } else if (parsers.findLine('a=recvonly', lines, sessionLines)) { content.senders = SENDERS[role][direction].recvonly; } else if (parsers.findLine('a=inactive', lines, sessionLines)) { content.senders = 'none'; } if (desc.applicationType == 'rtp') { var bandwidth = parsers.findLine('b=', lines); if (bandwidth) { desc.bandwidth = parsers.bandwidth(bandwidth); } var ssrc = parsers.findLine('a=ssrc:', lines); if (ssrc) { desc.ssrc = ssrc.substr(7).split(' ')[0]; } var rtpmapLines = parsers.findLines('a=rtpmap:', lines); rtpmapLines.forEach(function (line) { var payload = parsers.rtpmap(line); payload.parameters = []; payload.feedback = []; var fmtpLines = parsers.findLines('a=fmtp:' + payload.id, lines); // There should only be one fmtp line per payload fmtpLines.forEach(function (line) { payload.parameters = parsers.fmtp(line); }); var fbLines = parsers.findLines('a=rtcp-fb:' + payload.id, lines); fbLines.forEach(function (line) { payload.feedback.push(parsers.rtcpfb(line)); }); desc.payloads.push(payload); }); var cryptoLines = parsers.findLines('a=crypto:', lines, sessionLines); cryptoLines.forEach(function (line) { desc.encryption.push(parsers.crypto(line)); }); if (parsers.findLine('a=rtcp-mux', lines)) { desc.mux = true; } if (parsers.findLine('a=rtcp-rsize', lines)) { desc.rsize = true; } var fbLines = parsers.findLines('a=rtcp-fb:*', lines); fbLines.forEach(function (line) { desc.feedback.push(parsers.rtcpfb(line)); }); var extLines = parsers.findLines('a=extmap:', lines); extLines.forEach(function (line) { var ext = parsers.extmap(line); ext.senders = SENDERS[role][direction][ext.senders]; desc.headerExtensions.push(ext); }); var ssrcGroupLines = parsers.findLines('a=ssrc-group:', lines); desc.sourceGroups = parsers.sourceGroups(ssrcGroupLines || []); var ssrcLines = parsers.findLines('a=ssrc:', lines); var sources = desc.sources = parsers.sources(ssrcLines || []); var msidLine = parsers.findLine('a=msid:', lines); if (msidLine) { var msid = parsers.msid(msidLine); ['msid', 'mslabel', 'label'].forEach(function (key) { for (var i = 0; i < sources.length; i++) { var found = false; for (var j = 0; j < sources[i].parameters.length; j++) { if (sources[i].parameters[j].key === key) { found = true; } } if (!found) { sources[i].parameters.push({ key: key, value: msid[key] }); } } }); } if (parsers.findLine('a=x-google-flag:conference', lines, sessionLines)) { desc.googConferenceFlag = true; } } // transport specific attributes var fingerprintLines = parsers.findLines('a=fingerprint:', lines, sessionLines); var setup = parsers.findLine('a=setup:', lines, sessionLines); fingerprintLines.forEach(function (line) { var fp = parsers.fingerprint(line); if (setup) { fp.setup = setup.substr(8); } trans.fingerprints.push(fp); }); var ufragLine = parsers.findLine('a=ice-ufrag:', lines, sessionLines); var pwdLine = parsers.findLine('a=ice-pwd:', lines, sessionLines); if (ufragLine && pwdLine) { trans.ufrag = ufragLine.substr(12); trans.pwd = pwdLine.substr(10); trans.candidates = []; var candidateLines = parsers.findLines('a=candidate:', lines, sessionLines); candidateLines.forEach(function (line) { trans.candidates.push(exports.toCandidateJSON(line)); }); } if (desc.applicationType == 'datachannel') { var sctpmapLines = parsers.findLines('a=sctpmap:', lines); sctpmapLines.forEach(function (line) { var sctp = parsers.sctpmap(line); trans.sctp.push(sctp); }); } return content; }; exports.toCandidateJSON = function (line) { var candidate = parsers.candidate(line.split(/\r?\n/)[0]); candidate.id = (idCounter++).toString(36).substr(0, 12); return candidate; };