serializing offer handling in mobile webrtc

This commit is contained in:
Roland Osborne 2023-03-29 14:21:08 -07:00
parent 99e1aa88f5
commit 94b18234cc
3 changed files with 65 additions and 67 deletions

View File

@ -44,6 +44,8 @@ export function useRingContext() {
const videoTrack = useRef();
const audioTrack = useRef();
const candidates = useRef([]);
const offers = useRef([]);
const processing = useRef(false);
const iceServers = [
{
@ -140,18 +142,6 @@ export function useRingContext() {
} );
pc.current.addEventListener( 'negotiationneeded', event => {
console.log("ICE NEGOTIATION", event);
console.log("ICE NEGOTIATION", event);
console.log("ICE NEGOTIATION", event);
console.log("ICE NEGOTIATION", event);
console.log("ICE NEGOTIATION", event);
console.log("ICE NEGOTIATION", event);
console.log("ICE NEGOTIATION", event);
console.log("ICE NEGOTIATION", event);
console.log("ICE NEGOTIATION", event);
console.log("ICE NEGOTIATION", event);
console.log("ICE NEGOTIATION", event);
console.log("ICE NEGOTIATION", event);
console.log("ICE NEGOTIATION", event);
} );
pc.current.addEventListener( 'signalingstatechange', event => {
console.log("ICE SIGNALING", event);
@ -160,14 +150,6 @@ export function useRingContext() {
console.log("ICE TRACK", event.track);
if (stream.current == null) {
console.log("NEW STREAM.");
console.log("NEW STREAM.");
console.log("NEW STREAM.");
console.log("NEW STREAM.");
console.log("NEW STREAM.");
console.log("NEW STREAM.");
console.log("NEW STREAM.");
console.log("NEW STREAM.");
console.log("NEW STREAM.");
stream.current = new MediaStream();
updateState({ remoteStream: stream.current });
@ -175,7 +157,40 @@ console.log("NEW STREAM.");
stream.current.addTrack(event.track, stream.current);
} );
const processOffers = async () => {
if (processing.current) {
return;
}
processing.current = true;
while (offers.current.length > 0) {
descriptions = offers.current;
offers.current = [];
for (let i = 0; i < descriptions.length; i++) {
const description = descriptions[i];
stream.current = null;
console.log("OFFER:", description);
if (description.type === 'offer' && pc.current.signalingState !== 'stable') {
console.log("IGNORE");
continue;
}
const offer = new RTCSessionDescription(description);
await pc.current.setRemoteDescription(offer);
if (description.type === 'offer') {
const answer = await pc.current.createAnswer();
await pc.current.setLocalDescription(answer);
ws.current.send(JSON.stringify({ description: answer }));
}
}
}
processing.current = false;
}
ws.current = createWebsocket(`wss://${contactNode}/signal`);
@ -187,44 +202,12 @@ console.log("NEW STREAM.");
ws.current.close();
}
else if (signal.description) {
console.log("DESCRIPTION", signal.description);
stream.current = null;
if (signal.description.type === 'offer' && pc.current.signalingState !== 'stable') {
console.log("IGNORING OFFER!");
return; //rudely ignore
}
const offer = new RTCSessionDescription(signal.description);
await pc.current.setRemoteDescription(offer);
if (signal.description.type === 'offer') {
const answer = await pc.current.createAnswer();
await pc.current.setLocalDescription(answer);
ws.current.send(JSON.stringify({ description: answer }));
}
console.log("STATE:", pc.current.signalingState);
const adding = candidates.current;
candidates.current = [];
for (let i = 0; i < adding.length; i++) {
try {
const candidate = new RTCIceCandidate(adding[i]);
await pc.current.addIceCandidate(candidate);
console.log("success:", adding[i]);
}
catch (err) {
console.log(err);
console.log(adding[i]);
}
};
offers.current.push(signal.description);
processOffers();
}
else if (signal.candidate) {
if (pc.current.remoteDescription == null) {
candidates.current.push(signal.candidate);
console.log("IGNOREING CANDIDATE");
return;
}
const candidate = new RTCIceCandidate(signal.candidate);

View File

@ -1,4 +1,4 @@
import { View, ScrollView, TouchableOpacity, StatusBar, Text, Image, Modal } from 'react-native';
import { useWindowDimensions, View, ScrollView, TouchableOpacity, StatusBar, Text, Image, Modal } from 'react-native';
import { useState, useEffect, useContext } from 'react';
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
@ -164,9 +164,29 @@ export function Session() {
const [ringing, setRinging] = useState([]);
const { state, actions } = useSession();
const {height, width} = useWindowDimensions();
const [callWidth, setCallWidth] = useState(0);
const [callHeight, setCallHeight] = useState(0);
const drawerParams = { drawerPosition: 'right', headerShown: false, swipeEnabled: false, drawerType: 'front' };
useEffect(() => {
console.log(width, height);
if (width > height) {
setCallWidth((height * 9)/10);
setCallHeight((height * 9)/10);
}
else {
setCallWidth((width * 9)/10);
setCallHeight((width * 9)/10);
}
}, [width, height]);
useEffect(() => {
console.log("**** REMOTE ****");
console.log(state.remoteStream);
}, [state.remoteStream]);
const HomeScreen = ({ navParams }) => {
const conversation = useContext(ConversationContext);
@ -427,18 +447,16 @@ export function Session() {
supportedOrientations={['portrait', 'landscape']}
>
<View style={styles.callBase}>
<View style={styles.callFrame}>
<View style={{ ...styles.callFrame, width: callWidth, height: callHeight }}>
{ state.remoteStream && (
<View style={styles.callRemote}>
<RTCView
style={styles.callRemote}
mirror={true}
objectFit={'cover'}
objectFit={'contain'}
streamURL={state.remoteStream.toURL()}
zOrder={0}
/>
</View>
)}
<Text>{ JSON.stringify(state.callStatus == null) }</Text>
</View>
</View>
</Modal>

View File

@ -196,15 +196,12 @@ export const styles = StyleSheet.create({
},
callFrame: {
backgroundColor: Colors.formBackground,
padding: 16,
width: '90%',
maxWidth: 400,
padding: 8,
borderRadius: 4,
},
callRemote: {
width: 256,
height: 256,
backgroundColor: 'yellow',
width: '100%',
height: '100%',
},
});