Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to make chrome to receive modified sdp from webrtcpeer.js? #13

Open
mkvalakkunja opened this issue Aug 25, 2020 · 0 comments
Open

Comments

@mkvalakkunja
Copy link

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}

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, 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.


in console I get:

This appears to be Chrome
WebRtcPeer.js:30 Chrome: using SDP PlanB
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant