MCPcopy Create free account
hub / github.com/heygen-com/hyperframes / extractVideoFrameToBuffer

Function extractVideoFrameToBuffer

packages/cli/src/commands/snapshot.ts:60–119  ·  view source on GitHub ↗

* Extract a single frame from a video file at `timeSeconds` via FFmpeg. * Used to work around Chrome-headless's inability to reliably seek * elements during snapshot capture.

(
  videoPath: string,
  timeSeconds: number,
  useVp9AlphaDecoder = false,
)

Source from the content-addressed store, hash-verified

58 * <video> elements during snapshot capture.
59 */
60async function extractVideoFrameToBuffer(
61 videoPath: string,
62 timeSeconds: number,
63 useVp9AlphaDecoder = false,
64): Promise<Buffer | null> {
65 const tmp = mkdtempSync(join(tmpdir(), "hf-snapshot-frame-"));
66 const outPath = join(tmp, "frame.png");
67 try {
68 const ffmpegPath = findFFmpeg();
69 if (!ffmpegPath) return null;
70 const result = await new Promise<{ code: number | null; stderr: string; timedOut: boolean }>(
71 (resolvePromise) => {
72 // `-ss` before `-i` performs a fast keyframe seek; adequate for snapshot accuracy
73 // (±1 frame) and orders of magnitude faster than the decode-and-scan alternative.
74 const args = ["-hide_banner", "-loglevel", "error"];
75 if (useVp9AlphaDecoder) {
76 args.push("-c:v", "libvpx-vp9");
77 }
78 args.push(
79 "-ss",
80 String(Math.max(0, timeSeconds)),
81 "-i",
82 videoPath,
83 "-frames:v",
84 "1",
85 "-q:v",
86 "2",
87 "-y",
88 outPath,
89 );
90 const ff = spawn(ffmpegPath, args);
91 let stderr = "";
92 let timedOut = false;
93 const timer = setTimeout(() => {
94 timedOut = true;
95 ff.kill("SIGTERM");
96 }, FFMPEG_EXTRACT_TIMEOUT_MS);
97 ff.stderr.on("data", (d: Buffer) => {
98 stderr += d.toString();
99 });
100 ff.on("close", (code) => {
101 clearTimeout(timer);
102 resolvePromise({ code, stderr, timedOut });
103 });
104 ff.on("error", () => {
105 clearTimeout(timer);
106 resolvePromise({ code: null, stderr: "ffmpeg spawn failed", timedOut });
107 });
108 },
109 );
110 if (result.code !== 0 || result.timedOut || !existsSync(outPath)) return null;
111 return readFileSync(outPath);
112 } finally {
113 try {
114 rmSync(tmp, { recursive: true, force: true });
115 } catch {
116 /* best-effort */
117 }

Callers 1

captureSnapshotsFunction · 0.85

Calls 4

findFFmpegFunction · 0.85
killMethod · 0.80
onMethod · 0.65
spawnFunction · 0.50

Tested by

no test coverage detected