如何使Chrome从webrtcpeer.js接收修改过的sdp?

问题描述

我正在使用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 (将#修改为@)