# `FFix.Command`
[🔗](https://github.com/akash-akya/ffix/blob/v0.2.0/lib/ffix/command.ex#L1)

The inputs, outputs, and options for one FFmpeg invocation.

Usually, build a command with `FFix.command/2`: it collects input dependencies
from your outputs. This module also supports constructing and editing command
data directly, which is useful for tools that already manage explicit inputs
and filtergraphs.

## FFmpeg options and their scope

Put options on the thing they configure:

| Setting | Where it belongs | Example |
| --- | --- | --- |
| Invocation-wide control | `FFix.command/2`, under `global:` | `n: :flag` |
| Input seeking or limits | `FFix.input/2` | `ss: 30` |
| Decoding | `FFix.Decoder` | `threads: 2` |
| Output encoding | `FFix.Encoder` | `crf: 23` |
| Container settings | `FFix.Muxer` | `movflags: [:faststart]` |
| Output duration or metadata | `FFix.output/3` | `t: 10` |

Demuxer and muxer helpers take general input/output controls in
`input_options:` and `output_options:` respectively.

General CLI option names are atoms or strings without the leading dash.
Use `:flag` for a valueless switch; `true` and `false` are values, rendered as
`1` and `0`. Lists of values are joined with `+`. Strings can carry FFmpeg's
compound syntax, and floats are written as decimal numbers.

    source = FFix.input("interview.mp4", ss: 30)
    output = FFix.output(FFix.audio(source, 0), "excerpt.wav", t: 10)
    FFix.command(output, global: [n: :flag])

Prefer encoder and muxer helpers for codec and format settings: they apply
the right stream scopes. If using raw scoped options, write the full name,
such as `"metadata:s:a:0"`. FFix rejects raw controls that conflict with
structured settings or change the mapping layout those settings depend on.
Conflict checks ignore stream scopes.
Raw option names can repeat where FFmpeg allows it; structured component
options must be specified once.

## Explicit construction

For input ordering alone, use `FFix.command(outputs, inputs: ordered_inputs)`.

    alias FFix.Command
    source = FFix.input("interview.mp4")
    output = FFix.output(FFix.audio(source, 0), "interview.wav")

    command =
      Command.new(global: [n: :flag])
      |> Command.add_input(source)
      |> Command.add_output(output)

    FFix.to_argv(command)

This path uses exactly the supplied inputs, in order. Repeated input
declarations are rejected. Selections must use the same input configuration
as the supplied declaration; see `FFix.Command.Input` for input reuse.

Commands may be incomplete while you assemble them. Call `validate!/1` to
check the finished command, or serialize it with `to_argv/1`.

# `av_option`

```elixir
@type av_option() :: {atom() | String.t(), av_value()}
```

# `av_value`

```elixir
@type av_value() :: String.t() | atom() | number()
```

# `binding`

```elixir
@type binding() :: mapping() | {atom(), mapping()}
```

# `mapping`

```elixir
@type mapping() :: FFix.Command.Mapping.t() | source()
```

# `option`

```elixir
@type option() :: {atom() | String.t(), term()}
```

# `option_callback`

```elixir
@type option_callback() :: (streams() -&gt; term())
```

# `output_av_option`

```elixir
@type output_av_option() :: {atom() | String.t(), av_value() | option_callback()}
```

# `source`

```elixir
@type source() ::
  FFix.Graph.Export.t() | FFix.Graph.StreamRef.t() | FFix.Selection.t()
```

# `stream_info`

```elixir
@type stream_info() :: %{index: non_neg_integer(), specifier: String.t()}
```

# `streams`

```elixir
@type streams() :: %{required(atom()) =&gt; stream_info()}
```

# `t`

```elixir
@type t() :: %FFix.Command{
  global_options: [option()],
  graph: FFix.Graph.t() | nil,
  inputs: [FFix.Command.Input.t()],
  outputs: [FFix.Command.Output.t()]
}
```

# `add_input`

```elixir
@spec add_input(t(), FFix.Command.Input.t()) :: t()
```

Appends an input created with `FFix.input/2` or a `FFix.Demuxer` helper.

# `add_output`

```elixir
@spec add_output(t(), FFix.Command.Output.t()) :: t()
```

Appends an output created with `FFix.output/3` or a `FFix.Muxer` helper.

# `global`

```elixir
@spec global(t(), [option()]) :: t()
```

Appends invocation-wide options, rendered before inputs. For example, `global(command, n: :flag)`.

# `graph`

```elixir
@spec graph(t(), FFix.Graph.t()) :: t()
```

Sets the command's filtergraph. See `new/1` for mapping its exports to outputs.

# `new`

```elixir
@spec new() :: t()
```

Returns an empty command for use with `add_input/2`, `graph/2`, and `add_output/2`.

# `new`

```elixir
@spec new(keyword()) :: t()
```

Builds command data from `global:`, `inputs:`, `graph:`, and `outputs:`.

Inputs and outputs are ordered lists of declarations. All options are optional
during construction. For dependency collection, use `FFix.command/2` instead.

Filtered outputs use export handles from the supplied graph's `exports` field:

    source = FFix.input("interview.mp4")
    graph = FFix.Graph.parse!("[0:v:0]hflip[picture]")
    output = FFix.output(hd(graph.exports), "mirrored.mp4")
    FFix.Command.new(inputs: [source], graph: graph, outputs: [output])

Export handles belong to their graph. Use `graph[:picture]` for ordinary filter
composition through `FFix.command/2`, which collects the whole referenced graph.

# `to_argv`

```elixir
@spec to_argv(t()) :: [String.t()]
```

Validates and returns FFmpeg arguments as a list, beginning with `"ffmpeg"`. See `FFix.to_argv/1`.

# `to_shell_string`

```elixir
@spec to_shell_string(t()) :: String.t()
```

Returns shell-quoted command text for logs and debugging. See `FFix.to_shell_string/1`.

# `validate!`

```elixir
@spec validate!(t()) :: t()
```

Checks the command's connections, input declarations, and option scopes.

Returns the original command or raises `ArgumentError`. Output callback
layouts are checked here; callback values are evaluated during serialization.
FFmpeg checks files, installed components, and format compatibility at execution.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
