我有一个项目,编写了一个组件,但它运行起来很奇怪。
代码:
const Call = ({ navigation }) => {
const [localStream, setlocalStream] = useState(null)
const [remoteStream, setRemoteStream] = useState(new MediaStream())
const [localMicOn, setlocalMicOn] = useState(true)
const [localWebcamOn, setlocalWebcamOn] = useState(true)
const [type, setType] = useState('JOIN')
const [callerId] = useState(
Math.floor(100 + Math.random() * 900).toString(),
)
const otherUserId = useRef(null)
const socket = useRef(null)
const pc = useRef(new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
}))
const candidates = useRef([])
const remoteRTCMessage = useRef(null)
function answerCall(data) {
socket.current.emit('answerCall', data);
}
function sendCall(data) {
socket.current.emit('call', data);
}
function sendICEcandidate(data) {
socket.current.emit('iICEcandidate', data);
}
useEffect(() => {
socket.current = SocketIOClient('http://myserver.com:3500', {
transports: ['websocket'], query: { callerId }
})
socket.current.on('newCall', data => {
console.log('\n newCall data: \n', data)
remoteRTCMessage.current = data.rtcMessage
otherUserId.current = data.callerId
setType('INCOMING_CALL')
})
socket.current.on('callAnswered', data => {
console.log('\n callAnswered data: \n', candidates)
remoteRTCMessage.current = data.rtcMessage
pc.current.setRemoteDescription(data.rtcMessage)
setType('WEBRTC_ROOM')
})
socket.current.on('oICEcandidate', data => {
if (pc.current.remoteDescription !== null) {
pc.current.addIceCandidate(
new RTCIceCandidate({
candidate: data.rtcMessage.candidate,
sdpMid: data.rtcMessage.id,
sdpMLineIndex: data.rtcMessage.label,
}))
.then(data => { console.log('\n oICEcandidate ice SUCCESS \n') })
.catch(err => { console.log('\n ICEERROR \n', err) })
} else {
candidates.current.push(data.rtcMessage)
}
console.log('\n oICEcandidate data: \n', data)
})
}, [])
useEffect(() => {
pc.current.addEventListener('connectionstatechange', event => {
console.log('\n listener connectionstatechange event: \n', event)
})
pc.current.addEventListener('icecandidate', event => {
console.log('\n listener icecandidate event: \n', event)
if (!event.candidate) {
return
} else {
sendICEcandidate({
calleeId: otherUserId.current,
rtcMessage: {
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate,
},
})
}
})
pc.current.addEventListener('icecandidateerror', event => {
console.log('\n listener icecandidateerror event: \n', event)
})
pc.current.addEventListener('iceconnectionstatechange', event => {
console.log('\n listener iceconnectionstatechange event: \n', event)
})
pc.current.addEventListener('negotiationneeded', event => {
console.log('\n listener negotiationneeded event: \n', event)
pc.current.createOffer()
.then(offer => {
pc.current.setLocalDescription(offer)
return Promise.resolve(offer)
})
.then(offer => {
sendCall({
calleeId: otherUserId.current,
rtcMessage: offer,
})
})
.catch(err => console.error(err))
})
pc.current.addEventListener('signalingstatechange', event => {
console.log('\n listener signalingstatechange event: \n', event)
})
pc.current.addEventListener('track', event => {
console.log('\n listener track event: \n', event)
const newStream = remoteStream
newStream.addTrack(event.track)
setRemoteStream(newStream)
})
}, [])
const processCall = async () => {
let mediaConstraints = {
audio: true,
video: {
frameRate: 30,
facingMode: 'user'
}
}
try {
const mediaStream = await mediaDevices.getUserMedia(mediaConstraints)
setlocalStream(mediaStream)
mediaStream.getTracks().forEach((track) => {
pc.current.addTrack(track, mediaStream)
})
//console.log("\nDEBUG\n", pc.current)
} catch (err) {
console.log(console.log('\n processCall error: \n', err))
}
}
const processAccept = async () => {
let mediaConstraints = {
audio: true,
video: {
frameRate: 30,
facingMode: 'user'
}
}
try {
pc.current.setRemoteDescription(remoteRTCMessage.current)
const answer = await pc.current.createAnswer()
await pc.current.setLocalDescription(answer)
const mediaStream = await mediaDevices.getUserMedia(mediaConstraints)
setlocalStream(mediaStream)
answerCall({
callerId: otherUserId.current,
rtcMessage: answer,
})
candidates.current.map(candidate => pc.current.addIceCandidate(
new RTCIceCandidate({
candidate: candidate.candidate,
sdpMid: candidate.id,
sdpMLineIndex: candidate.label,
}))
.then(data => { console.log('\n processAccept ice SUCCESS \n') })
.catch(err => { console.log('Errorseew', err) })
)
candidates.current = []
//console.log("\nDEBUG\n", pc.current)
} catch (err) {
console.log(console.log('\n processCall error: \n', err))
}
}
function switchCamera() {
localStream.getVideoTracks().forEach(track => {
track._switchCamera()
})
}
function toggleCamera() {
localWebcamOn ? setlocalWebcamOn(false) : setlocalWebcamOn(true)
localStream.getVideoTracks().forEach(track => {
localWebcamOn ? (track.enabled = false) : (track.enabled = true)
})
}
function toggleMic() {
localMicOn ? setlocalMicOn(false) : setlocalMicOn(true)
localStream.getAudioTracks().forEach(track => {
localMicOn ? (track.enabled = false) : (track.enabled = true)
})
}
function leave() {
pc.current.close()
setlocalStream(null)
setType('JOIN')
}
用户界面代码:
const JoinScreen = () => {
return (
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={{
flex: 1,
backgroundColor: '#050A0E',
justifyContent: 'center',
paddingHorizontal: 42,
}}>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<>
<View
style={{
padding: 35,
backgroundColor: '#1A1C22',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 14,
}}>
<Text
style={{
fontSize: 18,
color: '#D0D4DD',
}}>
Your Caller ID
</Text>
<View
style={{
flexDirection: 'row',
marginTop: 12,
alignItems: 'center',
}}>
<Text
style={{
fontSize: 32,
color: '#ffff',
letterSpacing: 6,
}}>
{callerId}
</Text>
</View>
</View>
<View
style={{
backgroundColor: '#1A1C22',
padding: 40,
marginTop: 25,
justifyContent: 'center',
borderRadius: 14,
}}>
<Text
style={{
fontSize: 18,
color: '#D0D4DD',
}}>
Enter call id of another user
</Text>
<TextInputContainer
placeholder={'Enter Caller ID'}
value={otherUserId.current}
setValue={text => {
otherUserId.current = text;
console.log('TEST', otherUserId.current);
}}
keyboardType={'number-pad'}
/>
<TouchableOpacity
onPress={() => {
setType('OUTGOING_CALL')
processCall()
}}
style={{
height: 50,
backgroundColor: '#5568FE',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 12,
marginTop: 16,
}}>
<Text
style={{
fontSize: 16,
color: '#FFFFFF',
}}>
Call Now
</Text>
</TouchableOpacity>
</View>
</>
</TouchableWithoutFeedback>
</KeyboardAvoidingView>
);
};
const OutgoingCallScreen = () => {
return (
<View
style={{
flex: 1,
justifyContent: 'space-around',
backgroundColor: '#050A0E',
}}>
<View
style={{
padding: 35,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 14,
}}>
<Text
style={{
fontSize: 16,
color: '#D0D4DD',
}}>
Calling to...
</Text>
<Text
style={{
fontSize: 36,
marginTop: 12,
color: '#ffff',
letterSpacing: 6,
}}>
{otherUserId.current}
</Text>
</View>
<View
style={{
justifyContent: 'center',
alignItems: 'center',
}}>
<TouchableOpacity
onPress={() => {
setType('JOIN');
otherUserId.current = null;
}}
style={{
backgroundColor: '#FF5D5D',
borderRadius: 30,
height: 60,
aspectRatio: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<CallEnd width={50} height={12} />
</TouchableOpacity>
</View>
</View>
);
};
const IncomingCallScreen = () => {
return (
<View
style={{
flex: 1,
justifyContent: 'space-around',
backgroundColor: '#050A0E',
}}>
<View
style={{
padding: 35,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 14,
}}>
<Text
style={{
fontSize: 36,
marginTop: 12,
color: '#ffff',
}}>
{otherUserId.current} is calling..
</Text>
</View>
<View
style={{
justifyContent: 'center',
alignItems: 'center',
}}>
<TouchableOpacity
onPress={() => {
processAccept()
setType('WEBRTC_ROOM')
}}
style={{
backgroundColor: 'green',
borderRadius: 30,
height: 60,
aspectRatio: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<CallAnswer height={28} fill={'#fff'} />
</TouchableOpacity>
</View>
</View>
);
};
const WebrtcRoomScreen = () => {
return (
<View
style={{
flex: 1,
backgroundColor: '#050A0E',
paddingHorizontal: 12,
paddingVertical: 12,
}}>
{localStream ? (
<RTCView
objectFit={'cover'}
style={{ flex: 1, backgroundColor: '#050A0E' }}
streamURL={localStream.toURL()}
/>
) : null}
{remoteStream ? (
<RTCView
objectFit={'cover'}
style={{
flex: 1,
backgroundColor: '#050A0E',
marginTop: 8,
}}
streamURL={remoteStream.toURL()}
/>
) : null}
<View
style={{
marginVertical: 12,
flexDirection: 'row',
justifyContent: 'space-evenly',
}}>
<IconContainer
backgroundColor={'red'}
onPress={() => {
leave()
}}
Icon={() => {
return <CallEnd height={26} width={26} fill="#FFF" />;
}}
/>
<IconContainer
style={{
borderWidth: 1.5,
borderColor: '#2B3034',
}}
backgroundColor={!localMicOn ? '#fff' : 'transparent'}
onPress={() => {
toggleMic()
}}
Icon={() => {
return localMicOn ? (
<MicOn height={24} width={24} fill="#FFF" />
) : (
<MicOff height={28} width={28} fill="#1D2939" />
);
}}
/>
<IconContainer
style={{
borderWidth: 1.5,
borderColor: '#2B3034',
}}
backgroundColor={!localWebcamOn ? '#fff' : 'transparent'}
onPress={() => {
toggleCamera()
}}
Icon={() => {
return localWebcamOn ? (
<VideoOn height={24} width={24} fill="#FFF" />
) : (
<VideoOff height={36} width={36} fill="#1D2939" />
);
}}
/>
<IconContainer
style={{
borderWidth: 1.5,
borderColor: '#2B3034',
}}
backgroundColor={'transparent'}
onPress={() => {
switchCamera()
}}
Icon={() => {
return <CameraSwitch height={24} width={24} fill="#FFF" />;
}}
/>
</View>
</View>
);
};
switch (type) {
case 'JOIN':
return JoinScreen();
case 'INCOMING_CALL':
return IncomingCallScreen();
case 'OUTGOING_CALL':
return OutgoingCallScreen();
case 'WEBRTC_ROOM':
return WebrtcRoomScreen();
default:
return null;
}
}
export default Call
A开始通话,B接听,B能听到A,A听不到B。我把他们换了。它是相同的,但是是镜像的。我浪费了 30 小时试图修复它。感觉糟透了