1"use client"
2
3import { CLIP_SAMPLE_INTERVAL_MS } from "@/features/capture/clipRecorder"
4import { LIVE_MIME_TYPES, type LiveKind } from "@/lib/schemas"
5
6import { pickLiveKind } from "./mediaSupport"
7
8/* SH-03: unduhan versi hidup. Kanvas strip direkam lewat `captureStream` dan
9 `MediaRecorder` (ARCHITECTURE §3: API platform, tanpa pustaka). Durasi
10 bawaan 2 putaran x 10 bingkai x 150 ms = 3 detik, sekitar 0,9 MB pada
11 2,5 Mbps. */
12
13const VIDEO_BITS_PER_SECOND = 2_500_000
14const STREAM_FPS = 30
15
16export interface RecordedLiveVideo {
17 blob: Blob
18 kind: LiveKind
19}
20
21function sleep(ms: number): Promise<void> {
22 const { promise, resolve } = Promise.withResolvers<void>()
23 window.setTimeout(resolve, ms)
24 return promise
25}
26
27export async function recordStripVideo(
28 canvas: HTMLCanvasElement,
29 tick: (frameIndex: number) => void,
30 frameCount: number,
31 loops = 2
32): Promise<RecordedLiveVideo> {
33 const kind = pickLiveKind()
34 if (kind === null) {
35 throw new Error("Browser ini tidak bisa merekam video.")
36 }
37
38 const stream = canvas.captureStream(STREAM_FPS)
39 const recorder = new MediaRecorder(stream, {
40 mimeType: LIVE_MIME_TYPES[kind],
41 videoBitsPerSecond: VIDEO_BITS_PER_SECOND,
42 })
43 const chunks: Blob[] = []
44 const stopped = Promise.withResolvers<Blob>()
45 recorder.ondataavailable = (event) => {
46 if (event.data.size > 0) {
47 chunks.push(event.data)
48 }
49 }
50 recorder.onstop = () => stopped.resolve(new Blob(chunks, { type: LIVE_MIME_TYPES[kind] }))
51 recorder.onerror = () => stopped.reject(new Error("Perekaman video gagal."))
52
53 recorder.start()
54 try {
55 for (let step = 0; step < Math.max(1, loops * frameCount); step += 1) {
56 tick(step % frameCount)
57 await sleep(CLIP_SAMPLE_INTERVAL_MS)
58 }
59 } finally {
60 recorder.stop()
61 }
62
63 const blob = await stopped.promise
64 for (const track of stream.getTracks()) {
65 track.stop()
66 }
67 return { blob, kind }
68}
69