我正在尝试使用React-native实现视频流我在用webrtc软件包(https://github.com/oney/reaeact-native-webrtc)插座-IO-CLIENT和 Oney/rctwebrtcdemo(https://github.com/oney/rctwebrtcdemo)当启用debug 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;
repo具有从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;
链接到repo:rctwebrtcdemo
如果您要同时配置iOS/Android Check rctwebrtcdemo2,请考虑仅为Android配置。