Last active
December 27, 2018 17:49
-
-
Save LFSCamargo/3e03c53c538206ae42d01b6f208791be to your computer and use it in GitHub Desktop.
Code for the webRTC on React native
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @flow | |
import React, { Component } from 'react'; | |
import { | |
View, | |
Text, | |
StyleSheet, | |
Dimensions, | |
TouchableOpacity, | |
Image, | |
StatusBar, | |
AsyncStorage, | |
} from 'react-native'; | |
import { | |
RTCPeerConnection, | |
RTCIceCandidate, | |
RTCSessionDescription, | |
RTCView, | |
MediaStreamTrack, | |
getUserMedia, | |
} from 'react-native-webrtc'; | |
import { connect } from 'react-redux'; | |
import { NavigationInjectedProps } from 'react-navigation'; | |
import sendRemoteMessage from '../../actions/Messaging/SendRemoteMessage'; | |
import Colors from '../../config/Colors'; | |
import { Messaging } from '../../reducers/Messaging/Messaging'; | |
const { width, height } = Dimensions.get('window'); | |
const peerVideoHeight = Math.round(height * 0.2); | |
type Props = { | |
sendRemoteMessage: (message: Messaging) => void, | |
candidate: string, | |
description: string, | |
hasReceivedDescription: boolean, | |
} & NavigationInjectedProps; | |
type State = { | |
muted: boolean, | |
videoEnabled: boolean, | |
videoURL: string, | |
remoteVideoURL: string, | |
isFront: boolean, | |
username: string, | |
}; | |
class Video extends Component<Props, State> { | |
constructor(props: Props) { | |
super(props); | |
const configuration = { | |
iceServers: [ | |
{ | |
username: 'f2e4e42e-eaa4-11e8-bfaf-ee023c3d6cbc', | |
url: 'stun:sp-turn1.xirsys.com', | |
credential: 'f2e4e49c-eaa4-11e8-9904-278c135c7895', | |
}, | |
{ | |
username: 'f2e4e42e-eaa4-11e8-bfaf-ee023c3d6cbc', | |
url: 'turn:sp-turn1.xirsys.com:80?transport=udp', | |
credential: 'f2e4e49c-eaa4-11e8-9904-278c135c7895', | |
}, | |
{ | |
username: 'f2e4e42e-eaa4-11e8-bfaf-ee023c3d6cbc', | |
url: 'turn:sp-turn1.xirsys.com:3478?transport=udp', | |
credential: 'f2e4e49c-eaa4-11e8-9904-278c135c7895', | |
}, | |
{ | |
username: 'f2e4e42e-eaa4-11e8-bfaf-ee023c3d6cbc', | |
url: 'turn:sp-turn1.xirsys.com:80?transport=tcp', | |
credential: 'f2e4e49c-eaa4-11e8-9904-278c135c7895', | |
}, | |
{ | |
username: 'f2e4e42e-eaa4-11e8-bfaf-ee023c3d6cbc', | |
url: 'turn:sp-turn1.xirsys.com:3478?transport=tcp', | |
credential: 'f2e4e49c-eaa4-11e8-9904-278c135c7895', | |
}, | |
{ | |
username: 'f2e4e42e-eaa4-11e8-bfaf-ee023c3d6cbc', | |
url: 'turns:sp-turn1.xirsys.com:443?transport=tcp', | |
credential: 'f2e4e49c-eaa4-11e8-9904-278c135c7895', | |
}, | |
{ | |
username: 'f2e4e42e-eaa4-11e8-bfaf-ee023c3d6cbc', | |
url: 'turns:sp-turn1.xirsys.com:5349?transport=tcp', | |
credential: 'f2e4e49c-eaa4-11e8-9904-278c135c7895', | |
}, | |
], | |
}; | |
this.pc = new RTCPeerConnection(configuration); | |
this.state = { | |
username: '', | |
muted: false, | |
videoEnabled: false, | |
videoURL: '', | |
remoteVideoURL: '', | |
isFront: true, | |
remoteDescriptionSet: false, | |
}; | |
} | |
pc: RTCPeerConnection = {}; | |
componentDidUpdate(prevProps: Props) { | |
const { calling } = this.props.navigation.state.params; | |
if (!calling) { | |
if (this.props.hasReceivedDescription) { | |
if (prevProps.candidate !== this.props.candidate) { | |
this.setIceCandidate(this.props.candidate); | |
} | |
} | |
} | |
if (calling) { | |
if ( | |
!this.state.remoteDescriptionSet && | |
prevProps.description !== this.props.description && | |
this.props.description | |
) { | |
this.setRemoteDescription(this.props.description); | |
} | |
} | |
} | |
async componentDidMount() { | |
console.log(this.props); | |
const { calling, callingTo } = this.props.navigation.state.params; | |
const username = await AsyncStorage.getItem('username'); | |
this.setState({ | |
username, | |
}); | |
MediaStreamTrack.getSources(sourceInfos => { | |
console.log(sourceInfos); | |
let videoSourceId; | |
sourceInfos.forEach(element => { | |
videoSourceId = element.id; | |
}); | |
getUserMedia( | |
{ | |
audio: true, | |
video: { | |
mandatory: { | |
minWidth: 500, | |
minHeight: 300, | |
minFrameRate: 30, | |
}, | |
facingMode: this.state.isFront ? 'user' : 'environment', | |
optional: videoSourceId ? [{ sourceId: videoSourceId }] : [], | |
}, | |
}, | |
stream => { | |
this.pc.addStream(stream); | |
this.setState({ | |
videoURL: stream.toURL(), | |
}); | |
}, | |
e => console.log(e), | |
); | |
}); | |
if (calling) { | |
console.log('Ligando', calling); | |
this.pc.createOffer( | |
desc => { | |
this.pc.setLocalDescription( | |
desc, | |
() => { | |
console.log('Offer', desc); | |
this.props.sendRemoteMessage({ | |
to: callingTo, | |
from: username, | |
payload: { | |
description: JSON.stringify(desc), | |
}, | |
}); | |
this.props.sendRemoteMessage({ | |
to: callingTo, | |
from: username, | |
payload: { | |
text: 'Is calling you', | |
}, | |
}); | |
}, | |
e => { | |
throw e; | |
}, | |
); | |
}, | |
e => { | |
throw e; | |
}, | |
); | |
} | |
if (!calling) { | |
console.log('Ligando', calling); | |
this.setRemoteDescription(this.props.description); | |
this.pc.createAnswer( | |
desc => { | |
this.pc.setLocalDescription( | |
desc, | |
() => { | |
console.log('Answer', JSON.stringify(desc)); | |
this.props.sendRemoteMessage({ | |
to: callingTo, | |
from: username, | |
payload: { | |
description: JSON.stringify(desc), | |
}, | |
}); | |
}, | |
e => { | |
throw e; | |
}, | |
); | |
}, | |
e => { | |
throw e; | |
}, | |
); | |
} | |
this.pc.onicecandidate = e => { | |
if (e.candidate) { | |
this.props.sendRemoteMessage({ | |
to: callingTo, | |
from: username, | |
payload: { | |
candidate: JSON.stringify(e.candidate), | |
}, | |
}); | |
} | |
}; | |
this.pc.oniceconnectionstatechange = () => { | |
console.log('Ice Connection State', this.pc.iceConnectionState); | |
}; | |
this.pc.onaddstream = e => { | |
if (e) { | |
console.log(e.stream); | |
this.pc.addStream(e.stream); | |
this.setState({ | |
remoteVideoURL: e.strem.toURL(), | |
}); | |
} | |
}; | |
} | |
setRemoteDescription = (remoteDesc: string) => { | |
console.log('Remote Description', remoteDesc); | |
this.setState({ | |
remoteDescriptionSet: true, | |
}); | |
const remoteDescription = new RTCSessionDescription(JSON.parse(remoteDesc)); | |
this.pc.setRemoteDescription(remoteDescription); | |
const { calling, callingTo } = this.props.navigation.state.params; | |
if (calling) { | |
this.props.sendRemoteMessage({ | |
to: callingTo, | |
from: this.state.username, | |
payload: { | |
hasReceivedDescription: true, | |
}, | |
}); | |
} | |
}; | |
setIceCandidate = candidate => { | |
console.log('ICE Candidate', candidate); | |
const iceCandidate = new RTCIceCandidate(JSON.parse(candidate)); | |
this.pc.addIceCandidate(iceCandidate); | |
}; | |
setVideoEnabled = () => { | |
const { videoEnabled } = this.state; | |
this.setState({ | |
videoEnabled: !videoEnabled, | |
}); | |
}; | |
setMuted = () => { | |
const { muted } = this.state; | |
this.setState({ | |
muted: !muted, | |
}); | |
}; | |
render() { | |
console.log('PROPS UPDATED', this.props); | |
const { callingTo } = this.props.navigation.state.params; | |
const { muted, videoEnabled, videoURL, remoteVideoURL } = this.state; | |
return ( | |
<View style={styles.container}> | |
<StatusBar barStyle="light-content" /> | |
<RTCView streamURL={remoteVideoURL} style={styles.remoteVideoContainer} /> | |
<RTCView streamURL={videoURL} style={styles.videoContainer} /> | |
<View style={styles.textContainer}> | |
<Text style={styles.username}>{callingTo}</Text> | |
<Text style={styles.time}>00:00</Text> | |
</View> | |
<View style={styles.buttonsContainer}> | |
<TouchableOpacity onPress={this.setMuted} style={styles.actionButtons}> | |
{muted ? ( | |
<Image style={styles.icon} source={require('../../images/videoicon.png')} /> | |
) : ( | |
<Image style={styles.icon} source={require('../../images/videoofficon.png')} /> | |
)} | |
</TouchableOpacity> | |
<TouchableOpacity onPress={this.hangup} style={styles.hangupButton}> | |
<Image style={styles.callIcon} source={require('../../images/phoneicon.png')} /> | |
</TouchableOpacity> | |
<TouchableOpacity onPress={this.setVideoEnabled} style={styles.actionButtons}> | |
{videoEnabled ? ( | |
<Image style={styles.icon} source={require('../../images/micicon.png')} /> | |
) : ( | |
<Image style={styles.icon} source={require('../../images/micofficon.png')} /> | |
)} | |
</TouchableOpacity> | |
</View> | |
</View> | |
); | |
} | |
} | |
const styles = StyleSheet.create({ | |
icon: { | |
width: 24, | |
height: 24, | |
tintColor: 'black', | |
}, | |
callIcon: { | |
width: 30, | |
height: 11, | |
}, | |
hangupButton: { | |
width: 70, | |
height: 70, | |
borderRadius: 70 / 2, | |
backgroundColor: Colors.red, | |
alignItems: 'center', | |
justifyContent: 'center', | |
}, | |
actionButtons: { | |
width: 70, | |
height: 70, | |
borderRadius: 70 / 2, | |
backgroundColor: Colors.white, | |
alignItems: 'center', | |
justifyContent: 'center', | |
}, | |
container: { | |
flex: 1, | |
}, | |
videoContainer: { | |
width: (peerVideoHeight * 9) / 16, | |
height: peerVideoHeight, | |
borderRadius: 10, | |
backgroundColor: 'black', | |
position: 'absolute', | |
bottom: 120, | |
right: 30, | |
transform: [ | |
{ | |
rotateY: '180deg', | |
}, | |
], | |
}, | |
remoteVideoContainer: { | |
transform: [ | |
{ | |
rotateY: '180deg', | |
}, | |
], | |
width, | |
height, | |
backgroundColor: 'black', | |
position: 'absolute', | |
top: 0, | |
bottom: 0, | |
right: 0, | |
left: 0, | |
}, | |
username: { | |
fontSize: 25, | |
color: Colors.white, | |
}, | |
time: { | |
fontSize: 20, | |
color: Colors.white, | |
}, | |
textContainer: { | |
position: 'absolute', | |
left: 30, | |
right: 30, | |
top: 90, | |
width: width - 60, | |
flexDirection: 'column', | |
alignItems: 'center', | |
justifyContent: 'center', | |
}, | |
buttonsContainer: { | |
position: 'absolute', | |
left: 30, | |
right: 30, | |
bottom: 30, | |
width: width - 60, | |
flexDirection: 'row', | |
alignItems: 'center', | |
justifyContent: 'space-between', | |
}, | |
}); | |
const mapDispatchToProps = dispatch => ({ | |
sendRemoteMessage: (message: Messaging) => dispatch(sendRemoteMessage(message)), | |
}); | |
const mapStateToProps = state => ({ | |
description: state.messaging.description, | |
candidate: state.messaging.candidate, | |
hasReceivedDescription: state.messaging.hasReceivedDescription, | |
}); | |
export default connect( | |
mapStateToProps, | |
mapDispatchToProps, | |
)(Video); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment