启用远程调试器后,我的RCTWebrtcDemo可以正常工作,但是当禁用远程调试js时,我的RCTWebrtcDemo无法工作

时间:2019-02-26 17:17:57

标签: reactjs react-native socket.io webrtc

我正在尝试使用react-native实施视频流 我在用 webrtc软件包(https://github.com/oney/react-native-webrtc) 套接字客户端 和  oney / RCTWebRTCDemo(https://github.com/oney/RCTWebRTCDemo) 当启用远程调试js时,实时流式传输完全可以按预期工作,但是当禁用远程调试时,它卡在连接上,我感觉到它停止工作了。 我正在尝试将此演示包含到我现有的应用中。

我的代码-live.js

'use strict';

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  TouchableHighlight,
  View,
  TextInput,
  ListView,
  Platform,
} from 'react-native';

import io from 'socket.io-client';

const socket = io.connect('https://react-native-webrtc.herokuapp.com', {transports: ['websocket']});

import {
  RTCPeerConnection,
  RTCMediaStream,
  RTCIceCandidate,
  RTCSessionDescription,
  RTCView,
  MediaStreamTrack,
  getUserMedia,
} from 'react-native-webrtc';
React.createClass=require('create-react-class');
const configuration = {"iceServers": [{"url": "stun:stun.l.google.com:19302"}]};

const pcPeers = {};
let localStream;

function getLocalStream(isFront, callback) {

  let videoSourceId;

  // on android, you don't have to specify sourceId manually, just use facingMode
  // uncomment it if you want to specify
  if (Platform.OS === 'ios') {
    MediaStreamTrack.getSources(sourceInfos => {
      console.log("sourceInfos: ", sourceInfos);

      for (const i = 0; i < sourceInfos.length; i++) {
        const sourceInfo = sourceInfos[i];
        if(sourceInfo.kind == "video" && sourceInfo.facing == (isFront ? "front" : "back")) {
          videoSourceId = sourceInfo.id;
        }
      }
    });
  }
  getUserMedia({
    audio: true,
    video: {
      mandatory: {
        minWidth: 640, // Provide your own width, height and frame rate here
        minHeight: 360,
        minFrameRate: 30,
      },
      facingMode: (isFront ? "user" : "environment"),
      optional: (videoSourceId ? [{sourceId: videoSourceId}] : []),
    }
  }, function (stream) {
    console.log('getUserMedia success', stream);
    callback(stream);
  }, logError);
}

function join(roomID) {
  socket.emit('join', roomID, function(socketIds){
    console.log('join', socketIds);
    for (const i in socketIds) {
      const socketId = socketIds[i];
      createPC(socketId, true);
    }
  });
}

function createPC(socketId, isOffer) {
  const pc = new RTCPeerConnection(configuration);
  pcPeers[socketId] = pc;

  pc.onicecandidate = function (event) {
    console.log('onicecandidate', event.candidate);
    if (event.candidate) {
      socket.emit('exchange', {'to': socketId, 'candidate': event.candidate });
    }
  };

  function createOffer() {
    pc.createOffer(function(desc) {
      console.log('createOffer', desc);
      pc.setLocalDescription(desc, function () {
        console.log('setLocalDescription', pc.localDescription);
        socket.emit('exchange', {'to': socketId, 'sdp': pc.localDescription });
      }, logError);
    }, logError);
  }

  pc.onnegotiationneeded = function () {
    console.log('onnegotiationneeded');
    if (isOffer) {
      createOffer();
    }
  }

  pc.oniceconnectionstatechange = function(event) {
    console.log('oniceconnectionstatechange', event.target.iceConnectionState);
    if (event.target.iceConnectionState === 'completed') {
      setTimeout(() => {
        getStats();
      }, 1000);
    }
    if (event.target.iceConnectionState === 'connected') {
      createDataChannel();
    }
  };
  pc.onsignalingstatechange = function(event) {
    console.log('onsignalingstatechange', event.target.signalingState);
  };

  pc.onaddstream = function (event) {
    console.log('onaddstream', event.stream);
    container.setState({info: 'One peer join!'});

    const remoteList = container.state.remoteList;
    remoteList[socketId] = event.stream.toURL();
    container.setState({ remoteList: remoteList });
  };
  pc.onremovestream = function (event) {
    console.log('onremovestream', event.stream);
  };

  pc.addStream(localStream);
  function createDataChannel() {
    if (pc.textDataChannel) {
      return;
    }
    const dataChannel = pc.createDataChannel("text");

    dataChannel.onerror = function (error) {
      console.log("dataChannel.onerror", error);
    };

    dataChannel.onmessage = function (event) {
      console.log("dataChannel.onmessage:", event.data);
      container.receiveTextData({user: socketId, message: event.data});
    };

    dataChannel.onopen = function () {
      console.log('dataChannel.onopen');
      container.setState({textRoomConnected: true});
    };

    dataChannel.onclose = function () {
      console.log("dataChannel.onclose");
    };

    pc.textDataChannel = dataChannel;
  }
  return pc;
}

function exchange(data) {
  const fromId = data.from;
  let pc;
  if (fromId in pcPeers) {
    pc = pcPeers[fromId];
  } else {
    pc = createPC(fromId, false);
  }

  if (data.sdp) {
    console.log('exchange sdp', data);
    pc.setRemoteDescription(new RTCSessionDescription(data.sdp), function () {
      if (pc.remoteDescription.type == "offer")
        pc.createAnswer(function(desc) {
          console.log('createAnswer', desc);
          pc.setLocalDescription(desc, function () {
            console.log('setLocalDescription', pc.localDescription);
            socket.emit('exchange', {'to': fromId, 'sdp': pc.localDescription });
          }, logError);
        }, logError);
    }, logError);
  } else {
    console.log('exchange candidate', data);
    pc.addIceCandidate(new RTCIceCandidate(data.candidate));
  }
}

function leave(socketId) {
  console.log('leave', socketId);
  const pc = pcPeers[socketId];
  const viewIndex = pc.viewIndex;
  pc.close();
  delete pcPeers[socketId];

  const remoteList = container.state.remoteList;
  delete remoteList[socketId]
  container.setState({ remoteList: remoteList });
  container.setState({info: 'One peer leave!'});
}

socket.on('exchange', function(data){
  exchange(data);
});
socket.on('leave', function(socketId){
  leave(socketId);
});

socket.on('connect', function(data) {
  console.log('connect');
  getLocalStream(true, function(stream) {
    localStream = stream;
    container.setState({selfViewSrc: stream.toURL()});
    container.setState({status: 'ready', info: 'Please enter or create room ID'});
  });
});

function logError(error) {
  console.log("logError", error);
}

function mapHash(hash, func) {
  const array = [];
  for (const key in hash) {
    const obj = hash[key];
    array.push(func(obj, key));
  }
  return array;
}

function getStats() {
  const pc = pcPeers[Object.keys(pcPeers)[0]];
  if (pc.getRemoteStreams()[0] && pc.getRemoteStreams()[0].getAudioTracks()[0]) {
    const track = pc.getRemoteStreams()[0].getAudioTracks()[0];
    console.log('track', track);
    pc.getStats(track, function(report) {
      console.log('getStats report', report);
    }, logError);
  }
}

let container;

const RCTWebRTCDemo = React.createClass({
  getInitialState: function() {
    this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => true});
    return {
      info: 'Initializing',
      status: 'init',
      roomID: '',
      isFront: true,
      selfViewSrc: null,
      remoteList: {},
      textRoomConnected: false,
      textRoomData: [],
      textRoomValue: '',
    };
  },
  componentDidMount: function() {
    container = this;
  },
  _press(event) {
    this.refs.roomID.blur();
    this.setState({status: 'connect', info: 'Connecting'});
    join(this.state.roomID);
  },
  _switchVideoType() {
    const isFront = !this.state.isFront;
    this.setState({isFront});
    getLocalStream(isFront, function(stream) {
      if (localStream) {
        for (const id in pcPeers) {
          const pc = pcPeers[id];
          pc && pc.removeStream(localStream);
        }
        localStream.release();
      }
      localStream = stream;
      container.setState({selfViewSrc: stream.toURL()});

      for (const id in pcPeers) {
        const pc = pcPeers[id];
        pc && pc.addStream(localStream);
      }
    });
  },
  receiveTextData(data) {
    const textRoomData = this.state.textRoomData.slice();
    textRoomData.push(data);
    this.setState({textRoomData, textRoomValue: ''});
  },
  _textRoomPress() {
    if (!this.state.textRoomValue) {
      return
    }
    const textRoomData = this.state.textRoomData.slice();
    textRoomData.push({user: 'Me', message: this.state.textRoomValue});
    for (const key in pcPeers) {
      const pc = pcPeers[key];
      pc.textDataChannel.send(this.state.textRoomValue);
    }
    this.setState({textRoomData, textRoomValue: ''});
  },
  _renderTextRoom() {
    return (
      <View style={styles.listViewContainer}>
        <ListView
          dataSource={this.ds.cloneWithRows(this.state.textRoomData)}
          renderRow={rowData => <Text>{`${rowData.user}: ${rowData.message}`}</Text>}
          />
        <TextInput
          style={{width: 200, height: 30, borderColor: 'gray', borderWidth: 1}}
          onChangeText={value => this.setState({textRoomValue: value})}
          value={this.state.textRoomValue}
        />
        <TouchableHighlight
          onPress={this._textRoomPress}>
          <Text>Send</Text>
        </TouchableHighlight>
      </View>
    );
  },
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          {this.state.info}
        </Text>
        {this.state.textRoomConnected && this._renderTextRoom()}
        <View style={{flexDirection: 'row'}}>
          <Text>
            {this.state.isFront ? "Use front camera" : "Use back camera"}
          </Text>
          <TouchableHighlight
            style={{borderWidth: 1, borderColor: 'black'}}
            onPress={this._switchVideoType}>
            <Text>Switch camera</Text>
          </TouchableHighlight>
        </View>
        { this.state.status == 'ready' ?
          (<View>
            <TextInput
              ref='roomID'
              autoCorrect={false}
              style={{width: 200, height: 40, borderColor: 'gray', borderWidth: 1}}
              onChangeText={(text) => this.setState({roomID: text})}
              value={this.state.roomID}
            />
            <TouchableHighlight
              onPress={this._press}>
              <Text>Enter room</Text>
            </TouchableHighlight>
          </View>) : null
        }
        <RTCView streamURL={this.state.selfViewSrc} style={styles.selfView}/>
        {
          mapHash(this.state.remoteList, function(remote, index) {
            return <RTCView key={index} streamURL={remote} style={styles.remoteView}/>
          })
        }
      </View>
    );
  }
});

const styles = StyleSheet.create({
  selfView: {
    width: 200,
    height: 150,
  },
  remoteView: {
    width: 200,
    height: 150,
  },
  container: {
    flex: 1,
    justifyContent: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  listViewContainer: {
    height: 150,
  },
});

export default RCTWebRTCDemo;

1 个答案:

答案 0 :(得分:0)

回购包含WebRTC不推荐使用的代码。您可以从developer.mozilla.org检查webrtc api,并查看react-native-webrtc库以查看新的api。

这是工作代码:

!请注意,在const url = 'http...';中,您必须输入服务器端链接

import React, { Component }                                                                 from "react";
import { Text, TouchableHighlight, View, YellowBox }                                        from "react-native";
import { getUserMedia, RTCIceCandidate, RTCPeerConnection, RTCSessionDescription, RTCView } from "react-native-webrtc";
import io                                                                                   from "socket.io-client";
import s                                                                                    from './style';


YellowBox.ignoreWarnings(['Setting a timer', 'Unrecognized WebSocket connection', 'ListView is deprecated and will be removed']);

const url = 'https://ac07cd91.ngrok.io';
const socket = io.connect(url, { transports: ["websocket"] });
const configuration = { iceServers: [{ urls: "stun:stun.l.google.com:19302" }] };

let pcPeers = {};
let container;
let localStream;

const initStream = () => {
  let videoSourceId;
  let isFront = true;
  let constrains = {
    audio: false,
    video: {
      mandatory: {
        minWidth: 640,
        minHeight: 360,
        minFrameRate: 30,
      },
      facingMode: isFront ? "user" : "environment",
      optional: videoSourceId ? [{ sourceId: videoSourceId }] : [],
    },
  };
  let callback = stream => {
    localStream = stream;

    container.setState({
      localStream: stream.toURL(),
      status: "ready",
      info: "Welcome to WebRTC demo",
    });
  };

  getUserMedia(constrains, callback, logError);
};

const join = roomID => {
  let state = 'join';
  let callback = socketIds => {
    for (const i in socketIds) {
      if (socketIds.hasOwnProperty(i)) {
        const socketId = socketIds[i];
        createPC(socketId, true);
      }
    }
  };

  socket.emit(state, roomID, callback);
};

const createPC = (socketId, isOffer) => {
  const peer = new RTCPeerConnection(configuration);

  pcPeers = {
    ...pcPeers,
    [socketId]: peer,
  };

  peer.addStream(localStream);

  peer.onicecandidate = event => {
    //console.log("onicecandidate", event.candidate);
    if (event.candidate) {
      socket.emit("exchange", { to: socketId, candidate: event.candidate });
    }
  };

  peer.onnegotiationneeded = () => {
    //console.log("onnegotiationneeded");
    if (isOffer) {
      createOffer();
    }
  };

  peer.oniceconnectionstatechange = event => {
    //console.log("oniceconnectionstatechange", event.target.iceConnectionState);
    if (event.target.iceConnectionState === "completed") {
      console.log('event.target.iceConnectionState === "completed"');
      setTimeout(() => {
        getStats();
      }, 1000);
    }
    if (event.target.iceConnectionState === "connected") {
      console.log('event.target.iceConnectionState === "connected"');
    }
  };
  peer.onsignalingstatechange = event => {
    console.log("on signaling state change", event.target.signalingState);
  };

  peer.onaddstream = event => {
    //console.log("onaddstream", event.stream);
    const remoteList = container.state.remoteList;

    remoteList[socketId] = event.stream.toURL();
    container.setState({
      info: "One peer join!",
      remoteList: remoteList,
    });
  };
  peer.onremovestream = event => {
    console.log("on remove stream", event.stream);
  };

  const createOffer = () => {
    let callback = desc => {
      //console.log("createOffer", desc);
      peer.setLocalDescription(desc, callback2, logError);
    };
    let callback2 = () => {
      //console.log("setLocalDescription", peer.localDescription);
      socket.emit("exchange", { to: socketId, sdp: peer.localDescription });
    };

    peer.createOffer(callback, logError);
  };

  return peer;
};

socket.on("connect", () => {
  console.log("connect");
});
socket.on("leave", socketId => {
  leave(socketId);
});
socket.on("exchange", data => {
  exchange(data);
});

const leave = socketId => {
  console.log("leave", socketId);

  const peer = pcPeers[socketId];

  peer.close();

  delete pcPeers[socketId];

  const remoteList = container.state.remoteList;

  delete remoteList[socketId];

  container.setState({
    info: "One peer leave!",
    remoteList: remoteList,
  });
};

const exchange = data => {
  const fromId = data.from;
  let pc;
  if (fromId in pcPeers) {
    pc = pcPeers[fromId];
  } else {
    pc = createPC(fromId, false);
  }

  if (data.sdp) {
    //console.log("exchange sdp", data);
    let sdp = new RTCSessionDescription(data.sdp);

    let callback = () => pc.remoteDescription.type === "offer" ? pc.createAnswer(callback2, logError) : null;
    let callback2 = desc => pc.setLocalDescription(desc, callback3, logError);
    let callback3 = () => socket.emit("exchange", { to: fromId, sdp: pc.localDescription });

    pc.setRemoteDescription(sdp, callback, logError);
  } else {
    //console.log("exchange candidate", data);
    pc.addIceCandidate(new RTCIceCandidate(data.candidate));
  }
};

const logError = error => {
  console.log("logError", error);
};

const mapHash = (hash, func) => {
  const array = [];
  for (const key in hash) {
    if (hash.hasOwnProperty(key)) {
      const obj = hash[key];
      array.push(func(obj, key));
    }
  }
  return array;
};

const getStats = () => {
  const pc = pcPeers[Object.keys(pcPeers)[0]];
  if (pc.getRemoteStreams()[0] && pc.getRemoteStreams()[0].getAudioTracks()[0]) {
    const track = pc.getRemoteStreams()[0].getAudioTracks()[0];

    //console.log("track", track);

    pc.getStats(
      track,
      function (report) {
        //console.log("getStats report", report);
      },
      logError,
    );
  }
};

class App extends Component {
  state = {
    info: "Initializing",
    status: "init",
    roomID: "abc",
    isFront: true,
    localStream: null,
    remoteList: {},
  };

  componentDidMount() {
    container = this;
    initStream();
  }

  _press = () => {
    this.setState({
      status: "connect",
      info: "Connecting",
    });

    join(this.state.roomID);
  };

  button = () => (
    <TouchableHighlight style={s.button} onPress={this._press}>
      <Text style={s.buttonText}>Enter room</Text>
    </TouchableHighlight>
  );

  render() {
    const { status, info, localStream, remoteList } = this.state;

    return (
      <View style={s.container}>
        <Text style={s.welcome}>{info}</Text>

        {status === "ready" ? this.button() : null}

        <RTCView streamURL={localStream} style={s.selfView}/>

        {
          mapHash(remoteList, (remote, index) => {
            return (<RTCView key={index} streamURL={remote} style={s.remoteView}/>);
          })
        }
      </View>
    );
  }
}

export default App;
import { StyleSheet } from "react-native";


const s = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
  },
  selfView: {
    flex: 0.5,
    justifyContent: 'center',
    alignItems: 'center',
    height: 150,
  },
  remoteView: {
    flex: 0.5,
    justifyContent: 'center',
    alignItems: 'center',
    height: 150,
  },
  welcome: {
    fontSize: 20,
    textAlign: "center",
    margin: 10,
    borderRadius: 10,
  },
  buttonText: {
    textAlign: 'center',
    borderWidth: 1,
    borderColor: 'black',
    width: '50%',
    margin: 10,
    padding: 10,
    borderRadius: 10,
  },
  button: {
    alignItems: 'center',
  },
});

export default s;

回购链接:RCTWebRTCDemo

请考虑仅针对Android进行配置,如果您希望同时配置iOS / Android,请选中RCTWebRTCDemo2。