Live video on the AT Protocol
1/**
2 * Utility functions for capturing video frames
3 */
4import { isWeb } from "tamagui";
5import React from "react";
6
7/**
8 * Captures a frame from a video ref or element and returns it as a compressed JPEG blob
9 *
10 * @param videoRefOrElement The video ref or element to capture from
11 * @param maxWidth Maximum width of the output image (maintains aspect ratio)
12 * @param quality JPEG quality (0-1), lower means smaller file size
13 * @returns A Promise that resolves to a Blob of the compressed image
14 */
15export const captureVideoFrame = async (
16 videoRefOrElement:
17 | React.MutableRefObject<HTMLVideoElement | null>
18 | HTMLVideoElement,
19 maxWidth = 1280,
20 quality = 0.85,
21): Promise<Blob> => {
22 let videoElement: HTMLVideoElement;
23
24 if (
25 videoRefOrElement &&
26 typeof videoRefOrElement === "object" &&
27 "current" in videoRefOrElement
28 ) {
29 if (!videoRefOrElement.current) {
30 throw new Error("No video element available in ref");
31 }
32 videoElement = videoRefOrElement.current;
33 } else {
34 videoElement = videoRefOrElement as HTMLVideoElement;
35 }
36
37 if (!isWeb) {
38 throw new Error("captureVideoFrame is only available on web platforms");
39 }
40
41 return new Promise((resolve, reject) => {
42 try {
43 const canvas = document.createElement("canvas");
44 const ctx = canvas.getContext("2d");
45
46 if (!ctx) {
47 reject(new Error("Could not get canvas context"));
48 return;
49 }
50
51 const videoWidth = videoElement.videoWidth;
52 const videoHeight = videoElement.videoHeight;
53
54 if (videoWidth === 0 || videoHeight === 0) {
55 reject(new Error("Video has no dimensions, may not be playing yet"));
56 return;
57 }
58
59 let width = videoWidth;
60 let height = videoHeight;
61
62 if (width > maxWidth) {
63 const ratio = maxWidth / width;
64 width = maxWidth;
65 height = height * ratio;
66 }
67
68 canvas.width = width;
69 canvas.height = height;
70
71 ctx.drawImage(videoElement, 0, 0, width, height);
72
73 canvas.toBlob(
74 (blob) => {
75 if (blob) {
76 resolve(blob);
77 } else {
78 reject(new Error("Failed to create blob from canvas"));
79 }
80 },
81 "image/jpeg",
82 quality,
83 );
84 } catch (error) {
85 reject(error);
86 }
87 });
88};