Files
videocall-app/videocall-frontend/src/services/media.js
2025-09-07 14:08:11 +00:00

160 lines
4.3 KiB
JavaScript

// src/services/media.js - Media handling utilities
export const mediaService = {
/**
* Get available media devices
*/
async getMediaDevices() {
try {
const devices = await navigator.mediaDevices.enumerateDevices()
return {
videoDevices: devices.filter((device) => device.kind === 'videoinput'),
audioDevices: devices.filter((device) => device.kind === 'audioinput'),
audioOutputDevices: devices.filter((device) => device.kind === 'audiooutput'),
}
} catch (error) {
console.error('Failed to get media devices:', error)
return {
videoDevices: [],
audioDevices: [],
audioOutputDevices: [],
}
}
},
/**
* Check if user has granted media permissions
*/
async checkMediaPermissions() {
try {
const permissions = await Promise.all([
navigator.permissions.query({ name: 'camera' }),
navigator.permissions.query({ name: 'microphone' }),
])
return {
camera: permissions[0].state,
microphone: permissions[1].state,
}
} catch (error) {
console.error('Failed to check media permissions:', error)
return {
camera: 'unknown',
microphone: 'unknown',
}
}
},
/**
* Get optimal media constraints based on device capabilities
*/
async getOptimalConstraints() {
try {
const devices = await this.getMediaDevices()
// Default constraints
let constraints = {
video: {
width: { ideal: 640, max: 1280 },
height: { ideal: 480, max: 720 },
frameRate: { ideal: 15, max: 30 }
},
audio: {
echoCancellation: true,
noiseSuppression: true,
},
}
// Adjust constraints based on available devices
if (devices.videoDevices.length === 0) {
constraints.video = false
}
if (devices.audioDevices.length === 0) {
constraints.audio = false
}
return constraints
} catch (error) {
console.error('Failed to get optimal constraints:', error)
return {
video: true,
audio: true,
}
}
},
/**
* Test media access without keeping the stream
*/
async testMediaAccess() {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true,
})
// Immediately stop all tracks
stream.getTracks().forEach((track) => track.stop())
return {
success: true,
hasVideo: stream.getVideoTracks().length > 0,
hasAudio: stream.getAudioTracks().length > 0,
}
} catch (error) {
return {
success: false,
error: error.name,
message: this.getMediaErrorMessage(error),
}
}
},
/**
* Get user-friendly error message for media errors
*/
getMediaErrorMessage(error) {
switch (error.name) {
case 'NotAllowedError':
return 'Camera and microphone access denied. Please allow permissions and try again.'
case 'NotFoundError':
return 'No camera or microphone found on this device.'
case 'NotReadableError':
return 'Camera or microphone is already in use by another application.'
case 'OverconstrainedError':
return 'Camera or microphone does not support the requested settings.'
case 'SecurityError':
return 'Media access blocked due to security restrictions.'
case 'AbortError':
return 'Media access was aborted.'
default:
return 'Failed to access camera or microphone.'
}
},
/**
* Create media stream with fallback options
*/
async createStreamWithFallback(preferredConstraints) {
const fallbackOptions = [
preferredConstraints,
{ video: true, audio: true }, // Basic constraints
{ video: { width: 640, height: 480 }, audio: true }, // Lower resolution
{ video: false, audio: true }, // Audio only
]
for (const constraints of fallbackOptions) {
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints)
return { success: true, stream, constraints }
} catch (error) {
console.warn('Failed to create stream with constraints:', constraints, error)
continue
}
}
throw new Error('Failed to create media stream with any constraints')
},
}