UNPKG

webrtc2-peer

Version:

WebRTC2 Peer - Low-level WebRTC peer connection management for cross-platform real-time communication with signaling, ICE handling, and media streaming

166 lines (138 loc) 4.98 kB
/** * MediaManager - Media device and stream management */ export interface MediaConstraints { video?: boolean | MediaTrackConstraints; audio?: boolean | MediaTrackConstraints; } export interface MediaDevice { deviceId: string; kind: MediaDeviceKind; label: string; groupId: string; } export class MediaManager { private currentStream?: MediaStream; private devices: MediaDevice[] = []; async getDevices(): Promise<MediaDevice[]> { try { const devices = await navigator.mediaDevices.enumerateDevices(); this.devices = devices.map(device => ({ deviceId: device.deviceId, kind: device.kind, label: device.label, groupId: device.groupId })); return this.devices; } catch (error) { throw new Error(`Failed to get media devices: ${error}`); } } async getUserMedia(constraints: MediaConstraints = { video: true, audio: true }): Promise<MediaStream> { try { const stream = await navigator.mediaDevices.getUserMedia(constraints); this.currentStream = stream; return stream; } catch (error) { throw new Error(`Failed to get user media: ${error}`); } } async getDisplayMedia(constraints?: MediaStreamConstraints): Promise<MediaStream> { try { const stream = await navigator.mediaDevices.getDisplayMedia(constraints); return stream; } catch (error) { throw new Error(`Failed to get display media: ${error}`); } } async switchCamera(deviceId?: string): Promise<MediaStream> { if (!this.currentStream) { throw new Error('No active stream to switch camera'); } const videoTrack = this.currentStream.getVideoTracks()[0]; if (!videoTrack) { throw new Error('No video track found'); } const constraints: MediaTrackConstraints = { deviceId: deviceId ? { exact: deviceId } : undefined }; try { const newStream = await navigator.mediaDevices.getUserMedia({ video: constraints, audio: this.hasAudio() }); // Replace video track const newVideoTrack = newStream.getVideoTracks()[0]; if (newVideoTrack) { this.currentStream.removeTrack(videoTrack); this.currentStream.addTrack(newVideoTrack); videoTrack.stop(); } return this.currentStream; } catch (error) { throw new Error(`Failed to switch camera: ${error}`); } } muteAudio(muted: boolean = true): void { if (!this.currentStream) return; this.currentStream.getAudioTracks().forEach(track => { track.enabled = !muted; }); } muteVideo(muted: boolean = true): void { if (!this.currentStream) return; this.currentStream.getVideoTracks().forEach(track => { track.enabled = !muted; }); } stopStream(stream?: MediaStream): void { const targetStream = stream || this.currentStream; if (!targetStream) return; targetStream.getTracks().forEach(track => { track.stop(); }); if (targetStream === this.currentStream) { this.currentStream = undefined; } } getCurrentStream(): MediaStream | undefined { return this.currentStream; } hasVideo(): boolean { return (this.currentStream?.getVideoTracks().length || 0) > 0; } hasAudio(): boolean { return (this.currentStream?.getAudioTracks().length || 0) > 0; } isAudioMuted(): boolean { const audioTrack = this.currentStream?.getAudioTracks()[0]; return audioTrack ? !audioTrack.enabled : true; } isVideoMuted(): boolean { const videoTrack = this.currentStream?.getVideoTracks()[0]; return videoTrack ? !videoTrack.enabled : true; } getVideoDevices(): MediaDevice[] { return this.devices.filter(device => device.kind === 'videoinput'); } getAudioDevices(): MediaDevice[] { return this.devices.filter(device => device.kind === 'audioinput'); } async checkPermissions(): Promise<{ camera: PermissionState; microphone: PermissionState; }> { try { const [camera, microphone] = await Promise.all([ navigator.permissions.query({ name: 'camera' as PermissionName }), navigator.permissions.query({ name: 'microphone' as PermissionName }) ]); return { camera: camera.state, microphone: microphone.state }; } catch (error) { throw new Error(`Failed to check permissions: ${error}`); } } }