From 6eccb60f35a20a135b05676bf4ac4cb6f00adc37 Mon Sep 17 00:00:00 2001 From: Mustafa BOLEKEN Date: Mon, 8 Jul 2024 23:47:59 +0300 Subject: [PATCH 1/3] Add mechanism to cleanup local stream and websocket --- example/index.tsx | 4 +- example/src/App.tsx | 37 +++--- src/index.tsx | 280 ++++++++++++++++++++++++++++---------------- 3 files changed, 199 insertions(+), 122 deletions(-) diff --git a/example/index.tsx b/example/index.tsx index 203603e..23a0ec8 100644 --- a/example/index.tsx +++ b/example/index.tsx @@ -1,8 +1,8 @@ import { AppRegistry } from 'react-native'; -//import App from './src/App'; +import App from './src/App'; //import App from './src/Play'; //import App from './src/Peer'; -import App from './src/Conference'; +//import App from './src/Conference'; import { name as appName } from './app.json'; AppRegistry.registerComponent(appName, () => App); diff --git a/example/src/App.tsx b/example/src/App.tsx index a5ee7c7..c2d97a7 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -20,7 +20,6 @@ export default function App() { const [localMedia, setLocalMedia] = useState(''); const [isPlaying, setIsPlaying] = useState(false); - let localStream: any = useRef(null); useEffect(() => { @@ -50,6 +49,18 @@ export default function App() { console.log('publish_finished'); InCallManager.stop(); setIsPlaying(false); + adaptor.closeWebSocket(); + break; + case 'local_stream_updated': + console.log('local_stream_updated'); + verify(); + break; + case 'websocket_not_initialized': + adaptor.initialiseWebSocket(); + break; + case 'websocket_closed': + console.log('websocket_closed'); + adaptor.stopLocalStream(); break; default: console.log(command); @@ -69,22 +80,16 @@ export default function App() { debug: true, }); - useEffect(() => { - const verify = () => { - console.log('in verify'); - - if (adaptor.localStream.current && adaptor.localStream.current.toURL()) { - console.log('in verify if adaptor local stream', adaptor.localStream); - - console.log( - 'localStream.current.toURL()', - adaptor.localStream.current.toURL(), - ); + const verify = () => { + console.log('in verify'); + if (adaptor.localStream.current && adaptor.localStream.current.toURL()) { + console.log('in verify if adaptor local stream', adaptor.localStream); + return setLocalMedia(adaptor.localStream.current.toURL()); + } + setTimeout(verify, 5000); + }; - return setLocalMedia(adaptor.localStream.current.toURL()); - } - setTimeout(verify, 5000); - }; + useEffect(() => { verify(); }, [adaptor.localStream]); diff --git a/src/index.tsx b/src/index.tsx index 8fefb2d..fdf0083 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -33,6 +33,9 @@ export interface Adaptor { publish: (streamId: string, token?: string, subscriberId?:string , subscriberCode?: string, streamName?: string, mainTrack?:string, metaData?:string) => void; play: (streamId: string, token?: string, room?: string , enableTracks?: MediaStream[],subscriberId?:string , subscriberCode?: string, metaData?:string) => void; stop: (streamId: string) => void; + stopLocalStream: () => void; + initialiseWebSocket: () => void; + closeWebSocket: () => void; join: (streamId: string) => void; leave: (streamId: string) => void; getRoomInfo: (room: string, streamId?: string) => void; @@ -91,6 +94,10 @@ export function useAntMedia(params: Params) { const adaptorRef: any = useRef(null); + const wsRef: any = useRef(new WebSocket(url)); + + var ws = wsRef.current; + let localStream: any = useRef(null); const remotePeerConnection = useRef({}).current; @@ -137,6 +144,8 @@ export function useAntMedia(params: Params) { clearInterval(remotePeerConnectionStats[streamId].timerId); delete remotePeerConnectionStats[streamId]; } + + clearPingTimer(); }, [playStreamIds, remotePeerConnection, remotePeerConnectionStats] ); @@ -444,114 +453,122 @@ export function useAntMedia(params: Params) { ] ); - var ws: any = useRef(new WebSocket(url)).current; - - ws.sendJson = (dt: any) => { - ws.send(JSON.stringify(dt)); - }; - - useEffect(() => { - ws.onopen = () => { - if (debug) console.log('web socket opened !'); - callback.call(adaptorRef.current, 'initiated'); - // connection opened - - getDevices(); - - if (!onlyDataChannel) { - mediaDevices.getUserMedia(mediaConstraints) - .then((stream: any) => { - // Got stream! - if (debug) console.log('got stream'); + const setWebSocketListeners = useCallback(() => { + if (!ws) return; + ws.sendJson = (dt: any) => { + if (ws && ws.send && ws.readyState === ws.OPEN) { + ws.send(JSON.stringify(dt)); + } + }; - localStream.current = stream; - if (debug) console.log('in stream', localStream.current); - }) - .catch((error: any) => { - // Log error - if (debug) console.log('got error', error , mediaConstraints); - }); - } else { - if (debug) console.log('only data channel'); - } - setPingTimer(); - }; + ws.onopen = () => { + if (debug) console.log('web socket opened !'); + callback.call(adaptorRef.current, 'initiated'); + // connection opened + + getDevices(); + + if (!onlyDataChannel) { + mediaDevices.getUserMedia(mediaConstraints) + .then((stream: any) => { + // Got stream! + if (debug) console.log('got stream'); + + localStream.current = stream; + if (adaptorRef.current) callback.call(adaptorRef.current, 'local_stream_updated', stream); + if (debug) console.log('in stream', localStream.current); + }) + .catch((error: any) => { + // Log error + if (debug) console.log('got error', error , mediaConstraints); + }); + } else { + if (debug) console.log('only data channel'); + } + setPingTimer(); + }; - ws.onmessage = (e: any) => { - // a message was received - const data = JSON.parse(e.data); - if (debug) console.log(' onmessage', data); - - switch (data.command) { - case 'start': - // start publishing - startPublishing(data.streamId); - break; - case 'takeCandidate': - //console.log(' in takeCandidate', data); - takeCandidate(data.streamId, data.label, data.candidate, data.id); - break; - case 'takeConfiguration': - takeConfiguration(data.streamId, data.sdp, data.type,data.idMapping); - break; - case 'stop': - if (debug) console.log(' in stop', data); - closePeerConnection(data.streamId); - break; - case 'error': - if (debug) console.log(' in error', data); - if (callbackError) { - callbackError(data.definition, data); + ws.onmessage = (e: any) => { + // a message was received + const data = JSON.parse(e.data); + if (debug) console.log(' onmessage', data); + + switch (data.command) { + case 'start': + // start publishing + startPublishing(data.streamId); + break; + case 'takeCandidate': + //console.log(' in takeCandidate', data); + takeCandidate(data.streamId, data.label, data.candidate, data.id); + break; + case 'takeConfiguration': + takeConfiguration(data.streamId, data.sdp, data.type,data.idMapping); + break; + case 'stop': + if (debug) console.log(' in stop', data); + closePeerConnection(data.streamId); + break; + case 'error': + if (debug) console.log(' in error', data); + if (callbackError) { + callbackError(data.definition, data); + } + break; + case 'notification': + if (debug) console.log(' in notification', data); + + if (adaptorRef.current) + callback.call(adaptorRef.current, data.definition, data); + break; + case 'roomInformation': + if (debug) console.log(' in roomInformation', data); + callback.call(adaptorRef.current, data.command, data); + break; + case 'pong': + if (debug) console.log(' in pong', data); + break; + case 'streamInformation': + if (debug) console.log(' in streamInformation', data); + callback.call(adaptorRef.current, data.command, data); + break; + case 'trackList': + if (debug) console.log(' in trackList', data); + callback.call(adaptorRef.current, data.command, data); + break; + case 'connectWithNewId': + if (debug) console.log(' in connectWithNewId', data); + callback.call(adaptorRef.current, data.command, data); + break; + case 'peerMessageCommand': + if (debug) console.log(' in peerMessageCommand', data); + callback.call(adaptorRef.current, data.command, data); + break; + default: + if (debug) console.log(' in default', data); + callback.call(adaptorRef.current, data.command, data); + break; } - break; - case 'notification': - if (debug) console.log(' in notification', data); - - if (adaptorRef.current) - callback.call(adaptorRef.current, data.definition, data); - break; - case 'roomInformation': - if (debug) console.log(' in roomInformation', data); - callback.call(adaptorRef.current, data.command, data); - break; - case 'pong': - if (debug) console.log(' in pong', data); - break; - case 'streamInformation': - if (debug) console.log(' in streamInformation', data); - callback.call(adaptorRef.current, data.command, data); - break; - case 'trackList': - if (debug) console.log(' in trackList', data); - callback.call(adaptorRef.current, data.command, data); - break; - case 'connectWithNewId': - if (debug) console.log(' in connectWithNewId', data); - callback.call(adaptorRef.current, data.command, data); - break; - case 'peerMessageCommand': - if (debug) console.log(' in peerMessageCommand', data); - callback.call(adaptorRef.current, data.command, data); - break; - default: - if (debug) console.log(' in default', data); - callback.call(adaptorRef.current, data.command, data); - break; - } - }; + }; - ws.onerror = (e: any) => { - // an error occurred - clearPingTimer(); - if (debug) console.log(e.message); - }; + ws.onerror = (e: any) => { + // an error occurred + clearPingTimer(); + if (debug) console.log(e.message); + }; - ws.onclose = (e: any) => { - // connection closed - clearPingTimer(); - if (debug) console.log(e.code, e.reason); - }; - }, [ + ws.onclose = (e: any) => { + // connection closed + clearPingTimer(); + if (debug) console.log(e.code, e.reason); + if (callback && adaptorRef.current) callback.call(adaptorRef.current, 'websocket_closed', '' ); + ws = null; + }; + }, [callback, callbackError, closePeerConnection, debug, mediaConstraints, startPublishing, takeCandidate, takeConfiguration, ws]); + + useEffect(() => { + setWebSocketListeners(); + }, [ callback, callbackError, closePeerConnection, @@ -574,6 +591,16 @@ export function useAntMedia(params: Params) { mainTrack?:string, metaData?:string ) => { + if (ws && ws.readyState === ws.CLOSED) { + if (debug) console.log('WebSocket is not connected'); + if (adaptorRef.current) callback.call(adaptorRef.current, 'websocket_not_initialized', ''); + } + + if (localStream.current === null) { + if (debug) console.log('Local stream is not ready'); + return; + } + let data = {} as any; if (onlyDataChannel) { data = { @@ -586,7 +613,6 @@ export function useAntMedia(params: Params) { audio: false, }; } else { - if (!localStream.current) return; let [video, audio] = [false, false]; @@ -617,6 +643,11 @@ export function useAntMedia(params: Params) { //play const play = useCallback( (streamId: string, token?: string, room?: string , enableTracks?:MediaStreamTrack[],subscriberId?:string, subscriberCode?:string ,metaData?:string ) => { + if (ws && ws.readyState === ws.CLOSED) { + if (debug) console.log('WebSocket is not connected'); + if (adaptorRef.current) callback.call(adaptorRef.current, 'websocket_not_initialized', ''); + } + playStreamIds.push(streamId); const data = { command: 'play', @@ -638,6 +669,38 @@ export function useAntMedia(params: Params) { [playStreamIds, ws] ); + const stopLocalStream = useCallback( + () => { + if (localStream.current) { + // @ts-ignore + localStream.current.getTracks().forEach((track) => { + track.stop(); + }); + localStream.current = null; + } + }, + [localStream] + ); + + const initialiseWebSocket = useCallback(() => { + console.log('initialising websocket') + if (ws && ws.readyState === ws.OPEN) { + if (debug) console.log('WebSocket is already connected'); + return; + } + + wsRef.current = new WebSocket(url); + ws = wsRef.current; + setWebSocketListeners(); + console.log('WebSocket is connected'); + }, [ws]); + + const closeWebSocket = useCallback(() => { + if (ws) { + ws.close(); + } + }, [ws]); + const stop = useCallback( (streamId: any) => { closePeerConnection(streamId); @@ -921,6 +984,9 @@ export function useAntMedia(params: Params) { publish, play, stop, + stopLocalStream, + initialiseWebSocket, + closeWebSocket, join, leave, getRoomInfo, @@ -945,6 +1011,9 @@ export function useAntMedia(params: Params) { publish, play, stop, + stopLocalStream, + initialiseWebSocket, + closeWebSocket, localStream, join, leave, @@ -970,6 +1039,9 @@ export function useAntMedia(params: Params) { publish, play, stop, + stopLocalStream, + initialiseWebSocket, + closeWebSocket, localStream, join, leave, From 89d1453e074de68f0e8f6c2be71a553c13692257 Mon Sep 17 00:00:00 2001 From: Mustafa BOLEKEN Date: Mon, 29 Jul 2024 13:36:04 +0300 Subject: [PATCH 2/3] Try publishing again in case of websocket is not initialised yet --- example/src/App.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/example/src/App.tsx b/example/src/App.tsx index c2d97a7..9f42aae 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -19,6 +19,7 @@ export default function App() { const streamNameRef = useRef(defaultStreamName); const [localMedia, setLocalMedia] = useState(''); const [isPlaying, setIsPlaying] = useState(false); + const [isWaitingWebsocketInit, setIsWaitingWebsocketInit] = useState(false); let localStream: any = useRef(null); @@ -56,6 +57,7 @@ export default function App() { verify(); break; case 'websocket_not_initialized': + setIsWaitingWebsocketInit(true); adaptor.initialiseWebSocket(); break; case 'websocket_closed': @@ -84,6 +86,11 @@ export default function App() { console.log('in verify'); if (adaptor.localStream.current && adaptor.localStream.current.toURL()) { console.log('in verify if adaptor local stream', adaptor.localStream); + if (isWaitingWebsocketInit) { + setIsWaitingWebsocketInit(false); + publishStreamId = generateRandomString(12); + adaptor.publish(streamNameRef.current); + } return setLocalMedia(adaptor.localStream.current.toURL()); } setTimeout(verify, 5000); From f10ae4935ccbc1a1032ffee778a99c5e16a6c368 Mon Sep 17 00:00:00 2001 From: Mustafa BOLEKEN Date: Sat, 31 Aug 2024 08:06:02 +0300 Subject: [PATCH 3/3] Add generateRandomString --- example/src/App.tsx | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index 9f42aae..2039939 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -11,6 +11,8 @@ import {useAntMedia, rtc_view} from '@antmedia/react-native-ant-media'; import InCallManager from 'react-native-incall-manager'; +var publishStreamId:string; + export default function App() { var defaultStreamName = 'streamTest1'; const webSocketUrl = 'ws://server.com:5080/WebRTCAppEE/websocket'; @@ -82,6 +84,18 @@ export default function App() { debug: true, }); + const generateRandomString = (length: number): string => { + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let result = ''; + const charactersLength = characters.length; + + for (let i = 0; i < length; i++) { + const randomIndex = Math.floor(Math.random() * charactersLength); + result += characters.charAt(randomIndex); + } + return result; + }; + const verify = () => { console.log('in verify'); if (adaptor.localStream.current && adaptor.localStream.current.toURL()) { @@ -89,7 +103,7 @@ export default function App() { if (isWaitingWebsocketInit) { setIsWaitingWebsocketInit(false); publishStreamId = generateRandomString(12); - adaptor.publish(streamNameRef.current); + adaptor.publish(publishStreamId); } return setLocalMedia(adaptor.localStream.current.toURL()); } @@ -110,15 +124,15 @@ export default function App() { if (!adaptor) { return; } - - adaptor.publish(streamNameRef.current); + publishStreamId = generateRandomString(12); + adaptor.publish(publishStreamId); }, [adaptor]); const handleStop = useCallback(() => { if (!adaptor) { return; } - adaptor.stop(streamNameRef.current); + adaptor.stop(publishStreamId); }, [adaptor]); return (