160 lines
4.3 KiB
JavaScript
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')
|
|
},
|
|
}
|