ffmpeg

package
v1.8.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 27, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package ffmpeg runs ffmpeg processes with typed outputs and stderr forensics. It contains no pipeline policy: arg builders are pure functions, and the runner only manages pipes, the stderr tail, and process exit.

Index

Constants

View Source
const CodecCopy = "copy"

CodecCopy is the ffmpeg "-c copy" keyword: stream-copy a track instead of re-encoding it. It is the one non-encoder value AudioCodec takes (VideoEncoder uses a nil pointer for the same intent), named so callers set and test it without repeating the bare "copy" literal.

View Source
const EncodeReadrate = "1.15"

EncodeReadrate paces the subtitle-burning encoder just above realtime. It must not be exactly 1.0: at dead-even playback speed the renderer's buffer has no steady-state headroom, so any encode or network jitter permanently erodes the initial preroll, and because the encoder never runs ahead it can never rebuild it. A slight margin lets the encoder's output spool accumulate a lead the renderer can draw from. Stays well under the puller's 2x, so the encode never overtakes whisper's committed frontier (the gate guarantees a lead before playback opens).

View Source
const EncodeReadrateBurstSeconds = 10

EncodeReadrateBurstSeconds is how much of the stream the subtitle-burning encoder may race through at full speed before -readrate pins it to its steady pace. Exported because the playback gate's transcription lead must cover it: frames encoded during the burst need their cues committed before the encode starts.

Variables

This section is empty.

Functions

func EncodeArgs

func EncodeArgs(opts EncodeOptions) ([]string, error)

EncodeArgs assembles the encode command line. No "magic" flags: every argument is either part of the standard input/output setup or comes straight from a field in EncodeOptions. It enforces the one cross-field contract EncodeOptions documents but can't express in its types: a SubtitleTextFile burn-in needs decoded frames, so it requires a real VideoEncoder rather than failing later inside ffmpeg with an unrelated "Filtering and streamcopy cannot be used together".

func Probe added in v1.6.1

func Probe(ctx context.Context, ffprobePath, input string, headers http.Header) (media.ProbeInfo, error)

Probe runs ffprobe against input (a URL or a local file path) and returns the per-track codec details as a media.ProbeInfo (the domain type). It is safe to point at a still-growing local spool: ffprobe reads from the start, analyses the leading packets, and returns. headers are the HTTP request headers for a network input behind a proxy/CDN (Referer, Cookie, etc.); pass nil for a local file.

func PullArgs

func PullArgs(opts PullOptions) []string

PullArgs assembles the upstream download command line: a codec-copy remux of the source into append-only MPEG-TS on stdout, paced like a buffering player, with an optional PCM tee for transcription.

func WatchProgress

func WatchProgress(r io.Reader, fn func(seconds float64))

WatchProgress parses ffmpeg's -progress key=value stream from r and calls fn with the encoder's output position in seconds after each progress block. It returns when r is exhausted (ffmpeg exited).

Only out_time_us is trusted: out_time_ms famously also contains microseconds (long-standing ffmpeg misnomer), and out_time needs string parsing for no benefit.

Types

type EncodeOptions

type EncodeOptions struct {
	// PipeFormat, when non-empty, names the demuxer for stdin input
	// ("mpegts"); the caller feeds the source via WithStdin. Used to encode
	// from the local spool: pipes never report EOF until the writer closes,
	// which is what lets ffmpeg consume a still-growing stream.
	PipeFormat string

	// SourceURL is the network input, used when PipeFormat is empty.
	SourceURL *url.URL

	// SourceHeaders are HTTP headers ffmpeg sends when fetching SourceURL
	// (Referer, User-Agent, Cookie, etc. — needed for HLS behind proxies).
	SourceHeaders http.Header

	// SourceContentType is the MIME type of SourceURL. It selects the
	// container-specific input flags (see containerInputArgs). Only consulted
	// for a network source (PipeFormat empty).
	SourceContentType string

	// RWTimeoutMicros is the upstream I/O timeout in microseconds, passed to
	// ffmpeg's -rw_timeout for HTTP(S) input. Ignored for stdin input.
	RWTimeoutMicros int64

	// OutputFormat is ffmpeg's muxer name ("mpegts", "mp4", "hls"). "hls" writes
	// a playlist plus rolling fMP4 segments into the process working directory
	// (see WithWorkDir) rather than a single stream on pipe:1.
	OutputFormat string

	// VideoEncoder re-encodes the video; nil stream-copies it. The encoder
	// carries its own device setup, filters, and flags, so EncodeArgs never
	// branches on the encoder kind. When SubtitleTextFile is set the planner
	// must supply one: drawtext needs decoded frames, so copy is not possible.
	VideoEncoder *Encoder

	// VideoBitrate target when re-encoding video (e.g. "4M"). Ignored when
	// VideoEncoder is nil (copy).
	VideoBitrate string

	// VideoMaxrate is the VBV peak-rate cap (e.g. "4M"), and VideoBufsize the
	// VBV buffer (e.g. "8M"). Together they bound the instantaneous bitrate so a
	// complex scene can't spike past what the renderer decodes and buffers. Both
	// empty leaves the encoder in unbounded ABR. Ignored when VideoEncoder is
	// nil (copy).
	VideoMaxrate string
	VideoBufsize string

	// VideoMaxHeight caps the output height while preserving aspect ratio.
	// 0 keeps the source height. Ignored when VideoEncoder is nil (copy).
	VideoMaxHeight int

	// KeyframeIntervalSec caps the GOP length in seconds via force_key_frames,
	// so a renderer joining mid-stream resyncs within this bound regardless of
	// source fps. 0 leaves the encoder default. Ignored when VideoEncoder is
	// nil (copy): a copied bitstream keeps the source's keyframes.
	KeyframeIntervalSec int

	// AudioCodec is CodecCopy or an encoder name like "aac".
	AudioCodec string

	// AudioBitrate target when re-encoding (e.g. "256k"). Ignored for copy.
	AudioBitrate string

	// AudioSampleRate target when re-encoding (Hz). 0 keeps the source rate.
	AudioSampleRate int

	// AudioChannels target when re-encoding. 0 keeps the source layout. The
	// planner's audio resolver sets this: 2 to downmix to stereo (the floor every
	// renderer decodes), or the source layout capped at the codec's ceiling to
	// keep 5.1/7.1. Ignored for copy.
	AudioChannels int

	// SubtitleTextFile, when non-empty, burns the file's current contents
	// into every frame via drawtext with reload=1: ffmpeg re-opens the file
	// by path before each frame, so an external writer can swap the active
	// subtitle line live (atomic rename only — a failed read kills ffmpeg).
	// The file must exist before ffmpeg starts. Forces a video re-encode.
	// Enabling this routes -progress to fd 3: start the process
	// WithExtraPipe and follow Process.Extra.
	SubtitleTextFile string
}

EncodeOptions is the full description of an encode invocation. Every choice is explicit; nothing is inferred from globals or context. The planner upstream is responsible for filling these in based on device capabilities and source media properties.

type Encoder added in v1.6.1

type Encoder struct {
	Name     string      // -c:v value, e.g. "libx264", "hevc_videotoolbox"
	Codec    media.Codec // the abstract codec produced, independent of the name
	Hardware bool        // GPU-backed: trusted only after a real test encode
	InitArgs []string    // emitted before the input: hardware device setup
	Filters  []string    // appended to the -vf chain: e.g. the GPU upload
	Flags    []string    // encoder-specific -c:v flags: preset, pix_fmt, GOP
}

Encoder is one concrete way to produce a codec on this host: the ffmpeg -c:v name plus the command fragments it contributes. EncodeArgs splices those fragments in verbatim and never special-cases an encoder, so supporting a new codec or backend is a registry entry, not new control flow. A nil *Encoder in EncodeOptions means stream-copy.

func SelectEncoder added in v1.6.1

func SelectEncoder(ctx context.Context, ffmpegPath string, codec media.Codec) (enc Encoder, ok bool)

SelectEncoder returns the best working encoder for codec on this host: a hardware encoder whose real test encode passes, otherwise the software baseline. ok is false only for a codec with no registered encoder at all; availability is cached, so repeat calls are cheap.

type Process

type Process struct {
	// Stdout is the primary output (pipe:1).
	Stdout io.ReadCloser

	// Extra is the fd-3 output (pipe:3) when started WithExtraPipe;
	// nil otherwise.
	Extra io.ReadCloser
	// contains filtered or unexported fields
}

Process is a running ffmpeg invocation.

func Start

func Start(ctx context.Context, path string, args []string, opts ...StartOption) (*Process, error)

Start launches ffmpeg at path with args. The process is killed when ctx is cancelled.

func (*Process) Kill added in v1.8.0

func (p *Process) Kill()

Kill signals the process to stop immediately. It is idempotent and safe to call after the process has already exited (the error is ignored), so callers can defer it as unconditional teardown on paths where context cancellation is not the only way the encoder must stop (the HLS serve path, whose output is files rather than a pipe that would EPIPE on close). Reap it with Wait.

func (*Process) LogStderrTail

func (p *Process) LogStderrTail(ctx context.Context, msg string)

LogStderrTail emits every retained stderr line at WARN under msg.

func (*Process) StderrTail

func (p *Process) StderrTail() []string

StderrTail returns the most recent stderr lines, retained even while the process is still running. This is what explains a stall after the process has been killed by context cancellation — its own error path never runs.

func (*Process) Wait

func (p *Process) Wait() error

Wait blocks until the process exits and returns its exit error, if any. Forensics are the caller's call: use StderrTail or LogStderrTail to surface the failure reason when the exit was not self-inflicted.

type PullOptions

type PullOptions struct {
	SourceURL         *url.URL
	SourceHeaders     http.Header
	SourceContentType string // MIME type of SourceURL; selects container-specific input flags
	RWTimeoutMicros   int64

	// Verbose selects -loglevel verbose (playlist/segment URLs, connection
	// lines) instead of the default warning level.
	Verbose bool

	// PCM additionally extracts mono s16le audio on fd 3 for the
	// transcriber; start the process WithExtraPipe.
	PCM bool
	// PCMSampleRate is the audio sample rate for the PCM output.
	PCMSampleRate int

	// Live selects live pacing (see pullReadrate constants) instead of VOD.
	Live bool
}

PullOptions configures the single upstream reader's command line.

type StartOption

type StartOption func(*startConfig)

func WithExtraPipe

func WithExtraPipe() StartOption

WithExtraPipe opens a second output pipe on fd 3 (pipe:3), exposed as Process.Extra. The arg builder must route an output there.

func WithStdin

func WithStdin(r io.Reader) StartOption

WithStdin feeds r to ffmpeg's stdin (pipe:0 input).

func WithWorkDir added in v1.8.0

func WithWorkDir(dir string) StartOption

WithWorkDir runs ffmpeg with dir as its working directory, so a muxer writing relative output files (the HLS playlist and segments) lands them there.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL