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