136 lines
4.4 KiB
TypeScript
136 lines
4.4 KiB
TypeScript
import { useState, useEffect, useRef, useCallback } from 'react';
|
|
import { API } from '../api/player';
|
|
|
|
interface UseScreenShareResult {
|
|
isScreenSharing: boolean;
|
|
isScreenSharingSupported: boolean;
|
|
toggleScreenShare: () => Promise<void>;
|
|
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<WebSocket | null>(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
|
|
};
|
|
};
|