簡體   English   中英

React Native + WebRTC TypeError: Undefined is not and Object(Evaluating: 'data.type')

[英]React Native + WebRTC TypeError: Undefined is not and Object(Evaluating: 'data.type')

我一直在嘗試用 React Native 學習 WebRTC,所以我一直在做這個視頻通話項目。 我以為我已經弄清楚了,直到這件事出現。

首先,這是我的 React Native 代碼

import React, {useEffect, useState, useCallback} from 'react';
import {View, StyleSheet, Alert} from 'react-native';
import {Text} from 'react-native-paper';
import {Button} from 'react-native-paper';
import AsyncStorage from '@react-native-community/async-storage';
import {TextInput} from 'react-native-paper';

import {useFocusEffect} from '@react-navigation/native';

import InCallManager from 'react-native-incall-manager';

import {
  RTCPeerConnection,
  RTCIceCandidate,
  RTCSessionDescription,
  RTCView,
  MediaStream,
  MediaStreamTrack,
  mediaDevices,
  registerGlobals,
} from 'react-native-webrtc';

export default function CallScreen({navigation, ...props}) {
  let name;
  let connectedUser;
  const [userId, setUserId] = useState('');
  const [socketActive, setSocketActive] = useState(false);
  const [calling, setCalling] = useState(false);
  // Video Scrs
  const [localStream, setLocalStream] = useState({toURL: () => null});
  const [remoteStream, setRemoteStream] = useState({toURL: () => null});
  const [conn, setConn] = useState(new WebSocket('ws://:4443/'));
  const [yourConn, setYourConn] = useState(
    new RTCPeerConnection({
      iceServers: [
        {
          urls: 'stun:stun.l.google.com:19302',  
        }, {
          urls: 'stun:stun1.l.google.com:19302',    
        }, {
          urls: 'stun:stun2.l.google.com:19302',    
        }, {
          username: "",
          credential: "",
          urls: 'turn::3478',
        }, {
          username: "",
          credential: "",
          urls: 'stun::3478',
        }
      ],
    }),
  );

  const [offer, setOffer] = useState(null);

  const [callToUsername, setCallToUsername] = useState(null);

  useFocusEffect(
    useCallback(() => {
      AsyncStorage.getItem('userId').then(id => {
        console.log(id);
        if (id) {
          setUserId(id);
        } else {
          setUserId('');
          navigation.push('Login');
        }
      });
    }, [userId]),
  );

  useEffect(() => {
    navigation.setOptions({
      title: 'Hola' + userId,
      headerRight: () => (
        <Button mode="text" onPress={onLogout} style={{paddingRight: 10}}>
          Salir
        </Button>
      ),
    });
  }, [userId]);


  useEffect(() => {
    if (socketActive && userId.length > 0) {
      try {
        InCallManager.start({media: 'audio'});
        InCallManager.setForceSpeakerphoneOn(true);
        InCallManager.setSpeakerphoneOn(true);
      } catch (err) {
        console.log('InApp Caller ---------------------->', err);
      }

      console.log(InCallManager);

      send({
        type: 'login',
        name: userId,
      });
    }
  }, [socketActive, userId]);

  const onLogin = () => {};

  useEffect(() => {

    conn.onopen = () => {
      console.log('Connected to the signaling server');
      setSocketActive(true);
    };
    conn.onmessage = msg => {
      let data;
      if (msg.data === 'Hello world') {
        data = {};
      } else {
        dcata = JSON.parse(msg.data);
        console.log('Data --------------------->', data);
        switch (data.type) {
          case 'login':
            console.log('Login');
            break;
          case 'offer':
            handleOffer(data.offer, data.name);
            console.log('Offer');
            break;
          case 'answer':
            handleAnswer(data.answer);
            console.log('Answer');
            break;
          case 'candidate':
            handleCandidate(data.candidate);
            console.log('Candidate');
            break;
          case 'leave':
            handleLeave();
            console.log('Leave');
            break;
          default:
            break;
        }
      }
    };
    conn.onerror = function(err) {
      console.log('Got error', err);
    };

    let isFront = false;
    mediaDevices.enumerateDevices().then(sourceInfos => {
      let videoSourceId;
      for (let i = 0; i < sourceInfos.length; i++) {
        const sourceInfo = sourceInfos[i];
        if (
          sourceInfo.kind == 'videoinput' &&
          sourceInfo.facing == (isFront ? 'front' : 'environment')
        ) {
          videoSourceId = sourceInfo.deviceId;
        }
      }
      mediaDevices
        .getUserMedia({
          audio: true,
          video: {
            mandatory: {
              minWidth: 500, 
              minHeight: 300,
              minFrameRate: 30,
            },
            facingMode: isFront ? 'user' : 'environment',
            optional: videoSourceId ? [{sourceId: videoSourceId}] : [],
          },
        })
        .then(stream => {
          setLocalStream(stream);

          yourConn.addStream(stream);
        })
        .catch(error => {

        });
    });

    yourConn.onaddstream = event => {
      console.log('On Add Stream', event);
      setRemoteStream(event.stream);
    };

    yourConn.onicecandidate = event => {
      if (event.candidate) {
        send({
          type: 'candidate',
          candidate: event.candidate,
        });
      }
    };
  }, []);

  const send = message => {
    if (connectedUser) {
      message.name = connectedUser;
      console.log('Connected iser in end----------', message);
    }

    conn.send(JSON.stringify(message));
  };

  const onCall = () => {
    setCalling(true);

    connectedUser = callToUsername;
    console.log('Caling to', callToUsername);


    yourConn.createOffer().then(offer => {
      yourConn.setLocalDescription(offer).then(() => {
        console.log('Sending Ofer');
        console.log(offer);
        send({
          type: 'offer',
          offer: offer,
        });
      });
    });
  };

  const handleOffer = async (offer, name) => {
    console.log(name + ' is calling you.');

    console.log('Accepting Call===========>', offer);
    connectedUser = name;

    try {
      await yourConn.setRemoteDescription(new RTCSessionDescription(offer));

      const answer = await yourConn.createAnswer();

      await yourConn.setLocalDescription(answer);
      send({
        type: 'answer',
        answer: answer,
      });
    } catch (err) {
      console.log('Offerr Error', err);
    }
  };

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

  const handleCandidate = candidate => {
    setCalling(false);
    console.log('Candidate ----------------->', candidate);
    yourConn.addIceCandidate(new RTCIceCandidate(candidate));
  };

  const hangUp = () => {
    send({
      type: 'leave',
    });

    handleLeave();
  };

  const handleLeave = () => {
    connectedUser = null;
    setRemoteStream({toURL: () => null});

    yourConn.close();

  };

  const onLogout = () => {

    AsyncStorage.removeItem('userId').then(res => {
      navigation.push('Login');
    });
  };

  const acceptCall = async () => {
    console.log('Accepting Call===========>', offer);
    connectedUser = offer.name;

    try {
      await yourConn.setRemoteDescription(new RTCSessionDescription(offer));

      const answer = await yourConn.createAnswer();

      await yourConn.setLocalDescription(answer);

      send({
        type: 'answer',
        answer: answer,
      });
    } catch (err) {
      console.log('Offerr Error', err);
    }
  };
  const rejectCall = async () => {
    send({
      type: 'leave',
    });
    ``;
    setOffer(null);

    handleLeave();
  };


  return (
    <View style={styles.root}>
      <View style={styles.inputField}>
        <TextInput
          label="Ingresa el ID del familiar"
          mode="outlined"
          style={{marginBottom: 7}}
          onChangeText={text => setCallToUsername(text)}
        />
        <Button
          mode="contained"
          onPress={onCall}
          loading={calling}
          //   style={styles.btn}
          contentStyle={styles.btnContent}
          disabled={!(socketActive && userId.length > 0)}>
          Llamar
        </Button>
      </View>

      <View style={styles.videoContainer}>
        <View style={[styles.videos, styles.localVideos]}>
          <Text>Tu video</Text>
          <RTCView streamURL={localStream.toURL()} style={styles.localVideo} />
        </View>
        <View style={[styles.videos, styles.remoteVideos]}>
          <Text>Video del familiar</Text>
          <RTCView
            streamURL={remoteStream.toURL()}
            style={styles.remoteVideo}
          />
        </View>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  root: {
    backgroundColor: '#fff',
    flex: 1,
    padding: 20,
  },
  inputField: {
    marginBottom: 10,
    flexDirection: 'column',
  },
  videoContainer: {
    flex: 1,
    minHeight: 450,
  },
  videos: {
    width: '100%',
    flex: 1,
    position: 'relative',
    overflow: 'hidden',

    borderRadius: 6,
  },
  localVideos: {
    height: 100,
    marginBottom: 10,
  },
  remoteVideos: {
    height: 400,
  },
  localVideo: {
    backgroundColor: '#f2f2f2',
    height: '100%',
    width: '100%',
  },
  remoteVideo: {
    backgroundColor: '#f2f2f2',
    height: '100%',
    width: '100%',
  },
});

此時值得一提的是,我的 STUN/TURN 服務器完美運行,這是我的信號套接字

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

var wss = new WebSocketServer({port: 4443}); 
var users = {};

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 
               users[data.name] = connection; 
               connection.name = data.name; 

               sendTo(connection, { 
                  type: 'login', 
                  success: true 
               }); 
            } 

            break; 

         case 'offer': 
            //for ex. UserA wants to call UserB 
            console.log('Sending offer to: ', data.offer, 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.answer); 
            //for ex. UserB answers UserA 
            var conn = users[data.answer]; 

            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 'leave': 
            console.log('Disconnecting from', data.name); 
            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() { 

      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)); 
}

但是這里是問題開始的地方。

當我使用應用程序登錄時,這是 Socket 顯示的內容。

User connected
User connected
User connected
User logged 1
User connected
User connected
User connected
User connected
User connected

但立即,該應用程序顯示了這一點。

socket連接后出現紅框錯誤

如果我解除錯誤並嘗試呼叫另一個已連接的對等方,則錯誤會顯示在我嘗試呼叫的設備上。

呼叫嘗試后的紅框

React Native cli,告訴我與錯誤相同

Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle.
[Tue Jun 02 2020 10:34:26.400]  LOG      Running "webrtcApp" with {"rootTag":1}
[Tue Jun 02 2020 10:37:20.365]  LOG      Connected to the signaling server
[Tue Jun 02 2020 10:37:20.487]  LOG      1
[Tue Jun 02 2020 10:37:20.747]  LOG      {"audioUriMap": {"busytone": {"_BUNDLE_": null, "_DEFAULT_": null}, "ringback": {"_BUNDLE_": null, "_DEFAULT_": null}, "ringtone": {"_BUNDLE_": null, "_DEFAULT_": null}}, "cameraPermission": "granted", "checkCameraPermission": [Function bound checkCameraPermission], "checkRecordPermission": [Function bound checkRecordPermission], "recordPermission": "granted", "requestCameraPermission": [Function bound requestCameraPermission], "requestRecordPermission": [Function bound requestRecordPermission], "vibrate": false}
[Tue Jun 02 2020 10:37:20.914]  LOG      1
[Tue Jun 02 2020 10:37:20.933]  LOG      Data ---------------------> undefined
[Tue Jun 02 2020 10:37:52.746]  LOG      Data ---------------------> undefined

這是當我嘗試調用另一個已連接的對等點時套接字顯示的內容。

Sending offer to:  {
  sdp: 'v=0\r\n' +
    'o=- 3903065942025360998 2 IN IP4 127.0.0.1\r\n' +
    's=-\r\n' +
    't=0 0\r\n' +
    'a=group:BUNDLE audio video\r\n' +
    'a=msid-semantic: WMS 4169861f-fa77-4c52-b0a7-da4673eb4512\r\n' +
    'm=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 102 0 8 106 105 13 110 112 113 126\r\n' +
    'c=IN IP4 0.0.0.0\r\n' +
    'a=rtcp:9 IN IP4 0.0.0.0\r\n' +
    'a=ice-ufrag:+CFh\r\n' +
    'a=ice-pwd:ccPSGw5U3fnoHysgp1bquCYJ\r\n' +
    'a=ice-options:trickle renomination\r\n' +
    'a=fingerprint:sha-256 2A:2B:C1:A4:1D:8C:B3:C3:B8:AA:6D:C2:36:71:F7:95:61:CC:54:E6:2D:79:C0:4D:A5:D4:E1:45:3D:00:BF:F4\r\n' +
    'a=setup:actpass\r\n' +
    'a=mid:audio\r\n' +
    'a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n' +
    'a=extmap:2 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n' +
    'a=sendrecv\r\n' +
    'a=rtcp-mux\r\n' +
    'a=rtpmap:111 opus/48000/2\r\n' +
    'a=rtcp-fb:111 transport-cc\r\n' +
    'a=fmtp:111 minptime=10;useinbandfec=1\r\n' +
    'a=rtpmap:103 ISAC/16000\r\n' +
    'a=rtpmap:104 ISAC/32000\r\n' +
    'a=rtpmap:9 G722/8000\r\n' +
    'a=rtpmap:102 ILBC/8000\r\n' +
    'a=rtpmap:0 PCMU/8000\r\n' +
    'a=rtpmap:8 PCMA/8000\r\n' +
    'a=rtpmap:106 CN/32000\r\n' +
    'a=rtpmap:105 CN/16000\r\n' +
    'a=rtpmap:13 CN/8000\r\n' +
    'a=rtpmap:110 telephone-event/48000\r\n' +
    'a=rtpmap:112 telephone-event/32000\r\n' +
    'a=rtpmap:113 telephone-event/16000\r\n' +
    'a=rtpmap:126 telephone-event/8000\r\n' +
    'a=ssrc:3496221613 cname:7efsTJMX4YNf740V\r\n' +
    'a=ssrc:3496221613 msid:4169861f-fa77-4c52-b0a7-da4673eb4512 8c0eb0bb-a1ab-422a-a527-5d5bc73635b7\r\n' +
    'a=ssrc:3496221613 mslabel:4169861f-fa77-4c52-b0a7-da4673eb4512\r\n' +
    'a=ssrc:3496221613 label:8c0eb0bb-a1ab-422a-a527-5d5bc73635b7\r\n' +
    'm=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 127\r\n' +
    'c=IN IP4 0.0.0.0\r\n' +
    'a=rtcp:9 IN IP4 0.0.0.0\r\n' +
    'a=ice-ufrag:+CFh\r\n' +
    'a=ice-pwd:ccPSGw5U3fnoHysgp1bquCYJ\r\n' +
    'a=ice-options:trickle renomination\r\n' +
    'a=fingerprint:sha-256 2A:2B:C1:A4:1D:8C:B3:C3:B8:AA:6D:C2:36:71:F7:95:61:CC:54:E6:2D:79:C0:4D:A5:D4:E1:45:3D:00:BF:F4\r\n' +
    'a=setup:actpass\r\n' +
    'a=mid:video\r\n' +
    'a=extmap:14 urn:ietf:params:rtp-hdrext:toffset\r\n' +
    'a=extmap:13 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n' +
    'a=extmap:3 urn:3gpp:video-orientation\r\n' +
    'a=extmap:2 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n' +
    'a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\n' +
    'a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type\r\n' +
    'a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing\r\n' +
    'a=extmap:8 http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07\r\n' +
    'a=extmap:9 http://www.webrtc.org/experiments/rtp-hdrext/color-space\r\n' +
    'a=sendrecv\r\n' +
    'a=rtcp-mux\r\n' +
    'a=rtcp-rsize\r\n' +
    'a=rtpmap:96 VP8/90000\r\n' +
    'a=rtcp-fb:96 goog-remb\r\n' +
    'a=rtcp-fb:96 transport-cc\r\n' +
    'a=rtcp-fb:96 ccm fir\r\n' +
    'a=rtcp-fb:96 nack\r\n' +
    'a=rtcp-fb:96 nack pli\r\n' +
    'a=rtpmap:97 rtx/90000\r\n' +
    'a=fmtp:97 apt=96\r\n' +
    'a=rtpmap:98 VP9/90000\r\n' +
    'a=rtcp-fb:98 goog-remb\r\n' +
    'a=rtcp-fb:98 transport-cc\r\n' +
    'a=rtcp-fb:98 ccm fir\r\n' +
    'a=rtcp-fb:98 nack\r\n' +
    'a=rtcp-fb:98 nack pli\r\n' +
    'a=rtpmap:99 rtx/90000\r\n' +
    'a=fmtp:99 apt=98\r\n' +
    'a=rtpmap:100 red/90000\r\n' +
    'a=rtpmap:101 rtx/90000\r\n' +
    'a=fmtp:101 apt=100\r\n' +
    'a=rtpmap:127 ulpfec/90000\r\n' +
    'a=ssrc-group:FID 1093641884 443757454\r\n' +
    'a=ssrc:1093641884 cname:7efsTJMX4YNf740V\r\n' +
    'a=ssrc:1093641884 msid:4169861f-fa77-4c52-b0a7-da4673eb4512 47eb8496-acdf-48ae-a7bb-bb44e758b23e\r\n' +
    'a=ssrc:1093641884 mslabel:4169861f-fa77-4c52-b0a7-da4673eb4512\r\n' +
    'a=ssrc:1093641884 label:47eb8496-acdf-48ae-a7bb-bb44e758b23e\r\n' +
    'a=ssrc:443757454 cname:7efsTJMX4YNf740V\r\n' +
    'a=ssrc:443757454 msid:4169861f-fa77-4c52-b0a7-da4673eb4512 47eb8496-acdf-48ae-a7bb-bb44e758b23e\r\n' +
    'a=ssrc:443757454 mslabel:4169861f-fa77-4c52-b0a7-da4673eb4512\r\n' +
    'a=ssrc:443757454 label:47eb8496-acdf-48ae-a7bb-bb44e758b23e\r\n',
  type: 'offer'
} 2
Sending candidate to: {
  candidate: 'candidate:3362660723 1 udp 2122260223 192.168.232.2 51977 typ host generation 0 ufrag +CFh network-id 3 network-cost 10',
  sdpMLineIndex: 0,
  sdpMid: 'audio'
}
Sending candidate to: {
  candidate: 'candidate:1914131086 1 udp 2122187263 fec0::ff:fe44:5566 41727 typ host generation 0 ufrag +CFh network-id 4 network-cost 10',
  sdpMLineIndex: 0,
  sdpMid: 'audio'
}
Sending candidate to: {
  candidate: 'candidate:3362660723 1 udp 2122260223 192.168.232.2 46626 typ host generation 0 ufrag +CFh network-id 3 network-cost 10',
  sdpMLineIndex: 1,
  sdpMid: 'video'
}
Sending candidate to: {
  candidate: 'candidate:1914131086 1 udp 2122187263 fec0::ff:fe44:5566 39496 typ host generation 0 ufrag +CFh network-id 4 network-cost 10',
  sdpMLineIndex: 1,
  sdpMid: 'video'
}
Sending candidate to: {
  candidate: 'candidate:842163049 1 udp 1686052607 189.133.49.117 60389 typ srflx raddr 192.168.232.2 rport 51977 generation 0 ufrag +CFh network-id 3 network-cost 10',
  sdpMLineIndex: 0,
  sdpMid: 'audio'
}
Sending candidate to: {
  candidate: 'candidate:4231669940 1 udp 1685989631 2806:106e:a:f03d:7d79:9dd4:5653:7381 60393 typ srflx raddr fec0::ff:fe44:5566 rport 41727 generation 0 ufrag +CFh network-id 4 network-cost 10',
  sdpMLineIndex: 0,
  sdpMid: 'audio'
}
Sending candidate to: {
  candidate: 'candidate:4231669940 1 udp 1685989631 2806:106e:a:f03d:7d79:9dd4:5653:7381 60394 typ srflx raddr fec0::ff:fe44:5566 rport 39496 generation 0 ufrag +CFh network-id 4 network-cost 10',
  sdpMLineIndex: 1,
  sdpMid: 'video'
}
Sending candidate to: {
  candidate: 'candidate:842163049 1 udp 1686052607 189.133.49.117 60392 typ srflx raddr 192.168.232.2 rport 46626 generation 0 ufrag +CFh network-id 3 network-cost 10',
  sdpMLineIndex: 1,
  sdpMid: 'video'
}
Sending candidate to: {
  candidate: 'candidate:2108125103 1 udp 41885439 172.31.19.115 64788 typ relay raddr 189.133.49.117 rport 60392 generation 0 ufrag +CFh network-id 3 network-cost 10',
  sdpMLineIndex: 1,
  sdpMid: 'video'
}
Sending candidate to: {
  candidate: 'candidate:2108125103 1 udp 41885439 172.31.19.115 55279 typ relay raddr 189.133.49.117 rport 60389 generation 0 ufrag +CFh network-id 3 network-cost 10',
  sdpMLineIndex: 0,
  sdpMid: 'audio'
}

我不知道我做錯了什么,但是對於我的問題的任何幫助將不勝感激。

React Native 上的onmessage中有一個錯字。 dcata重命名為data 如果您覺得答案有用,請點贊。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM