{"version":3,"file":"video-recorder.cjs","names":[],"sources":["../../src/capture/video-recorder.ts"],"sourcesContent":["import {\n    createMediaRecorder,\n    pickRecordingMimeType,\n    type MediaRecorderHandle,\n    type MediaRecording,\n} from \"./media-recorder\";\n\n/**\n * Container/codec candidates for video, best first.\n *\n * VP9 in WebM leads because it is the best quality per byte that Chromium and Firefox\n * both encode natively; VP8 is the fallback for older Chromium and for hardware that\n * refuses VP9. `video/mp4` is last and exists for Safari, which produces H.264 in MP4\n * and nothing else — and only since Safari 14.1.\n *\n * Every candidate carries **Opus or AAC audio in the same container**, because a\n * screen recording with the tab's audio dropped is a support ticket, not a\n * simplification. On a stream with no audio track the browser simply omits it.\n *\n * Ported from the intersection of what `MediaRecorder.isTypeSupported` accepts in\n * Chrome 120+, Firefox 115+ and Safari 17. Re-check against\n * <https://developer.mozilla.org/docs/Web/API/MediaRecorder/isTypeSupported> before\n * adding to the list.\n */\nexport const VIDEO_MIME_CANDIDATES: readonly string[] = [\n    \"video/webm;codecs=vp9,opus\",\n    \"video/webm;codecs=vp8,opus\",\n    \"video/webm\",\n    \"video/mp4;codecs=avc1.42E01E,mp4a.40.2\",\n    \"video/mp4\",\n];\n\n/**\n * A finished video recording.\n *\n * Structurally the same three fields as an audio one — there is nothing\n * video-specific to add, and inventing a `width`/`height` here would be a lie: the\n * browser negotiates the frame size with the device and can change it mid-recording.\n * Read it from the track's `getSettings()` if you need it.\n */\nexport type VideoRecording = MediaRecording;\n\n/** Imperative video recorder. Same shape as the audio one. */\nexport type VideoRecorderHandle = MediaRecorderHandle;\n\n/** Options for {@link createVideoRecorder}. */\nexport interface VideoRecorderOptions {\n    /**\n     * Force a container. Throws when the browser cannot produce it.\n     *\n     * Leave it out. The default negotiates from {@link VIDEO_MIME_CANDIDATES}, which\n     * is the only way one call site works on both Chromium and Safari.\n     */\n    mimeType?: string;\n    /**\n     * Target video bitrate.\n     *\n     * The browser's default is conservative and a screen recording of text at\n     * 1080p looks smeared under it. 2_500_000 is a good floor for a UI capture,\n     * 8_000_000 for full-motion camera video.\n     */\n    videoBitsPerSecond?: number;\n    /** Target audio bitrate, when the stream carries audio. 64000–128000 is plenty. */\n    audioBitsPerSecond?: number;\n    /**\n     * Emit a chunk every N ms through `onChunk`, for streaming upload.\n     *\n     * Worth setting for video far sooner than for audio: a minute of 1080p at\n     * 2.5 Mbps is roughly 19 MB sitting in memory.\n     */\n    timesliceMs?: number;\n    /** Receives each chunk when `timesliceMs` is set. Chunks are **not** independently playable. */\n    onChunk?: (chunk: Blob) => void;\n    /** Recorder-level failure (screen share revoked mid-recording, encoder error). */\n    onError?: (error: unknown) => void;\n}\n\n/** Whether `MediaRecorder` exists and can produce at least one video container. */\nexport function isVideoRecordingSupported(): boolean {\n    return typeof MediaRecorder !== \"undefined\" && pickVideoMimeType() !== null;\n}\n\n/**\n * First video container in `preferred` the browser can actually produce, or `null`.\n *\n * @param preferred - Candidates, best first. Defaults to {@link VIDEO_MIME_CANDIDATES}.\n * @returns A supported MIME type, or `null` when none is.\n */\nexport function pickVideoMimeType(\n    preferred: readonly string[] = VIDEO_MIME_CANDIDATES,\n): string | null {\n    return pickRecordingMimeType(preferred);\n}\n\n/**\n * Record a video stream — a camera, a screen share, or a `canvas.captureStream()`.\n *\n * The state machine, the clock that subtracts paused time and the `stop()` that\n * resolves in `onstop` with every chunk in hand are shared with the audio recorder\n * (see `createMediaRecorder`); what this adds is the container list and\n * `videoBitsPerSecond`.\n *\n * The stream is **not** owned here. For a screen share in particular, stopping the\n * recorder must not stop the sharing: a support flow usually records, stops, lets the\n * user look at the result and then records again.\n *\n * @param stream - A live stream carrying at least one video track.\n * @param options - See {@link VideoRecorderOptions}.\n * @returns The imperative recorder.\n * @throws When `MediaRecorder` is unavailable, or an explicit `mimeType` is unsupported.\n *\n * @example\n * const recorder = createVideoRecorder(stream, { videoBitsPerSecond: 2_500_000 });\n * recorder.start();\n * const { blob, durationMs } = await recorder.stop();\n */\nexport function createVideoRecorder(\n    stream: MediaStream,\n    options: VideoRecorderOptions = {},\n): VideoRecorderHandle {\n    return createMediaRecorder(stream, {\n        candidates: VIDEO_MIME_CANDIDATES,\n        kind: \"video\",\n        mimeType: options.mimeType,\n        videoBitsPerSecond: options.videoBitsPerSecond,\n        audioBitsPerSecond: options.audioBitsPerSecond,\n        timesliceMs: options.timesliceMs,\n        onChunk: options.onChunk,\n        onError: options.onError,\n    });\n}\n"],"mappings":"wCAwBA,IAAa,EAA2C,CACpD,6BACA,6BACA,aACA,yCACA,WACJ,EAgDA,SAAgB,GAAqC,CACjD,OAAO,OAAO,cAAkB,KAAe,EAAkB,IAAM,IAC3E,CAQA,SAAgB,EACZ,EAA+B,EAClB,CACb,OAAO,EAAA,sBAAsB,CAAS,CAC1C,CAwBA,SAAgB,EACZ,EACA,EAAgC,CAAC,EACd,CACnB,OAAO,EAAA,oBAAoB,EAAQ,CAC/B,WAAY,EACZ,KAAM,QACN,SAAU,EAAQ,SAClB,mBAAoB,EAAQ,mBAC5B,mBAAoB,EAAQ,mBAC5B,YAAa,EAAQ,YACrB,QAAS,EAAQ,QACjB,QAAS,EAAQ,OACrB,CAAC,CACL"}