mirror of
https://github.com/balzack/databag.git
synced 2025-02-12 03:29:16 +00:00
mid mobile webrtc integration
This commit is contained in:
parent
ee474b5f73
commit
99e1aa88f5
@ -6,6 +6,18 @@ import { keepCall } from 'api/keepCall';
|
||||
import { removeCall } from 'api/removeCall';
|
||||
import { removeContactCall } from 'api/removeContactCall';
|
||||
|
||||
import {
|
||||
ScreenCapturePickerView,
|
||||
RTCPeerConnection,
|
||||
RTCIceCandidate,
|
||||
RTCSessionDescription,
|
||||
RTCView,
|
||||
MediaStream,
|
||||
MediaStreamTrack,
|
||||
mediaDevices,
|
||||
registerGlobals
|
||||
} from 'react-native-webrtc';
|
||||
|
||||
export function useRingContext() {
|
||||
const [state, setState] = useState({
|
||||
ringing: new Map(),
|
||||
@ -31,6 +43,7 @@ export function useRingContext() {
|
||||
const accessAudio = useRef(false);
|
||||
const videoTrack = useRef();
|
||||
const audioTrack = useRef();
|
||||
const candidates = useRef([]);
|
||||
|
||||
const iceServers = [
|
||||
{
|
||||
@ -96,6 +109,182 @@ export function useRingContext() {
|
||||
}
|
||||
},
|
||||
accept: async (cardId, callId, contactNode, contactToken, calleeToken) => {
|
||||
if (calling.current) {
|
||||
throw new Error("active session");
|
||||
}
|
||||
|
||||
const key = `${cardId}:${callId}`
|
||||
const call = ringing.current.get(key);
|
||||
if (call) {
|
||||
call.status = 'accepted'
|
||||
ringing.current.set(key, call);
|
||||
updateState({ ringing: ringing.current });
|
||||
|
||||
// connect signal socket
|
||||
candidates.current = [];
|
||||
calling.current = { state: "connecting", callId, contactNode, contactToken, host: false };
|
||||
updateState({ callStatus: "connecting", cardId, remoteVideo: false, remoteAudio: false });
|
||||
|
||||
pc.current = new RTCPeerConnection({ iceServers });
|
||||
pc.current.addEventListener( 'connectionstatechange', event => {
|
||||
console.log("CONNECTION STATE", event);
|
||||
} );
|
||||
pc.current.addEventListener( 'icecandidate', event => {
|
||||
ws.current.send(JSON.stringify({ candidate: event.candidate }));
|
||||
} );
|
||||
pc.current.addEventListener( 'icecandidateerror', event => {
|
||||
console.log("ICE ERROR");
|
||||
} );
|
||||
pc.current.addEventListener( 'iceconnectionstatechange', event => {
|
||||
console.log("ICE STATE CHANGE", event);
|
||||
} );
|
||||
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);
|
||||
} );
|
||||
pc.current.addEventListener( 'track', event => {
|
||||
|
||||
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 });
|
||||
}
|
||||
stream.current.addTrack(event.track, stream.current);
|
||||
} );
|
||||
|
||||
|
||||
|
||||
|
||||
ws.current = createWebsocket(`wss://${contactNode}/signal`);
|
||||
ws.current.onmessage = async (ev) => {
|
||||
// handle messages [impolite]
|
||||
try {
|
||||
const signal = JSON.parse(ev.data);
|
||||
if (signal.status === 'closed') {
|
||||
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]);
|
||||
}
|
||||
};
|
||||
}
|
||||
else if (signal.candidate) {
|
||||
if (pc.current.remoteDescription == null) {
|
||||
candidates.current.push(signal.candidate);
|
||||
return;
|
||||
}
|
||||
const candidate = new RTCIceCandidate(signal.candidate);
|
||||
await pc.current.addIceCandidate(candidate);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
ws.current.onclose = (e) => {
|
||||
// update state to disconnected
|
||||
pc.current.close();
|
||||
calling.current = null;
|
||||
if (videoTrack.current) {
|
||||
videoTrack.current.stop();
|
||||
videoTrack.current = null;
|
||||
}
|
||||
if (audioTrack.current) {
|
||||
audioTrack.current.stop();
|
||||
audioTrack.current = null;
|
||||
}
|
||||
updateState({ callStatus: null });
|
||||
}
|
||||
ws.current.onopen = async () => {
|
||||
calling.current.state = "connected"
|
||||
updateState({ callStatus: "connected" });
|
||||
ws.current.send(JSON.stringify({ AppToken: calleeToken }))
|
||||
|
||||
try {
|
||||
const constraints = {
|
||||
mandatory: {
|
||||
OfferToReceiveAudio: true,
|
||||
OfferToReceiveVideo: true,
|
||||
VoiceActivityDetection: true
|
||||
}
|
||||
};
|
||||
const offer = await pc.current.createOffer(constraints);
|
||||
await pc.current.setLocalDescription(offer);
|
||||
ws.current.send(JSON.stringify({ description: offer }));
|
||||
console.log("OPENING OFFER");
|
||||
console.log("OPENING OFFER");
|
||||
console.log("OPENING OFFER");
|
||||
console.log("OPENING OFFER");
|
||||
console.log("OPENING OFFER");
|
||||
console.log("OPENING OFFER");
|
||||
console.log("OPENING OFFER");
|
||||
console.log("OPENING OFFER");
|
||||
|
||||
}
|
||||
catch(err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
ws.current.error = (e) => {
|
||||
console.log(e)
|
||||
ws.current.close();
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
end: async () => {
|
||||
},
|
||||
|
@ -25,6 +25,7 @@ import { ProfileIcon } from './profileIcon/ProfileIcon';
|
||||
import { CardsIcon } from './cardsIcon/CardsIcon';
|
||||
import { Logo } from 'utils/Logo';
|
||||
import splash from 'images/session.png';
|
||||
import { RTCView } from 'react-native-webrtc';
|
||||
|
||||
const ConversationStack = createStackNavigator();
|
||||
const ProfileStack = createStackNavigator();
|
||||
@ -323,13 +324,13 @@ export function Session() {
|
||||
<View key={key} style={styles.ringEntry}>
|
||||
<Logo src={img} width={40} height={40} radius={4} />
|
||||
<Text style={styles.ringName} numberOfLines={1} ellipsizeMode={'tail'}>{ label }</Text>
|
||||
<TouchableOpacity style={styles.ringIgnore} onPress={() => actions.ignore(cardId, callId)}>
|
||||
<TouchableOpacity style={styles.ringIgnore} onPress={() => actions.ignore({ cardId, callId })}>
|
||||
<MatIcons name={'eye-off-outline'} size={20} color={Colors.text} />
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.ringDecline} onPress={() => actions.decline(cardId, contactNode, contactToken, callId)}>
|
||||
<TouchableOpacity style={styles.ringDecline} onPress={() => actions.decline({ cardId, contactNode, contactToken, callId })}>
|
||||
<MatIcons name={'phone-hangup'} size={20} color={Colors.alert} />
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.ringAccept} onPress={() => actions.accept(cardId, callId, contactNode, contactToken, calleeToken)}>
|
||||
<TouchableOpacity style={styles.ringAccept} onPress={() => actions.accept({ cardId, callId, contactNode, contactToken, calleeToken })}>
|
||||
<MatIcons name={'phone'} size={20} color={Colors.primary} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
@ -410,7 +411,7 @@ export function Session() {
|
||||
<Modal
|
||||
animationType="fade"
|
||||
transparent={true}
|
||||
visible={ringing.length > 0}
|
||||
visible={ringing.length > 0 && state.callStatus == null}
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
>
|
||||
<View style={styles.ringBase}>
|
||||
@ -419,6 +420,28 @@ export function Session() {
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
<Modal
|
||||
animationType="fade"
|
||||
transparent={true}
|
||||
visible={state.callStatus != null}
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
>
|
||||
<View style={styles.callBase}>
|
||||
<View style={styles.callFrame}>
|
||||
{ state.remoteStream && (
|
||||
<View style={styles.callRemote}>
|
||||
<RTCView
|
||||
mirror={true}
|
||||
objectFit={'cover'}
|
||||
streamURL={state.remoteStream.toURL()}
|
||||
zOrder={0}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
<Text>{ JSON.stringify(state.callStatus == null) }</Text>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
</NavigationContainer>
|
||||
);
|
||||
}
|
||||
|
@ -186,5 +186,26 @@ export const styles = StyleSheet.create({
|
||||
padding: 6,
|
||||
marginLeft: 4,
|
||||
},
|
||||
callBase: {
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'rgba(52, 52, 52, 0.8)'
|
||||
},
|
||||
callFrame: {
|
||||
backgroundColor: Colors.formBackground,
|
||||
padding: 16,
|
||||
width: '90%',
|
||||
maxWidth: 400,
|
||||
borderRadius: 4,
|
||||
},
|
||||
callRemote: {
|
||||
width: 256,
|
||||
height: 256,
|
||||
backgroundColor: 'yellow',
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
@ -9,13 +9,21 @@ import { RingContext } from 'context/RingContext';
|
||||
export function useSession() {
|
||||
|
||||
const [state, setState] = useState({
|
||||
ringing: [],
|
||||
tabbled: null,
|
||||
subWidth: '50%',
|
||||
baseWidth: '50%',
|
||||
cardId: null,
|
||||
converstaionId: null,
|
||||
firstRun: null,
|
||||
ringing: [],
|
||||
callStatus: null,
|
||||
callLogo: null,
|
||||
localStream: null,
|
||||
localVideo: false,
|
||||
localAudio: false,
|
||||
remoteStream: null,
|
||||
remoteVideo: false,
|
||||
remoteAudio: false,
|
||||
});
|
||||
|
||||
const ring = useContext(RingContext);
|
||||
@ -46,8 +54,15 @@ export function useSession() {
|
||||
}
|
||||
});
|
||||
|
||||
let callLogo = null;
|
||||
const contact = card.state.cards.get(ring.state.cardId);
|
||||
if (contact) {
|
||||
const { imageSet } = contact.card?.profile || {};
|
||||
callLogo = imageSet ? card.actions.getCardImageUrl(ring.state.cardId) : null;
|
||||
}
|
||||
|
||||
const { callStatus, localStream, localVideo, localAudio, remoteStream, remoteVideo, remoteAudio } = ring.state;
|
||||
updateState({ ringing, callStatus, localStream, localVideo, localAudio, remoteStream, remoteVideo, remoteAudio });
|
||||
updateState({ ringing, callStatus, callLogo, localStream, localVideo, localAudio, remoteStream, remoteVideo, remoteAudio });
|
||||
}, [ring.state]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -81,16 +96,32 @@ export function useSession() {
|
||||
updateState({ firstRun: false });
|
||||
store.actions.setFirstRun();
|
||||
},
|
||||
ignore: async (cardId, callId) => {
|
||||
await ring.actions.ignore(cardId, callId);
|
||||
ignore: (call) => {
|
||||
ring.actions.ignore(call.cardId, call.callId);
|
||||
},
|
||||
decline: async (cardId, contactNode, contactToken, callId) => {
|
||||
decline: async (call) => {
|
||||
const { cardId, contactNode, contactToken, callId } = call;
|
||||
await ring.actions.decline(cardId, contactNode, contactToken, callId);
|
||||
},
|
||||
accept: async (cardId, callId, contactNode, contactToken, calleeToken) => {
|
||||
accept: async (call) => {
|
||||
const { cardId, callId, contactNode, contactToken, calleeToken } = call;
|
||||
await ring.actions.accept(cardId, callId, contactNode, contactToken, calleeToken);
|
||||
},
|
||||
|
||||
end: async () => {
|
||||
await ring.actions.end();
|
||||
},
|
||||
enableVideo: async () => {
|
||||
await ring.actions.enableVideo();
|
||||
},
|
||||
disableVideo: async () => {
|
||||
await ring.actions.disableVideo();
|
||||
},
|
||||
enableAudio: async () => {
|
||||
await ring.actions.enableAudio();
|
||||
},
|
||||
disableAudio: async () => {
|
||||
await ring.actions.disableAudio();
|
||||
},
|
||||
};
|
||||
|
||||
return { state, actions };
|
||||
|
Loading…
Reference in New Issue
Block a user