# `FFix.Graph`
[🔗](https://github.com/akash-akya/ffix/blob/v0.1.0/lib/ffix/graph.ex#L1)

Filtergraph data model.

A graph is a pure Elixir value containing input references, filter nodes,
exported streams, terminal sinks, and graph-level settings. It is serialized
to ffmpeg syntax only at `FFix.to_filtergraph/1`, `FFix.to_argv/1`, or `FFix.run/1`.

Most users build filter pipelines inside `FFix.command/3` callbacks:

    FFix.command(
      "input.mp4",
      fn src ->
        src[:video] |> FFix.Filter.scale(w: 1280, h: -1)
      end,
      fn video, src ->
        FFix.output("out.mp4", video: video, audio: src[:audio])
      end
    )

Use `input/2` when building a reusable graph outside a command callback:

    video = FFix.Graph.input(0, :video)

    graph =
      FFix.graph(
        outputs: [
          preview: video |> FFix.Filter.scale(w: 320, h: -1)
        ]
      )

`graph[:name]` and `graph[index]` return exported streams from a `%FFix.Graph{}`
value. In `FFix.command/3`, callback input and graph output shapes are preserved,
so use normal Elixir access for the shape you return.

## Selectors

  * `:input` maps the whole input, like `-map 0`
  * `:video` maps/selects the video stream class, like `0:v`
  * `:audio` maps/selects the audio stream class, like `0:a`
  * `{:video, 1}` or `{:audio, 1}` selects a stream-class index
  * `{:raw, "s?"}` keeps an ffmpeg selector escape hatch

# `input`

```elixir
@spec input(input_id(), input_selector()) :: FFix.Stream.t()
```

Builds a stream reference for an ffmpeg command input.

Use this outside command callbacks, where callback input values are not
available. Inside `FFix.command/3`, prefer the input value you received, such as
`src[:video]` or `inputs[:src][:video]`.

## Examples

    video = FFix.Graph.input(0, :video)
    audio = FFix.Graph.input(0, :audio)

    graph =
      FFix.graph(
        outputs: [
          main: video |> FFix.Filter.scale(w: 1280, h: -1)
        ]
      )

    FFix.Command.new(
      inputs: [FFix.input("input.mp4")],
      graph: graph,
      outputs: [FFix.Command.output("out.mp4", [graph[:main], audio])]
    )

# `input_raw`

```elixir
@spec input_raw(String.t()) :: FFix.Stream.t()
```

Builds a stream reference from a raw ffmpeg input selector.

This is an escape hatch for selectors that do not have a structured form yet.
It currently expects an indexed selector such as `"0:v"` or `"1:s?"`.

# `export`

```elixir
@spec export(t(), FFix.Graph.Export.name() | non_neg_integer()) ::
  FFix.Graph.Export.t() | nil
```

Looks up a graph export by name or zero-based position.

Returns `nil` when the export does not exist.

# `export!`

```elixir
@spec export!(t(), FFix.Graph.Export.name() | non_neg_integer()) ::
  FFix.Graph.Export.t()
```

Looks up a graph export by name or position, raising when it is missing.

# `exports`

```elixir
@spec exports(t()) :: [FFix.Graph.Export.t()]
```

Returns graph exports in declaration order.

# `parse!`

```elixir
@spec parse!(String.t()) :: t()
```

Parses a filtergraph string into `%FFix.Graph{}`.

The parser is pragmatic: it targets graphs produced by `FFix` and common
ffmpeg filtergraph syntax. It is not meant to accept every hand-written
filtergraph form.

## Examples

    graph = FFix.Graph.parse!("[0:v]scale=w=320:h=-1[preview]")
    FFix.to_filtergraph(graph)
    #=> "[0:v]scale=w=320:h=-1[preview];"

# `to_filtergraph`

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

Serializes a graph to ffmpeg filtergraph syntax.

Prefer `FFix.to_filtergraph/1` when you want validation before serialization.

# `input_id`

```elixir
@type input_id() :: non_neg_integer() | atom() | String.t() | reference()
```

# `input_selector`

```elixir
@type input_selector() ::
  :input
  | :video
  | :audio
  | {:video, non_neg_integer()}
  | {:audio, non_neg_integer()}
  | {:raw, String.t()}
```

# `node_id`

```elixir
@type node_id() :: pos_integer()
```

# `setting`

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

# `t`

```elixir
@type t() :: %FFix.Graph{
  exports: [FFix.Graph.Export.t()],
  nodes: %{required(node_id()) =&gt; term()},
  order: [node_id()],
  settings: [setting()],
  terminals: [node_id()]
}
```

---

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