stash
This commit is contained in:
parent
5c3b7fc252
commit
bbe52e4a78
9 changed files with 817 additions and 287 deletions
|
|
@ -54,12 +54,13 @@
|
|||
<b-icon-save></b-icon-save> Save
|
||||
</button>
|
||||
</div>
|
||||
<select v-model="selectedCamera" v-on:change="onUserSelect"
|
||||
class="form-select position-relative w-50 mx-auto mb-5 mt-2 shadow"
|
||||
aria-label="Select Camera Source">
|
||||
<option disabled value="">Select Camera Source</option>
|
||||
<option v-for="camera in availableCameras" :key="camera.deviceId" :value="camera">
|
||||
{{ camera.label }}
|
||||
<select
|
||||
v-if="availableCameras.length > 1"
|
||||
v-model="selectedCameraId"
|
||||
class="form-select position-relative w-50 mx-auto mb-5 mt-2 shadow"
|
||||
aria-label="Select Camera Source">
|
||||
<option v-for="camera in availableCameras" :key="camera.deviceId" :value="camera.deviceId">
|
||||
{{ cleanCameraLabel(camera.label) || `Camera ${availableCameras.indexOf(camera) + 1}` }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
|
@ -74,6 +75,8 @@
|
|||
</style>
|
||||
|
||||
<script>
|
||||
import cameraManager from '@/cameraManager.js';
|
||||
|
||||
export default {
|
||||
name: "WebcamFileSource",
|
||||
data: () => ({
|
||||
|
|
@ -81,80 +84,105 @@ export default {
|
|||
error: false,
|
||||
show_modal: false,
|
||||
availableCameras: [],
|
||||
selectedCamera: undefined,
|
||||
localSelectedCameraId: null,
|
||||
capturing: false,
|
||||
streaming: false,
|
||||
stream: undefined,
|
||||
dataImage: undefined
|
||||
}),
|
||||
methods: {
|
||||
async attemptGetUserMedia(constraints) {
|
||||
this.stream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: false,
|
||||
video: constraints
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
if (error.name === "NotAllowedError") this.lastError = "Camera Permission Not Granted";
|
||||
if (error.name === "NotReadableError") this.lastError = "Camera Hardware Error";
|
||||
this.lastError = "Unknown Error"
|
||||
});
|
||||
if (this.stream) this.allowed = true;
|
||||
emits: ['input'],
|
||||
computed: {
|
||||
selectedCameraId: {
|
||||
get() {
|
||||
return this.localSelectedCameraId || cameraManager.getSelectedCameraId();
|
||||
},
|
||||
set(value) {
|
||||
if (value) {
|
||||
this.localSelectedCameraId = value;
|
||||
this.switchCamera(value);
|
||||
}
|
||||
},
|
||||
},
|
||||
async assignStream() {
|
||||
console.log(this.stream.getTracks()[0]);
|
||||
const track = this.stream.getTracks()[0];
|
||||
if (track.getCapabilities) {
|
||||
const capabilities = this.stream.getTracks()[0].getCapabilities();
|
||||
await this.attemptGetUserMedia({
|
||||
deviceId: capabilities.deviceId,
|
||||
width: {ideal: capabilities.width.max},
|
||||
height: {ideal: capabilities.height.max}
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setupVideoStream(stream) {
|
||||
this.capturing = true;
|
||||
this.streaming = false;
|
||||
const {video} = this.$refs;
|
||||
video.srcObject = this.stream;
|
||||
video.addEventListener('canplay', () => {
|
||||
const video = this.$refs.video;
|
||||
if (!video) return;
|
||||
|
||||
video.srcObject = stream;
|
||||
video.play();
|
||||
video.oncanplay = () => {
|
||||
this.streaming = true;
|
||||
localStorage.setItem("WebcamFileSource#previousDevice", this.selectedCamera.deviceId);
|
||||
}, false);
|
||||
await video.play();
|
||||
this.availableCameras = cameraManager.getAvailableCameras();
|
||||
this.localSelectedCameraId = cameraManager.getSelectedCameraId();
|
||||
};
|
||||
},
|
||||
async openStream(cameraId) {
|
||||
this.error = false;
|
||||
this.lastError = undefined;
|
||||
try {
|
||||
const stream = await cameraManager.openStream(cameraId);
|
||||
this.setupVideoStream(stream);
|
||||
} catch (err) {
|
||||
console.error('Failed to open camera stream:', err);
|
||||
if (err.name === "NotAllowedError") this.lastError = "Camera Permission Not Granted";
|
||||
else if (err.name === "NotReadableError") this.lastError = "Camera Hardware Error";
|
||||
else this.lastError = "Unknown Error";
|
||||
this.error = true;
|
||||
this.capturing = false;
|
||||
}
|
||||
},
|
||||
async switchCamera(cameraId) {
|
||||
if (!this.capturing) return;
|
||||
|
||||
this.streaming = false;
|
||||
try {
|
||||
const stream = await cameraManager.switchCamera(cameraId);
|
||||
if (stream) this.setupVideoStream(stream);
|
||||
} catch (err) {
|
||||
console.error('Failed to switch camera:', err);
|
||||
}
|
||||
},
|
||||
closeStream() {
|
||||
if (this.capturing) {
|
||||
this.stream.getTracks().forEach(s => s.stop());
|
||||
cameraManager.closeStream();
|
||||
this.capturing = false;
|
||||
this.streaming = false;
|
||||
this.stream = undefined;
|
||||
}
|
||||
},
|
||||
async enumerateCameras() {
|
||||
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||
this.availableCameras = devices.filter(device => device.kind === "videoinput");
|
||||
cleanCameraLabel(label) {
|
||||
if (!label) return '';
|
||||
const parts = label.split(':').map((p) => p.trim());
|
||||
if (parts.length === 2 && parts[0] === parts[1]) {
|
||||
return parts[0];
|
||||
}
|
||||
return label;
|
||||
},
|
||||
handleCameraDisconnected(event) {
|
||||
console.error('Camera disconnected:', event.detail);
|
||||
this.streaming = false;
|
||||
this.error = true;
|
||||
this.lastError = 'Camera Disconnected';
|
||||
},
|
||||
handleCameraReconnected() {
|
||||
const stream = cameraManager.getActiveStream();
|
||||
if (stream) {
|
||||
this.error = false;
|
||||
this.lastError = undefined;
|
||||
this.setupVideoStream(stream);
|
||||
}
|
||||
},
|
||||
async open() {
|
||||
this.show_modal = true;
|
||||
const previousDevice = localStorage.getItem("WebcamFileSource#previousDevice");
|
||||
if (previousDevice) await this.attemptGetUserMedia({deviceId: previousDevice});
|
||||
if (!this.stream) await this.attemptGetUserMedia({facingMode: "environment"});
|
||||
if (!this.stream) await this.attemptGetUserMedia(true);
|
||||
if (!this.stream) this.error = true;
|
||||
await this.enumerateCameras();
|
||||
this.selectedCamera = this.availableCameras.find(({deviceId}) => deviceId === this.stream.getTracks()[0].getSettings().deviceId);
|
||||
await this.assignStream();
|
||||
},
|
||||
async onUserSelect() {
|
||||
this.closeStream();
|
||||
if (!this.dataImage) {
|
||||
await this.attemptGetUserMedia({deviceId: this.selectedCamera.deviceId})
|
||||
await this.assignStream();
|
||||
}
|
||||
await this.openStream();
|
||||
},
|
||||
async close() {
|
||||
this.closeStream();
|
||||
this.capturing = false;
|
||||
this.show_modal = false;
|
||||
this.dataImage = undefined;
|
||||
this.error = false;
|
||||
this.lastError = undefined;
|
||||
},
|
||||
captureVideoImage() {
|
||||
const {video, canvas} = this.$refs;
|
||||
|
|
@ -165,11 +193,14 @@ export default {
|
|||
context.drawImage(video, 0, 0, videoWidth, videoHeight);
|
||||
this.dataImage = canvas.toDataURL('image/jpeg', 0.5);
|
||||
this.closeStream();
|
||||
this.capturing = false;
|
||||
},
|
||||
retake() {
|
||||
this.dataImage = undefined;
|
||||
this.open();
|
||||
this.openStream();
|
||||
},
|
||||
async refreshCameraList() {
|
||||
await cameraManager.enumerateDevices();
|
||||
this.availableCameras = cameraManager.getAvailableCameras();
|
||||
},
|
||||
async save() {
|
||||
const mimeType = this.dataImage.split(';')[0].split(':')[1];
|
||||
|
|
@ -194,14 +225,15 @@ export default {
|
|||
}
|
||||
},
|
||||
mounted() {
|
||||
navigator.mediaDevices.addEventListener("devicechange", (event) => {
|
||||
console.log("devicechange");
|
||||
this.enumerateCameras();
|
||||
if (this.availableCameras.findIndex(({deviceId}) => deviceId === this.selectedCamera.deviceId) === -1) {
|
||||
this.closeStream();
|
||||
this.open();
|
||||
}
|
||||
});
|
||||
}
|
||||
window.addEventListener('camera-disconnected', this.handleCameraDisconnected);
|
||||
window.addEventListener('camera-reconnected', this.handleCameraReconnected);
|
||||
navigator.mediaDevices?.addEventListener('devicechange', this.refreshCameraList);
|
||||
},
|
||||
beforeUnmount() {
|
||||
window.removeEventListener('camera-disconnected', this.handleCameraDisconnected);
|
||||
window.removeEventListener('camera-reconnected', this.handleCameraReconnected);
|
||||
navigator.mediaDevices?.removeEventListener('devicechange', this.refreshCameraList);
|
||||
this.closeStream();
|
||||
},
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue