WebRTC无法在控制台上的RTCPeerConnection上执行'addIceCandidate'错误,但仍可以显示远程和本地视频 [英] WebRTC getting Failed to execute 'addIceCandidate' on RTCPeerConnection error on console but can still display remote and local videos

查看:89
本文介绍了WebRTC无法在控制台上的RTCPeerConnection上执行'addIceCandidate'错误,但仍可以显示远程和本地视频的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用webRTC连接两个对等方.我能够正确显示本地和远程视频,但是一旦出现远程视频,候选对象将变为 null ,并在控制台上记录以下错误消息.

I am trying to connect two peers using webRTC. I am able to display both local and remote videos correctly but as soon as the remote video appears, the candidate object becomes null and on the console it logs the following error message.

TypeError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': Candidate missing values for both sdpMid and sdpMLineIndex

我正在使用两台单独的笔记本电脑来测试连接,并且由于同时显示了远程视频和本地视频,我认为我已经成功连接了两个对等设备,但是由于出现错误消息,我不确定.

I am using two separate laptops to test the connection and since both remote and local videos are showing, I think that I have connected two peers successfully but I'm not sure because of the error message.

关于为什么会发生这种情况的任何想法?我什至成功地连接了两个同行?

Any idea on why this might be happening? Am I even successfully connecting the two peers?

下面是代码.

谢谢!

前端

import React, { Component } from 'react';
import io from 'socket.io-client';
class App extends Component {
  constructor(props) {
    super(props);
    this.room = 'test-room';
    this.socket = io.connect('http://localhost:5000');
    this.localPeerConnection = new RTCPeerConnection({
      iceServers: [
        {
          urls: 'stun:stun.l.google.com:19302'
        }
      ]
    });
    this.remotePeerConnection = new RTCPeerConnection({
      iceServers: [
        {
          urls: 'stun:stun.l.google.com:19302'
        }
      ]
    });
  };

  componentDidMount() {
    this.socket.on('connect', () => {
      this.socket.emit('join', this.room, err => {
        if (err) {
          console.error(err);
        } else {
          this.socket.on('offer', offer => {
            console.log('OFFER RECEIVED: ', offer);
            this.createAnswer(offer);
          });

          this.socket.on('candidate', candidate => {
            console.log('CANDIDATE RECEIVED', candidate);
            this.localPeerConnection.addIceCandidate(candidate).catch(error => console.error(error));
            this.remotePeerConnection.addIceCandidate(candidate).catch(error => console.error(error));
          });

          this.socket.on('answer', answer => {
            console.log('ANSWER RECEIVED:', answer);
            this.localPeerConnection.setRemoteDescription(answer);
          });
        }
      });
    });
  }

  startCall = async () => {
    this.localPeerConnection.onicecandidate = e => {
      const iceCandidate = e.candidate;
      this.socket.emit('candidate', { room: this.room, candidate: iceCandidate });
      console.log('candidate generated', e.candidate);
    };

    this.localPeerConnection.ontrack = e => {
      this.remoteVideo.srcObject = e.streams[0];
      console.log('REMOTE STREAM?: ', e.streams[0]);
    };

    try {
      const stream = await navigator.mediaDevices.getUserMedia({ video: { width: 150, height: 150 }, audio: false });
      for (const track of stream.getTracks()) {
        this.localPeerConnection.addTrack(track, stream);
      }

      this.localVideo.srcObject = stream;
      console.log('LOCAL STREAMS: ', this.localPeerConnection.getLocalStreams())

      return this.createOffer();
    } catch (error) {
      console.error(error);
    }
  }

  createOffer = async () => {
    try {
      const offer = await this.localPeerConnection.createOffer();
      await this.localPeerConnection.setLocalDescription(offer);
      await this.remotePeerConnection.setRemoteDescription(offer);

      this.socket.emit('offer', { room: this.room, offer });
      console.log('SENDING OFFER: ', offer);
    } catch (error) {
      console.error(error);
    }
  }

  createAnswer = async description => {
    this.remotePeerConnection.onicecandidate = e => {
      const iceCandidate = e.candidate;
      this.socket.emit('candidate', { room: this.room, candidate: iceCandidate });
      console.log('candidate generated', e.candidate);
    };

    this.remotePeerConnection.ontrack = e => {
      this.remoteVideo.srcObject = e.streams[0];
    };

    this.remotePeerConnection.setRemoteDescription(description)
      .then(() => navigator.mediaDevices.getUserMedia({ video: { width: 150, height: 150 }, audio: false }))
      .then(stream => {
        for (const track of stream.getTracks()) {
          this.remotePeerConnection.addTrack(track, stream);
        }

        this.localVideo.srcObject = stream;

        return this.remotePeerConnection.createAnswer();
      })
      .then(answer => {
        this.remotePeerConnection.setLocalDescription(answer);
        return answer;
      })
      .then(answer => {
        this.socket.emit('answer', { room: this.room, answer });
        console.log('SENDING ANSWER: ', answer);
      })
      .catch(error => console.error(error))
  }

  render() {
    return (
      <div>
        <h1>Webrtc</h1>
        <div>
          <button onClick={this.startCall}>CALL</button>
        </div>
        <div style={{ display: 'flex' }}>
          <div>
            <video id='localVideo' autoPlay muted playsInline ref={ref => this.localVideo = ref} />
            <p>LOCAL VIDEO</p>
          </div>
          <div>
            <video id='remoteVideo' autoPlay muted playsInline ref={ref => this.remoteVideo = ref} />
            <p>REMOTE VIDEO</p>
          </div>
        </div>
      </div>
    );
  }
}

export default App;

服务器

const express = require('express');

const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
const PORT = process.env.PORT || 5000;

const connections = [];
const clients = [];

io.set('origins', '*:*');
io.on('connection', socket => {
  connections.push(socket);
  clients.push({ socket_id: socket.id });
  console.log('Connected: %s sockets connected ', connections.length);

  socket.on('join', (room, callback) => {
    const clients = io.sockets.adapter.rooms[room];
    const numClients = (typeof clients !== 'undefined') ? clients.length : 0;
    console.log('joined room', room);
    if (numClients > 1) {
      return callback('already_full');
    }
    else if (numClients === 1) {
      socket.join(room);
      io.in(room).emit('ready');
    }
    else {
      socket.join(room);
    }

    callback();
  });

  socket.on('offer', (data) => {
    const { room, offer } = data;
    console.log('offer from: ', offer);
    socket.to(room).emit('offer', offer);
  });

  socket.on('answer', (data) => {
    const { room, answer } = data;
    console.log('answer from: ', answer);
    socket.to(room).emit('answer', answer);
  });

  socket.on('candidate', (data) => {
    const { room, candidate } = data;
    console.log('candidate: ', candidate);
    socket.to(room).emit('candidate', candidate);
  });

  socket.on('disconnect', () => {
    connections.splice(connections.indexOf(socket), 1);
    console.log('Disconnected: %s sockets connected, ', connections.length);
    clients.forEach((client, i) => {
      if (client.socket_id === socket.id) {
        clients.splice(i, 1);
      }
    });
  });
});

server.listen(PORT, () => {
  console.log(`Server listening on port ${PORT}`);
});

更新

在阅读了臂章的评论后,我对客户端js进行了如下修改.

After reading jib's comment, I have modified my client js as follows.

import React, { Component } from 'react';
import io from 'socket.io-client';

class App extends Component {
  constructor(props) {
    super(props);
    this.room = 'test-room';
    this.socket = io.connect('http://localhost:5000');
    this.peerConnection = new RTCPeerConnection({
      iceServers: [
        {
          urls: 'stun:stun.l.google.com:19302'
        }
      ]
    });
  };

  componentDidMount() {
    this.socket.on('connect', () => {
      this.socket.emit('join', this.room, err => {
        if (err) {
          console.error(err);
        } else {
          this.socket.on('offer', offer => {
            console.log('OFFER RECEIVED: ', offer);
            this.createAnswer(offer);
          });

          this.socket.on('candidate', candidate => {
            this.peerConnection.addIceCandidate(candidate).catch(error => console.error(error));
            console.log('CANDIDATE RECEIVED', candidate);
          });

          this.socket.on('answer', answer => {
            console.log('ANSWER RECEIVED:', answer);
            this.peerConnection.setRemoteDescription(answer);
          });
        }
      });
    });
  }

  startCall = async () => {
    this.peerConnection.oniceconnectionstatechange = () => console.log('ICE CONNECTION STATE: ', this.peerConnection.iceConnectionState);

    this.peerConnection.onicecandidate = e => {
      const iceCandidate = e.candidate;
      this.socket.emit('candidate', { room: this.room, candidate: iceCandidate });
      console.log('candidate generated', e.candidate);
    };

    this.peerConnection.ontrack = e => {
      this.remoteVideo.srcObject = e.streams[0];
      console.log('REMOTE STREAMS: ', this.peerConnection.getRemoteStreams());
    };

    try {
      const stream = await navigator.mediaDevices.getUserMedia({ video: { width: 150, height: 150 }, audio: false });
      for (const track of stream.getTracks()) {
        this.peerConnection.addTrack(track, stream);
      }

      this.localVideo.srcObject = stream;
      console.log('LOCAL STREAMS: ', this.peerConnection.getLocalStreams())

      return this.createOffer();
    } catch (error) {
      console.error(error);
    }
  }

  createOffer = async () => {
    try {
      const offer = await this.peerConnection.createOffer();
      await this.peerConnection.setLocalDescription(offer);
      this.socket.emit('offer', { room: this.room, offer });
      console.log('SENDING OFFER: ', offer);
    } catch (error) {
      console.error(error);
    }
  }

  createAnswer = async description => {
    this.peerConnection.onicecandidate = e => {
      const iceCandidate = e.candidate;
      this.socket.emit('candidate', { room: this.room, candidate: iceCandidate });
      console.log('candidate generated', e.candidate);
    };

    this.peerConnection.ontrack = e => {
      this.remoteVideo.srcObject = e.streams[0];
    };

    this.peerConnection.setRemoteDescription(description)
      .then(() => navigator.mediaDevices.getUserMedia({ video: { width: 150, height: 150 }, audio: false }))
      .then(stream => {
        for (const track of stream.getTracks()) {
          this.peerConnection.addTrack(track, stream);
        }

        this.localVideo.srcObject = stream;

        return this.peerConnection.createAnswer();
      })
      .then(answer => {
        this.peerConnection.setLocalDescription(answer);
        return answer;
      })
      .then(answer => {
        this.socket.emit('answer', { room: this.room, answer });
        console.log('SENDING ANSWER: ', answer);
      })
      .catch(error => console.error(error))
  }

  render() {
    return (
      <div>
        <h1>Webrtc</h1>
        <div>
          <button onClick={this.startCall}>CALL</button>
        </div>
        <div style={{ display: 'flex' }}>
          <div>
            <video id='localVideo' autoPlay muted playsInline ref={ref => this.localVideo = ref} />
            <p>LOCAL VIDEO</p>
          </div>
          <div>
            <video id='remoteVideo' autoPlay muted playsInline ref={ref => this.remoteVideo = ref} />
            <p>REMOTE VIDEO</p>
          </div>
        </div>
      </div>
    );
  }
}

export default App;

控制台上的错误仍然存​​在...为什么知道?

The error on my console still persists... any idea why?

推荐答案

控制台上的错误仍然存​​在...为什么知道?

The error on my console still persists... any idea why?

这是 Chrome中的已知错误(请★进行修复!)

This is a known bug in Chrome (please ★ it to have it fixed!)

要查看它,请在Chrome 78的网络控制台中键入以下内容:

To see it, type the following into web console in Chrome 78:

const pc = new RTCPeerConnection(); pc.setRemoteDescription(await pc.createOffer())

然后

pc.addIceCandidate(undefined)

它会产生:

TypeError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection':
 Candidate missing values for both sdpMid and sdpMLineIndex

现在尝试

pc.addIceCandidate()

它说:

TypeError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection':
 1 argument required, but only 0 present.

两者都违反了最新规范,该规范说 pc.addIceCandidate()候选结束指示" 适用于所有媒体描述."

Both are in violation of the latest spec, which says that pc.addIceCandidate() is "an end-of-candidates indication" that "applies to all media descriptions."

您可以放心地忽略此错误,直到Chrome对其进行修复为止,或者捕获到 TypeError 并将其排除.

You can safely ignore this error until Chrome fixes it, or catch the TypeError and suppress it.

我建议不要使用 if(候选)pc.addIceCandidate(候选)作为解决方法,因为Chrome修复了此问题后,它将阻止 iceConnectionState 转到已完成" 状态.

I recommend against if (candidate) pc.addIceCandidate(candidate) as a workaround, as once Chrome fixes this, it would prevent iceConnectionState from ever going to the "completed" state in Chrome or any other browser.

这篇关于WebRTC无法在控制台上的RTCPeerConnection上执行'addIceCandidate'错误,但仍可以显示远程和本地视频的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆