1- import React from 'react' ;
1+ import React , { Component } from 'react' ;
22import PropTypes from 'prop-types' ;
33import { MuiThemeProvider , createMuiTheme } from '@material-ui/core/styles' ;
44import { withStyles } from '@material-ui/core/styles' ;
@@ -10,48 +10,258 @@ import IconButton from '@material-ui/core/IconButton';
1010import MenuIcon from '@material-ui/icons/Menu' ;
1111import blue from '@material-ui/core/colors/blue' ;
1212
13+ var socket = require ( 'socket.io-client' ) ( 'https://localhost:4443' ) ;
14+
15+ var RTCPeerConnection ;
16+ var RTCSessionDescription ;
17+
18+
19+ var pcPeers = { } ;
20+ var selfView ;
21+ var remoteView ;
22+ var localStream ;
23+ var configuration ;
24+
25+
1326const theme = createMuiTheme ( {
1427 palette : {
1528 primary : blue ,
1629 } ,
1730} ) ;
1831
19- const styles = {
20- root : {
21- flexGrow : 1 ,
22- } ,
23- flex : {
24- flex : 1 ,
25- } ,
26- menuButton : {
27- marginLeft : - 12 ,
28- marginRight : 20 ,
29- } ,
30- } ;
32+ export default class App extends Component {
33+
34+ componentDidMount = ( ) => {
35+
36+ remoteView = this . refs [ 'remoteView' ] ;
37+ selfView = this . refs [ 'selfView' ] ;
38+
39+ RTCPeerConnection = window . RTCPeerConnection || window . mozRTCPeerConnection || window . webkitRTCPeerConnection || window . msRTCPeerConnection ;
40+ RTCSessionDescription = window . RTCSessionDescription || window . mozRTCSessionDescription || window . webkitRTCSessionDescription || window . msRTCSessionDescription ;
41+ navigator . getUserMedia = navigator . getUserMedia || navigator . mozGetUserMedia || navigator . webkitGetUserMedia || navigator . msGetUserMedia ;
42+
43+ var twilioIceServers = [
44+ { url : 'stun:global.stun.twilio.com:3478?transport=udp' }
45+ ] ;
46+ configuration = { "iceServers" : [ { "url" : "stun:stun.l.google.com:19302" } ] } ;
47+
48+
49+ socket . on ( 'exchange' , ( data ) => {
50+ this . exchange ( data ) ;
51+ } ) ;
52+ socket . on ( 'leave' , ( socketId ) => {
53+ this . leave ( socketId ) ;
54+ } ) ;
55+
56+ socket . on ( 'connect' , ( data ) => {
57+ console . log ( 'connect' ) ;
58+ this . getLocalStream ( ) ;
59+ } ) ;
60+
61+ }
62+
63+ getLocalStream = ( ) => {
64+
65+ var constraints = { audio : true , video : { width : 1280 , height : 720 } } ;
66+
67+ navigator . mediaDevices . getUserMedia ( constraints )
68+ . then ( function ( mediaStream ) {
69+ localStream = mediaStream ;
70+ selfView . srcObject = mediaStream ;
71+ selfView . mute = true ;
72+ selfView . onloadedmetadata = function ( e ) {
73+ selfView . play ( ) ;
74+ } ;
75+ } )
76+ . catch ( ( err ) => {
77+ console . log ( err . name + ": " + err . message ) ;
78+ }
79+ ) ;
80+
81+
82+ }
83+
84+ join = ( roomID ) => {
85+ socket . emit ( 'join' , roomID , ( socketIds ) => {
86+ console . log ( 'join' , socketIds ) ;
87+ for ( var i in socketIds ) {
88+ var socketId = socketIds [ i ] ;
89+ this . createPC ( socketId , true ) ;
90+ }
91+ } ) ;
92+ }
93+
94+ createOffer = ( pc , socketId ) => {
95+ pc . createOffer ( ( desc ) => {
96+ console . log ( 'createOffer' , desc ) ;
97+ pc . setLocalDescription ( desc , ( ) => {
98+ console . log ( 'setLocalDescription' , pc . localDescription ) ;
99+ socket . emit ( 'exchange' , { 'to' : socketId , 'sdp' : pc . localDescription } ) ;
100+ } , this . logError ) ;
101+ } , this . logError ) ;
102+ }
103+
104+ createPC = ( socketId , isOffer ) => {
105+ var pc = new RTCPeerConnection ( configuration ) ;
106+ pcPeers [ socketId ] = pc ;
107+
108+ pc . onicecandidate = ( event ) => {
109+ console . log ( 'onicecandidate' , event ) ;
110+ if ( event . candidate ) {
111+ socket . emit ( 'exchange' , { 'to' : socketId , 'candidate' : event . candidate } ) ;
112+ }
113+ } ;
114+
115+ pc . onnegotiationneeded = ( ) => {
116+ console . log ( 'onnegotiationneeded' ) ;
117+ if ( isOffer ) {
118+ this . createOffer ( pc , socketId ) ;
119+ }
120+ }
121+
122+ pc . oniceconnectionstatechange = ( event ) => {
123+ console . log ( 'oniceconnectionstatechange' , event ) ;
124+ if ( event . target . iceConnectionState === 'connected' ) {
125+ this . createDataChannel ( pc ) ;
126+ }
127+ } ;
128+ pc . onsignalingstatechange = ( event ) => {
129+ console . log ( 'onsignalingstatechange' , event ) ;
130+ } ;
131+
132+ pc . onaddstream = ( event ) => {
133+ console . log ( 'onaddstream' , event ) ;
134+ // var element = document.createElement('video');
135+ // element.id = "remoteView" + socketId;
136+ // element.autoplay = 'autoplay';
137+ // element.src = URL.createObjectURL(event.stream);
138+ // remoteViewContainer.appendChild(element);
139+
140+ remoteView . srcObject = event . stream ;
141+ remoteView . onloadedmetadata = function ( e ) {
142+ remoteView . play ( ) ;
143+ } ;
144+
145+
146+ } ;
147+ pc . addStream ( localStream ) ;
148+
149+ return pc ;
150+ }
151+
152+ createDataChannel = ( pc ) => {
153+ if ( pc . textDataChannel ) {
154+ return ;
155+ }
156+ var dataChannel = pc . createDataChannel ( "text" ) ;
157+
158+ dataChannel . onerror = ( error ) => {
159+ console . log ( "dataChannel.onerror" , error ) ;
160+ } ;
161+
162+ dataChannel . onmessage = ( event ) => {
163+ console . log ( "dataChannel.onmessage:" , event . data ) ;
164+ var content = document . getElementById ( 'textRoomContent' ) ;
165+ //content.innerHTML = content.innerHTML + '<p>' + socketId + ': ' + event.data + '</p>';
166+ } ;
167+
168+ dataChannel . onopen = ( ) => {
169+ console . log ( 'dataChannel.onopen' ) ;
170+ // var textRoom = document.getElementById('textRoom');
171+ // textRoom.style.display = "block";
172+ } ;
173+
174+ dataChannel . onclose = ( ) => {
175+ console . log ( "dataChannel.onclose" ) ;
176+ } ;
177+
178+ pc . textDataChannel = dataChannel ;
179+ }
180+
181+ exchange = ( data ) => {
182+ var fromId = data . from ;
183+ var pc ;
184+ if ( fromId in pcPeers ) {
185+ pc = pcPeers [ fromId ] ;
186+ } else {
187+ pc = this . createPC ( fromId , false ) ;
188+ }
189+
190+ if ( data . sdp ) {
191+ console . log ( 'exchange sdp' , data ) ;
192+ pc . setRemoteDescription ( new RTCSessionDescription ( data . sdp ) , ( ) => {
193+ if ( pc . remoteDescription . type == "offer" )
194+ pc . createAnswer ( ( desc ) => {
195+ console . log ( 'createAnswer' , desc ) ;
196+ pc . setLocalDescription ( desc , ( ) => {
197+ console . log ( 'setLocalDescription' , pc . localDescription ) ;
198+ socket . emit ( 'exchange' , { 'to' : fromId , 'sdp' : pc . localDescription } ) ;
199+ } , this . logError ) ;
200+ } , this . logError ) ;
201+ } , this . logError ) ;
202+ } else {
203+ console . log ( 'exchange candidate' , data ) ;
204+ pc . addIceCandidate ( new RTCIceCandidate ( data . candidate ) ) ;
205+ }
206+ }
207+
208+ leave = ( socketId ) => {
209+ console . log ( 'leave' , socketId ) ;
210+ var pc = pcPeers [ socketId ] ;
211+ pc . close ( ) ;
212+ delete pcPeers [ socketId ] ;
213+ var video = document . getElementById ( "remoteView" + socketId ) ;
214+ if ( video ) video . remove ( ) ;
215+ }
216+
217+
218+
219+ logError = ( error ) => {
220+ console . log ( "logError" , error ) ;
221+ }
222+
223+ joinRoomPress = ( ) => {
224+ var roomID = '111111' ;
225+ if ( roomID == "" ) {
226+ alert ( 'Please enter room ID' ) ;
227+ } else {
228+ this . join ( roomID ) ;
229+ }
230+ }
231+
232+ textRoomPress ( ) {
233+ var text = "test send text..." ; //document.getElementById('textRoomInput').value;
234+ if ( text == "" ) {
235+ alert ( 'Enter something' ) ;
236+ } else {
237+ //document.getElementById('textRoomInput').value = '';
238+ // var content = document.getElementById('textRoomContent');
239+ // content.innerHTML = content.innerHTML + '<p>' + 'Me' + ': ' + text + '</p>';
240+ for ( var key in pcPeers ) {
241+ var pc = pcPeers [ key ] ;
242+ pc . textDataChannel . send ( text ) ;
243+ }
244+ }
245+ }
246+
247+
248+ render ( ) {
249+ const { classes } = this . props ;
250+ return (
251+ < MuiThemeProvider theme = { theme } >
252+ < div >
253+ < video ref = 'selfView' autoplay style = { { width : '320px' , height : '240px' } } > </ video >
254+ < video ref = 'remoteView' autoplay style = { { width : '320px' , height : '240px' } } > </ video >
255+ < Button color = "primary" onClick = { this . joinRoomPress } >
256+ join room
257+ </ Button >
258+ </ div >
259+ </ MuiThemeProvider >
260+ ) ;
261+ }
31262
32- function App ( props ) {
33- const { classes } = props ;
34- return (
35- < MuiThemeProvider theme = { theme } >
36- < div className = { classes . root } >
37- < AppBar position = "static" >
38- < Toolbar >
39- < IconButton className = { classes . menuButton } color = "inherit" aria-label = "Menu" >
40- < MenuIcon />
41- </ IconButton >
42- < Typography variant = "title" color = "inherit" className = { classes . flex } >
43- Flutter WebRTC Demo
44- </ Typography >
45- < Button color = "inherit" > Join</ Button >
46- </ Toolbar >
47- </ AppBar >
48- </ div >
49- </ MuiThemeProvider >
50- ) ;
51263}
52264
53265App . propTypes = {
54266 classes : PropTypes . object . isRequired ,
55267} ;
56-
57- export default withStyles ( styles ) ( App ) ;
0 commit comments