import { useState, useEffect, useRef, useCallback } from 'react'; import { API } from '../api/player'; interface UseScreenShareResult { isScreenSharing: boolean; isScreenSharingSupported: boolean; toggleScreenShare: () => Promise; stopScreenShare: () => void; } function getBestSupportedMimeType() { // Ordered by preference (best first) - all of these include audio+video const mimeTypes = [ 'video/webm;codecs=vp9,opus', // Best quality, good compression 'video/webm;codecs=vp8,opus', // Good fallback, well supported 'video/webm;codecs=h264,opus', // Better compatibility with some systems 'video/mp4;codecs=h264,aac', // Good for Safari but may not be supported for MediaRecorder 'video/webm', // Generic fallback (browser will choose codecs) 'video/mp4' // Last resort ]; // Find the first supported mimetype for (const type of mimeTypes) { if (MediaRecorder.isTypeSupported(type)) { console.log(`Using mime type: ${type}`); return type; } } // If none are supported, return null or a basic fallback console.warn('No preferred mime types supported by this browser'); return 'video/webm'; // Most basic fallback } export const useScreenShare = (): UseScreenShareResult => { const [isScreenSharing, setIsScreenSharing] = useState(false); const [isScreenSharingSupported, setIsScreenSharingSupported] = useState(false); const screenShareSocketRef = useRef(null); // Check if screen sharing is supported useEffect(() => { setIsScreenSharingSupported( typeof navigator !== 'undefined' && navigator.mediaDevices !== undefined && typeof navigator.mediaDevices.getDisplayMedia === 'function' ); }, []); const stopScreenShare = useCallback(() => { if (screenShareSocketRef.current) { screenShareSocketRef.current.close(); screenShareSocketRef.current = null; } setIsScreenSharing(false); }, []); const startScreenShare = useCallback(async () => { try { const mediaStream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: true, }); let mimeType = getBestSupportedMimeType(); console.log('Using MIME type:', mimeType); const mediaRecorder = new MediaRecorder(mediaStream, { mimeType: mimeType, videoBitsPerSecond: 2500000, // 2.5 Mbps audioBitsPerSecond: 128000, // 128 kbps }); // Connect to WebSocket screenShareSocketRef.current = API.startScreenShare(mimeType); // Set up WebSocket event handlers screenShareSocketRef.current.onopen = () => { console.log('Screen sharing WebSocket connected'); setIsScreenSharing(true); mediaRecorder.start(100); }; screenShareSocketRef.current.onclose = () => { console.log('Screen sharing WebSocket closed'); setIsScreenSharing(false); // Stop all tracks when WebSocket is closed mediaStream.getTracks().forEach(track => track.stop()); }; screenShareSocketRef.current.onerror = (error) => { console.error('Screen sharing WebSocket error:', error); setIsScreenSharing(false); // Stop all tracks on error mediaStream.getTracks().forEach(track => track.stop()); }; // Send data over WebSocket when available mediaRecorder.ondataavailable = (event) => { if (event.data && event.data.size > 0 && screenShareSocketRef.current && screenShareSocketRef.current.readyState === WebSocket.OPEN) { screenShareSocketRef.current.send(event.data); } }; // Handle stream ending (user clicks "Stop sharing") mediaStream.getVideoTracks()[0].onended = () => { if (screenShareSocketRef.current) { screenShareSocketRef.current.close(); screenShareSocketRef.current = null; } setIsScreenSharing(false); }; } catch (error) { console.error('Error starting screen share:', error); setIsScreenSharing(false); } }, []); const toggleScreenShare = useCallback(async () => { if (screenShareSocketRef.current) { stopScreenShare(); } else { await startScreenShare(); } }, [startScreenShare, stopScreenShare]); return { isScreenSharing, isScreenSharingSupported, toggleScreenShare, stopScreenShare }; };