blob: a18d24da8a3d315c08cd37ead6d0a771b43b18bf [file] [log] [blame]
Charliec2c012f2022-10-05 14:09:28 -04001/*
2 * Copyright (C) 2022 Savoir-faire Linux Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as
6 * published by the Free Software Foundation; either version 3 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public
15 * License along with this program. If not, see
16 * <https://www.gnu.org/licenses/>.
17 */
18
19import React, { createContext, useCallback, useRef } from 'react';
20import { connect, Socket } from 'socket.io-client';
21
22import { WithChildren } from '../utils/utils';
23
24/*
25 * TODO: This socket is temporary, it will be replaced by the real socket
26 * for communication with webrtc
27 * */
28const socket = connect('http://192.168.0.12:8080', { transports: ['websocket'] });
29
30interface IWebRTCContext {
31 localVideoRef: React.RefObject<HTMLVideoElement> | null;
32 remoteVideoRef: React.RefObject<HTMLVideoElement> | null;
33 createWebRTCConnection: () => void;
34 sendWebRTCOffer: () => void;
35 sendWebRTCAnswer: (remoteSdp: RTCSessionDescriptionInit) => void;
36 handleWebRTCAnswer: (remoteSdp: RTCSessionDescriptionInit) => void;
37 addIceCandidate: (candidate: RTCIceCandidateInit) => void;
38 socket: Socket;
39}
40
41const DefaultWebRTCContext: IWebRTCContext = {
42 localVideoRef: null,
43 remoteVideoRef: null,
44 createWebRTCConnection: () => {},
45 sendWebRTCOffer: () => {},
46 sendWebRTCAnswer: () => {},
47 handleWebRTCAnswer: () => {},
48 addIceCandidate: () => {},
49 socket: socket,
50};
51
52export const WebRTCContext = createContext<IWebRTCContext>(DefaultWebRTCContext);
53
54export default ({ children }: WithChildren) => {
55 const localVideoRef = useRef<HTMLVideoElement>(null);
56 const remoteVideoRef = useRef<HTMLVideoElement>(null);
57 const webRTCConnectionRef = useRef<RTCPeerConnection>();
58
59 const createWebRTCConnection = useCallback(async () => {
60 //TODO use SFL iceServers
61 const iceConfig = { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] };
62 webRTCConnectionRef.current = new RTCPeerConnection(iceConfig);
63 const localStream = await navigator.mediaDevices.getUserMedia({
64 video: true,
65 audio: true,
66 });
67
68 if (localVideoRef.current) {
69 localVideoRef.current.srcObject = localStream;
70 }
71
72 localStream.getTracks().forEach((track) => {
73 if (webRTCConnectionRef.current) {
74 webRTCConnectionRef.current.addTrack(track, localStream);
75 }
76 });
77 webRTCConnectionRef.current.addEventListener('icecandidate', (event) => {
78 if (event.candidate && socket) {
79 console.log('webRTCConnection : onicecandidate');
80 socket.emit('candidate', event.candidate);
81 }
82 });
83 webRTCConnectionRef.current.addEventListener('track', async (event) => {
84 if (remoteVideoRef.current) {
85 remoteVideoRef.current.srcObject = event.streams[0];
86 console.log('webRTCConnection : add remotetrack success');
87 }
88 });
89 }, [webRTCConnectionRef, localVideoRef, remoteVideoRef]);
90
91 const sendWebRTCOffer = useCallback(async () => {
92 try {
93 if (webRTCConnectionRef.current && socket) {
94 const sdp = await webRTCConnectionRef.current.createOffer({
95 offerToReceiveAudio: true,
96 offerToReceiveVideo: true,
97 });
98 await webRTCConnectionRef.current.setLocalDescription(new RTCSessionDescription(sdp));
99 socket.emit('offer', sdp);
100 }
101 } catch (e) {
102 console.error(e);
103 }
104 }, [webRTCConnectionRef]);
105
106 const sendWebRTCAnswer = useCallback(
107 async (remoteSdp: RTCSessionDescriptionInit) => {
108 try {
109 if (webRTCConnectionRef.current && socket && remoteSdp) {
110 await webRTCConnectionRef.current.setRemoteDescription(new RTCSessionDescription(remoteSdp));
111 const mySdp = await webRTCConnectionRef.current.createAnswer({
112 offerToReceiveAudio: true,
113 offerToReceiveVideo: true,
114 });
115 await webRTCConnectionRef.current.setLocalDescription(new RTCSessionDescription(mySdp));
116 socket.emit('answer', mySdp);
117 }
118 } catch (e) {
119 console.error(e);
120 }
121 },
122 [webRTCConnectionRef]
123 );
124
125 const handleWebRTCAnswer = useCallback(
126 async (remoteSdp: RTCSessionDescriptionInit) => {
127 try {
128 if (webRTCConnectionRef.current && remoteSdp) {
129 await webRTCConnectionRef.current.setRemoteDescription(new RTCSessionDescription(remoteSdp));
130 }
131 } catch (e) {
132 console.error(e);
133 }
134 },
135 [webRTCConnectionRef]
136 );
137
138 const addIceCandidate = useCallback(
139 async (candidate: RTCIceCandidateInit) => {
140 try {
141 if (webRTCConnectionRef.current) {
142 await webRTCConnectionRef.current.addIceCandidate(new RTCIceCandidate(candidate));
143 }
144 } catch (e) {
145 console.error(e);
146 }
147 },
148 [webRTCConnectionRef]
149 );
150
151 return (
152 <WebRTCContext.Provider
153 value={{
154 localVideoRef,
155 remoteVideoRef,
156 createWebRTCConnection,
157 sendWebRTCOffer,
158 sendWebRTCAnswer,
159 handleWebRTCAnswer,
160 addIceCandidate,
161 socket,
162 }}
163 >
164 {children}
165 </WebRTCContext.Provider>
166 );
167};