1+ import { Socket } from 'socket.io-client' ;
2+
3+ type onMediaConnectedCb = ( socketId : string , remoteStream : MediaStream ) => void ;
4+ type onMediaDisconnectedCb = ( socketId : string ) => void ;
5+
6+ class RTC {
7+ static BITRATE = 30000 ;
8+ private signalingServerSocket : Socket ;
9+ private iceServerUrls : string [ ] ;
10+ private userMediaStream : MediaStream ;
11+ private connections : Map < string , RTCPeerConnection > ;
12+ private streams : Map < string , MediaStream > ;
13+ private onMediaConnectedCallback : onMediaConnectedCb ;
14+ private onMediaDisconnectedCallback : onMediaDisconnectedCb ;
15+
16+ constructor ( signalingServerSocket : Socket , iceServerUrls : string [ ] , userMediaStream : MediaStream ) {
17+ this . signalingServerSocket = signalingServerSocket ;
18+ this . iceServerUrls = iceServerUrls ;
19+ this . userMediaStream = userMediaStream ;
20+ this . connections = new Map ( ) ;
21+ this . streams = new Map ( ) ;
22+ this . onMediaConnectedCallback = ( ) => { } ; // eslint-disable-line @typescript-eslint/no-empty-function
23+ this . onMediaDisconnectedCallback = ( ) => { } ; // eslint-disable-line @typescript-eslint/no-empty-function
24+ }
25+
26+ onMediaConnected ( callback : onMediaConnectedCb ) {
27+ this . onMediaConnectedCallback = callback ;
28+ }
29+
30+ onMediaDisconnected ( callback : onMediaDisconnectedCb ) {
31+ this . onMediaDisconnectedCallback = callback ;
32+ }
33+
34+ #createPeerConnection( remoteSocketId : string ) {
35+ // initialize
36+ const pcOptions = {
37+ iceServers : [ { urls : this . iceServerUrls } ] ,
38+ } ;
39+ const pc = new RTCPeerConnection ( pcOptions ) ;
40+
41+ // add event listeners
42+ pc . addEventListener ( 'icecandidate' , iceEvent => {
43+ this . signalingServerSocket . emit ( 'send_ice' , iceEvent . candidate , remoteSocketId ) ;
44+ } ) ;
45+ pc . addEventListener ( 'track' , async ( event ) => {
46+ if ( this . streams . has ( remoteSocketId ) ) {
47+ return ;
48+ }
49+
50+ const [ remoteStream ] = event . streams ;
51+
52+ this . streams . set ( remoteSocketId , remoteStream ) ;
53+ this . onMediaConnectedCallback ( remoteSocketId , remoteStream ) ;
54+ } ) ;
55+
56+ // add tracks
57+ this . userMediaStream . getTracks ( ) . forEach ( track => pc . addTrack ( track , this . userMediaStream ) ) ;
58+
59+ return pc ;
60+ }
61+
62+ async #setVideoBitrate( pc : RTCPeerConnection , bitrate : number ) {
63+ // fetch video sender
64+ const [ videoSender ] = pc . getSenders ( ) . filter ( sender => sender ! . track ! . kind === 'video' ) ;
65+
66+ // set bitrate
67+ const params = videoSender . getParameters ( ) ;
68+ params . encodings [ 0 ] . maxBitrate = bitrate ;
69+ await videoSender . setParameters ( params ) ;
70+ }
71+
72+ connect ( ) {
73+ this . signalingServerSocket . on ( 'receive_hello' , async ( remoteSocketId ) => {
74+ const pc = this . #createPeerConnection( remoteSocketId ) ;
75+ this . connections . set ( remoteSocketId , pc ) ;
76+
77+ const offer = await pc . createOffer ( ) ;
78+ await pc . setLocalDescription ( offer ) ;
79+
80+ this . signalingServerSocket . emit ( 'send_offer' , pc . localDescription , remoteSocketId ) ;
81+ } ) ;
82+
83+ this . signalingServerSocket . on ( 'receive_offer' , async ( offer , remoteSocketId ) => {
84+ const pc = this . #createPeerConnection( remoteSocketId ) ;
85+ this . connections . set ( remoteSocketId , pc ) ;
86+
87+ await pc . setRemoteDescription ( offer ) ;
88+
89+ const answer = await pc . createAnswer ( ) ;
90+ await pc . setLocalDescription ( answer ) ;
91+
92+ this . signalingServerSocket . emit ( 'send_answer' , answer , remoteSocketId ) ;
93+ } ) ;
94+
95+ this . signalingServerSocket . on ( 'receive_answer' , async ( answer , remoteSocketId ) => {
96+ const pc = this . connections . get ( remoteSocketId ) ;
97+ if ( ! pc ) {
98+ throw new Error ( 'No RTCPeerConnection on answer received.' ) ;
99+ }
100+
101+ await pc . setRemoteDescription ( answer ) ;
102+ } ) ;
103+
104+ this . signalingServerSocket . on ( 'receive_ice' , ( ice , remoteSocketId ) => {
105+ const pc = this . connections . get ( remoteSocketId ) ;
106+ console . log ( 'received ice' ) ;
107+ if ( ! pc ) {
108+ throw new Error ( 'No RTCPeerConnection on ice candindate received.' ) ;
109+ }
110+
111+ pc . addIceCandidate ( ice ) ;
112+ } ) ;
113+
114+ this . signalingServerSocket . on ( 'receive_bye' , remoteSocketId => {
115+ this . connections . delete ( remoteSocketId ) ;
116+ this . streams . delete ( remoteSocketId ) ;
117+
118+ this . onMediaDisconnectedCallback ( remoteSocketId ) ;
119+ } )
120+
121+ this . signalingServerSocket . emit ( 'send_hello' ) ;
122+ }
123+ }
124+
125+ export default RTC ;
0 commit comments