问题描述
我正在使用https://github.com/chapin666/kurento-group-call-node进行kurento组调用nodejs项目。我想将修改后的sdp发送到Chrome。但是,它没有收到在sdp_pattern.txt
中更改的修改后的SDP。 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}
webrtcpeer.js
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,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);
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',callback);
}
inherits(WebRtcPeerRecvonly,WebRtcPeer);
function WebRtcPeerSendonly(options,callback) {
if (!(this instanceof WebRtcPeerSendonly)) {
return new WebRtcPeerSendonly(options,callback);
}
WebRtcPeerSendonly.super_.call(this,'sendonly',callback);
}
inherits(WebRtcPeerSendonly,WebRtcPeer);
function WebRtcPeerSendrecv(options,callback) {
if (!(this instanceof WebRtcPeerSendrecv)) {
return new WebRtcPeerSendrecv(options,callback);
}
WebRtcPeerSendrecv.super_.call(this,'sendrecv',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;
我想提供上述SDP,但是chrome正在提供其默认SDP。另外,我已经在webrtcpeer.js中更新为统一计划,但是chrome在planb上运行。
在控制台中,我得到:
This appears to be Chrome
WebRtcPeer.js:30 Chrome: using SDP PlanB
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)