1

I have built a vuejs super basic One - on - One webRTC video chat. I use PubNub for Signaling, and Xirsys for ICE.

But I get this error on the 'call'. However the page does load fine.

Errors InvalidStateError: Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer

<template>
    <div>
        <video ref="localVideo" id="localVideo" autoplay playsinline></video>
        <br />
        <video ref="remoteVideo" id="remoteVideo" autoplay playsinline></video>
        <br />
        <button type="button" class="btn btn-light btn-lg btn-block" @click="callClick">Call</button>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                localVideo: {type: Object},
                remoteVideo: {type: Object},
                iceServers: [],
                myPeerConnection: {type: Object},
                channelName: ['testtesttest'],
                mediaConstraintsLocal: { 
                    video: {
                        width: { min: 640, ideal: 640 },
                        height: { min: 480, ideal: 480 },
                        aspectRatio: { ideal: 1.7777777778 },
                        frameRate: { min: 10, ideal: 30},
                        facingMode: { ideal: 'user' }
                    },
                    audio: {
                        sampleSize: 16,
                        channelCount: 2,
                        volume: 0
                    }
                },
                mediaConstraintsRemote: {
                    video: {
                        width: { min: 640, ideal: 640 },
                        height: { min: 480, ideal: 480 },
                        aspectRatio: { ideal: 1.7777777778 },
                        frameRate: { min: 10, ideal: 30},
                        facingMode: { ideal: 'user' }
                    },
                    audio: {
                        sampleSize: 16,
                        channelCount: 2,
                        volume: 0
                    }
                },
            };
        },

        mounted() {
            var _this = this;

            // LOAD PUBNUB
            this.pubnub.load();

            // SUBSCRIBE TO PUBNUB CHANNEL
            this.$pubnub.subscribe({
                channels: this.channelName
            });

            // REGISTER VIDEO DISPLAYS
            _this.localVideo = _this.$refs.localVideo;
            _this.remoteVideo = _this.$refs.remoteVideo;

            // GET PEER CONNECTION AND XIRSYS ICE SERVERS
            this.createPeerConnection();

            // GET USER MEDIA SET CONSTRAINTS AND STREAM
            navigator.mediaDevices.getUserMedia(this.mediaConstraintsLocal)
            .then(stream => {
                window.stream = stream;
                this.localVideo.srcObject = stream;
                this.localVideo.play();
            })
            .catch(function(errors) {
                console.log('%c Errors ' + errors, 'background: #222; color: #bada55');
            });

            // REGISTER PUBNUB LISTENER
            _this.$pubnub.addListener({
                // PUBNUB ON MESSAGE
                message: function(message) {
                    // PUBNUB LISTENING CHANNEL
                    this.channelName = message.channel;

                    // ICE OFFER
                    if (message.ice) {
                        _this.myPeerConnection.addIceCandidate(new RTCIceCandidate(message.ice))
                        .catch(function(errors) {
                            console.log('%c Errors ' + errors, 'background: #222; color: #bada55');
                        });
                    }

                    // SDP OFFER
                    if (message.message.type == 'offer') {
                        console.log(message.message.type + ' ON ' + message.channel);
                        console.log('SDP OFFER ' + _this.myPeerConnection.signalingState);

                        _this.myPeerConnection.setRemoteDescription(new RTCSessionDescription(message.message))
                        .then(function() {
                            return navigator.mediaDevices.getUserMedia(_this.mediaConstraintsRemote);
                        })
                        // GET VIDEO ADD VIDEO OBJECT
                        .then(function(stream) {
                            _this.remoteVideo.srcObject = stream;
                            _this.remoteVideo.play();
                            return _this.myPeerConnection.addStream(stream);
                        })
                        // PUBLISH ANSWER
                        .then(function() {
                            _this.myPeerConnection.createAnswer()
                            .then(function(answer) {
                                return _this.myPeerConnection.setLocalDescription(answer);
                            })
                            .then(function() {
                                // PUBNUB PUBLISH LOCAL DESCRIPTION AS ANSWER
                                console.log('PUBNUB PUBLISH LOCAL DESCRIPTION AS ANSWER ' + _this.myPeerConnection.signalingState);
                                _this.$pubnub.publish({
                                    message: _this.myPeerConnection.localDescription,
                                    channel: _this.channelName
                                });
                            });
                        })
                        .catch(function(errors) {
                            console.log('%c Errors ' + errors, 'background: #222; color: #bada55');
                        });
                    }

                    // // SDP ANSWER
                    if (message.message.type == 'answer') {
                        console.log(message.message.type + ' ON ' + message.channel);
                        console.log('SDP ANSWER ' + _this.myPeerConnection.signalingState);

                        _this.myPeerConnection.setRemoteDescription(new RTCSessionDescription(message.message))
                        .then(function() {
                            console.log('SET REMOTE DESCRIPTION ' + _this.myPeerConnection.signalingState);
                            return navigator.mediaDevices.getUserMedia(_this.mediaConstraintsRemote);
                        })
                        // ADD VIDEO OBJECT
                        .then(function(stream) {
                            _this.remoteVideo.srcObject = stream;
                            _this.remoteVideo.play();
                            return _this.myPeerConnection.addStream(stream);
                        })
                        .catch(function(errors) {
                            console.log('%c Errors ' + errors, 'background: #222; color: #bada55');
                        });
                    }
                }
            });
        },

        methods: {
            callClick() {
                console.log('call');
                var _this = this;

                // GET PEER CONNECTION AND XIRSYS ICE SERVERS
                this.createPeerConnection();

                // myPeerConnection
                console.log('this.myPeerConnection ' + this.myPeerConnection);

                // CREATE OFFER
                console.log('CREATE OFFER ' + this.myPeerConnection.signalingState);

                // ADD MEDIA TRACKS
                stream.getTracks().forEach(track => this.myPeerConnection.addTrack(track, stream));

                this.myPeerConnection.createOffer()
                .then(function(offer) {
                    console.log('SET LOCAL DESCRIPTION ' + _this.myPeerConnection.signalingState);
                    return _this.myPeerConnection.setLocalDescription(offer);
                })
                // PUBLISH OFFER
                .then(function() {
                    // PUBNUB PUBLISH LOCAL DESCRIPTION AS OFFER
                    console.log('PUBNUB PUBLISH LOCAL DESCRIPTION AS OFFER ' + _this.myPeerConnection.signalingState);
                    _this.$pubnub.publish({
                        message: _this.myPeerConnection.localDescription,
                        channel: _this.channelName
                    });
                })
                .catch(function(errors) {
                    console.log('%c Errors ' + errors, 'background: #222; color: #bada55');
                });
            },

            createPeerConnection() {
                // GET XIRSYS ICE SERVERS
                Vue.http.options.root = '/root';
                Vue.http.headers.common['Authorization'] = 'Basic ' + btoa('ive:648');
                this.$http.put('https://global.xirsys.net/_turn/Default/')
                .then(response => (this.iceServers = response.data.v))
                .then(function() {
                    console.log('ICE SERVERS ' + this.iceServers);
                    this.myPeerConnection = new RTCPeerConnection(this.iceServers);
                })
                .catch(function(errors) {
                    console.log('%c Errors ' + errors, 'background: #222; color: #bada55');
                });
            }
        }
    }
</script>

Any Help would be greatly appreciated.

  • Hi! There may be some handshake step that is misaligned. I see some differences from a known-working WebRTC SDK: https://github.com/stephenlb/webrtc-sdk/blob/master/js/webrtc.js#L145 the `onicecandidate` event in your code is missing. It will be a good start to review this SDK https://github.com/stephenlb/webrtc-sdk/blob/master/js/webrtc.js and note that there is a V2 which uses ES6 and is less stable for some reason. So keep with V1 which is the linked file. Docs for V1 and V2 are here: https://github.com/stephenlb/webrtc-sdk – Stephen Blum Jun 27 '18 at 16:20
  • hi @StephenBlum I am currently using a modified version if your webrtc.js the v1 version. It's really good. I wanted to move to PubNub v4 and use Vue for more fancy UI. Camera Switching, and more controls of the video sessions. After a lot of trial and error I've noticed I am not using RTCIceCandidate in my script. After reading this doc, https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addIceCandidate I do not know where to put the RTCIceCandidate bits in their example. Any Help? – Benjamin Schroeder Jun 27 '18 at 18:31
  • RTCIceCandidate is a nice wrapper object which is optional and I've not seen a successful webrtc app without it yet. – Stephen Blum Jun 27 '18 at 18:59
  • it is also possible to receive events out-of-order when publising on PubNub as PubNub doesn't know what order the messages should be delivered in. You may need to add a `sequenceID` number to your payload and ensure that the receiving target client is following the sequence. However this may not be your issue because you'd notice that only sometimes the WebRTC session would fail due to the event race conditions. So you are likely going to find an effort in using some of the framework of RTC to capture the SDP session details and ICE packets. – Stephen Blum Jun 27 '18 at 19:01

0 Answers0