11import React , { useEffect , useRef , useState } from 'react'
22import { useLocation } from 'react-router-dom'
3- import ErrorHeader from '../error-header/errorHeader'
3+ import ErrorHeader from '../error-header/ErrorHeader'
4+ import { useNavigate } from 'react-router-dom'
45
56const mediaOptions = {
67 audio : true ,
@@ -28,24 +29,36 @@ function getMediaErrorMessage(value: ErrorMessageEnum): string {
2829
2930function BrowserBroadcaster ( ) {
3031 const videoRef = useRef < HTMLVideoElement > ( null )
32+
33+ //TODO: Use prop instead of location
3134 const location = useLocation ( )
35+ const navigate = useNavigate ( ) ;
3236 const [ mediaAccessError , setMediaAccessError ] = useState < ErrorMessageEnum | null > ( null )
3337 const [ publishSuccess , setPublishSuccess ] = useState ( false )
34- const [ useDisplayMedia , setUseDisplayMedia ] = useState ( false )
38+ const [ useDisplayMedia , setUseDisplayMedia ] = useState < "Screen" | "Webcam" | "None" > ( "None" ) ;
39+ const [ peerConnection , _ ] = useState < RTCPeerConnection > ( new RTCPeerConnection ( ) ) ;
3540 const [ peerConnectionDisconnected , setPeerConnectionDisconnected ] = useState ( false )
3641
3742 const apiPath = import . meta. env . VITE_API_PATH ;
3843
44+ const endStream = ( ) => {
45+ navigate ( '/' )
46+ }
47+
3948 useEffect ( ( ) => {
40- const peerConnection = new RTCPeerConnection ( )
49+ if ( useDisplayMedia === "None" || ! peerConnection ) {
50+ return ;
51+ }
52+
4153 let stream : MediaStream | undefined = undefined ;
4254
4355 if ( ! navigator . mediaDevices ) {
44- setMediaAccessError ( ErrorMessageEnum . NoMediaDevices ) ;
56+ setMediaAccessError ( ( ) => ErrorMessageEnum . NoMediaDevices ) ;
57+ setUseDisplayMedia ( ( ) => "None" )
4558 return
4659 }
4760
48- const mediaPromise = useDisplayMedia ?
61+ const mediaPromise = useDisplayMedia == "Screen" ?
4962 navigator . mediaDevices . getDisplayMedia ( mediaOptions ) :
5063 navigator . mediaDevices . getUserMedia ( mediaOptions )
5164
@@ -91,6 +104,7 @@ function BrowserBroadcaster() {
91104 peerConnection . oniceconnectionstatechange = ( ) => {
92105 if ( peerConnection . iceConnectionState === 'connected' || peerConnection . iceConnectionState === 'completed' ) {
93106 setPublishSuccess ( true )
107+ setMediaAccessError ( ( ) => null )
94108 setPeerConnectionDisconnected ( false )
95109 } else if ( peerConnection . iceConnectionState === 'disconnected' || peerConnection . iceConnectionState === 'failed' ) {
96110 setPublishSuccess ( false )
@@ -102,7 +116,7 @@ function BrowserBroadcaster() {
102116 . createOffer ( )
103117 . then ( offer => {
104118 peerConnection . setLocalDescription ( offer )
105- . catch ( ( err ) => console . error ( err ) ) ;
119+ . catch ( ( err ) => console . error ( "SetLocalDescription" , err ) ) ;
106120
107121 fetch ( `${ apiPath } /whip` , {
108122 method : 'POST' ,
@@ -117,10 +131,13 @@ function BrowserBroadcaster() {
117131 sdp : answer ,
118132 type : 'answer'
119133 } )
120- . catch ( ( err ) => console . error ( err ) )
134+ . catch ( ( err ) => console . error ( "SetRemoveDescription" , err ) )
121135 } )
122136 } )
123- } , setMediaAccessError )
137+ } , ( reason : ErrorMessageEnum ) => {
138+ setMediaAccessError ( ( ) => reason )
139+ setUseDisplayMedia ( "None" ) ;
140+ } )
124141
125142 return function cleanup ( ) {
126143 peerConnection . close ( )
@@ -147,12 +164,28 @@ function BrowserBroadcaster() {
147164 className = 'w-full h-full'
148165 />
149166
150- < button
151- onClick = { ( ) => setUseDisplayMedia ( ! useDisplayMedia ) }
152- className = "appearance-none border w-full mt-5 py-2 px-3 leading-tight focus:outline-hidden focus:shadow-outline bg-gray-700 border-gray-700 text-white rounded-sm shadow-md placeholder-gray-200" >
153- { ! useDisplayMedia && < > Publish Screen/Window/Tab instead </ > }
154- { useDisplayMedia && < > Publish Webcam instead </ > }
155- </ button >
167+ < div className = "flex flex-row gap-2" >
168+ < button
169+ onClick = { ( ) => setUseDisplayMedia ( "Screen" ) }
170+ className = "appearance-none border w-full mt-5 py-2 px-3 leading-tight focus:outline-hidden focus:shadow-outline bg-blue-900 hover:bg-blue-800 border-gray-700 text-white rounded-sm shadow-md placeholder-gray-200" >
171+ Publish Screen/Window/Tab
172+ </ button >
173+ < button
174+ onClick = { ( ) => setUseDisplayMedia ( "Webcam" ) }
175+ className = "appearance-none border w-full mt-5 py-2 px-3 leading-tight focus:outline-hidden focus:shadow-outline bg-blue-900 hover:bg-blue-800 border-gray-700 text-white rounded-sm shadow-md placeholder-gray-200" >
176+ Publish Webcam
177+ </ button >
178+ </ div >
179+
180+ { publishSuccess && (
181+ < div >
182+ < button
183+ onClick = { endStream }
184+ className = "appearance-none border w-full mt-5 py-2 px-3 leading-tight focus:outline-hidden focus:shadow-outline bg-red-900 hover:bg-red-800 border-gray-700 text-white rounded-sm shadow-md placeholder-gray-200" >
185+ End stream
186+ </ button >
187+ </ div >
188+ ) }
156189 </ div >
157190 )
158191}
0 commit comments