diff --git a/react-native-webrtc-app/client/App.js b/react-native-webrtc-app/client/App.js
index bb7ebd0..445ba4c 100644
--- a/react-native-webrtc-app/client/App.js
+++ b/react-native-webrtc-app/client/App.js
@@ -8,11 +8,10 @@ import {
Text,
TouchableOpacity,
StyleSheet,
- DeviceEventEmitter,
+ TextInput,
} from 'react-native';
import * as Animatable from 'react-native-animatable';
import TextInputContainer from './components/TextInputContainer';
-import SocketIOClient from 'socket.io-client';
import {
mediaDevices,
RTCPeerConnection,
@@ -32,52 +31,74 @@ import IconContainer from './components/IconContainer';
import InCallManager from 'react-native-incall-manager';
import Logo from './asset/Logo';
import * as JsSIP from 'react-native-jssip';
-import EventEmitter from 'react-native/Libraries/vendor/emitter/EventEmitter';
export default function App({}) {
- const eventoSip = new EventEmitter();
+ const [session, setSession] = useState(null);
+ const [ua, setUa] = useState(null);
+ const [screen, setScreen] = useState('JOIN');
+ const [incomingCaller, setIncomingCaller] = useState(null);
+ const [calle, setCalle] = useState(null);
+ const [myRamal, setMyRamal] = useState(null);
+ const [authData, setAuthData] = useState({
+ protocolo: 'ws',
+ servidor: '192.168.115.179',
+ porta: '8088',
+ username: 'Matheo',
+ ramal: '1100',
+ senha: 'SIP1100',
+ });
+ const [localMicOn, setlocalMicOn] = useState(true);
+ const [localWebcamOn, setlocalWebcamOn] = useState(false);
+ const [localStream, setLocalStream] = useState(null);
+ const [remoteStream, setRemoteStream] = useState(null);
+ const remoteAudio = useRef(null);
+ const localAudio = useRef(null);
- function Autenticacao(PROTOCOLO, SERVIDOR, PORTA, NOME, RAMAL, SENHA) {
- this.PROTOCOLO = 'ws';
- this.SERVIDOR = '192.168.115.179';
- this.PORTA = '8088';
- this.NOME = 'matheo';
- this.RAMAL = '1100';
- this.SENHA = 'SIP1100';
- }
+ class EventEmitter {
+ constructor() {
+ this.events = {};
+ }
- const _Autenticacao = new Autenticacao();
+ on(eventName, listener) {
+ if (!this.events[eventName]) {
+ this.events[eventName] = [];
+ }
+ this.events[eventName].push(listener);
+ }
- let phone;
- let session;
+ emit(eventName, ...args) {
+ if (this.events[eventName]) {
+ this.events[eventName].forEach(listener => listener(...args));
+ }
+ }
- function iniciandoAutenticacaonoPBX() {
+ off(eventName, listener) {
+ if (this.events[eventName]) {
+ this.events[eventName] = this.events[eventName].filter(
+ fn => fn !== listener,
+ );
+ }
+ }
+ }
+ const eventoSip = new EventEmitter();
+ const {protocolo, servidor, porta, username, ramal, senha} = authData;
+
+ useEffect(() => {
+ setMyRamal(ramal);
const socket = new JsSIP.WebSocketInterface(
- _Autenticacao.PROTOCOLO +
- '://' +
- _Autenticacao.SERVIDOR +
- ':' +
- _Autenticacao.PORTA +
- '/ws',
+ protocolo + '://' + servidor + ':' + porta + '/ws',
);
const configuration = {
- uri: 'sip:' + _Autenticacao.RAMAL + '@' + _Autenticacao.SERVIDOR,
- password: _Autenticacao.SENHA,
+ uri: 'sip:' + ramal + '@' + servidor,
+ password: senha,
sockets: [socket],
session_timers: false,
no_answer_timeout: 180,
hack_via_tcp: false,
hack_via_ws: true,
- display_name:
- _Autenticacao.NOME !== null ? _Autenticacao.NOME : _Autenticacao.RAMAL,
- user_agent: 'UASimpleSIP 1.2.0',
- contact_uri:
- 'sip:' +
- _Autenticacao.RAMAL +
- '@' +
- _Autenticacao.SERVIDOR +
- ';transport=' +
- _Autenticacao.PROTOCOLO,
+ display_name: username !== null ? username : ramal,
+ user_agent: 'Softphone React Native',
+ contact_uri: 'sip:' + ramal + '@' + servidor + ';transport=' + protocolo,
pcConfig: {
iceServers: [
{
@@ -92,532 +113,244 @@ export default function App({}) {
],
},
};
- if (configuration.uri && configuration.password) {
- phone = new JsSIP.UA(configuration);
- phone.on('registrationFailed', function (ev) {
- console.log('Registering on SIP server failed with error: ' + ev.cause);
- configuration.uri = null;
- configuration.password = null;
- });
+ const phone = new JsSIP.UA(configuration);
- phone.on('newRTCSession', function (ev) {
- const newSession = ev.session;
-
- session = newSession;
-
- eventoSip.on('endCall', () => {
- if (session) {
- eventoSip.emit('confirmedEnded');
- session.terminate();
- }
- });
+ phone.on('registrationFailed', function (ev) {
+ console.log('Registering on SIP server failed with error: ' + ev.cause);
+ configuration.uri = null;
+ configuration.password = null;
+ });
- eventoSip.on('evento', payload => {
- console.log(payload);
- });
- eventoSip.on('mute', () => {
- session.mute();
- console.log(session?.isMuted());
- });
+ phone.on('newRTCSession', ev => {
+ const newSession = ev.session;
- eventoSip.on('unmute', () => {
- session.unmute();
- console.log(session?.isMuted());
- });
+ ///////////////////////////////////////////////////
+ eventoSip.on('mute', () => {
+ session.mute();
+ console.log(session?.isMuted());
+ });
- eventoSip.on('enviarDTMF', data => {
- session.sendDTMF(data);
- });
+ eventoSip.on('unmute', () => {
+ session.unmute();
+ console.log(session?.isMuted());
+ });
- eventoSip.on('transferir', data => {
- session.refer(data);
- });
+ eventoSip.on('enviarDTMF', data => {
+ session.sendDTMF(data);
+ });
- session.on('candidate', event => {
- console.log(event);
+ eventoSip.on('transferir', data => {
+ session.refer(data);
+ });
+ ///////////////////////////////////////////////////
+
+ //RECEBENDO UMA CHAMADA
+ if (newSession.direction === 'incoming') {
+ setScreen('INCOMING_CALL');
+ setIncomingCaller(newSession.remote_identity.uri.user);
+ setSession(newSession);
+ // Set InCallManager settings when a call starts
+ InCallManager.start({media: 'audio'});
+ InCallManager.setForceSpeakerphoneOn(true);
+
+ //Quando finalizar a chamada
+ newSession.on('ended', () => {
+ InCallManager.stop();
+ setSession(null);
+ setIncomingCaller(null);
+ setScreen('JOIN');
});
- session.on('ended', () => {
- endCallAudio.play();
- eventoSip.emit('confirmedEnded');
+ //QUANDO UMA CHAMADA APRESENTAR PROBLEMAS
+ newSession.on('failed', () => {
+ InCallManager.stop();
+ setSession(null);
+ setIncomingCaller(null);
+ setScreen('JOIN');
});
+ } else {
+ setSession(newSession);
- session.on('newDTMF', function (event) {
- console.log('DTMF recebido:', event.dtmf.tone);
+ //Quando finalizar a chamada
+ newSession.on('ended', () => {
+ InCallManager.stop();
+ setSession(null);
});
- //QUANDO UMA CHAMADA Ã REJEITADA
- session.on('failed', () => {
- incomingCallAudio.pause();
- outgoingCallAudio.pause();
- eventoSip.emit('home');
+ //QUANDO UMA CHAMADA APRESENTAR PROBLEMAS
+ newSession.on('failed', () => {
+ InCallManager.stop();
+ setSession(null);
});
+ }
- session.on('confirmed', function (confirmed) {
- // Verifica se session.connection está definido
- if (
- session.connection &&
- session.connection.getRemoteStreams().length > 0
- ) {
- const remoteStreams = session.connection.getRemoteStreams()[0];
- remoteAudio.srcObject = remoteStreams;
- remoteAudio.volume = 1;
- }
+ newSession.on('peerconnection', e => {
+ const peerconnection = e.peerconnection;
- // Verifica se session.connection está definido e se existem streams locais
- if (
- session.connection &&
- session.connection.getLocalStreams().length > 0
- ) {
- const localStreams = session.connection.getLocalStreams()[0];
- localAudio.srcObject = localStreams;
- localAudio.volume = 0;
- }
- //PAUSA TODOS OS AUDIOS DE FEEDBACK
- incomingCallAudio.pause();
- outgoingCallAudio.pause();
+ //////////////Media/////////START//////
+ if (peerconnection) {
+ peerconnection.addEventListener('addstream', event => {
+ console.log('Remote stream added:', event.stream);
+ setRemoteStream(event.stream);
+ });
+ }
+ //////////////Media/////////END////////
- eventoSip.emit('incall');
- });
- session.on('icecandidate', function (event) {
- if (
- event.candidate.type === 'srflx' &&
- event.candidate.relatedAddress !== null &&
- event.candidate.relatedPort !== null
- ) {
- event.ready();
+ peerconnection.addEventListener('icecandidate', event => {
+ if (event.candidate) {
+ console.log('Have NEW ICE Candidate: ', event.candidate);
}
});
- session.on('addstream', function (e) {
- // Verifica se session.connection está definido
- if (session.connection) {
- remoteAudio.srcObject = e.stream;
- remoteAudio.play();
- }
+ peerconnection.addEventListener('iceconnectionstatechange', () => {
+ console.log(
+ 'ICE connection state change: ',
+ peerconnection.iceConnectionState,
+ );
});
-
- //RECEBENDO UMA CHAMADA
- if (session.direction === 'incoming') {
- incomingCallAudio.play();
- eventoSip.emit('incomingcall', session.remote_identity.uri.user);
-
- eventoSip.on('rejected', () => {
- if (session?.isInProgress()) {
- incomingCallAudio.pause();
- eventoSip.emit('home');
- session.terminate();
- session = null;
- } else {
- eventoSip.emit('home');
- }
- });
-
- eventoSip.on('accepted', () => {
- if (session?.isInProgress()) {
- session.answer();
- incomingCallAudio.pause();
- }
- });
- }
- //REALIZANDO UMA CHAMADA
- if (session.direction === 'outgoing') {
- eventoSip.emit('outgoingcall', session.remote_identity.uri.user);
- if (session.isInProgress()) {
- outgoingCallAudio.play();
- }
- }
- });
- phone.on('registered', function (e) {
- eventoSip.emit('statusChange', 'registered');
- });
-
- phone.on('unregistered', function (e) {
- eventoSip.emit('statusChange', 'unregistered');
});
- phone.on('registrationFailed', function (e) {
- eventoSip.emit('statusChange', 'registrationFailed');
+ newSession.on('newDTMF', function (event) {
+ console.log('DTMF recebido:', event.dtmf.tone);
});
- phone.on('connected', function (e) {
- eventoSip.emit('statusChange', 'connected');
+ newSession.on('confirmed', function (confirmed) {
+ setScreen('WEBRTC_ROOM');
});
-
- phone.on('disconnected', function (e) {
- eventoSip.emit('statusChange', 'disconnected');
+ newSession.on('icecandidate', function (event) {
+ if (
+ event.candidate.type === 'srflx' &&
+ event.candidate.relatedAddress !== null &&
+ event.candidate.relatedPort !== null
+ ) {
+ event.ready();
+ }
});
- phone.start();
- }
- }
- useEffect(() => {
- iniciandoAutenticacaonoPBX();
- }, []);
-
- const [localStream, setlocalStream] = useState(null);
- const [remoteStream, setRemoteStream] = useState(null);
- const [isSpeakerOn, setIsSpeakerOn] = useState(false);
- const [type, setType] = useState('JOIN');
- const [callerId] = useState(
- Math.floor(100000 + Math.random() * 900000).toString(),
- );
- const otherUserId = useRef(null);
- const socket = SocketIOClient('http://129.148.58.190:8088', {
- transports: ['websocket'],
- query: {
- callerId,
- },
- reconnectionAttempts: 3,
- reconnectionDelay: 1000,
- });
- const [localMicOn, setlocalMicOn] = useState(true);
- const [localWebcamOn, setlocalWebcamOn] = useState(true);
- const [iniciarChamada, setIniciarChamada] = useState(false);
- let remoteRTCMessage = useRef(null);
- const peerConnection = useRef(null);
-
- const createPeerConnection = () => {
- return new RTCPeerConnection({
- iceServers: [
- {
- urls: 'stun:stun.l.google.com:19302',
- },
- {
- urls: 'stun:stun1.l.google.com:19302',
- },
- {urls: 'stun:stun2.l.google.com:19302'},
- ],
- });
- };
-
- const initializePeerConnection = async () => {
- try {
- peerConnection.current = createPeerConnection();
- console.log('PeerConnection initialized successfully.');
- } catch (error) {
- console.error('Error initializing PeerConnection:', error);
- }
- };
-
- //Função para limpar todos os listeners do socket
- const cleanUp = () => {
- try {
- if (peerConnection.current) {
- peerConnection.current.close();
- }
- // Fechar o socket e remover os listeners
- socket.off('newCall');
- socket.off('callAnswered');
- socket.off('ICEcandidate');
- socket.close();
-
- // Parar o InCallManager e fechar a conexão Peer
- InCallManager.stop();
-
- // Parar e remover os fluxos de mídia
- if (localStream) {
- localStream.getTracks().forEach(track => track.stop());
- }
-
- // Limpar o estado localStream e remoteRTCMessage
- setlocalStream(null);
- remoteRTCMessage.current = null;
-
- if (peerConnection.current) {
- peerConnection.current.onconnectionstatechange = function () {
- console.log('--->' + peerConnection.current.connectionState);
- };
- } else {
- console.error(
- 'O objeto peerConnection.current não está definido ou é nulo.',
- );
+ //REALIZANDO UMA CHAMADA
+ if (newSession.direction === 'outgoing') {
+ setScreen('OUTGOING_CALL');
}
- console.log('Limpeza concluída com sucesso.');
- } catch (error) {
- console.error('Erro durante a limpeza:', error);
- // Lidar com o erro de limpeza aqui, se necessário
- }
- };
-
- const socketConfig = () => {
- console.log('Iniciando a conexão com o socket');
-
- socket.on('newCall', data => {
- console.log('Received new call');
- remoteRTCMessage.current = data.rtcMessage;
- otherUserId.current = data.callerId;
- setType('INCOMING_CALL');
});
-
- socket.on('callAnswered', data => {
- console.log('Call answered');
- remoteRTCMessage.current = data.rtcMessage;
- try {
- peerConnection.current.setRemoteDescription(
- new RTCSessionDescription(remoteRTCMessage.current),
- );
- setType('WEBRTC_ROOM');
- } catch (error) {
- console.log('Erro ao atender chamada: ', error);
- }
+ phone.on('registered', function (e) {
+ eventoSip.emit('statusChange', 'registered');
});
- const closePeerConnection = () => {
- if (peerConnection.current) {
- console.log('Closing peer connection');
- peerConnection.current.close();
- peerConnection.current = null;
- }
- };
-
- // No cliente, ouvir o evento "endCallAndLeaveRoom" do servidor
- socket.on('endCallAndLeaveRoom', () => {
- // Deixar a sala após encerrar a chamada
- socket.emit('leaveRoom'); // Envia um evento para o servidor para deixar a sala
- closePeerConnection();
- setlocalStream(null);
- setType('JOIN');
+ phone.on('unregistered', function (e) {
+ eventoSip.emit('statusChange', 'unregistered');
});
- // Lógica para sair da sala no cliente
- socket.on('leaveRoom', () => {
- socket.leave(socket.user); // Deixa a sala com o mesmo nome do usuário
- console.log(`${socket.user} left the conference room.`);
+ phone.on('registrationFailed', function (e) {
+ eventoSip.emit('statusChange', 'registrationFailed');
});
- socket.on('ICEcandidate', data => {
- let message = data.rtcMessage;
-
- if (peerConnection.current) {
- peerConnection.current
- .addIceCandidate(
- new RTCIceCandidate({
- candidate: message.candidate,
- sdpMid: message.id,
- sdpMLineIndex: message.label,
- }),
- )
- .then(data => {
- console.log('Added ICE candidate successfully:', message);
- })
- .catch(err => {
- console.log('Error adding ICE candidate:', err);
- });
- }
+ phone.on('connected', function (e) {
+ eventoSip.emit('statusChange', 'connected');
});
- };
-
- const mediaConfig = async () => {
- try {
- const devices = await mediaDevices.enumerateDevices();
- let isFront = true;
- let videoSourceId = null;
-
- devices.forEach(device => {
- if (
- device.kind === 'videoinput' &&
- device.facingMode === (isFront ? 'user' : 'environment')
- ) {
- videoSourceId = device.deviceId;
- }
- });
-
- const constraints = {
- audio: true,
- video: false,
- // video: {
- // mandatory: {
- // minWidth: 500,
- // minHeight: 300,
- // minFrameRate: 30,
- // },
- // facingMode: isFront ? 'user' : 'environment',
- // },
- };
-
- if (videoSourceId) {
- constraints.video.optional = [{sourceId: videoSourceId}];
- }
- const stream = await mediaDevices.getUserMedia(constraints);
- setlocalStream(stream);
-
- peerConnection.current.addStream(stream);
-
- peerConnection.current.onaddstream = event => {
- setRemoteStream(event.stream);
- };
- } catch (error) {
- console.error('Error accessing media devices:', error);
- }
- };
+ phone.on('disconnected', function (e) {
+ eventoSip.emit('statusChange', 'disconnected');
+ });
+ phone.start();
+ setUa(phone);
- const handleIniciarChamada = async () => {
- setIniciarChamada(true);
- await initializeApp(); // Aguarda a conclusão da inicialização
- setType('OUTGOING_CALL');
- await processCall();
- };
+ return () => {
+ InCallManager.stop();
+ phone.stop();
+ };
+ }, []);
const handleAceitarChamada = async () => {
- setType('WEBRTC_ROOM');
- await processAccept();
- };
-
- async function initializeApp() {
- try {
- await initializePeerConnection();
- await mediaConfig();
- socketConfig();
-
- peerConnection.current.addEventListener('signalingstatechange', event => {
- console.log(
- 'Signaling state changed:',
- event.type,
- ':',
- peerConnection.current.signalingState,
- );
+ if (session) {
+ session.answer({
+ mediaConstraints: {
+ audio: true,
+ video: false,
+ },
});
-
- setIniciarChamada(false);
- } catch (error) {
- console.error('Erro durante a inicialização:', error);
- // Trate o erro conforme necessário
- }
- }
-
- useEffect(() => {
- initializeApp();
- setIniciarChamada(false);
- }, []);
-
- useEffect(() => {
- if (iniciarChamada) {
- initializeApp();
- setIniciarChamada(false);
+ setScreen('WEBRTC_ROOM');
}
- }, [iniciarChamada]);
-
- //useEffect para iniciar o gerenciamento de chamadas
- useEffect(() => {
- const callOptions = {
- media: 'audioVideo',
- };
- InCallManager.start(callOptions);
- InCallManager.setKeepScreenOn(true);
- InCallManager.setForceSpeakerphoneOn(true);
+ };
- setIniciarChamada(false);
- return () => {
+ const handleRejectCall = () => {
+ if (session) {
+ session.terminate();
+ setSession(null);
+ setIncomingCaller(null);
InCallManager.stop();
- };
- }, [iniciarChamada]);
-
- function sendICEcandidate(data) {
- socket.emit('ICEcandidate', data);
- }
-
- async function processCall() {
- console.log('Processing call...');
- InCallManager.startRingback();
- const sessionDescription = await peerConnection.current.createOffer();
- await peerConnection.current.setLocalDescription(sessionDescription);
- sendCall({
- calleeId: otherUserId.current,
- rtcMessage: sessionDescription,
- });
- }
-
- async function processAccept() {
- console.log('Processing call acceptance...');
- try {
- await peerConnection.current.setRemoteDescription(
- new RTCSessionDescription(remoteRTCMessage.current),
- );
-
- const sessionDescription = await peerConnection.current.createAnswer();
- await peerConnection.current.setLocalDescription(sessionDescription);
-
- answerCall({
- callerId: otherUserId.current,
- rtcMessage: sessionDescription,
- });
+ }
+ };
- // Adicionar evento onicecandidate após a criação da resposta local
- peerConnection.current.onicecandidate = event => {
- if (event.candidate) {
- // Enviar ICE candidate apenas quando estiver disponível
- sendICEcandidate({
- calleeId: otherUserId.current,
- rtcMessage: {
- label: event.candidate.sdpMLineIndex,
- id: event.candidate.sdpMid,
- candidate: event.candidate.candidate,
- },
- });
- } else {
- console.log('End of candidates.');
- }
+ const handleIniciarChamada = () => {
+ console.log('AQUI:::::::' + calle);
+ if (ua && calle) {
+ const eventHandlers = {
+ progress: () => {
+ console.log('Call is in progress');
+ },
+ failed: e => {
+ console.log('Call failed with cause: ' + e.cause);
+ InCallManager.stop();
+ },
+ ended: () => {
+ console.log('Call ended');
+ InCallManager.stop();
+ },
+ confirmed: () => {
+ console.log('Call confirmed');
+ InCallManager.setForceSpeakerphoneOn(true);
+ },
};
- } catch (error) {
- console.error('Error processing call acceptance:', error);
- // Tratar o erro conforme necessário
- }
- }
- function answerCall(data) {
- socket.emit('answerCall', data);
- }
+ const options = {
+ eventHandlers: eventHandlers,
+ mediaConstraints: {
+ audio: true,
+ video: false,
+ },
+ rtcOfferContraints: {
+ offerToReceiveAudio: true,
+ offerToReceiveVideo: false,
+ },
+ };
- function sendCall(data) {
- socket.emit('call', data);
- }
+ const newSession = ua.call(`sip:${calle}@${servidor}`, options);
+ console.log('URI: ' + `sip:${calle}@${servidor}`);
+ setSession(newSession);
- function endCall(data) {
- socket.emit('endCall', data);
- }
+ InCallManager.start({media: 'audio'});
+ }
+ };
- function switchCamera() {
- localStream.getVideoTracks().forEach(track => {
- track._switchCamera();
+ const toggleMic = () => {
+ console.log('Toggling microphone...');
+ localMicOn ? setlocalMicOn(false) : setlocalMicOn(true);
+ localStream.getAudioTracks().forEach(track => {
+ localMicOn ? (track.enabled = false) : (track.enabled = true);
});
- }
+ };
- function toggleCamera() {
+ const toggleCamera = () => {
console.log('Toggling camera...');
localWebcamOn ? setlocalWebcamOn(false) : setlocalWebcamOn(true);
localStream.getVideoTracks().forEach(track => {
localWebcamOn ? (track.enabled = false) : (track.enabled = true);
});
- }
-
- function toggleMic() {
- console.log('Toggling microphone...');
- localMicOn ? setlocalMicOn(false) : setlocalMicOn(true);
- localStream.getAudioTracks().forEach(track => {
- localMicOn ? (track.enabled = false) : (track.enabled = true);
- });
- }
-
- function toggleSpeaker() {
- console.log('Toggling speaker...');
- setIsSpeakerOn(!isSpeakerOn);
- InCallManager.setSpeakerphoneOn(!isSpeakerOn);
- }
+ };
- function cancelCall() {
- setType('JOIN');
- InCallManager.stopRingback();
- otherUserId.current = null;
- }
+ // useEffect(() => {
+ // if (remoteStream && remoteAudio.current) {
+ // remoteAudio.current = remoteStream;
+ // remoteAudio.current.play();
+ // }
+ // }, [remoteStream]);
- function leave() {
- // Emitir o evento 'endCall' para o servidor
- endCall({otherPeer: otherUserId.current});
- setType('JOIN');
- }
+ // useEffect(() => {
+ // if (localAudio.current && localStream) {
+ // localAudio.current = localStream;
+ // }
+ // }, [localStream]);
const styles = StyleSheet.create({
container: {
@@ -684,7 +417,7 @@ export default function App({}) {
Seu ID para ligação
- {callerId}
+ {myRamal}
@@ -693,10 +426,9 @@ export default function App({}) {
Ligação
{
- otherUserId.current = text;
- console.log('TEST', otherUserId.current);
+ setCalle(text);
}}
keyboardType={'number-pad'}
/>
@@ -744,7 +476,7 @@ export default function App({}) {
color: '#ffff',
letterSpacing: 6,
}}>
- {otherUserId.current}
+ {calle}
- Recebendo ligação de {otherUserId.current}
+ Recebendo ligação de {incomingCaller}
{
- cancelCall();
+ handleRejectCall();
}}>
=6"
}
},
+ "node_modules/react-native-sound": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/react-native-sound/-/react-native-sound-0.11.2.tgz",
+ "integrity": "sha512-LmGc8lgOK3qecYMVQpyHvww/C+wgT6sWeMpVbOe4NCRGC2yKd4fo4U0KBUo9PO7AqKESO3I/2GZg1/C0+bwiiA==",
+ "peerDependencies": {
+ "react-native": ">=0.8.0"
+ }
+ },
"node_modules/react-native-svg": {
"version": "13.7.0",
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-13.7.0.tgz",
diff --git a/react-native-webrtc-app/client/package.json b/react-native-webrtc-app/client/package.json
index 2d1e753..81ea7d0 100644
--- a/react-native-webrtc-app/client/package.json
+++ b/react-native-webrtc-app/client/package.json
@@ -17,6 +17,7 @@
"react-native-animatable": "^1.4.0",
"react-native-incall-manager": "^4.0.1",
"react-native-jssip": "^3.7.6",
+ "react-native-sound": "^0.11.2",
"react-native-svg": "^13.7.0",
"react-native-webrtc": "^1.94.2",
"socket.io-client": "^4.5.4"
diff --git a/react-native-webrtc-app/client/yarn.lock b/react-native-webrtc-app/client/yarn.lock
index d5098f4..8a13b52 100644
--- a/react-native-webrtc-app/client/yarn.lock
+++ b/react-native-webrtc-app/client/yarn.lock
@@ -7233,6 +7233,11 @@ react-native-jssip@^3.7.6:
react-native-webrtc "^1.84.0"
sdp-transform "^2.14.1"
+react-native-sound@^0.11.2:
+ version "0.11.2"
+ resolved "https://registry.npmjs.org/react-native-sound/-/react-native-sound-0.11.2.tgz"
+ integrity sha512-LmGc8lgOK3qecYMVQpyHvww/C+wgT6sWeMpVbOe4NCRGC2yKd4fo4U0KBUo9PO7AqKESO3I/2GZg1/C0+bwiiA==
+
react-native-svg@^13.7.0:
version "13.7.0"
resolved "https://registry.npmjs.org/react-native-svg/-/react-native-svg-13.7.0.tgz"
@@ -7251,7 +7256,7 @@ react-native-webrtc@^1.84.0, react-native-webrtc@^1.94.2:
event-target-shim "6.0.2"
tar "6.1.11"
-react-native@*, react-native@>=0.40.0, react-native@>=0.60.0, react-native@0.68.2:
+react-native@*, react-native@>=0.40.0, react-native@>=0.60.0, react-native@>=0.8.0, react-native@0.68.2:
version "0.68.2"
resolved "https://registry.npmjs.org/react-native/-/react-native-0.68.2.tgz"
integrity sha512-qNMz+mdIirCEmlrhapAtAG+SWVx6MAiSfCbFNhfHqiqu1xw1OKXdzIrjaBEPihRC2pcORCoCHduHGQe/Pz9Yuw==