Initial Commit
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# `FluentFfmpegRunner`
|
||||
|
||||
## Purpose
|
||||
|
||||
Default `FfmpegRunner` implementation used by `ThumbnailGenerator` and
|
||||
`SpriteSheetGenerator` when no other runner is injected. It wraps `fluent-ffmpeg` and pins
|
||||
ffmpeg to the binary shipped by `@ffmpeg-installer/ffmpeg`, and ffprobe to
|
||||
`@ffprobe-installer/ffprobe`, so the package works on any host without requiring system
|
||||
ffmpeg/ffprobe to be on `PATH`. Every operation respects an optional `AbortSignal` by killing
|
||||
the underlying child process and rejecting with `VIDEO_THUMBNAILS.ABORTED`.
|
||||
|
||||
## Features
|
||||
|
||||
- Bundled ffmpeg + ffprobe binaries — zero system dependency.
|
||||
- Override-friendly: pass `ffmpegPath` and/or `ffprobePath` to point at a custom binary.
|
||||
- `extract` issues a single-frame ffmpeg pipeline with the canonical filter from
|
||||
`buildScaleFilter` and quality from `buildQualityArgs`.
|
||||
- `tile` issues the canonical `fps=…,scale=…,tile=COLSxROWS` sprite-sheet pipeline.
|
||||
- `probe` returns a strict `ProbeResult` with duration, dimensions, codec, fps, bitrate.
|
||||
- `isAvailable` spawns `ffmpeg -version` and resolves to `true` only on exit code 0.
|
||||
- Every call honours `AbortSignal`: `command.kill("SIGKILL")` on abort, then reject with
|
||||
`ABORTED`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { FluentFfmpegRunner } from "@suaveplan/video-thumbnails/runner";
|
||||
|
||||
const runner = new FluentFfmpegRunner();
|
||||
const ok = await runner.isAvailable();
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { FluentFfmpegRunner } from "@suaveplan/video-thumbnails/runner";
|
||||
|
||||
const runner = new FluentFfmpegRunner({
|
||||
ffmpegPath: "/usr/local/bin/ffmpeg-hwaccel",
|
||||
ffprobePath: "/usr/local/bin/ffprobe-hwaccel",
|
||||
});
|
||||
const controller = new AbortController();
|
||||
setTimeout(() => controller.abort(), 250);
|
||||
try {
|
||||
await runner.extract(
|
||||
{
|
||||
inputPath: "/data/big.mkv",
|
||||
outputPath: "/tmp/thumb.jpg",
|
||||
timestamp: "00:00:10",
|
||||
width: 640,
|
||||
height: 360,
|
||||
fit: "cover",
|
||||
format: "jpg",
|
||||
quality: 80,
|
||||
},
|
||||
controller.signal,
|
||||
);
|
||||
} catch (error) {
|
||||
// VideoThumbnailsError { code: "VIDEO_THUMBNAILS.ABORTED" } when aborted.
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `class FluentFfmpegRunner implements FfmpegRunner`
|
||||
- `constructor(options?: { ffmpegPath?: string; ffprobePath?: string })`
|
||||
- `isAvailable(signal?): Promise<boolean>` — exit code 0 from `ffmpeg -version`.
|
||||
- `extract(input, signal?): Promise<void>` — single-frame extraction.
|
||||
- `tile(input, signal?): Promise<void>` — N×M sprite-sheet render.
|
||||
- `probe(inputPath, signal?): Promise<ProbeResult>` — ffprobe metadata.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`run` listens for `end` and `error` on the `FfmpegCommand`, tracks a `settled` flag so a late
|
||||
abort cannot double-resolve, and attaches the abort listener with `{ once: true }` to keep
|
||||
event-emitter memory bounded. `probe` re-pins the ffprobe path on the module-level
|
||||
`fluent-ffmpeg` singleton before each call because `ffmpeg.ffprobe` (the top-level helper)
|
||||
does not consult per-instance state — without the re-pin the singleton would happily fall
|
||||
back to system `ffprobe`. The probe failure paths split into two: a real ffprobe crash
|
||||
(`PROBE_FAILED`) and a structurally invalid response (`VIDEO_INVALID`, raised when the file
|
||||
has no video stream or a non-positive duration).
|
||||
Reference in New Issue
Block a user