File size: 3,362 Bytes
1123781 ff9325e 12ffaf3 ff9325e 3207814 3dd0aef ff9325e 3dd0aef 46bd9ac ff9325e 12ffaf3 ff9325e 46bd9ac 12ffaf3 a659304 3dd0aef 547a086 12ffaf3 46bd9ac 12ffaf3 3dd0aef ff9325e 3207814 ff9325e 12ffaf3 ff9325e a659304 12ffaf3 547a086 1d3190d ff9325e 547a086 ff9325e 1123781 0e5136c d1f4c77 3dd0aef 12ffaf3 3dd0aef d1f4c77 3dd0aef d1f4c77 547a086 d1f4c77 12ffaf3 d1f4c77 d6fedfa |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
<script lang="ts">
import 'rvfc-polyfill';
import { onDestroy, onMount } from 'svelte';
import {
mediaStreamStatus,
MediaStreamStatusEnum,
onFrameChangeStore,
mediaStream,
mediaDevices
} from '$lib/mediaStream';
import MediaListSwitcher from './MediaListSwitcher.svelte';
export let width = 512;
export let height = 512;
const size = { width, height };
let videoEl: HTMLVideoElement;
let canvasEl: HTMLCanvasElement;
let ctx: CanvasRenderingContext2D;
let videoFrameCallbackId: number;
// ajust the throttle time to your needs
const THROTTLE = 1000 / 120;
let selectedDevice: string = '';
let videoIsReady = false;
onMount(() => {
ctx = canvasEl.getContext('2d') as CanvasRenderingContext2D;
canvasEl.width = size.width;
canvasEl.height = size.height;
});
$: {
console.log(selectedDevice);
}
onDestroy(() => {
if (videoFrameCallbackId) videoEl.cancelVideoFrameCallback(videoFrameCallbackId);
});
$: if (videoEl) {
videoEl.srcObject = $mediaStream;
}
let lastMillis = 0;
async function onFrameChange(now: DOMHighResTimeStamp, metadata: VideoFrameCallbackMetadata) {
if (now - lastMillis < THROTTLE) {
videoFrameCallbackId = videoEl.requestVideoFrameCallback(onFrameChange);
return;
}
const videoWidth = videoEl.videoWidth;
const videoHeight = videoEl.videoHeight;
let height0 = videoHeight;
let width0 = videoWidth;
let x0 = 0;
let y0 = 0;
if (videoWidth > videoHeight) {
width0 = videoHeight;
x0 = (videoWidth - videoHeight) / 2;
} else {
height0 = videoWidth;
y0 = (videoHeight - videoWidth) / 2;
}
ctx.drawImage(videoEl, x0, y0, width0, height0, 0, 0, size.width, size.height);
const blob = await new Promise<Blob>((resolve) => {
canvasEl.toBlob(
(blob) => {
resolve(blob as Blob);
},
'image/jpeg',
1
);
});
onFrameChangeStore.set({ blob });
videoFrameCallbackId = videoEl.requestVideoFrameCallback(onFrameChange);
}
$: if ($mediaStreamStatus == MediaStreamStatusEnum.CONNECTED && videoIsReady) {
videoFrameCallbackId = videoEl.requestVideoFrameCallback(onFrameChange);
}
</script>
<div class="relative mx-auto max-w-lg overflow-hidden rounded-lg border border-slate-300">
<div class="relative z-10 aspect-square w-full object-cover">
{#if $mediaDevices.length > 0}
<div class="absolute bottom-0 right-0 z-10">
<MediaListSwitcher />
</div>
{/if}
<video
class="pointer-events-none aspect-square w-full object-cover"
bind:this={videoEl}
on:loadeddata={() => {
videoIsReady = true;
}}
playsinline
autoplay
muted
loop
></video>
<canvas bind:this={canvasEl} class="absolute left-0 top-0 aspect-square w-full object-cover"
></canvas>
</div>
<div class="absolute left-0 top-0 flex aspect-square w-full items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 448" class="w-40 p-5 opacity-20">
<path
fill="currentColor"
d="M224 256a128 128 0 1 0 0-256 128 128 0 1 0 0 256zm-45.7 48A178.3 178.3 0 0 0 0 482.3 29.7 29.7 0 0 0 29.7 512h388.6a29.7 29.7 0 0 0 29.7-29.7c0-98.5-79.8-178.3-178.3-178.3h-91.4z"
/>
</svg>
</div>
</div>
|