File size: 3,240 Bytes
1123781
ff9325e
12ffaf3
 
ff9325e
3207814
 
 
3dd0aef
 
ff9325e
3dd0aef
ff9325e
 
12ffaf3
 
ff9325e
12ffaf3
 
 
 
3dd0aef
12ffaf3
 
 
 
 
 
3dd0aef
 
 
ff9325e
 
 
 
3207814
 
ff9325e
12ffaf3
ff9325e
12ffaf3
 
 
 
 
 
 
 
 
 
 
 
 
 
1d3190d
ff9325e
 
 
3207814
ff9325e
 
12ffaf3
 
 
 
 
 
 
 
ff9325e
 
12ffaf3
ff9325e
 
 
1123781
 
0e5136c
d1f4c77
3dd0aef
12ffaf3
3dd0aef
 
 
d1f4c77
3dd0aef
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
<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';

  let videoEl: HTMLVideoElement;
  let canvasEl: HTMLCanvasElement;
  let ctx: CanvasRenderingContext2D;
  let videoFrameCallbackId: number;
  const WIDTH = 768;
  const HEIGHT = 768;
  // ajust the throttle time to your needs
  const THROTTLE_TIME = 1000 / 15;
  let selectedDevice: string = '';

  onMount(() => {
    ctx = canvasEl.getContext('2d') as CanvasRenderingContext2D;
    canvasEl.width = WIDTH;
    canvasEl.height = 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_TIME) {
      videoFrameCallbackId = videoEl.requestVideoFrameCallback(onFrameChange);
      return;
    }
    const videoWidth = videoEl.videoWidth;
    const videoHeight = videoEl.videoHeight;
    const blob = await grapCropBlobImg(
      videoEl,
      videoWidth / 2 - WIDTH / 2,
      videoHeight / 2 - HEIGHT / 2,
      WIDTH,
      HEIGHT
    );

    onFrameChangeStore.set({ blob });
    videoFrameCallbackId = videoEl.requestVideoFrameCallback(onFrameChange);
  }

  $: if ($mediaStreamStatus == MediaStreamStatusEnum.CONNECTED) {
    videoFrameCallbackId = videoEl.requestVideoFrameCallback(onFrameChange);
  }
  async function grapCropBlobImg(
    video: HTMLVideoElement,
    x: number,
    y: number,
    width: number,
    height: number
  ) {
    const canvas = new OffscreenCanvas(width, height);

    const ctx = canvas.getContext('2d') as OffscreenCanvasRenderingContext2D;
    ctx.drawImage(video, x, y, width, height, 0, 0, width, height);
    const blob = await canvas.convertToBlob({ type: 'image/jpeg', quality: 1 });
    return blob;
  }
</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}
      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>