Results, probes and progress values¶
What each call returns, field by field, and when a field is zero rather than meaningful.
When is a failure an error, and when is it a Result?¶
This distinction runs through every call, so it is worth stating once:
- A non-zero engine exit is not a Go error.
Run/RunJobreturn aResultwith the exit code and the captured output, and anilerror. A bad codec name, an unparseable filtergraph or a missing input all arrive this way. - Only host-side failures return a non-nil error: module instantiation, the filesystem bridge, a cancelled or expired context, a driver that would not start.
- The typed helpers invert this for convenience.
Probe,ProbeInputandFramesturn a non-zero exit into an error, because there is no useful typed value to return. The error carries the last 1,500 bytes of stderr, not the whole log.
So a RunJob caller checks err and res.ExitCode; a Probe caller checks err only.
Result¶
| Field | Meaning |
|---|---|
ExitCode |
The engine's process exit status. 0 is success. |
Stdout |
The engine's structured JSON result. Parse it with ParseResult for a process job; Probe and Frames parse it for you. |
Stderr |
The engine's log, captured in full. This is where a failure explains itself. |
ProcessResult¶
afmpeg.ParseResult(res) decodes Result.Stdout from a process job.
type ProcessResult struct {
Outputs []OutputResult
Analysis []Measurement
}
type OutputResult struct {
Path string
Segmented bool
Streams []OutputStream
}
type OutputStream struct {
Type string // "video", "audio", …
Codec string // the encoder actually used
Disposition string // "copy" for a stream-copied stream
}
| Field | Meaning |
|---|---|
Outputs[].Path |
The output that was written. For a segmenting muxer (hls, dash, segment) this is the playlist or manifest, not the segments. |
Outputs[].Segmented |
True when the output is a segment set rather than a single file. |
Outputs[].Streams[].Codec |
The encoder the engine used, worth logging because it confirms which H.264 encoder a module actually has. |
Analysis |
Measurements emitted by analysis filters. Empty unless the graph contains one. |
ParseResult on an empty Stdout returns a zero ProcessResult and no error, so it is safe
to call on any result. It does not look at the exit code, so check that first, because the engine
only emits this JSON on success.
Measurement¶
type Measurement struct {
Time float64 // seconds into the source
Key string // metadata key, "lavfi." prefix dropped
Value string // the filter's own string
}
Keys arrive as the filter names them minus the lavfi. prefix: cropdetect.w, r128.I,
silence_start, black_start. Values are the filters' raw strings, so parse the numeric ones
yourself.
Two behaviours to plan for:
- The series is consecutive-deduplicated per key. A stable measurement such as
cropdetectappears once; discrete events such assilence_start/silence_endeach appear. - Some filters log without emitting metadata.
ebur128andastatsneed theirmetadata=1option before anything reachesAnalysis: for exampleebur128=metadata=1.
Probe¶
type Probe struct {
Format string
DurationSec float64
StartSec float64
Streams []ProbeStream
Tags map[string]string
Chapters []Chapter
}
| Field | Meaning |
|---|---|
Format |
The demuxer name as libav reports it. Often a comma-separated family rather than one name, so an MP4 probes as mov,mp4,m4a,3gp,3g2,mj2. Match on a substring, not on equality. |
DurationSec |
Container duration in seconds. |
StartSec |
The container's start time. Non-zero after, for instance, a CopyTS trim. |
Streams |
One entry per stream, in container order. |
Tags |
Container-level metadata (title, artist, …). |
Chapters |
The container's chapters. |
type ProbeStream struct {
Index int
Type string // "video" or "audio"
Codec string
Width int // video
Height int // video
SampleRate int // audio
Channels int // audio
Language string
Disposition []string // set flags: "default", "forced", "attached_pic", …
Tags map[string]string
}
type Chapter struct {
Start float64
End float64
Title string
}
The video and audio fields are mutually irrelevant: Width/Height are zero on an audio
stream, SampleRate/Channels are zero on a video stream. Language is commonly und.
Probe(ctx, fs, path) auto-probes. A headerless or raw input only opens with a forced demuxer,
so use ProbeInput(ctx, fs, Input{Path: …, Format: …, Options: …}) for those, because it forwards
Format and Options exactly as a process job would.
Probing needs the ffmpeg-wasi engine: it is the engine's probe op, not something afmpeg
computes. A generic wasm module cannot answer it.
FramesResult¶
type FramesResult struct {
Frames []ExtractedFrame
Count int
}
type ExtractedFrame struct {
Path string // the file written, with the template expanded
Index int // 0-based position in this extraction
Timestamp float64 // source position, in seconds
}
The files are already in the afero.Fs you passed; Frames tells you what they are called and
where each came from.
Progress¶
Delivered on the channel you attach with WithProgress(ctx, ch), one value per sample.
type Progress struct {
Fraction float64
Source FractionSource
Elapsed time.Duration
InputBytes int64
InputTotal int64
OutputBytes int64
Frame int64
OutTime time.Duration
Speed float64
}
| Field | Meaning | Zero when |
|---|---|---|
Fraction |
Completion in [0,1], or -1 when it cannot be determined. Never decreases across a run. |
(it is -1, not 0, when unknown) |
Source |
How Fraction was derived. SourceUnknown exactly when Fraction is -1. |
— |
Elapsed |
Since the invocation began. | — |
InputBytes |
Input bytes read so far, counted at the filesystem bridge. | — |
InputTotal |
Total size of the declared inputs, fixed before the run starts. | 0 when the job spec declared nothing statable |
OutputBytes |
Output bytes written, counted at the filesystem bridge. | — |
Frame |
Frames processed. | before the first engine record, and always on an engine that emits none |
OutTime |
Media timestamp reached. | as Frame |
Speed |
×realtime, derived by the host as OutTime / Elapsed. |
as Frame |
OutputBytes counts bytes written, which is not the same as the output file's final size.
An MP4 muxer under +faststart seeks back and rewrites its header, so those bytes are counted
twice and OutputBytes finishes a little above the file length.
FractionSource¶
| Value | String() |
Meaning |
|---|---|---|
SourceEngine |
"engine" |
Derived from the engine's out_time / duration. Accurate regardless of input size, including for generative inputs. Preferred whenever present. |
SourceBytes |
"bytes" |
Derived at the filesystem bridge as bytes_read / input_size. Good when the inputs dominate the work, poor when the encode does. |
SourceUnknown |
"unknown" |
Fraction is -1. |
When Fraction is -1¶
Deliberately, in five situations. Each is a case where a number could be produced but would be wrong within a second or two:
- The first moment of a job on a backend that can deliver engine records (up to a 2-second grace period), rather than showing a byte ratio the engine is about to contradict.
- Every declared input byte has been read while the job is still encoding. A render whose
inputs are small next to its output exhausts them early;
-1beats a full bar for the rest of the run. - A purely generative input with no file to measure, on an engine that reports no duration.
- The engine's
out_timehas overrun the duration it reported by more than 2%, so the job is demonstrably longer than predicted, so the ratio is withheld rather than pinned at 1.0. - No statable input at all and no engine duration.
Fraction == 1 is not a completion signal either way. Use the invocation's return.
See also¶
- Watch job progress: the task-shaped guide, with a worked loop
- Read analysis-filter measurements
- Error catalogue: the sentinel errors and how to match them