Skip to content

API Reference — Sync

The following reference is generated directly from the package's Google-style docstrings via mkdocstrings.

Builder

ffmpeg_wrap.input

input(filename: str | PathLike[str], ffmpeg_path: str = 'ffmpeg', **kwargs: Any) -> FFmpeg

Start a new FFmpeg chain with an input file.

This is the recommended entry point for building a fluent chain. It creates a fresh :class:FFmpeg instance and calls :meth:~FFmpeg.input on it.

Parameters:

Name Type Description Default
filename str | PathLike[str]

Input file path or PathLike object.

required
ffmpeg_path str

Path to the ffmpeg executable. Defaults to "ffmpeg" (resolved from PATH).

'ffmpeg'
**kwargs Any

FFmpeg input options passed through to the input slot (e.g. ss=30, t=60).

{}

Returns:

Type Description
FFmpeg

A new :class:FFmpeg instance with the input added, ready for further

FFmpeg

chaining.

Example
from ffmpeg_wrap import input

input("in.mkv", ss=30, t=60).output("clip.mp4").codec("v", "copy").run()
Source code in src/ffmpeg_wrap/_builder.py
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
def input(filename: str | os.PathLike[str], ffmpeg_path: str = "ffmpeg", **kwargs: Any) -> FFmpeg:
    """Start a new FFmpeg chain with an input file.

    This is the recommended entry point for building a fluent chain. It creates
    a fresh :class:`FFmpeg` instance and calls :meth:`~FFmpeg.input` on it.

    Args:
        filename: Input file path or PathLike object.
        ffmpeg_path: Path to the ffmpeg executable. Defaults to ``"ffmpeg"``
            (resolved from ``PATH``).
        **kwargs: FFmpeg input options passed through to the input slot
            (e.g. ``ss=30``, ``t=60``).

    Returns:
        A new :class:`FFmpeg` instance with the input added, ready for further
        chaining.

    Example:
        ```python
        from ffmpeg_wrap import input

        input("in.mkv", ss=30, t=60).output("clip.mp4").codec("v", "copy").run()
        ```
    """
    instance = FFmpeg(ffmpeg_path=ffmpeg_path)
    return instance.input(filename, **kwargs)

ffmpeg_wrap.FFmpeg

FFmpeg(ffmpeg_path: str = 'ffmpeg')

A fluent interface wrapper for ffmpeg.

Build an ffmpeg command incrementally via method chaining and execute it with :meth:run (sync) or :meth:arun (async). Every builder method returns self so calls can be chained in a single expression.

Parameters:

Name Type Description Default
ffmpeg_path str

Path to the ffmpeg executable. Defaults to "ffmpeg", which relies on the executable being present on PATH. Pass an absolute path to pin a specific build.

'ffmpeg'
Example
from ffmpeg_wrap import input

out, _ = (
    input("in.mkv")
    .output("out.mp4")
    .codec("v", "libx264")
    .codec("a", "aac")
    .overwrite_output()
    .run(capture_stderr=True, text=True)
)

Initialise the FFmpeg command builder.

Parameters:

Name Type Description Default
ffmpeg_path str

Path to the ffmpeg executable. Defaults to "ffmpeg" (resolved from PATH). Pass an absolute path to target a specific ffmpeg build, e.g. "/usr/local/bin/ffmpeg".

'ffmpeg'
Example
from ffmpeg_wrap import FFmpeg

ff = FFmpeg(ffmpeg_path="/usr/local/bin/ffmpeg")
ff.input("in.mkv").output("out.mp4").run()
Source code in src/ffmpeg_wrap/_builder.py
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
def __init__(self, ffmpeg_path: str = "ffmpeg") -> None:
    """Initialise the FFmpeg command builder.

    Args:
        ffmpeg_path: Path to the ffmpeg executable. Defaults to
            ``"ffmpeg"`` (resolved from ``PATH``). Pass an absolute path
            to target a specific ffmpeg build, e.g.
            ``"/usr/local/bin/ffmpeg"``.

    Example:
        ```python
        from ffmpeg_wrap import FFmpeg

        ff = FFmpeg(ffmpeg_path="/usr/local/bin/ffmpeg")
        ff.input("in.mkv").output("out.mp4").run()
        ```
    """
    self._inputs: list[dict[str, Any]] = []
    self._outputs: list[dict[str, Any]] = []
    self._global_args: list[str] = []
    self._filter_graph_args: list[str] = []
    self._overwrite: bool = False
    self._ffmpeg_path: str = ffmpeg_path

compile

compile() -> list[str]

Build the FFmpeg command as a list of arguments without executing it.

Returns:

Type Description
list[str]

The command as a list of strings.

Example
from ffmpeg_wrap import input

cmd = input("in.mkv").output("out.mp4").codec("v", "copy").compile()
# ['ffmpeg', '-i', '/abs/path/in.mkv', '-c:v', 'copy', 'out.mp4']
print(cmd)
Source code in src/ffmpeg_wrap/_builder.py
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def compile(self) -> list[str]:
    """Build the FFmpeg command as a list of arguments without executing it.

    Returns:
        The command as a list of strings.

    Example:
        ```python
        from ffmpeg_wrap import input

        cmd = input("in.mkv").output("out.mp4").codec("v", "copy").compile()
        # ['ffmpeg', '-i', '/abs/path/in.mkv', '-c:v', 'copy', 'out.mp4']
        print(cmd)
        ```
    """
    cmd = [self._ffmpeg_path]

    if self._overwrite:
        cmd.append("-y")

    cmd.extend(self._global_args)

    cmd.extend(self._filter_graph_args)

    for inp in self._inputs:
        for k, v in inp["kwargs"].items():
            cmd.extend(_convert_arg(k, v))
        cmd.extend(["-i", inp["filename"]])

    for out in self._outputs:
        for k, v in out["kwargs"].items():
            cmd.extend(_convert_arg(k, v))
        cmd.append(out["filename"])

    return cmd

input

input(filename: str | PathLike[str], **kwargs: Any) -> FFmpeg

Add an input file with optional arguments.

Parameters:

Name Type Description Default
filename str | PathLike[str]

Input file path or PathLike object.

required
**kwargs Any

FFmpeg input options (e.g., t=10).

{}

Returns:

Type Description
FFmpeg

Self for chaining.

Example
from ffmpeg_wrap import FFmpeg

ff = FFmpeg().input("clip.mkv", ss=30, t=60)
# Emits: -ss 30 -t 60 -i clip.mkv
Source code in src/ffmpeg_wrap/_builder.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
def input(self, filename: str | os.PathLike[str], **kwargs: Any) -> FFmpeg:
    """Add an input file with optional arguments.

    Args:
        filename: Input file path or PathLike object.
        **kwargs: FFmpeg input options (e.g., t=10).

    Returns:
        Self for chaining.

    Example:
        ```python
        from ffmpeg_wrap import FFmpeg

        ff = FFmpeg().input("clip.mkv", ss=30, t=60)
        # Emits: -ss 30 -t 60 -i clip.mkv
        ```
    """
    self._inputs.append({"filename": os.fsdecode(filename), "kwargs": kwargs})
    return self

output

output(filename: str | PathLike[str], **kwargs: Any) -> FFmpeg

Add an output file with optional arguments.

Parameters:

Name Type Description Default
filename str | PathLike[str]

Output file path or PathLike object.

required
**kwargs Any

FFmpeg output options (e.g., c="copy", ac=2).

{}

Returns:

Type Description
FFmpeg

Self for chaining.

Example
from ffmpeg_wrap import input

input("in.mkv").output("out.mp4", c="copy", ac=2)
Source code in src/ffmpeg_wrap/_builder.py
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def output(self, filename: str | os.PathLike[str], **kwargs: Any) -> FFmpeg:
    """Add an output file with optional arguments.

    Args:
        filename: Output file path or PathLike object.
        **kwargs: FFmpeg output options (e.g., c="copy", ac=2).

    Returns:
        Self for chaining.

    Example:
        ```python
        from ffmpeg_wrap import input

        input("in.mkv").output("out.mp4", c="copy", ac=2)
        ```
    """
    self._outputs.append({"filename": os.fsdecode(filename), "kwargs": kwargs})
    return self

map

map(*specs: str | Stream) -> FFmpeg

Add one or more -map specifiers to the current output.

Accepts raw specifier strings ("0:v") and Stream objects. For a Stream, the per-type specifier (e.g. 0:s:0) is used, computed from probe data rather than the raw absolute index.

Parameters:

Name Type Description Default
*specs str | Stream

Map specifiers as strings or Stream objects.

()

Returns:

Type Description
FFmpeg

Self for chaining.

Example
from ffmpeg_wrap import input, probe

result = probe("in.mkv")
video = next(s for s in result.streams if s.is_video)
input("in.mkv").output("out.mkv").map("0:a", video)
Source code in src/ffmpeg_wrap/_builder.py
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
def map(self, *specs: str | Stream) -> FFmpeg:
    """Add one or more ``-map`` specifiers to the current output.

    Accepts raw specifier strings (``"0:v"``) and ``Stream`` objects. For a
    ``Stream``, the per-type specifier (e.g. ``0:s:0``) is used, computed
    from probe data rather than the raw absolute index.

    Args:
        *specs: Map specifiers as strings or ``Stream`` objects.

    Returns:
        Self for chaining.

    Example:
        ```python
        from ffmpeg_wrap import input, probe

        result = probe("in.mkv")
        video = next(s for s in result.streams if s.is_video)
        input("in.mkv").output("out.mkv").map("0:a", video)
        ```
    """
    tokens = [spec.map_specifier() if isinstance(spec, Stream) else str(spec) for spec in specs]
    self._append_output_list("map", tokens)
    return self

map_stream

map_stream(kind: str, ordinal: int, input: int = 0) -> FFmpeg

Add a per-type -map specifier to the current output.

Parameters:

Name Type Description Default
kind str

Stream-type letter (e.g. "v", "a", "s").

required
ordinal int

Zero-based index within that stream type.

required
input int

Index of the ffmpeg input to map from.

0

Returns:

Type Description
FFmpeg

Self for chaining.

Example
from ffmpeg_wrap import input

# Map first video and second audio stream from input 0
input("in.mkv").output("out.mkv").map_stream("v", 0).map_stream("a", 1)
Source code in src/ffmpeg_wrap/_builder.py
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
def map_stream(self, kind: str, ordinal: int, input: int = 0) -> FFmpeg:
    """Add a per-type ``-map`` specifier to the current output.

    Args:
        kind: Stream-type letter (e.g. ``"v"``, ``"a"``, ``"s"``).
        ordinal: Zero-based index within that stream type.
        input: Index of the ffmpeg input to map from.

    Returns:
        Self for chaining.

    Example:
        ```python
        from ffmpeg_wrap import input

        # Map first video and second audio stream from input 0
        input("in.mkv").output("out.mkv").map_stream("v", 0).map_stream("a", 1)
        ```
    """
    self._append_output_list("map", [f"{input}:{kind}:{ordinal}"])
    return self

hwaccel

hwaccel(name: str) -> FFmpeg

Request a hardware-acceleration backend on the current input.

Discoverable sugar for input(..., hwaccel=name): emits -hwaccel {name} before the current input's -i (e.g. hwaccel("cuda") -> -hwaccel cuda -i in.mkv).

Parameters:

Name Type Description Default
name str

Hardware-acceleration backend (e.g. "cuda", "vaapi").

required

Returns:

Type Description
FFmpeg

Self for chaining.

Example
from ffmpeg_wrap import FFmpeg

ff = FFmpeg().input("in.mkv").hwaccel("cuda").output("out.mp4")
# Emits: -hwaccel cuda -i in.mkv out.mp4
Source code in src/ffmpeg_wrap/_builder.py
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def hwaccel(self, name: str) -> FFmpeg:
    """Request a hardware-acceleration backend on the current input.

    Discoverable sugar for ``input(..., hwaccel=name)``: emits
    ``-hwaccel {name}`` before the current input's ``-i`` (e.g.
    ``hwaccel("cuda")`` -> ``-hwaccel cuda -i in.mkv``).

    Args:
        name: Hardware-acceleration backend (e.g. ``"cuda"``, ``"vaapi"``).

    Returns:
        Self for chaining.

    Example:
        ```python
        from ffmpeg_wrap import FFmpeg

        ff = FFmpeg().input("in.mkv").hwaccel("cuda").output("out.mp4")
        # Emits: -hwaccel cuda -i in.mkv out.mp4
        ```
    """
    self._set_input_option("hwaccel", name)
    return self

codec

codec(kind: str, name: str) -> FFmpeg

Set the codec for a stream type on the current output.

Emits -c:{kind} {name} (e.g. codec("v", "libx265") -> -c:v libx265).

Parameters:

Name Type Description Default
kind str

Stream-type letter (e.g. "v", "a", "s").

required
name str

Codec name (e.g. "libx265", "copy").

required

Returns:

Type Description
FFmpeg

Self for chaining.

Example
from ffmpeg_wrap import input

input("in.mkv").output("out.mp4").codec("v", "libx264").codec("a", "aac")
Source code in src/ffmpeg_wrap/_builder.py
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
def codec(self, kind: str, name: str) -> FFmpeg:
    """Set the codec for a stream type on the current output.

    Emits ``-c:{kind} {name}`` (e.g. ``codec("v", "libx265")`` ->
    ``-c:v libx265``).

    Args:
        kind: Stream-type letter (e.g. ``"v"``, ``"a"``, ``"s"``).
        name: Codec name (e.g. ``"libx265"``, ``"copy"``).

    Returns:
        Self for chaining.

    Example:
        ```python
        from ffmpeg_wrap import input

        input("in.mkv").output("out.mp4").codec("v", "libx264").codec("a", "aac")
        ```
    """
    self._set_output_option(f"c:{kind}", name)
    return self

bitrate

bitrate(kind: str, value: str | int) -> FFmpeg

Set the bitrate for a stream type on the current output.

Emits -b:{kind} {value} (e.g. bitrate("v", "2M") -> -b:v 2M).

Parameters:

Name Type Description Default
kind str

Stream-type letter (e.g. "v", "a").

required
value str | int

Bitrate value (e.g. "2M", 128000).

required

Returns:

Type Description
FFmpeg

Self for chaining.

Example
from ffmpeg_wrap import input

input("in.mkv").output("out.mp4").codec("v", "libx264").bitrate("v", "2M")
Source code in src/ffmpeg_wrap/_builder.py
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
def bitrate(self, kind: str, value: str | int) -> FFmpeg:
    """Set the bitrate for a stream type on the current output.

    Emits ``-b:{kind} {value}`` (e.g. ``bitrate("v", "2M")`` ->
    ``-b:v 2M``).

    Args:
        kind: Stream-type letter (e.g. ``"v"``, ``"a"``).
        value: Bitrate value (e.g. ``"2M"``, ``128000``).

    Returns:
        Self for chaining.

    Example:
        ```python
        from ffmpeg_wrap import input

        input("in.mkv").output("out.mp4").codec("v", "libx264").bitrate("v", "2M")
        ```
    """
    self._set_output_option(f"b:{kind}", value)
    return self

quality

quality(kind: str, value: str | int) -> FFmpeg

Set the quality scale for a stream type on the current output.

Emits -q:{kind} {value} (e.g. quality("a", 2) -> -q:a 2).

Parameters:

Name Type Description Default
kind str

Stream-type letter (e.g. "v", "a").

required
value str | int

Quality value.

required

Returns:

Type Description
FFmpeg

Self for chaining.

Example
from ffmpeg_wrap import input

input("in.mkv").output("out.mp3").codec("a", "libmp3lame").quality("a", 2)
Source code in src/ffmpeg_wrap/_builder.py
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
def quality(self, kind: str, value: str | int) -> FFmpeg:
    """Set the quality scale for a stream type on the current output.

    Emits ``-q:{kind} {value}`` (e.g. ``quality("a", 2)`` -> ``-q:a 2``).

    Args:
        kind: Stream-type letter (e.g. ``"v"``, ``"a"``).
        value: Quality value.

    Returns:
        Self for chaining.

    Example:
        ```python
        from ffmpeg_wrap import input

        input("in.mkv").output("out.mp3").codec("a", "libmp3lame").quality("a", 2)
        ```
    """
    self._set_output_option(f"q:{kind}", value)
    return self

audio_filter

audio_filter(chain: str) -> FFmpeg

Set the audio filter chain on the current output.

Emits -filter:a {chain}.

Parameters:

Name Type Description Default
chain str

Filter chain string (e.g. "loudnorm").

required

Returns:

Type Description
FFmpeg

Self for chaining.

Example
from ffmpeg_wrap import input

input("in.mkv").output("out.mkv").audio_filter("loudnorm")
Source code in src/ffmpeg_wrap/_builder.py
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
def audio_filter(self, chain: str) -> FFmpeg:
    """Set the audio filter chain on the current output.

    Emits ``-filter:a {chain}``.

    Args:
        chain: Filter chain string (e.g. ``"loudnorm"``).

    Returns:
        Self for chaining.

    Example:
        ```python
        from ffmpeg_wrap import input

        input("in.mkv").output("out.mkv").audio_filter("loudnorm")
        ```
    """
    self._set_output_option("filter:a", chain)
    return self

video_filter

video_filter(chain: str) -> FFmpeg

Set the video filter chain on the current output.

Emits -filter:v {chain}.

Parameters:

Name Type Description Default
chain str

Filter chain string (e.g. "scale=1280:-2").

required

Returns:

Type Description
FFmpeg

Self for chaining.

Example
from ffmpeg_wrap import input

input("in.mkv").output("out.mp4").video_filter("scale=1280:-2")
Source code in src/ffmpeg_wrap/_builder.py
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
def video_filter(self, chain: str) -> FFmpeg:
    """Set the video filter chain on the current output.

    Emits ``-filter:v {chain}``.

    Args:
        chain: Filter chain string (e.g. ``"scale=1280:-2"``).

    Returns:
        Self for chaining.

    Example:
        ```python
        from ffmpeg_wrap import input

        input("in.mkv").output("out.mp4").video_filter("scale=1280:-2")
        ```
    """
    self._set_output_option("filter:v", chain)
    return self

flag

flag(*names: str) -> FFmpeg

Add one or more valueless switches to the current output.

Emits a bare -{name} token per name (e.g. flag("vn", "sn") -> -vn -sn). This is the intentional way to request a switch; note the contrast with output(..., vn=None), which omits the flag entirely.

Parameters:

Name Type Description Default
*names str

Switch names without the leading dash (e.g. "vn").

()

Returns:

Type Description
FFmpeg

Self for chaining.

Example
from ffmpeg_wrap import input

# Extract audio only: suppress video and subtitle streams
input("in.mkv").output("out.aac").flag("vn", "sn")
Source code in src/ffmpeg_wrap/_builder.py
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
def flag(self, *names: str) -> FFmpeg:
    """Add one or more valueless switches to the current output.

    Emits a bare ``-{name}`` token per name (e.g. ``flag("vn", "sn")`` ->
    ``-vn -sn``). This is the intentional way to request a switch; note the
    contrast with ``output(..., vn=None)``, which *omits* the flag entirely.

    Args:
        *names: Switch names without the leading dash (e.g. ``"vn"``).

    Returns:
        Self for chaining.

    Example:
        ```python
        from ffmpeg_wrap import input

        # Extract audio only: suppress video and subtitle streams
        input("in.mkv").output("out.aac").flag("vn", "sn")
        ```
    """
    for name in names:
        self._set_output_option(name, True)
    return self

overwrite_output

overwrite_output() -> FFmpeg

Add the -y flag to overwrite output files without prompting.

Returns:

Type Description
FFmpeg

Self for chaining.

Example
from ffmpeg_wrap import input

input("in.mkv").output("out.mp4").overwrite_output().run()
Source code in src/ffmpeg_wrap/_builder.py
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
def overwrite_output(self) -> FFmpeg:
    """Add the ``-y`` flag to overwrite output files without prompting.

    Returns:
        Self for chaining.

    Example:
        ```python
        from ffmpeg_wrap import input

        input("in.mkv").output("out.mp4").overwrite_output().run()
        ```
    """
    self._overwrite = True
    return self

global_args

global_args(*args: str) -> FFmpeg

Add raw global arguments emitted before input options.

Parameters:

Name Type Description Default
*args str

Global arguments (e.g. "-nostdin", "-benchmark").

()

Returns:

Type Description
FFmpeg

Self for chaining.

Example
from ffmpeg_wrap import input

input("in.mkv").output("out.mp4").global_args("-nostdin", "-benchmark")
Source code in src/ffmpeg_wrap/_builder.py
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
def global_args(self, *args: str) -> FFmpeg:
    """Add raw global arguments emitted before input options.

    Args:
        *args: Global arguments (e.g. ``"-nostdin"``, ``"-benchmark"``).

    Returns:
        Self for chaining.

    Example:
        ```python
        from ffmpeg_wrap import input

        input("in.mkv").output("out.mp4").global_args("-nostdin", "-benchmark")
        ```
    """
    self._global_args.extend(args)
    return self

hide_banner

hide_banner() -> FFmpeg

Add the -hide_banner global flag.

Thin sugar over :meth:global_args; emitted in the global slot.

Returns:

Type Description
FFmpeg

Self for chaining.

Example
from ffmpeg_wrap import input

input("in.mkv").output("out.mp4").hide_banner().run()
Source code in src/ffmpeg_wrap/_builder.py
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
def hide_banner(self) -> FFmpeg:
    """Add the ``-hide_banner`` global flag.

    Thin sugar over :meth:`global_args`; emitted in the global slot.

    Returns:
        Self for chaining.

    Example:
        ```python
        from ffmpeg_wrap import input

        input("in.mkv").output("out.mp4").hide_banner().run()
        ```
    """
    self._global_args.append("-hide_banner")
    return self

loglevel

loglevel(level: str) -> FFmpeg

Set the ffmpeg log level via -loglevel {level}.

Thin sugar over :meth:global_args; emitted in the global slot.

Parameters:

Name Type Description Default
level str

Log level (e.g. "error", "warning", "verbose").

required

Returns:

Type Description
FFmpeg

Self for chaining.

Example
from ffmpeg_wrap import input

input("in.mkv").output("out.mp4").loglevel("error").run()
Source code in src/ffmpeg_wrap/_builder.py
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
def loglevel(self, level: str) -> FFmpeg:
    """Set the ffmpeg log level via ``-loglevel {level}``.

    Thin sugar over :meth:`global_args`; emitted in the global slot.

    Args:
        level: Log level (e.g. ``"error"``, ``"warning"``, ``"verbose"``).

    Returns:
        Self for chaining.

    Example:
        ```python
        from ffmpeg_wrap import input

        input("in.mkv").output("out.mp4").loglevel("error").run()
        ```
    """
    self._global_args.extend(["-loglevel", level])
    return self

filter_complex

filter_complex(graph_str: str) -> FFmpeg

Set a graph-level -filter_complex filtergraph.

Emits -filter_complex <graph_str> in a dedicated slot after global args and before inputs, so placement no longer depends on parking the option on an output. Mutually exclusive with :meth:filter_complex_script at runtime (not enforced here).

Parameters:

Name Type Description Default
graph_str str

The filtergraph string (e.g. "[0:v]split=2[a][b]").

required

Returns:

Type Description
FFmpeg

Self for chaining.

Example
from ffmpeg_wrap import input

(
    input("in.mkv")
    .filter_complex("[0:v]split=2[a][b];[a]scale=640:-2[out]")
    .output("out.mp4", map="[out]")
)
Source code in src/ffmpeg_wrap/_builder.py
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
def filter_complex(self, graph_str: str) -> FFmpeg:
    """Set a graph-level ``-filter_complex`` filtergraph.

    Emits ``-filter_complex <graph_str>`` in a dedicated slot after global
    args and before inputs, so placement no longer depends on parking the
    option on an output. Mutually exclusive with
    :meth:`filter_complex_script` at runtime (not enforced here).

    Args:
        graph_str: The filtergraph string (e.g.
            ``"[0:v]split=2[a][b]"``).

    Returns:
        Self for chaining.

    Example:
        ```python
        from ffmpeg_wrap import input

        (
            input("in.mkv")
            .filter_complex("[0:v]split=2[a][b];[a]scale=640:-2[out]")
            .output("out.mp4", map="[out]")
        )
        ```
    """
    self._filter_graph_args = ["-filter_complex", graph_str]
    return self

filter_complex_script

filter_complex_script(path: str | PathLike[str]) -> FFmpeg

Read a graph-level filtergraph from a file via -filter_complex_script.

Emits -filter_complex_script <path> in the same dedicated slot as :meth:filter_complex (after global args, before inputs). Mutually exclusive with :meth:filter_complex at runtime (not enforced here).

Parameters:

Name Type Description Default
path str | PathLike[str]

Path to a file containing the filtergraph.

required

Returns:

Type Description
FFmpeg

Self for chaining.

Example
from ffmpeg_wrap import input

input("in.mkv").filter_complex_script("graph.txt").output("out.mp4").run()
Source code in src/ffmpeg_wrap/_builder.py
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
def filter_complex_script(self, path: str | os.PathLike[str]) -> FFmpeg:
    """Read a graph-level filtergraph from a file via ``-filter_complex_script``.

    Emits ``-filter_complex_script <path>`` in the same dedicated slot as
    :meth:`filter_complex` (after global args, before inputs). Mutually
    exclusive with :meth:`filter_complex` at runtime (not enforced here).

    Args:
        path: Path to a file containing the filtergraph.

    Returns:
        Self for chaining.

    Example:
        ```python
        from ffmpeg_wrap import input

        input("in.mkv").filter_complex_script("graph.txt").output("out.mp4").run()
        ```
    """
    self._filter_graph_args = ["-filter_complex_script", os.fsdecode(path)]
    return self

run

run(capture_stdout: bool = ..., capture_stderr: bool = ..., *, text: Literal[False] = ...) -> tuple[bytes | None, bytes | None]
run(capture_stdout: bool = ..., capture_stderr: bool = ..., *, text: Literal[True]) -> tuple[str | None, str | None]
run(capture_stdout: bool = False, capture_stderr: bool = False, *, text: bool = False) -> tuple[bytes | None, bytes | None] | tuple[str | None, str | None]

Build and execute the FFmpeg command.

Parameters:

Name Type Description Default
capture_stdout bool

Whether to capture stdout.

False
capture_stderr bool

Whether to capture stderr.

False
text bool

When True, decode stdout/stderr (and FFmpegError.stderr) as text using the platform default encoding; otherwise return raw bytes.

False

Returns:

Type Description
tuple[bytes | None, bytes | None] | tuple[str | None, str | None]

Tuple of (stdout, stderr). Values are str when text=True

tuple[bytes | None, bytes | None] | tuple[str | None, str | None]

else bytes when the corresponding capture flag is True, or

tuple[bytes | None, bytes | None] | tuple[str | None, str | None]

None otherwise.

Raises:

Type Description
FFmpegError

If ffmpeg fails.

Example
import ffmpeg_wrap as ffmpeg

try:
    _, stderr = (
        ffmpeg.input("in.mkv")
        .output("out.mp4")
        .overwrite_output()
        .run(capture_stderr=True, text=True)
    )
except ffmpeg.FFmpegError as e:
    print(e.returncode, e.stderr)
Source code in src/ffmpeg_wrap/_builder.py
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
def run(
    self,
    capture_stdout: bool = False,
    capture_stderr: bool = False,
    *,
    text: bool = False,
) -> tuple[bytes | None, bytes | None] | tuple[str | None, str | None]:
    """Build and execute the FFmpeg command.

    Args:
        capture_stdout: Whether to capture stdout.
        capture_stderr: Whether to capture stderr.
        text: When True, decode stdout/stderr (and ``FFmpegError.stderr``)
            as text using the platform default encoding; otherwise return
            raw bytes.

    Returns:
        Tuple of (stdout, stderr). Values are ``str`` when ``text=True``
        else ``bytes`` when the corresponding capture flag is True, or
        None otherwise.

    Raises:
        FFmpegError: If ffmpeg fails.

    Example:
        ```python
        import ffmpeg_wrap as ffmpeg

        try:
            _, stderr = (
                ffmpeg.input("in.mkv")
                .output("out.mp4")
                .overwrite_output()
                .run(capture_stderr=True, text=True)
            )
        except ffmpeg.FFmpegError as e:
            print(e.returncode, e.stderr)
        ```
    """
    cmd = self.compile()

    stdout_dest = subprocess.PIPE if capture_stdout else None

    logger.debug(f"Running ffmpeg command: {' '.join(cmd)}")

    try:
        if capture_stderr:
            # Caller wants stderr returned: capture it directly.
            process = subprocess.run(
                cmd,
                stdout=stdout_dest,
                stderr=subprocess.PIPE,
                check=True,
                text=text,
            )
            return process.stdout, process.stderr
        # Caller does not capture stderr: tee ffmpeg's stderr to the
        # inherited stderr stream in real time (preserving the live
        # progress output of a bare ``run()``) while collecting it, so the
        # failure path can still populate ``FFmpegError.stderr`` without
        # silently buffering an entire successful run in memory.
        stdout_data = self._run_tee(cmd, stdout_dest, text)
        return stdout_data, None
    except subprocess.CalledProcessError as e:
        if e.stderr is None:
            stderr_text: str | None = None
        elif isinstance(e.stderr, str):
            stderr_text = e.stderr
        else:
            stderr_text = e.stderr.decode("utf-8", errors="replace")
        err_msg = stderr_text or str(e)
        logger.error(f"FFmpeg command failed: {err_msg}")
        raise _build_ffmpeg_error(
            f"ffmpeg error: {err_msg}",
            stderr=stderr_text,
            returncode=e.returncode,
            cmd=cmd,
        ) from e
    except OSError as e:
        logger.error(f"ffmpeg could not be executed: {e}")
        raise _build_ffmpeg_error(f"ffmpeg could not be executed: {e}", cmd=cmd) from e

arun async

arun(capture_stdout: bool = ..., capture_stderr: bool = ..., *, text: Literal[False] = ...) -> tuple[bytes | None, bytes | None]
arun(capture_stdout: bool = ..., capture_stderr: bool = ..., *, text: Literal[True]) -> tuple[str | None, str | None]
arun(capture_stdout: bool = False, capture_stderr: bool = False, *, text: bool = False) -> tuple[bytes | None, bytes | None] | tuple[str | None, str | None]

Build and execute the FFmpeg command asynchronously.

Async mirror of :meth:run, powered by AnyIO via the optional [async] extra. The return contract is identical to :meth:run. Importing this module never imports anyio — the dependency is pulled in lazily here, so ffmpeg_wrap.aio (and thus anyio) is only loaded when arun is actually awaited.

Parameters:

Name Type Description Default
capture_stdout bool

Whether to capture stdout.

False
capture_stderr bool

Whether to capture stderr.

False
text bool

When True, decode stdout/stderr (and FFmpegError.stderr) leniently as text using the platform default encoding.

False

Returns:

Type Description
tuple[bytes | None, bytes | None] | tuple[str | None, str | None]

Tuple of (stdout, stderr); see :meth:run.

Raises:

Type Description
FFmpegError

If ffmpeg fails or cannot be executed.

ImportError

If the optional [async] extra is not installed.

Example
import anyio
from ffmpeg_wrap import input

async def main():
    _, stderr = await (
        input("in.mkv")
        .output("out.mp4")
        .overwrite_output()
        .arun(capture_stderr=True, text=True)
    )

anyio.run(main)
Source code in src/ffmpeg_wrap/_builder.py
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
async def arun(
    self,
    capture_stdout: bool = False,
    capture_stderr: bool = False,
    *,
    text: bool = False,
) -> tuple[bytes | None, bytes | None] | tuple[str | None, str | None]:
    """Build and execute the FFmpeg command asynchronously.

    Async mirror of :meth:`run`, powered by AnyIO via the optional
    ``[async]`` extra. The return contract is identical to :meth:`run`.
    Importing this module never imports anyio — the dependency is pulled in
    lazily here, so ``ffmpeg_wrap.aio`` (and thus anyio) is only loaded when
    ``arun`` is actually awaited.

    Args:
        capture_stdout: Whether to capture stdout.
        capture_stderr: Whether to capture stderr.
        text: When True, decode stdout/stderr (and ``FFmpegError.stderr``)
            leniently as text using the platform default encoding.

    Returns:
        Tuple of (stdout, stderr); see :meth:`run`.

    Raises:
        FFmpegError: If ffmpeg fails or cannot be executed.
        ImportError: If the optional ``[async]`` extra is not installed.

    Example:
        ```python
        import anyio
        from ffmpeg_wrap import input

        async def main():
            _, stderr = await (
                input("in.mkv")
                .output("out.mp4")
                .overwrite_output()
                .arun(capture_stderr=True, text=True)
            )

        anyio.run(main)
        ```
    """
    from ffmpeg_wrap import aio

    return await aio.run(self, capture_stdout, capture_stderr, text=text)

Probing

ffmpeg_wrap.probe

probe(filename: str | PathLike[str], ffprobe_path: str = 'ffprobe') -> ProbeResult

Run ffprobe on the specified file and return typed output.

Parameters:

Name Type Description Default
filename str | PathLike[str]

Path to the file to probe.

required
ffprobe_path str

Path to the ffprobe executable.

'ffprobe'

Returns:

Type Description
ProbeResult

Parsed and typed :class:ProbeResult containing all streams and

ProbeResult

container format information.

Raises:

Type Description
FFmpegError

If ffprobe fails or the output cannot be parsed.

Example
import ffmpeg_wrap as ffmpeg

result = ffmpeg.probe("video.mkv")
print(result.format.format_name if result.format else None)
for stream in result.streams:
    print(stream.index, stream.codec_type, stream.codec_name)
Source code in src/ffmpeg_wrap/_probe.py
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
def probe(filename: str | os.PathLike[str], ffprobe_path: str = "ffprobe") -> ProbeResult:
    """Run ffprobe on the specified file and return typed output.

    Args:
        filename: Path to the file to probe.
        ffprobe_path: Path to the ffprobe executable.

    Returns:
        Parsed and typed :class:`ProbeResult` containing all streams and
        container format information.

    Raises:
        FFmpegError: If ffprobe fails or the output cannot be parsed.

    Example:
        ```python
        import ffmpeg_wrap as ffmpeg

        result = ffmpeg.probe("video.mkv")
        print(result.format.format_name if result.format else None)
        for stream in result.streams:
            print(stream.index, stream.codec_type, stream.codec_name)
        ```
    """
    cmd = _build_probe_cmd(filename, ffprobe_path)
    try:
        result = subprocess.run(
            cmd,
            capture_output=True,
            check=True,
            text=False,
        )
    except subprocess.CalledProcessError as e:
        stderr_text = e.stderr.decode("utf-8", errors="replace") if e.stderr else None
        err_msg = stderr_text or str(e)
        logger.error(f"ffprobe failed: {err_msg}")
        raise _build_ffmpeg_error(
            f"ffprobe error: {err_msg}",
            stderr=stderr_text,
            returncode=e.returncode,
            cmd=cmd,
        ) from e
    except OSError as e:
        logger.error(f"ffprobe could not be executed: {e}")
        raise _build_ffmpeg_error(f"ffprobe could not be executed: {e}", cmd=cmd) from e
    return _parse_probe_output(result.stdout, cmd)

ffmpeg_wrap.validate

validate(filename: str | PathLike[str], ffprobe_path: str = 'ffprobe', loglevel: str = 'warning', extra_args: tuple[str, ...] = ()) -> tuple[bool, str]

Run ffprobe in validation mode and report diagnostics.

Does not raise on bad media — a non-zero exit or non-empty stderr is a normal outcome (returned as ok=False). This makes it safe to use as a boolean health-check without wrapping in try/except.

Parameters:

Name Type Description Default
filename str | PathLike[str]

Path to the media file (str or PathLike).

required
ffprobe_path str

Path to the ffprobe executable.

'ffprobe'
loglevel str

Value passed to ffprobe's -v flag. Defaults to "warning" so DTS/codec warnings surface in stderr. Use "error" for a stricter check that ignores warnings, "fatal"/"panic" for only unrecoverable failures, or any other ffprobe loglevel keyword. See ffprobe -loglevel help.

'warning'
extra_args tuple[str, ...]

Additional raw arguments forwarded to ffprobe before the filename, e.g. ("-show_format",). Use with care; no validation is performed on these args.

()

Returns:

Type Description
bool

(ok, stderr_text) where ok is True iff the ffprobe exit

str

code is 0 and stderr.strip() is empty. stderr_text is the

tuple[bool, str]

raw decoded stderr (never None, possibly an empty string).

Raises:

Type Description
FFmpegError

Only when the ffprobe executable itself cannot be run (e.g. not found on PATH). A corrupt or unreadable media file does not raise — it returns (False, stderr_text).

ValueError

If loglevel is not a recognised ffprobe loglevel keyword.

Example
import ffmpeg_wrap as ffmpeg

ok, diag = ffmpeg.validate("recording.mkv")
if not ok:
    print("Validation failed:", diag)
Source code in src/ffmpeg_wrap/_probe.py
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
def validate(
    filename: str | os.PathLike[str],
    ffprobe_path: str = "ffprobe",
    loglevel: str = "warning",
    extra_args: tuple[str, ...] = (),
) -> tuple[bool, str]:
    """Run ffprobe in validation mode and report diagnostics.

    Does **not** raise on bad media — a non-zero exit or non-empty stderr is a
    normal outcome (returned as ``ok=False``). This makes it safe to use as a
    boolean health-check without wrapping in ``try/except``.

    Args:
        filename: Path to the media file (str or PathLike).
        ffprobe_path: Path to the ffprobe executable.
        loglevel: Value passed to ffprobe's ``-v`` flag. Defaults to
            ``"warning"`` so DTS/codec warnings surface in stderr.
            Use ``"error"`` for a stricter check that ignores warnings,
            ``"fatal"``/``"panic"`` for only unrecoverable failures,
            or any other ffprobe loglevel keyword. See ``ffprobe -loglevel help``.
        extra_args: Additional raw arguments forwarded to ffprobe before the
            filename, e.g. ``("-show_format",)``. Use with care; no validation
            is performed on these args.

    Returns:
        ``(ok, stderr_text)`` where ``ok`` is ``True`` iff the ffprobe exit
        code is 0 *and* ``stderr.strip()`` is empty. ``stderr_text`` is the
        raw decoded stderr (never ``None``, possibly an empty string).

    Raises:
        FFmpegError: Only when the ffprobe executable itself cannot be run
            (e.g. not found on ``PATH``). A corrupt or unreadable media file
            does *not* raise — it returns ``(False, stderr_text)``.
        ValueError: If ``loglevel`` is not a recognised ffprobe loglevel
            keyword.

    Example:
        ```python
        import ffmpeg_wrap as ffmpeg

        ok, diag = ffmpeg.validate("recording.mkv")
        if not ok:
            print("Validation failed:", diag)
        ```
    """
    cmd = _build_validate_cmd(filename, ffprobe_path, loglevel, extra_args)
    try:
        result = subprocess.run(cmd, capture_output=True, check=False, text=False)
    except OSError as e:
        logger.error(f"ffprobe could not be executed: {e}")
        raise _build_ffmpeg_error(f"ffprobe could not be executed: {e}", cmd=cmd) from e
    return _interpret_validate(result.returncode, result.stderr)

ffmpeg_wrap.ProbeResult

Bases: Struct

Typed result of running :func:probe on a media file.

Top-level container decoded from ffprobe's -print_format json output. Provides convenient access to all streams and the container format information in a single structured object.

Attributes:

Name Type Description
streams list[Stream]

All streams found in the file, in index order. Each entry is a :class:Stream with type_index populated by :func:probe.

format Format | None

Container-level format information, or None when ffprobe did not emit a "format" section (rare for well-formed files).

Example
from ffmpeg_wrap import probe

result = probe("video.mkv")
print(result.format.format_name if result.format else None)
print([s.codec_name for s in result.streams if s.is_video])

duration_seconds

duration_seconds() -> float | None

Return the container duration as float seconds.

Delegates to self.format; returns None when format is absent or its duration is missing/non-numeric.

Example
from ffmpeg_wrap import probe

result = probe("video.mkv")
secs = result.duration_seconds()
print(f"Duration: {secs:.1f}s" if secs else "Duration unknown")
Source code in src/ffmpeg_wrap/_probe.py
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
def duration_seconds(self) -> float | None:
    """Return the container ``duration`` as ``float`` seconds.

    Delegates to ``self.format``; returns ``None`` when ``format`` is
    absent or its ``duration`` is missing/non-numeric.

    Example:
        ```python
        from ffmpeg_wrap import probe

        result = probe("video.mkv")
        secs = result.duration_seconds()
        print(f"Duration: {secs:.1f}s" if secs else "Duration unknown")
        ```
    """
    if self.format is None:
        return None
    return self.format.duration_seconds()

ffmpeg_wrap.Stream

Bases: Struct

A single stream entry from ffprobe JSON output.

Decoded by msgspec from the ffprobe streams array. All fields except :attr:index are optional because ffprobe omits irrelevant fields per stream type (e.g. width/height are absent on audio streams).

Attributes:

Name Type Description
index int

Absolute stream index within the file (0-based).

codec_name str | None

Short codec name as reported by ffprobe (e.g. "h264", "aac", "subrip"). None if not reported.

codec_type str | None

Stream type string (e.g. "video", "audio", "subtitle"). None if not reported. Compare against :class:CodecType members rather than raw strings.

width int | None

Video frame width in pixels. None for non-video streams.

height int | None

Video frame height in pixels. None for non-video streams.

channels int | None

Number of audio channels. None for non-audio streams.

sample_rate str | None

Audio sample rate as a string (e.g. "48000"). None for non-audio streams.

duration str | None

Stream duration as a decimal-seconds string (e.g. "3600.123000"). None or "N/A" when unavailable.

bit_rate str | None

Stream bit rate as a string (e.g. "128000"). None when not reported.

tags dict[str, str] | None

Key/value metadata tags from the stream header (e.g. {"language": "eng", "title": "Commentary"}). None when no tags are present.

disposition dict[str, int] | None

ffprobe disposition flags as a dict[str, int] (e.g. {"default": 1, "forced": 0}). None when absent.

type_index int

Zero-based ordinal of this stream within its :attr:codec_type group (the N in the ffmpeg specifier 0:<type>:N). Set by :func:probe; defaults to 0 when a Stream is constructed outside of :func:probe.

Example
from ffmpeg_wrap import probe

result = probe("video.mkv")
for stream in result.streams:
    print(stream.index, stream.codec_type, stream.codec_name)

is_video property

is_video: bool

True iff codec_type is "video".

Returns:

Type Description
bool

True for video streams, False otherwise.

Example
from ffmpeg_wrap import probe

result = probe("video.mkv")
videos = [s for s in result.streams if s.is_video]
audios = [s for s in result.streams if s.is_audio]
subs = [s for s in result.streams if s.is_subtitle]

is_audio property

is_audio: bool

True iff codec_type is "audio".

Returns:

Type Description
bool

True for audio streams, False otherwise.

Example
from ffmpeg_wrap import probe

result = probe("video.mkv")
audio_streams = [s for s in result.streams if s.is_audio]

is_subtitle property

is_subtitle: bool

True iff codec_type is "subtitle".

Returns:

Type Description
bool

True for subtitle streams, False otherwise.

Example
from ffmpeg_wrap import probe

result = probe("video.mkv")
subtitle_streams = [s for s in result.streams if s.is_subtitle]

is_data property

is_data: bool

True iff codec_type is "data".

Returns:

Type Description
bool

True for data streams, False otherwise.

Example
from ffmpeg_wrap import probe

result = probe("video.mkv")
data_streams = [s for s in result.streams if s.is_data]

is_attachment property

is_attachment: bool

True iff codec_type is "attachment".

Returns:

Type Description
bool

True for attachment streams, False otherwise.

Example
from ffmpeg_wrap import probe

result = probe("video.mkv")
attachments = [s for s in result.streams if s.is_attachment]

is_text_subtitle property

is_text_subtitle: bool

True iff this is a subtitle stream with a known text-based codec.

Best-effort: driven by a documented codec-name set (subrip, ass, ssa, ...). Unknown subtitle codecs return False.

Returns:

Type Description
bool

True for text-based subtitle streams (SRT, ASS, WebVTT, etc.),

bool

False otherwise.

Example
from ffmpeg_wrap import probe

result = probe("video.mkv")
text_subs = [s for s in result.streams if s.is_text_subtitle]

is_image_subtitle property

is_image_subtitle: bool

True iff this is a subtitle stream with a known image-based codec.

Best-effort: driven by a documented codec-name set (hdmv_pgs_subtitle, dvd_subtitle, dvb_subtitle, ...). Unknown subtitle codecs return False.

Returns:

Type Description
bool

True for bitmap/image subtitle streams (PGS, DVD, DVB, etc.),

bool

False otherwise.

Example
from ffmpeg_wrap import probe

result = probe("video.mkv")
image_subs = [s for s in result.streams if s.is_image_subtitle]

map_specifier

map_specifier(input_index: int = 0) -> str

Return the ffmpeg -map specifier for this stream.

Emits the per-type form {input}:{letter}:{ordinal} (e.g. 0:s:0) derived from codec_type and type_index. When codec_type is unknown or None, falls back to the unambiguous absolute-index form {input}:{index}.

Parameters:

Name Type Description Default
input_index int

Index of the ffmpeg input the stream belongs to.

0

Returns:

Type Description
str

The map specifier string.

Example
from ffmpeg_wrap import probe

result = probe("video.mkv")
sub = next(s for s in result.streams if s.is_subtitle)
print(sub.map_specifier())   # e.g. "0:s:0"
print(sub.map_specifier(1))  # e.g. "1:s:0"
Source code in src/ffmpeg_wrap/_probe.py
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
def map_specifier(self, input_index: int = 0) -> str:
    """Return the ffmpeg ``-map`` specifier for this stream.

    Emits the per-type form ``{input}:{letter}:{ordinal}`` (e.g. ``0:s:0``)
    derived from ``codec_type`` and ``type_index``. When ``codec_type`` is
    unknown or ``None``, falls back to the unambiguous absolute-index form
    ``{input}:{index}``.

    Args:
        input_index: Index of the ffmpeg input the stream belongs to.

    Returns:
        The map specifier string.

    Example:
        ```python
        from ffmpeg_wrap import probe

        result = probe("video.mkv")
        sub = next(s for s in result.streams if s.is_subtitle)
        print(sub.map_specifier())   # e.g. "0:s:0"
        print(sub.map_specifier(1))  # e.g. "1:s:0"
        ```
    """
    letter = _STREAM_TYPE_LETTERS.get(self.codec_type or "")
    if letter is None:
        return f"{input_index}:{self.index}"
    return f"{input_index}:{letter}:{self.type_index}"

duration_seconds

duration_seconds() -> float | None

Return this stream's duration as float seconds.

Returns None when duration is missing or non-numeric (e.g. "N/A"); the raw duration string is preserved.

Example
from ffmpeg_wrap import probe

result = probe("video.mkv")
for stream in result.streams:
    secs = stream.duration_seconds()
    if secs is not None:
        print(f"stream {stream.index}: {secs:.1f}s")
Source code in src/ffmpeg_wrap/_probe.py
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
def duration_seconds(self) -> float | None:
    """Return this stream's ``duration`` as ``float`` seconds.

    Returns ``None`` when ``duration`` is missing or non-numeric
    (e.g. ``"N/A"``); the raw ``duration`` string is preserved.

    Example:
        ```python
        from ffmpeg_wrap import probe

        result = probe("video.mkv")
        for stream in result.streams:
            secs = stream.duration_seconds()
            if secs is not None:
                print(f"stream {stream.index}: {secs:.1f}s")
        ```
    """
    return _parse_duration(self.duration)

ffmpeg_wrap.Format

Bases: Struct

The format/container section from ffprobe JSON output.

Decoded from the top-level "format" key of ffprobe's JSON output. All fields are optional because ffprobe may omit them for certain inputs.

Attributes:

Name Type Description
filename str | None

Resolved path of the probed file as reported by ffprobe.

nb_streams int | None

Total number of streams in the container.

nb_programs int | None

Number of programs (relevant for MPEG-TS and similar).

format_name str | None

Short format/muxer name (e.g. "matroska,webm").

format_long_name str | None

Human-readable format name (e.g. "Matroska / WebM").

start_time str | None

Container start time in decimal seconds (e.g. "0.000000"). None when not reported.

duration str | None

Container duration in decimal seconds (e.g. "3600.123000"). None or "N/A" when unavailable.

size str | None

File size in bytes as a string (e.g. "1234567890").

bit_rate str | None

Overall container bit rate in bits/s as a string.

probe_score int | None

ffprobe format-detection confidence score (0-100).

tags dict[str, str] | None

Container-level metadata tags (e.g. {"title": "My Movie", "encoder": "Lavf58.76.100"}).

Example
from ffmpeg_wrap import probe

result = probe("video.mkv")
fmt = result.format
if fmt:
    print(fmt.format_name, fmt.duration_seconds())

duration_seconds

duration_seconds() -> float | None

Return the container duration as float seconds.

Returns None when duration is missing or non-numeric (e.g. "N/A"); the raw duration string is preserved.

Example
from ffmpeg_wrap import probe

result = probe("video.mkv")
if result.format:
    secs = result.format.duration_seconds()
    print(f"Duration: {secs:.1f}s" if secs else "Unknown duration")
Source code in src/ffmpeg_wrap/_probe.py
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
def duration_seconds(self) -> float | None:
    """Return the container ``duration`` as ``float`` seconds.

    Returns ``None`` when ``duration`` is missing or non-numeric
    (e.g. ``"N/A"``); the raw ``duration`` string is preserved.

    Example:
        ```python
        from ffmpeg_wrap import probe

        result = probe("video.mkv")
        if result.format:
            secs = result.format.duration_seconds()
            print(f"Duration: {secs:.1f}s" if secs else "Unknown duration")
        ```
    """
    return _parse_duration(self.duration)

ffmpeg_wrap.CodecType

Bases: StrEnum

The known ffprobe codec_type values.

Used for the typed predicates on :class:Stream (is_video etc.). Note that :attr:Stream.codec_type stays typed str | None rather than this enum: ffmpeg can report codec_type values outside this set, and decoding into a strict enum would raise msgspec.ValidationError on such files. Compare against these members instead of retyping the field.

Attributes:

Name Type Description
VIDEO

Represents a video stream ("video").

AUDIO

Represents an audio stream ("audio").

SUBTITLE

Represents a subtitle stream ("subtitle").

DATA

Represents a data stream ("data").

ATTACHMENT

Represents an attachment stream ("attachment").

Example
from ffmpeg_wrap import CodecType, probe

result = probe("video.mkv")
videos = [s for s in result.streams if s.codec_type == CodecType.VIDEO]

Encoder discovery

ffmpeg_wrap.encoders

encoders(ffmpeg_path: str = 'ffmpeg') -> frozenset[str]

Return the set of encoder names reported by ffmpeg -encoders.

The result is cached per ffmpeg_path for the lifetime of the process. Use :func:has_encoder for a single-name membership check.

Parameters:

Name Type Description Default
ffmpeg_path str

Path to the ffmpeg executable.

'ffmpeg'

Returns:

Type Description
frozenset[str]

A frozenset of encoder names (e.g. "libx264", "h264_nvenc").

Raises:

Type Description
FFmpegError

If ffmpeg cannot be run or exits non-zero.

Example
import ffmpeg_wrap as ffmpeg

available = ffmpeg.encoders()
print("libx264" in available)   # True if ffmpeg was compiled with it
print(sorted(available)[:5])    # first five encoder names alphabetically
Source code in src/ffmpeg_wrap/_encoders.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def encoders(ffmpeg_path: str = "ffmpeg") -> frozenset[str]:
    """Return the set of encoder names reported by ``ffmpeg -encoders``.

    The result is cached per ``ffmpeg_path`` for the lifetime of the process.
    Use :func:`has_encoder` for a single-name membership check.

    Args:
        ffmpeg_path: Path to the ffmpeg executable.

    Returns:
        A frozenset of encoder names (e.g. ``"libx264"``, ``"h264_nvenc"``).

    Raises:
        FFmpegError: If ffmpeg cannot be run or exits non-zero.

    Example:
        ```python
        import ffmpeg_wrap as ffmpeg

        available = ffmpeg.encoders()
        print("libx264" in available)   # True if ffmpeg was compiled with it
        print(sorted(available)[:5])    # first five encoder names alphabetically
        ```
    """
    cached = _ENCODERS_CACHE.get(ffmpeg_path)
    if cached is not None:
        return cached

    cmd = _build_encoders_cmd(ffmpeg_path)
    try:
        result = subprocess.run(cmd, capture_output=True, check=True, text=True)
    except subprocess.CalledProcessError as e:
        stderr_text = e.stderr if isinstance(e.stderr, str) else None
        err_msg = stderr_text or str(e)
        logger.error(f"ffmpeg -encoders failed: {err_msg}")
        raise _build_ffmpeg_error(
            f"ffmpeg -encoders error: {err_msg}",
            stderr=stderr_text,
            returncode=e.returncode,
            cmd=cmd,
        ) from e
    except OSError as e:
        logger.error(f"ffmpeg could not be executed: {e}")
        raise _build_ffmpeg_error(f"ffmpeg could not be executed: {e}", cmd=cmd) from e

    parsed = _parse_encoders(result.stdout)
    _ENCODERS_CACHE[ffmpeg_path] = parsed
    return parsed

ffmpeg_wrap.has_encoder

has_encoder(name: str, ffmpeg_path: str = 'ffmpeg') -> bool

Return whether name is an available ffmpeg encoder.

Parameters:

Name Type Description Default
name str

Encoder name to check (e.g. "h264_nvenc").

required
ffmpeg_path str

Path to the ffmpeg executable.

'ffmpeg'

Returns:

Type Description
bool

True iff name appears in :func:encoders.

Raises:

Type Description
FFmpegError

If ffmpeg cannot be run or exits non-zero.

Example
import ffmpeg_wrap as ffmpeg

if ffmpeg.has_encoder("h264_nvenc"):
    codec = "h264_nvenc"
else:
    codec = "libx264"
ffmpeg.input("in.mkv").output("out.mp4").codec("v", codec).run()
Source code in src/ffmpeg_wrap/_encoders.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def has_encoder(name: str, ffmpeg_path: str = "ffmpeg") -> bool:
    """Return whether ``name`` is an available ffmpeg encoder.

    Args:
        name: Encoder name to check (e.g. ``"h264_nvenc"``).
        ffmpeg_path: Path to the ffmpeg executable.

    Returns:
        ``True`` iff ``name`` appears in :func:`encoders`.

    Raises:
        FFmpegError: If ffmpeg cannot be run or exits non-zero.

    Example:
        ```python
        import ffmpeg_wrap as ffmpeg

        if ffmpeg.has_encoder("h264_nvenc"):
            codec = "h264_nvenc"
        else:
            codec = "libx264"
        ffmpeg.input("in.mkv").output("out.mp4").codec("v", codec).run()
        ```
    """
    return name in encoders(ffmpeg_path)

Filters

ffmpeg_wrap.filter_arg_escape

filter_arg_escape(value: str) -> str

Escape a value for safe embedding in a filtergraph option.

ffmpeg parses a filtergraph at two levels, and a value pasted into a filter option (such as a subtitles= path) must survive both:

  • The filter-option parser, where : separates options and \/' are escaping/quoting characters. A raw colon (every Windows drive path, and POSIX paths containing one) is otherwise read as an option separator, so ffmpeg mis-parses the tail (e.g. original_size). This level is handled by backslash-escaping \, ' and :.
  • The filtergraph parser, where ' quotes and [],; are special. This level is handled by wrapping the whole value in single quotes; a literal ' is emitted via the close/escape/reopen idiom ('\'').

Single-quote wrapping alone is not sufficient: the colon still reaches the filter-option parser and splits the value, so the backslash escaping is required as well. The result parses back to the original value.

Typical use is building a subtitles= (or any path-bearing) filter.

Example
from ffmpeg_wrap import filter_arg_escape, input

path = "/media/My Film: Director's Cut/subs.srt"
graph = f"subtitles={filter_arg_escape(path)}"
input("in.mkv").filter_complex(graph).output("out.mp4").run()

Parameters:

Name Type Description Default
value str

The raw string to escape (e.g. a filesystem path).

required

Returns:

Type Description
str

The escaped, single-quote-wrapped token.

Source code in src/ffmpeg_wrap/_filters.py
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
def filter_arg_escape(value: str) -> str:
    r"""Escape a value for safe embedding in a filtergraph option.

    ffmpeg parses a filtergraph at two levels, and a value pasted into a filter
    option (such as a ``subtitles=`` path) must survive both:

    * The *filter-option* parser, where ``:`` separates options and ``\``/``'``
      are escaping/quoting characters. A raw colon (every Windows drive path,
      and POSIX paths containing one) is otherwise read as an option separator,
      so ffmpeg mis-parses the tail (e.g. ``original_size``). This level is
      handled by backslash-escaping ``\``, ``'`` and ``:``.
    * The *filtergraph* parser, where ``'`` quotes and ``[],;`` are special.
      This level is handled by wrapping the whole value in single quotes; a
      literal ``'`` is emitted via the close/escape/reopen idiom (``'\''``).

    Single-quote wrapping alone is **not** sufficient: the colon still reaches
    the filter-option parser and splits the value, so the backslash escaping is
    required as well. The result parses back to the original ``value``.

    Typical use is building a ``subtitles=`` (or any path-bearing) filter.

    Example:
        ```python
        from ffmpeg_wrap import filter_arg_escape, input

        path = "/media/My Film: Director's Cut/subs.srt"
        graph = f"subtitles={filter_arg_escape(path)}"
        input("in.mkv").filter_complex(graph).output("out.mp4").run()
        ```

    Args:
        value: The raw string to escape (e.g. a filesystem path).

    Returns:
        The escaped, single-quote-wrapped token.
    """
    # Filter-option level: escape backslash first, then quote and colon.
    escaped = value.replace("\\", "\\\\").replace("'", "\\'").replace(":", "\\:")
    # Filtergraph level: single-quote wrap, emitting any embedded ' literally.
    return "'" + escaped.replace("'", "'\\''") + "'"

Errors

ffmpeg_wrap.FFmpegError

FFmpegError(message: str, *, stderr: str | None = None, returncode: int | None = None, cmd: list[str] | None = None)

Bases: Exception

Custom exception for FFmpeg errors.

Carries structured introspection from the underlying process failure so callers can branch on the exit code or inspect stderr without re-parsing str(e). The message remains args[0] so str(e) is unchanged.

Attributes:

Name Type Description
stderr

Decoded stderr from the failed process, or None if not captured. Present on both :meth:~ffmpeg_wrap.FFmpeg.run failures and :func:~ffmpeg_wrap.probe / :func:~ffmpeg_wrap.validate failures.

returncode

Process exit code, or None when the process could not be launched at all (e.g. executable not found).

cmd

The exact command list that was executed, or None.

Example
import ffmpeg_wrap as ffmpeg

try:
    ffmpeg.input("missing.mkv").output("out.mp4").run()
except ffmpeg.FFmpegError as e:
    print("exit code:", e.returncode)
    print("command:", e.cmd)
    print("stderr:", e.stderr)
Source code in src/ffmpeg_wrap/_errors.py
33
34
35
36
37
38
39
40
41
42
43
44
def __init__(
    self,
    message: str,
    *,
    stderr: str | None = None,
    returncode: int | None = None,
    cmd: list[str] | None = None,
) -> None:
    super().__init__(message)
    self.stderr = stderr
    self.returncode = returncode
    self.cmd = cmd