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 { removeCall } from 'api/removeCall';
|
||||||
import { removeContactCall } from 'api/removeContactCall';
|
import { removeContactCall } from 'api/removeContactCall';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ScreenCapturePickerView,
|
||||||
|
RTCPeerConnection,
|
||||||
|
RTCIceCandidate,
|
||||||
|
RTCSessionDescription,
|
||||||
|
RTCView,
|
||||||
|
MediaStream,
|
||||||
|
MediaStreamTrack,
|
||||||
|
mediaDevices,
|
||||||
|
registerGlobals
|
||||||
|
} from 'react-native-webrtc';
|
||||||
|
|
||||||
export function useRingContext() {
|
export function useRingContext() {
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
ringing: new Map(),
|
ringing: new Map(),
|
||||||
@ -31,6 +43,7 @@ export function useRingContext() {
|
|||||||
const accessAudio = useRef(false);
|
const accessAudio = useRef(false);
|
||||||
const videoTrack = useRef();
|
const videoTrack = useRef();
|
||||||
const audioTrack = useRef();
|
const audioTrack = useRef();
|
||||||
|
const candidates = useRef([]);
|
||||||
|
|
||||||
const iceServers = [
|
const iceServers = [
|
||||||
{
|
{
|
||||||
@ -96,6 +109,182 @@ export function useRingContext() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
accept: async (cardId, callId, contactNode, contactToken, calleeToken) => {
|
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 () => {
|
end: async () => {
|
||||||
},
|
},
|
||||||
|
@ -25,6 +25,7 @@ import { ProfileIcon } from './profileIcon/ProfileIcon';
|
|||||||
import { CardsIcon } from './cardsIcon/CardsIcon';
|
import { CardsIcon } from './cardsIcon/CardsIcon';
|
||||||
import { Logo } from 'utils/Logo';
|
import { Logo } from 'utils/Logo';
|
||||||
import splash from 'images/session.png';
|
import splash from 'images/session.png';
|
||||||
|
import { RTCView } from 'react-native-webrtc';
|
||||||
|
|
||||||
const ConversationStack = createStackNavigator();
|
const ConversationStack = createStackNavigator();
|
||||||
const ProfileStack = createStackNavigator();
|
const ProfileStack = createStackNavigator();
|
||||||
@ -323,13 +324,13 @@ export function Session() {
|
|||||||
<View key={key} style={styles.ringEntry}>
|
<View key={key} style={styles.ringEntry}>
|
||||||
<Logo src={img} width={40} height={40} radius={4} />
|
<Logo src={img} width={40} height={40} radius={4} />
|
||||||
<Text style={styles.ringName} numberOfLines={1} ellipsizeMode={'tail'}>{ label }</Text>
|
<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} />
|
<MatIcons name={'eye-off-outline'} size={20} color={Colors.text} />
|
||||||
</TouchableOpacity>
|
</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} />
|
<MatIcons name={'phone-hangup'} size={20} color={Colors.alert} />
|
||||||
</TouchableOpacity>
|
</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} />
|
<MatIcons name={'phone'} size={20} color={Colors.primary} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
@ -410,7 +411,7 @@ export function Session() {
|
|||||||
<Modal
|
<Modal
|
||||||
animationType="fade"
|
animationType="fade"
|
||||||
transparent={true}
|
transparent={true}
|
||||||
visible={ringing.length > 0}
|
visible={ringing.length > 0 && state.callStatus == null}
|
||||||
supportedOrientations={['portrait', 'landscape']}
|
supportedOrientations={['portrait', 'landscape']}
|
||||||
>
|
>
|
||||||
<View style={styles.ringBase}>
|
<View style={styles.ringBase}>
|
||||||
@ -419,6 +420,28 @@ export function Session() {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</Modal>
|
</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>
|
</NavigationContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -186,5 +186,26 @@ export const styles = StyleSheet.create({
|
|||||||
padding: 6,
|
padding: 6,
|
||||||
marginLeft: 4,
|
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() {
|
export function useSession() {
|
||||||
|
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
ringing: [],
|
|
||||||
tabbled: null,
|
tabbled: null,
|
||||||
subWidth: '50%',
|
subWidth: '50%',
|
||||||
baseWidth: '50%',
|
baseWidth: '50%',
|
||||||
cardId: null,
|
cardId: null,
|
||||||
converstaionId: null,
|
converstaionId: null,
|
||||||
firstRun: null,
|
firstRun: null,
|
||||||
|
ringing: [],
|
||||||
|
callStatus: null,
|
||||||
|
callLogo: null,
|
||||||
|
localStream: null,
|
||||||
|
localVideo: false,
|
||||||
|
localAudio: false,
|
||||||
|
remoteStream: null,
|
||||||
|
remoteVideo: false,
|
||||||
|
remoteAudio: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const ring = useContext(RingContext);
|
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;
|
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]);
|
}, [ring.state]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -81,16 +96,32 @@ export function useSession() {
|
|||||||
updateState({ firstRun: false });
|
updateState({ firstRun: false });
|
||||||
store.actions.setFirstRun();
|
store.actions.setFirstRun();
|
||||||
},
|
},
|
||||||
ignore: async (cardId, callId) => {
|
ignore: (call) => {
|
||||||
await ring.actions.ignore(cardId, callId);
|
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);
|
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);
|
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 };
|
return { state, actions };
|
||||||
|
Loading…
Reference in New Issue
Block a user