且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

无法读取未定义的属性“createDataChannel"

更新时间:2022-10-29 17:17:51

我发现了这个问题:我的 yourConn 分配 yourConn = new RTCPeerConnection(configuration, {optional: [{RtpDataChannels: true}]}); 被放入回调中,这就是为什么 yourConndataChannel = yourConn.createDataChannel("channel1", {reliable:true}); 未定义

I'm making a video call + chat application with WebRTC and ReactJS. I got this error while creating a data channel:

TypeError: Cannot read property 'createDataChannel' of undefined. The error was detected on this line:

dataChannel = yourConn.createDataChannel("channel1", {reliable:true});

My yourConn variable: yourConn = new RTCPeerConnection(configuration, {optional: [{RtpDataChannels: true}]});

I have tried searching for this problem on gg but there was no hope. Can anybody help me fix this problem? any help is appreciated.

Update: since it still seems to be hard to detect the problem, here is my component where the issue occur

import React from 'react';
import './CallPage.css';
import {conn} from '../../websocket/ws';
import {connect} from 'react-redux';

import TextArea from './textArea/TextArea';

var connectedUser, stream, yourConn, dataChannel;

class CallPage extends React.Component {
constructor(props) {
    super(props);
    this.state = {
  localVideoSrc: '',
        remoteVideoSrc: '',
        callToUser: '',
        tableRow: [],
        userList: [],
        message: '',
        receivedMessage: ''
    };
    this.handleCall = this.handleCall.bind(this);
    this.handleHangup = this.handleHangup.bind(this);
    this.handleSend = this.handleSend.bind(this);
}
componentWillMount() {
    conn.onmessage = (msg) => {
        if (msg.data !== "Hello world") {
            var data = JSON.parse(msg.data);        
            if (data.type && data.type !== "login") {
                console.log("Got message", msg.data);
                switch(data.type) {
                    case "offer":
                        this.handleOffer(data.offer, data.name); 
                        break; 
                    case "answer": 
                        this.handleAnswer(data.answer); 
                        break; 
                    //when a remote peer sends an ice candidate to us 
                    case "candidate": 
                        this.handleCandidate(data.candidate); 
                        break; 
                    case "leave": 
                        this.handleLeave(); 
                        break; 
                    case "update":
                        this.handleUpdate(data.list, data.usersOnCall);
                        break;
                    case "updateUserTable":
                        this.handleUpdateUserTable(data.usersOnCall);
                        break;
                    default: 
                        break; 
                }
            }           
        }       
    };
    this.initial();
}
initial() {
    navigator.getUserMedia({video: true, audio: true}, (myStream) => {
        stream = myStream;
        this.setState({localVideoSrc: window.URL.createObjectURL(stream)});
        var configuration = { 
            "iceServers": [{ "url": "stun:stun2.1.google.com:19302" }] 
     }; 
     yourConn = new RTCPeerConnection(configuration, {optional: [{RtpDataChannels: true}]}); 
     yourConn.addStream(stream);
     yourConn.onaddstream = (e) => {
         this.setState({remoteVideoSrc: window.URL.createObjectURL(e.stream)});
     };
     this.send({
        type: "update"
     });
     yourConn.onicecandidate = (event) => {
        if (event.candidate) {
            this.send({
                type: "candidate",
                candidate: event.candidate
            });
        }
     };
    }, (error) => {
        console.log(error);
    });
    dataChannel = yourConn.createDataChannel("channel1", {reliable:true}); 
    dataChannel.onerror = (error) => {
        console.log("Ooops...error:", error);
    };

    //when we receive a message from the other peer, display it on the screen
    dataChannel.onmessage = (event) => {
        var oldMessages = this.state.receivedMessage;
        var userB = this.state.callToUser;
        this.setState({
            receivedMessage: oldMessages + userB + ": " + event.data + "<br />"
        })
    };

    dataChannel.onclose = () => {
        console.log("data channel is closed");
    };
}
send(message) {
    if (connectedUser) {
        message.name = connectedUser;
    }
    conn.send(JSON.stringify(message));
}
handleCall(user) {
    // alert(user);
    var callToUsername = user;
    if (user.length > 0) {
        connectedUser = callToUsername;
        yourConn.createOffer((offer) => {
            this.send({
                type: "offer",
                offer: offer
            });
            yourConn.setLocalDescription(offer);
        }, (error) => {
            alert("Error when creating an offer");
        });
    }   
}
handleUpdate(list, usersOnCall) {
    var row = list.map((user, key) => {
        return (
            <tr key={key}>
                <td><i className={usersOnCall.indexOf(user) > -1 ? "fa fa-volume-control-phone" : "fa fa-volume-control-phone hidden"} aria-hidden="true"></i></td>
                <td className={user === this.props.yourName ? "your-name": ""}>{user}</td>
                <td><button className="btn btn-primary" onClick={() => this.handleCall(user)} disabled = {usersOnCall.indexOf(user) > -1 || user === this.props.yourName ? true : false }>Call</button></td>
                <td><button className="btn btn-danger" onClick={this.handleHangup} disabled = {true} >Hang up</button></td>
            </tr>
        );
    });
    this.setState({tableRow: row, userList: list});
}
handleUpdateUserTable(usersOnCall) {
    this.handleUpdate(this.state.userList, usersOnCall);
}
handleOffer(offer, name) {
    connectedUser = name;
    this.setState({callToUser: name})
    yourConn.setRemoteDescription(new RTCSessionDescription(offer));
    yourConn.createAnswer((answer) => {
        yourConn.setLocalDescription(answer);
        this.send({
            type: "answer",
            answer: answer
        });
        this.send({
            type: "updateUserTable",
            userA: this.props.yourName,
            userB: name
        });
    }, (error) => {
        alert("Error when creating an answer");
    });
}

handleAnswer(answer) {
    yourConn.setRemoteDescription(new RTCSessionDescription(answer));
}

handleCandidate(candidate) {
    yourConn.addIceCandidate(new RTCIceCandidate(candidate));
}
handleHangup() {
    this.send({
        type: "leave",
        userA: this.props.yourName,
        userB: this.state.callToUser
    });
    this.handleLeave();
}
handleChangeInputMessage(e) {
    this.setState({message: e.target.value});
}
handleSend() {
    var message = this.state.message;
    var oldMessages = this.state.receivedMessage;
    this.setState({receivedMessage: oldMessages + "You: " + message + "<br />"})
}
handleLeave() {
    connectedUser = null; 
    this.setState({remoteVideoSrc: null, callToUser: ''});
    yourConn.close(); 
    yourConn.onicecandidate = null; 
    yourConn.onaddstream = null; 
    this.initial();
}
render(){

    return(
        <div className="container-fluid" id="callPage">
            <div className="row">
                <div className="col-sm-5">
                    <div className="call-page">
                        <video src={this.state.localVideoSrc} id="localVideo" autoPlay></video>
                        <video src={this.state.remoteVideoSrc} id="remoteVideo" autoPlay></video>   
                    </div>
                </div>
                <TextArea  message={this.state.message} receivedMessage={this.state.receivedMessage} handleSend={this.handleSend} handleChangeInputMessage={this.handleChangeInputMessage}/>
                <div className="col-sm-3">
                    <table className="table table-responsive table-hover">
                        <thead>
                            <tr>
                                <th className="text-center" colSpan="4">List of user</th>
                            </tr>
                        </thead>
                        <tbody>
                            {this.state.tableRow}
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    );
  }
}

function mapStateToProps(state) {
return {
    yourName: state.yourName
  }
}

export default connect(mapStateToProps)(CallPage);

and here is my server

var WebSocket = require('ws'); 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090}); 

//all connected to the server users 
var users = {};
var userList = [];
var usersOnCall = [];
//when a user connects to our sever 
wss.on('connection', function(connection) {

 console.log("User connected");

//when server gets a message from a connected user 
connection.on('message', function(message) { 

  var data; 

  //accepting only JSON messages 
  try { 
     data = JSON.parse(message); 
  } catch (e) { 
     console.log("Invalid JSON"); 
     data = {}; 
  }

  //switching type of the user message 
  switch (data.type) { 
     //when a user tries to login
     case "login": 
        console.log("User logged", data.name); 
        //if anyone is logged in with this username then refuse 
        if(users[data.name]) { 
           sendTo(connection, { 
              type: "login", 
              success: false
           }); 
        } else { 
           //save user connection on the server 
           userList.push(data.name);
           users[data.name] = connection; 
           connection.name = data.name; 
           sendTo(connection, { 
              type: "login", 
              success: true
           });               
        }               
        break;

      case "update":
        broadcast(connection,{
          type: "update",
          list: userList,
          usersOnCall: usersOnCall
        });  
        break;

     case "offer": 
        //for ex. UserA wants to call UserB 
        console.log("Sending offer to: ", data.name);

        //if UserB exists then send him offer details 
        var conn = users[data.name]; 

        if(conn != null) { 
           //setting that UserA connected with UserB 
           connection.otherName = data.name; 

           sendTo(conn, { 
              type: "offer", 
              offer: data.offer, 
              name: connection.name 
           }); 
        }

        break;

     case "answer": 
        console.log("Sending answer to: ", data.name); 
        //for ex. UserB answers UserA 
        var conn = users[data.name]; 

        if(conn != null) { 
           connection.otherName = data.name; 
           sendTo(conn, { 
              type: "answer", 
              answer: data.answer 
           }); 
        } 

        break; 

     case "candidate": 
        console.log("Sending candidate to:",data.name); 
        var conn = users[data.name];

        if(conn != null) { 
           sendTo(conn, { 
              type: "candidate", 
              candidate: data.candidate 
           }); 
        } 

        break;

    case "updateUserTable":
      if (usersOnCall.indexOf(data.userA) === -1 && 
usersOnCall.indexOf(data.userB) === -1) {
        usersOnCall.push(data.userA);
        usersOnCall.push(data.userB);
        broadcast(connection, {
          type: "updateUserTable",
          usersOnCall: usersOnCall
        });
      }

      break;

     case "leave": 
        console.log("Disconnecting from", data.name); 
        var indexA = usersOnCall.indexOf(data.userA);
        usersOnCall.splice(indexA, 1);
        var indexB = usersOnCall.indexOf(data.userB);
        usersOnCall.splice(indexB, 1);
        broadcast(connection,{
          type: "update",
          list: userList,
          usersOnCall: usersOnCall
        });  
        var conn = users[data.name]; 
        conn.otherName = null; 

        //notify the other user so he can disconnect his peer connection 
        if(conn != null) {
           sendTo(conn, { 
              type: "leave" 
          }); 
        }
        break;

     default: 
        sendTo(connection, { 
           type: "error", 
           message: "Command not found: " + data.type 
        }); 

        break; 
  }

 }); 

 //when user exits, for example closes a browser window 
 //this may help if we are still in "offer","answer" or "candidate" state 
 connection.on("close", function() { 
 //  console.log(connection.name);
  userList.splice(userList.indexOf(connection.name),1);
  currentUser = "";
  broadcast(connection,{
    type: "update",
    list: userList
  });
  if(connection.name) { 
     delete users[connection.name]; 
     if(connection.otherName) { 
        console.log("Disconnecting from ", connection.otherName); 
        var conn = users[connection.otherName]; 
        conn.otherName = null;

        if(conn != null) { 
           sendTo(conn, { 
              type: "leave" 
           }); 
        }
     } 
  }

 });

connection.send("Hello world");  
});

function sendTo(connection, message) {
connection.send(JSON.stringify(message)); 
}

function broadcast(connection, message) {
  wss.clients.forEach(function each(client) {
    if (client.readyState === WebSocket.OPEN) {
      client.send(JSON.stringify(message));
    }
  });
}

I found the issue: My yourConn assignment yourConn = new RTCPeerConnection(configuration, {optional: [{RtpDataChannels: true}]}); was put in a callback, that's why yourConn in dataChannel = yourConn.createDataChannel("channel1", {reliable:true}); is undefined

登录 关闭
扫码关注1秒登录
无法读取未定义的属性“createDataChannel"
发送“验证码”获取 | 15天全站免登陆