import React, { Component } from 'react';
import {
View,
StyleSheet,
Image,
TouchableHighlight,
Dimensions,
SafeAreaView,
TouchableOpacity,
ActivityIndicator,
Text
} from “react-native”;
import {
RTCPeerConnection,
RTCIceCandidate,
RTCSessionDescription,
RTCView,
MediaStream,
MediaStreamTrack,
mediaDevices
} from ‘react-native-webrtc’;
import Icon from “react-native-vector-icons/FontAwesome5”;
const screenHeight =Math.round(Dimensions.get(‘window’).height);
const screenWidth = Math.round(Dimensions.get(‘window’).width);
class Videocall extends Component {
static navigationOptions = ({ navigation }) => {
return {
title: navigation.getParam(‘username’,""),
};
};
constructor(){
super();
this.handleJoin=this.handleJoin.bind(this)
this.setupWebRTC=this.setupWebRTC.bind(this)
this.onConnectionStateChange=this.onConnectionStateChange.bind(this)
this.onAddStream=this.onAddStream.bind(this)
this.onIceCandidate=this.onIceCandidate.bind(this)
this.handleOffer=this.handleOffer.bind(this)
this.onReceiveOffer=this.onReceiveOffer.bind(this)
this.onReceiveAnswer=this.onReceiveAnswer.bind(this)
this.state={
user:{},
receiverUserId:0,
socket:{},
caller:false,
localStreamURL:null,
remoteStreamURL:null,
iceConnectionState:'',
iceCandidates:[],
isAnswerReceived:false,
isOfferReceived:false,
offer:{},
answer:{},
localVideo:{},
remoteVideo:{},
localVideoStream:{},
localStream:'',
desc:"",
isMuted:false,
pc:'',
isConnected:false,
}
}
handleNegotiation = async () => {
const { pc } = this;
let offerDescription = await pc.createOffer( {
offerToReceiveAudio: true,
offerToReceiveVideo: true,
voiceActivityDetection: true
} );
await pc.setLocalDescription( offerDescription );
// add some code here to send the session description.
}
// add some code here to send the session description.
async setupWebRTC() {
const configuration = {“iceServers”: [{“url”: “stun:stun1.l.google.com:19302”}]};
const pc = new RTCPeerConnection(configuration);
pc.onconnectionstatechange=this.onConnectionStateChange
pc.onaddstream = this.onAddStream
pc.onicecandidate = this.onIceCandidate
pc.addStream(this.state.localVideoStream);
this.pc = pc;
}
async handleJoin(e) {
await this.setupWebRTC();
const { pc } = this;
try {
// Create Offer
pc.createOffer({offerToReceiveVideo:true,
offerToReceiveAudio:true}).then(desc => {
pc.setLocalDescription(desc).then(() => {
this.setState({desc});
});
});
} catch (error) {
console.log(error);
}
}
onConnectionStateChange(e) {
this.setState({
iceConnectionState: e.target.iceConnectionState
})
}
onAddStream(e) {
this.setState({
remoteVideo:e.stream,
remoteStreamURL: e.stream.toURL()
})
this.remoteStream = e.stream
}
onIceCandidate(e) {
const { candidate } = e;
console.log(candidate,123);
if (candidate) {
const { iceCandidates } = this.state;
this.setState({
iceCandidates: [candidate]
});
if (Array.isArray(iceCandidates)) {
this.setState({
iceCandidates: [...iceCandidates, candidate]
})
} else {
this.setState({
iceCandidates: [candidate]
})
}
} else {
if (this.state.iceCandidates.length > 1) {
this.setState({ isConnected : true});
//send this to signaling server
let offerOrAnswer = {
receiverUserId:this.props.navigation.getParam("receiver"),
type: this.state.isOfferReceived ? 'answer' : 'offer',
payload: {
description: this.pc.localDescription,
iceCandidates: this.state.iceCandidates
}
}
// send offer to signaling server
if (offerOrAnswer.type == "offer") {
this.state.socket.emit('offer', JSON.stringify(offerOrAnswer))
/*
setTimeout(() => {
this.state.socket.emit('offer', JSON.stringify(offerOrAnswer));
}, 5000);
*/
} else {
this.state.socket.emit('answer', JSON.stringify(offerOrAnswer));
}
} else {
console.error("No candidates found");
}
}
}
onReceiveOffer(offer) {
this.setState({
offer:JSON.parse(offer),
isOfferReceived: true
}, () => {
this.handleOffer();
})
}
handleOffer() {
const { payload } = this.state.offer;
this.setupWebRTC();
const { pc } = this;
var offerSdp = { "sdp": payload.description.sdp, "type": "offer" };
pc.setRemoteDescription(new RTCSessionDescription(offerSdp))
if (Array.isArray(payload.candidates)) {
payload.candidates.forEach((c) => peer.addIceCandidate(new RTCIceCandidate(c)))
}
try {
// Create Offer
pc.createAnswer().then(answer => {
pc.setLocalDescription(answer).then(() => {
this.setState({answer});
});
});
} catch (error) {
console.log(error);
}
}
onReceiveAnswer(answer) {
const { payload } = JSON.parse(answer);
var answerSdp = { "sdp": payload.description.sdp, "type": "answer" };
//set answersdp to current peer RemoteDescription.
this.pc.setRemoteDescription(new RTCSessionDescription(answerSdp))
payload.iceCandidates.forEach(c => this.pc.addIceCandidate(new RTCIceCandidate(c)))
this.setState({
answer:JSON.parse(answer),
isAnswerReceived: true
}, () => {
})
}
componentWillUnmount() {
this.state.socket.emit('call_close',{
receiverUserId:this.state.receiverUserId
});
}
componentDidMount = async() =>{
const self = this;
const { navigation } = this.props;
const user = navigation.getParam('user', {});
const caller = navigation.getParam('caller', false);
const socket = navigation.getParam('socket', {});
const receiverUserId = navigation.getParam('receiver',0);
this.setState({
user,
socket,
caller,
receiverUserId
});
socket.on('call_disconnect',function (data) {
});
socket.on('offer', function (offer) {
self.onReceiveOffer(offer);
});
socket.on('answer', function(answer){
self.onReceiveAnswer(answer);
});
const isFront = true;
const devices = await mediaDevices.enumerateDevices();
const facing = isFront ? 'front' : 'environment';
const videoSourceId = devices.find(device => device.kind === 'videoinput' && device.facing === facing);
const facingMode = isFront ? 'user' : 'environment';
const constraints = {
audio: true,
video: {
mandatory: {
minWidth: 500, // Provide your own width, height and frame rate here
minHeight: 300,
minFrameRate: 30,
},
facingMode,
optional: videoSourceId ? [{sourceId: videoSourceId}] : [],
},
};
const newStream = await mediaDevices.getUserMedia(constraints);
this.setState({
localVideoStream:newStream,
localStreamURL: newStream.toURL()
})
if(caller) {
this.handleJoin();
}
// WebRTC getUserMedia setup
/*
let isFront = true;
mediaDevices.enumerateDevices().then(sourceInfos => {
let videoSourceId;
for (let i = 0; i < sourceInfos.length; i++) {
const sourceInfo = sourceInfos[i];
if(sourceInfo.facing == (isFront ? "front" : "back")) {
videoSourceId = sourceInfo.deviceId;
console.log(sourceInfo);
}
}
const localStream = mediaDevices.getUserMedia({
audio: true,
video: {
mandatory: {
minWidth: 500, // Provide your own width, height and frame rate here
minHeight: 300,
minFrameRate: 30
},
facingMode: (isFront ? "user" : "environment"),
optional: (videoSourceId ? [{sourceId: videoSourceId}] : [])
}
})
.then(stream => {
// Got stream!
console.log("getUserMedia----stream",stream);
this.setState({
localVideoStream:stream,
localStreamURL: stream.toURL()
})
}).then(()=>{
if(caller) {
this.handleJoin();
}
})
.catch(error => {
// Log error
console.log(error);
});
this.setState({
localStream
})
});
*/
//handling join call
};
onCallHangUp=()=>{
this.state.localVideoStream.onremovetrack();
/*
pc.removeStream(this.state.localVideoStream);
if (this.remoteStream){
pc.removeStream(this.remoteStream);
}
this.props.navigation.goBack();
*/
};
switchCamera = async () => {
this.state.localVideoStream.getVideoTracks().forEach(track => track._switchCamera());
};
toggleMute = () => {
if (!this.remoteStream) return;
this.state.localVideoStream.getAudioTracks().forEach(track => {
track.enabled = !track.enabled;
this.setState({
isMuted:!track.enabled
})
});
};
render() {
return (
<SafeAreaView style={styles.root}>
<TouchableOpacity onPress={this.switchCamera} style={styles.changeCamera} >
<Image style={{ width:60,height:60}} source={require('../../../../assets/image/app/turn-camera.png') } />
</TouchableOpacity>
<RTCView streamURL={this.state.remoteStreamURL} style={styles.localVideo} />
<RTCView streamURL={this.state.localStreamURL} style={styles.remoteVideo} />
{!this.state.isConnected &&
<View style={styles.loading}>
<ActivityIndicator size={30} color={"white"}/>
<Text style={{color: 'white', fontSize: 14, marginLeft: 15, fontFamily: 'Rubik-Bold'}}>Bağlanıyor,
Lütfen bekleyiniz...</Text>
</View>
}
<View style={styles.buttonContainer}>
<TouchableHighlight style={[styles.button2, styles.microphoneButton]} onPress={() => this.toggleMute()}>
<Icon name={(this.state.isMuted) ? 'microphone-alt-slash' : 'microphone-alt' } size={20} color={'white'}/>
</TouchableHighlight>
<TouchableHighlight style={[styles.button, styles.buttonCall]} onPress={() => this.onCallHangUp()}>
<Icon name={"phone-slash"} size={20} color={"white"} />
</TouchableHighlight>
</View>
</SafeAreaView>
)
}
}
export default Videocall;
const remoteVideoWidth=Math.round(screenWidth-114);
const styles = StyleSheet.create({
root: {
flex: 1,
},
changeCamera:{
top: 30,
right: 10,
position: "absolute",
zIndex:1,
},
localVideo: {
top: 0,
left: 0,
width: screenWidth,
height: screenHeight,
backgroundColor:"black",
position: "absolute"
},
remoteVideo: {
top: 30,
left: 30,
width: 114,
height: 170,
backgroundColor: "rgba(230, 230, 230,1)",
position: "absolute"
},
buttonContainer:{
top: screenHeight-170,
//left: screenWidth/2.4,
flexDirection:'row',
justifyContent:'center',
marginTop:20,
alignItems:'center',
},
button: {
width:60,
height:60,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
marginBottom:20,
borderRadius:30,
margin:10,
shadowColor: 'black',
shadowOpacity: .8,
shadowOffset: {
height:2,
width:-2
},
elevation:4,
},
button2: {
width:40,
height:40,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
marginBottom:20,
borderRadius:30,
margin:10,
shadowColor: 'black',
shadowOpacity: .8,
shadowOffset: {
height:2,
width:-2
},
elevation:4,
},
buttonCall: {
backgroundColor: "red",
},
microphoneButton: {
backgroundColor: "rgba(255,255,255,0.4)",
},
icon: {
width:35,
height:35,
}
,
loading:{
flexDirection:'row',
justifyContent:'center',
alignItems:'center',
backgroundColor: "rgba(0, 0, 0,100.7)",
position:'absolute',
top:'50%',
width:'90%',
alignSelf: 'center',
height:50,
}
});