sections.forEach(function(mediaSection, sdpMLineIndex) { var lines = SDPUtils.splitLines(mediaSection); var mline = lines[0].substr(2).split(' '); var kind = mline[0]; var rejected = mline[1] === '0'; var direction = SDPUtils.getDirection(mediaSection, sessionpart); var transceiver; var iceGatherer; var iceTransport; var dtlsTransport; var rtpSender; var rtpReceiver; var sendEncodingParameters; var recvEncodingParameters; var localCapabilities; var track; // FIXME: ensure the mediaSection has rtcp-mux set. var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection); var remoteIceParameters; var remoteDtlsParameters; if (!rejected) { remoteIceParameters = SDPUtils.getIceParameters(mediaSection, sessionpart); remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection, sessionpart); remoteDtlsParameters.role = 'client'; } recvEncodingParameters = SDPUtils.parseRtpEncodingParameters(mediaSection); var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:'); if (mid.length) { mid = mid[0].substr(6); } else { mid = SDPUtils.generateIdentifier(); } var cname; // Gets the first SSRC. Note that with RTX there might be multiple // SSRCs. var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:') .map(function(line) { return SDPUtils.parseSsrcMedia(line); }) .filter(function(obj) { return obj.attribute === 'cname'; })[0]; if (remoteSsrc) { cname = remoteSsrc.value; } var isComplete = SDPUtils.matchPrefix(mediaSection, 'a=end-of-candidates').length > 0; var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:') .map(function(cand) { return SDPUtils.parseCandidate(cand); }) .filter(function(cand) { return cand.component === '1'; }); if (description.type === 'offer' && !rejected) { var transports = self.usingBundle && sdpMLineIndex > 0 ? { iceGatherer: self.transceivers[0].iceGatherer, iceTransport: self.transceivers[0].iceTransport, dtlsTransport: self.transceivers[0].dtlsTransport } : self._createIceAndDtlsTransports(mid, sdpMLineIndex); if (isComplete) { transports.iceTransport.setRemoteCandidates(cands); } localCapabilities = RTCRtpReceiver.getCapabilities(kind); sendEncodingParameters = [{ ssrc: (2 * sdpMLineIndex + 2) * 1001 }]; rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind); track = rtpReceiver.track; receiverList.push([track, rtpReceiver]); // FIXME: not correct when there are multiple streams but that is // not currently supported in this shim. stream.addTrack(track); // FIXME: look at direction. if (self.localStreams.length > 0 && self.localStreams[0].getTracks().length >= sdpMLineIndex) { // FIXME: actually more complicated, needs to match types etc var localtrack = self.localStreams[0] .getTracks()[sdpMLineIndex]; rtpSender = new RTCRtpSender(localtrack, transports.dtlsTransport); } self.transceivers[sdpMLineIndex] = { iceGatherer: transports.iceGatherer, iceTransport: transports.iceTransport, dtlsTransport: transports.dtlsTransport, localCapabilities: localCapabilities, remoteCapabilities: remoteCapabilities, rtpSender: rtpSender, rtpReceiver: rtpReceiver, kind: kind, mid: mid, cname: cname, sendEncodingParameters: sendEncodingParameters, recvEncodingParameters: recvEncodingParameters }; // Start the RTCRtpReceiver now. The RTPSender is started in // setLocalDescription. self._transceive(self.transceivers[sdpMLineIndex], false, direction === 'sendrecv' || direction === 'sendonly'); } else if (description.type === 'answer' && !rejected) { transceiver = self.transceivers[sdpMLineIndex]; iceGatherer = transceiver.iceGatherer; iceTransport = transceiver.iceTransport; dtlsTransport = transceiver.dtlsTransport; rtpSender = transceiver.rtpSender; rtpReceiver = transceiver.rtpReceiver; sendEncodingParameters = transceiver.sendEncodingParameters; localCapabilities = transceiver.localCapabilities; self.transceivers[sdpMLineIndex].recvEncodingParameters = recvEncodingParameters; self.transceivers[sdpMLineIndex].remoteCapabilities = remoteCapabilities; self.transceivers[sdpMLineIndex].cname = cname; if ((isIceLite || isComplete) && cands.length) { iceTransport.setRemoteCandidates(cands); } if (!self.usingBundle || sdpMLineIndex === 0) { iceTransport.start(iceGatherer, remoteIceParameters, 'controlling'); dtlsTransport.start(remoteDtlsParameters); } self._transceive(transceiver, direction === 'sendrecv' || direction === 'recvonly', direction === 'sendrecv' || direction === 'sendonly'); if (rtpReceiver && (direction === 'sendrecv' || direction === 'sendonly')) { track = rtpReceiver.track; receiverList.push([track, rtpReceiver]); stream.addTrack(track); } else { // FIXME: actually the receiver should be created later. delete transceiver.rtpReceiver; } } });
sections.forEach(function(mediaSection, sdpMLineIndex) { var lines = SDPUtils.splitLines(mediaSection); var kind = SDPUtils.getKind(mediaSection); var rejected = SDPUtils.isRejected(mediaSection); var protocol = lines[0].substr(2).split(' ')[2]; var direction = SDPUtils.getDirection(mediaSection, sessionpart); var remoteMsid = SDPUtils.parseMsid(mediaSection); var mid = SDPUtils.getMid(mediaSection) || SDPUtils.generateIdentifier(); // Reject datachannels which are not implemented yet. if (kind === 'application' && protocol === 'DTLS/SCTP') { self.transceivers[sdpMLineIndex] = { mid: mid, isDatachannel: true }; return; } var transceiver; var iceGatherer; var iceTransport; var dtlsTransport; var rtpReceiver; var sendEncodingParameters; var recvEncodingParameters; var localCapabilities; var track; // FIXME: ensure the mediaSection has rtcp-mux set. var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection); var remoteIceParameters; var remoteDtlsParameters; if (!rejected) { remoteIceParameters = SDPUtils.getIceParameters(mediaSection, sessionpart); remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection, sessionpart); remoteDtlsParameters.role = 'client'; } recvEncodingParameters = SDPUtils.parseRtpEncodingParameters(mediaSection); var rtcpParameters = SDPUtils.parseRtcpParameters(mediaSection); var isComplete = SDPUtils.matchPrefix(mediaSection, 'a=end-of-candidates', sessionpart).length > 0; var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:') .map(function(cand) { return SDPUtils.parseCandidate(cand); }) .filter(function(cand) { return cand.component === '1' || cand.component === 1; }); if (description.type === 'offer' && !rejected) { transceiver = self.transceivers[sdpMLineIndex] || self._createTransceiver(kind); transceiver.mid = mid; if (!transceiver.iceGatherer) { transceiver.iceGatherer = usingBundle && sdpMLineIndex > 0 ? self.transceivers[0].iceGatherer : self._createIceGatherer(mid, sdpMLineIndex); } if (isComplete && (!usingBundle || sdpMLineIndex === 0)) { transceiver.iceTransport.setRemoteCandidates(cands); } localCapabilities = window.RTCRtpReceiver.getCapabilities(kind); // filter RTX until additional stuff needed for RTX is implemented // in adapter.js if (edgeVersion < 15019) { localCapabilities.codecs = localCapabilities.codecs.filter( function(codec) { return codec.name !== 'rtx'; }); } sendEncodingParameters = [{ ssrc: (2 * sdpMLineIndex + 2) * 1001 }]; if (direction === 'sendrecv' || direction === 'sendonly') { rtpReceiver = new window.RTCRtpReceiver(transceiver.dtlsTransport, kind); track = rtpReceiver.track; // FIXME: does not work with Plan B. if (remoteMsid) { if (!streams[remoteMsid.stream]) { streams[remoteMsid.stream] = new window.MediaStream(); Object.defineProperty(streams[remoteMsid.stream], 'id', { get: function() { return remoteMsid.stream; } }); } Object.defineProperty(track, 'id', { get: function() { return remoteMsid.track; } }); streams[remoteMsid.stream].addTrack(track); receiverList.push([track, rtpReceiver, streams[remoteMsid.stream]]); } else { if (!streams.default) { streams.default = new window.MediaStream(); } streams.default.addTrack(track); receiverList.push([track, rtpReceiver, streams.default]); } } transceiver.localCapabilities = localCapabilities; transceiver.remoteCapabilities = remoteCapabilities; transceiver.rtpReceiver = rtpReceiver; transceiver.rtcpParameters = rtcpParameters; transceiver.sendEncodingParameters = sendEncodingParameters; transceiver.recvEncodingParameters = recvEncodingParameters; // Start the RTCRtpReceiver now. The RTPSender is started in // setLocalDescription. self._transceive(self.transceivers[sdpMLineIndex], false, direction === 'sendrecv' || direction === 'sendonly'); } else if (description.type === 'answer' && !rejected) { if (usingBundle && sdpMLineIndex > 0) { self._disposeIceAndDtlsTransports(sdpMLineIndex); self.transceivers[sdpMLineIndex].iceGatherer = self.transceivers[0].iceGatherer; self.transceivers[sdpMLineIndex].iceTransport = self.transceivers[0].iceTransport; self.transceivers[sdpMLineIndex].dtlsTransport = self.transceivers[0].dtlsTransport; if (self.transceivers[sdpMLineIndex].rtpSender) { self.transceivers[sdpMLineIndex].rtpSender.setTransport( self.transceivers[0].dtlsTransport); } if (self.transceivers[sdpMLineIndex].rtpReceiver) { self.transceivers[sdpMLineIndex].rtpReceiver.setTransport( self.transceivers[0].dtlsTransport); } } transceiver = self.transceivers[sdpMLineIndex]; iceGatherer = transceiver.iceGatherer; iceTransport = transceiver.iceTransport; dtlsTransport = transceiver.dtlsTransport; rtpReceiver = transceiver.rtpReceiver; sendEncodingParameters = transceiver.sendEncodingParameters; localCapabilities = transceiver.localCapabilities; self.transceivers[sdpMLineIndex].recvEncodingParameters = recvEncodingParameters; self.transceivers[sdpMLineIndex].remoteCapabilities = remoteCapabilities; self.transceivers[sdpMLineIndex].rtcpParameters = rtcpParameters; if ((isIceLite || isComplete) && cands.length) { iceTransport.setRemoteCandidates(cands); } if (!usingBundle || sdpMLineIndex === 0) { iceTransport.start(iceGatherer, remoteIceParameters, 'controlling'); dtlsTransport.start(remoteDtlsParameters); } self._transceive(transceiver, direction === 'sendrecv' || direction === 'recvonly', direction === 'sendrecv' || direction === 'sendonly'); if (rtpReceiver && (direction === 'sendrecv' || direction === 'sendonly')) { track = rtpReceiver.track; if (remoteMsid) { if (!streams[remoteMsid.stream]) { streams[remoteMsid.stream] = new window.MediaStream(); } streams[remoteMsid.stream].addTrack(track); receiverList.push([track, rtpReceiver, streams[remoteMsid.stream]]); } else { if (!streams.default) { streams.default = new window.MediaStream(); } streams.default.addTrack(track); receiverList.push([track, rtpReceiver, streams.default]); } } else { // FIXME: actually the receiver should be created later. delete transceiver.rtpReceiver; } } });