111 lines
2.9 KiB
JavaScript
111 lines
2.9 KiB
JavaScript
import { ref, watch, onUnmounted } from 'vue'
|
|
|
|
export function useCamera(videoRef) {
|
|
const stream = ref(null)
|
|
const facingMode = ref('environment')
|
|
const hasMultipleCameras = ref(false)
|
|
const isMirrored = ref(false)
|
|
const error = ref('')
|
|
|
|
const checkCameras = async () => {
|
|
try {
|
|
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
|
|
return
|
|
}
|
|
const devices = await navigator.mediaDevices.enumerateDevices()
|
|
const cameras = devices.filter(d => d.kind === 'videoinput')
|
|
hasMultipleCameras.value = cameras.length > 1
|
|
} catch (e) {
|
|
console.error('Error checking cameras:', e)
|
|
}
|
|
}
|
|
|
|
const stopCamera = () => {
|
|
if (stream.value) {
|
|
stream.value.getTracks().forEach(t => t.stop())
|
|
stream.value = null
|
|
}
|
|
}
|
|
|
|
const startCamera = async () => {
|
|
stopCamera()
|
|
error.value = ''
|
|
|
|
try {
|
|
const constraints = {
|
|
video: {
|
|
facingMode: facingMode.value,
|
|
width: { ideal: 1280 },
|
|
height: { ideal: 720 }
|
|
}
|
|
}
|
|
|
|
const mediaStream = await navigator.mediaDevices.getUserMedia(constraints)
|
|
stream.value = mediaStream
|
|
|
|
// Detect actual facing mode to mirror front camera correctly
|
|
const videoTrack = mediaStream.getVideoTracks()[0]
|
|
if (videoTrack) {
|
|
const settings = videoTrack.getSettings()
|
|
if (settings.facingMode) {
|
|
isMirrored.value = settings.facingMode === 'user'
|
|
} else {
|
|
// Fallback: check label for desktop cameras or assume requested mode
|
|
const label = videoTrack.label ? videoTrack.label.toLowerCase() : ''
|
|
if (label.includes('front') || label.includes('facetime') || label.includes('macbook')) {
|
|
isMirrored.value = true
|
|
} else {
|
|
isMirrored.value = facingMode.value === 'user'
|
|
}
|
|
}
|
|
}
|
|
|
|
if (videoRef.value) {
|
|
videoRef.value.srcObject = mediaStream
|
|
return new Promise((resolve) => {
|
|
videoRef.value.onloadedmetadata = () => {
|
|
videoRef.value.play().catch(e => console.error('Play error', e))
|
|
resolve()
|
|
}
|
|
})
|
|
}
|
|
} catch (err) {
|
|
if (err.name === 'NotAllowedError') {
|
|
error.value = 'Camera permission denied'
|
|
} else if (err.name === 'NotFoundError') {
|
|
error.value = 'No camera found'
|
|
} else {
|
|
error.value = `Camera error: ${err.name}`
|
|
}
|
|
throw err // Let caller know it failed
|
|
}
|
|
}
|
|
|
|
const switchCamera = () => {
|
|
facingMode.value = facingMode.value === 'environment' ? 'user' : 'environment'
|
|
}
|
|
|
|
watch(facingMode, () => {
|
|
if (stream.value) {
|
|
// Re-start if already running
|
|
startCamera().catch(() => { })
|
|
}
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
stopCamera()
|
|
})
|
|
|
|
return {
|
|
stream,
|
|
facingMode,
|
|
hasMultipleCameras,
|
|
isMirrored,
|
|
error,
|
|
checkCameras,
|
|
startCamera,
|
|
stopCamera,
|
|
switchCamera
|
|
}
|
|
}
|