I want to send modified sdp to chrome. However it is not receiving that modified sdp which is changed in sdp_pattern.txt
v=0
o=mozilla...THIS_IS_SDPARTA-65.0 2266787997050267451 0 IN IP4 0.0.0.0
s=-
t=0 0
a=sendrecv
a=fingerprint:sha-256 79:3F:28:AD:AE:2A:B7:05:60:8D:53:CF:88:3B:88:32:9A:04:71:04:A3:B9:F3:08:D2:41:0C:38:95:41:21:F5
a=group:BUNDLE 0 1 2 // Specify the mids to bundle
a=ice-options:trickle
a=msid-semantic:WMS *
m=audio 63259 UDP/TLS/RTP/SAVPF 109 9 0 8 101 // For 1st audio track
c=IN IP4 192.168.0.12
a=candidate:0 1 UDP 2122252543 192.168.0.12 63259 typ host
... some candidates
a=candidate:1 2 TCP 2105524478 192.168.0.12 9 typ host tcptype active
a=sendrecv
a=end-of-candidates
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
... some extmap
a=fmtp:109 maxplaybackrate=48000;stereo=1;useinbandfec=1
a=fmtp:101 0-15
a=ice-pwd:foo
a=ice-ufrag:bar
a=mid:0 // Identifier of m-line. Thus each m-line has one mid.
a=msid:{aaaaa} {bbbbb} // Media Stream ID. There is msid in each m-line.
a=rtcp-mux
a=rtpmap:109 opus/48000/2
... some rtpmap
a=setup:actpass
a=ssrc:45563795 cname:{ccccc} // Except simulcast case, there is one ssrc in each m-line
m=audio 63259 UDP/TLS/RTP/SAVPF 109 9 0 8 101 // For 2nd audio track
c=IN IP4 192.168.0.12
a=sendrecv
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
... some extmap
a=fmtp:109 maxplaybackrate=48000;stereo=1;useinbandfec=1
a=fmtp:101 0-15
a=ice-pwd:foo
a=ice-ufrag:bar
a=mid:1 // Identifier of m-line. Thus each m-line has one mid.
a=msid:{ddddd} {eeeee} // Media Stream ID. There is msid in each m-line.
a=rtcp-mux
a=rtpmap:109 opus/48000/2
... some rtpmap
a=setup:actpass
a=ssrc:12345678 cname:{fffff} // Except simulcast case, there is one ssrc in each m-line
m=video 63259 UDP/TLS/RTP/SAVPF 120 121 126 97 // For video track
c=IN IP4 192.168.0.12
a=sendrecv
a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid
... some extmap
a=fmtp:126 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1
... some fmtp
a=ice-pwd:foo
a=ice-ufrag:bar
a=mid:2
a=msid:{ggggg} {hhhhh}
a=rtcp-fb:120 nack
... some rtcp-fb
a=rtcp-mux
a=rtpmap:120 VP8/90000
... some rtpmap
a=setup:actpass
a=ssrc:1242852923 cname:{iiiii}
var freeice = require('freeice');
var inherits = require('inherits');
var UAParser = require('ua-parser-js');
var uuid = require('uuid');
var hark = require('hark');
var EventEmitter = require('events').EventEmitter;
var recursive = require('merge').recursive.bind(undefined, true);
var sdpTranslator = require('sdp-translator');
try {
require('kurento-browser-extensions');
} catch (error) {
if (typeof getScreenConstraints === 'undefined') {
console.warn('screen sharing is not available');
getScreenConstraints = function getScreenConstraints(sendSource, callback) {
callback(new Error('This library is not enabled for screen sharing'));
};
}
}
var MEDIA_CONSTRAINTS = {
audio: true,
video: {
width: 640,
framerate: 15
}
};
var ua = window && window.navigator ? window.navigator.userAgent : '';
var parser = new UAParser(ua);
var browser = parser.getBrowser();
var usePlanB = false;
if (browser.name === 'Chrome' || browser.name === 'Chromium') {
console.log(browser.name + ': using SDP PlanB');
usePlanB = true;
}
function noop(error) {
if (error)
console.error(error);
}
function trackStop(track) {
track.stop && track.stop();
}
function streamStop(stream) {
stream.getTracks().forEach(trackStop);
}
var dumpSDP = function (description) {
if (typeof description === 'undefined' || description === null) {
return '';
}
return 'type: ' + description.type + '\r\n' + description.sdp;
};
function bufferizeCandidates(pc, onerror) {
var candidatesQueue = [];
pc.addEventListener('signalingstatechange', function () {
if (this.signalingState === 'stable') {
while (candidatesQueue.length) {
var entry = candidatesQueue.shift();
this.addIceCandidate(entry.candidate, entry.callback, entry.callback);
}
}
});
return function (candidate, callback) {
callback = callback || onerror;
switch (pc.signalingState) {
case 'closed':
callback(new Error('PeerConnection object is closed'));
break;
case 'stable':
if (pc.remoteDescription) {
pc.addIceCandidate(candidate, callback, callback);
break;
}
default:
candidatesQueue.push({
candidate: candidate,
callback: callback
});
}
};
}
function removeFIDFromOffer(sdp) {
var n = sdp.indexOf('a=ssrc-group:FID');
if (n > 0) {
return sdp.slice(0, n);
} else {
return sdp;
}
}
function getSimulcastInfo(videoStream) {
var videoTracks = videoStream.getVideoTracks();
if (!videoTracks.length) {
console.warn('No video tracks available in the video stream');
return '';
}
var lines = [
'a=x-google-flag:conference',
'a=ssrc-group:SIM 1 2 3',
'a=ssrc:1 cname:localVideo',
'a=ssrc:1 msid:' + videoStream.id + ' ' + videoTracks[0].id,
'a=ssrc:1 mslabel:' + videoStream.id,
'a=ssrc:1 label:' + videoTracks[0].id,
'a=ssrc:2 cname:localVideo',
'a=ssrc:2 msid:' + videoStream.id + ' ' + videoTracks[0].id,
'a=ssrc:2 mslabel:' + videoStream.id,
'a=ssrc:2 label:' + videoTracks[0].id,
'a=ssrc:3 cname:localVideo',
'a=ssrc:3 msid:' + videoStream.id + ' ' + videoTracks[0].id,
'a=ssrc:3 mslabel:' + videoStream.id,
'a=ssrc:3 label:' + videoTracks[0].id
];
lines.push('');
return lines.join('\n');
}
function WebRtcPeer(mode, options, callback) {
if (!(this instanceof WebRtcPeer)) {
return new WebRtcPeer(mode, options, callback);
}
WebRtcPeer.super_.call(this);
if (options instanceof Function) {
callback = options;
options = undefined;
}
options = options || {};
callback = (callback || noop).bind(this);
var self = this;
var localVideo = options.localVideo;
var remoteVideo = options.remoteVideo;
var videoStream = options.videoStream;
var audioStream = options.audioStream;
var mediaConstraints = options.mediaConstraints;
var connectionConstraints = options.connectionConstraints;
var pc = options.peerConnection;
var sendSource = options.sendSource || 'webcam';
var dataChannelConfig = options.dataChannelConfig;
var useDataChannels = options.dataChannels || false;
var dataChannel;
var guid = uuid.v4();
var configuration = recursive({ iceServers: freeice() }, options.configuration);
var onicecandidate = options.onicecandidate;
if (onicecandidate)
this.on('icecandidate', onicecandidate);
var oncandidategatheringdone = options.oncandidategatheringdone;
if (oncandidategatheringdone) {
this.on('candidategatheringdone', oncandidategatheringdone);
}
var simulcast = options.simulcast;
var multistream = options.multistream;
var interop = new sdpTranslator.Interop();
var candidatesQueueOut = [];
var candidategatheringdone = false;
Object.defineProperties(this, {
'peerConnection': {
get: function () {
return pc;
}
},
'id': {
value: options.id || guid,
writable: false
},
'remoteVideo': {
get: function () {
return remoteVideo;
}
},
'localVideo': {
get: function () {
return localVideo;
}
},
'dataChannel': {
get: function () {
return dataChannel;
}
},
'currentFrame': {
get: function () {
if (!remoteVideo)
return;
if (remoteVideo.readyState < remoteVideo.HAVE_CURRENT_DATA)
throw new Error('No video stream data available');
var canvas = document.createElement('canvas');
canvas.width = remoteVideo.videoWidth;
canvas.height = remoteVideo.videoHeight;
canvas.getContext('2d').drawImage(remoteVideo, 0, 0);
return canvas;
}
}
});
if (!pc) {
pc = new RTCPeerConnection ({ sdpSemantics : "unified-plan" });
if (useDataChannels && !dataChannel) {
var dcId = 'WebRtcPeer-' + self.id;
var dcOptions = undefined;
if (dataChannelConfig) {
dcId = dataChannelConfig.id || dcId;
dcOptions = dataChannelConfig.options;
}
dataChannel = pc.createDataChannel(dcId, dcOptions);
if (dataChannelConfig) {
dataChannel.onopen = dataChannelConfig.onopen;
dataChannel.onclose = dataChannelConfig.onclose;
dataChannel.onmessage = dataChannelConfig.onmessage;
dataChannel.onbufferedamountlow = dataChannelConfig.onbufferedamountlow;
dataChannel.onerror = dataChannelConfig.onerror || noop;
}
}
}
pc.addEventListener('icecandidate', function (event) {
var candidate = event.candidate;
if (EventEmitter.listenerCount(self, 'icecandidate') || EventEmitter.listenerCount(self, 'candidategatheringdone')) {
if (candidate) {
var cand;
if (multistream && usePlanB) {
cand = interop.candidateToUnifiedPlan(candidate);
} else {
cand = candidate;
}
self.emit('icecandidate', cand);
candidategatheringdone = false;
} else if (!candidategatheringdone) {
self.emit('candidategatheringdone');
candidategatheringdone = true;
}
} else if (!candidategatheringdone) {
candidatesQueueOut.push(candidate);
if (!candidate)
candidategatheringdone = true;
}
});
pc.onaddstream = options.onaddstream;
pc.onnegotiationneeded = options.onnegotiationneeded;
this.on('newListener', function (event, listener) {
if (event === 'icecandidate' || event === 'candidategatheringdone') {
while (candidatesQueueOut.length) {
var candidate = candidatesQueueOut.shift();
if (!candidate === (event === 'candidategatheringdone')) {
listener(candidate);
}
}
}
});
var addIceCandidate = bufferizeCandidates(pc);
this.addIceCandidate = function (iceCandidate, callback) {
var candidate;
if (multistream && usePlanB) {
candidate = interop.candidateToPlanB(iceCandidate);
} else {
candidate = new RTCIceCandidate(iceCandidate);
}
console.log('ICE candidate received');
callback = (callback || noop).bind(this);
addIceCandidate(candidate, callback);
};
this.generateOffer = function (callback) {
callback = callback.bind(this);
var offerAudio = true;
var offerVideo = true;
if (mediaConstraints) {
offerAudio = typeof mediaConstraints.audio === 'boolean' ? mediaConstraints.audio : true;
offerVideo = typeof mediaConstraints.video === 'boolean' ? mediaConstraints.video : true;
}
var browserDependantConstraints = browser.name === 'Firefox' && browser.version > 34 ? {
offerToReceiveAudio: mode !== 'sendonly' && offerAudio,
offerToReceiveVideo: mode !== 'sendonly' && offerVideo
} : {
mandatory: {
OfferToReceiveAudio: mode !== 'sendonly' && offerAudio,
OfferToReceiveVideo: mode !== 'sendonly' && offerVideo
},
optional: [{ DtlsSrtpKeyAgreement: true }]
};
var constraints = recursive(browserDependantConstraints, connectionConstraints);
console.log('constraints: ' + JSON.stringify(constraints));
pc.createOffer(constraints).then(function (offer) {
console.log('Created SDP offer');
offer = mangleSdpToAddSimulcast(offer);
return pc.setLocalDescription(offer);
}).then(function () {
var localDescription = pc.localDescription;
console.log('Local description set', localDescription.sdp);
if (multistream && usePlanB) {
localDescription = interop.toUnifiedPlan(localDescription);
console.log('offer::origPlanB->UnifiedPlan', dumpSDP(localDescription));
}
callback(null, localDescription.sdp, self.processAnswer.bind(self));
}).catch(callback);
};
this.getLocalSessionDescriptor = function () {
return pc.localDescription;
};
this.getRemoteSessionDescriptor = function () {
return pc.remoteDescription;
};
function setRemoteVideo() {
if (remoteVideo) {
var stream = pc.getRemoteStreams()[0];
var url = stream;
remoteVideo.pause();
remoteVideo.srcObject = url;
remoteVideo.load();
console.log('Remote URL:', url);
}
}
this.showLocalVideo = function () {
localVideo.srcObject = videoStream;
localVideo.muted = true;
};
this.send = function (data) {
if (dataChannel && dataChannel.readyState === 'open') {
dataChannel.send(data);
} else {
console.warn('Trying to send data over a non-existing or closed data channel');
}
};
this.processAnswer = function (sdpAnswer, callback) {
callback = (callback || noop).bind(this);
var answer = new RTCSessionDescription({
type: 'answer',
sdp: sdpAnswer
});
if (multistream && usePlanB) {
var planBAnswer = interop.toPlanB(answer);
console.log('asnwer::planB', dumpSDP(planBAnswer));
answer = planBAnswer;
}
console.log('SDP answer received, setting remote description');
if (pc.signalingState === 'closed') {
return callback('PeerConnection is closed');
}
pc.setRemoteDescription(answer, function () {
setRemoteVideo();
callback();
}, callback);
};
this.processOffer = function (sdpOffer, callback) {
callback = callback.bind(this);
var offer = new RTCSessionDescription({
type: 'offer',
sdp: sdpOffer
});
if (multistream && usePlanB) {
var planBOffer = interop.toPlanB(offer);
console.log('offer::planB', dumpSDP(planBOffer));
offer = planBOffer;
}
console.log('SDP offer received, setting remote description');
if (pc.signalingState === 'closed') {
return callback('PeerConnection is closed');
}
pc.setRemoteDescription(offer).then(function () {
return setRemoteVideo();
}).then(function () {
return pc.createAnswer();
}).then(function (answer) {
answer = mangleSdpToAddSimulcast(answer);
console.log('Created SDP answer');
return pc.setLocalDescription(answer);
}).then(function () {
var localDescription = pc.localDescription;
if (multistream && usePlanB) {
localDescription = interop.toUnifiedPlan(localDescription);
console.log('answer::origPlanB->UnifiedPlan', dumpSDP(localDescription));
}
console.log('Local description set', localDescription.sdp);
callback(null, localDescription.sdp);
}).catch(callback);
};
function mangleSdpToAddSimulcast(answer) {
if (simulcast) {
if (browser.name === 'Chrome' || browser.name === 'Chromium') {
console.log('Adding multicast info');
answer = new RTCSessionDescription({
'type': answer.type,
'sdp': removeFIDFromOffer(answer.sdp) + getSimulcastInfo(videoStream)
});
} else {
console.warn('Simulcast is only available in Chrome browser.');
}
}
return answer;
}
function start() {
if (pc.signalingState === 'closed') {
callback('The peer connection object is in "closed" state. This is most likely due to an invocation of the dispose method before accepting in the dialogue');
}
if (videoStream && localVideo) {
self.showLocalVideo();
}
if (videoStream) {
pc.addStream(videoStream);
}
if (audioStream) {
pc.addStream(audioStream);
}
var browser = parser.getBrowser();
if (mode === 'sendonly' && (browser.name === 'Chrome' || browser.name === 'Chromium') && browser.major === 39) {
mode = 'sendrecv';
}
callback();
}
if (mode !== 'recvonly' && !videoStream && !audioStream) {
function getMedia(constraints) {
if (constraints === undefined) {
constraints = MEDIA_CONSTRAINTS;
}
getUserMedia(constraints, function (stream) {
videoStream = stream;
start();
}, callback);
}
if (sendSource === 'webcam') {
getMedia(mediaConstraints);
} else {
getScreenConstraints(sendSource, function (error, constraints_) {
if (error)
return callback(error);
constraints = [mediaConstraints];
constraints.unshift(constraints_);
getMedia(recursive.apply(undefined, constraints));
}, guid);
}
} else {
setTimeout(start, 0);
}
this.on('_dispose', function () {
if (localVideo) {
localVideo.pause();
localVideo.srcObject = '';
localVideo.load();
localVideo.muted = false;
}
if (remoteVideo) {
remoteVideo.pause();
remoteVideo.srcObject = '';
remoteVideo.load();
}
self.removeAllListeners();
if (window.cancelChooseDesktopMedia !== undefined) {
window.cancelChooseDesktopMedia(guid);
}
});
}
inherits(WebRtcPeer, EventEmitter);
function createEnableDescriptor(type) {
var method = 'get' + type + 'Tracks';
return {
enumerable: true,
get: function () {
if (!this.peerConnection)
return;
var streams = this.peerConnection.getLocalStreams();
if (!streams.length)
return;
for (var i = 0, stream; stream = streams[i]; i++) {
var tracks = stream[method]();
for (var j = 0, track; track = tracks[j]; j++)
if (!track.enabled)
return false;
}
return true;
},
set: function (value) {
function trackSetEnable(track) {
track.enabled = value;
}
this.peerConnection.getLocalStreams().forEach(function (stream) {
stream[method]().forEach(trackSetEnable);
});
}
};
}
Object.defineProperties(WebRtcPeer.prototype, {
'enabled': {
enumerable: true,
get: function () {
return this.audioEnabled && this.videoEnabled;
},
set: function (value) {
this.audioEnabled = this.videoEnabled = value;
}
},
'audioEnabled': createEnableDescriptor('Audio'),
'videoEnabled': createEnableDescriptor('Video')
});
WebRtcPeer.prototype.getLocalStream = function (index) {
if (this.peerConnection) {
return this.peerConnection.getLocalStreams()[index || 0];
}
};
WebRtcPeer.prototype.getRemoteStream = function (index) {
if (this.peerConnection) {
return this.peerConnection.getRemoteStreams()[index || 0];
}
};
WebRtcPeer.prototype.dispose = function () {
console.log('Disposing WebRtcPeer');
var pc = this.peerConnection;
var dc = this.dataChannel;
try {
if (dc) {
if (dc.signalingState === 'closed')
return;
dc.close();
}
if (pc) {
if (pc.signalingState === 'closed')
return;
pc.getLocalStreams().forEach(streamStop);
pc.close();
}
} catch (err) {
console.warn('Exception disposing webrtc peer ' + err);
}
this.emit('_dispose');
};
function WebRtcPeerRecvonly(options, callback) {
if (!(this instanceof WebRtcPeerRecvonly)) {
return new WebRtcPeerRecvonly(options, callback);
}
WebRtcPeerRecvonly.super_.call(this, 'recvonly', options, callback);
}
inherits(WebRtcPeerRecvonly, WebRtcPeer);
function WebRtcPeerSendonly(options, callback) {
if (!(this instanceof WebRtcPeerSendonly)) {
return new WebRtcPeerSendonly(options, callback);
}
WebRtcPeerSendonly.super_.call(this, 'sendonly', options, callback);
}
inherits(WebRtcPeerSendonly, WebRtcPeer);
function WebRtcPeerSendrecv(options, callback) {
if (!(this instanceof WebRtcPeerSendrecv)) {
return new WebRtcPeerSendrecv(options, callback);
}
WebRtcPeerSendrecv.super_.call(this, 'sendrecv', options, callback);
}
inherits(WebRtcPeerSendrecv, WebRtcPeer);
function harkUtils(stream, options) {
return hark(stream, options);
}
exports.bufferizeCandidates = bufferizeCandidates;
exports.WebRtcPeerRecvonly = WebRtcPeerRecvonly;
exports.WebRtcPeerSendonly = WebRtcPeerSendonly;
exports.WebRtcPeerSendrecv = WebRtcPeerSendrecv;
exports.hark = harkUtils;
I want to offer above mentioned sdp , however chrome is offering its defualt sdp. Also I have updated to unified plan in webrtcpeer.js , however chrome is running on planb.
This appears to be Chrome
WebRtcPeer.js:30 Chrome: using SDP PlanB