Video processing and creation

# Control video and audio codecs with Transloadit

Choose video and audio presets, override supported FFmpeg parameters, select an \`ffmpeg\_stack\` major version, and test codec outputs without operating an encoding fleet. A major selector resolves to the highest available build in that major, while the deprecated \`v5\` selector is accepted for backward compatibility and transparently upgraded to \`v6\`.

Published August 31, 2026

## Key takeaways

* Start with a preset when it already matches the playback target, then override only the settings the product has deliberately chosen.
* Treat the container, video codec, audio codec, profile, rate control, pixel format, and filters as separate compatibility decisions.
* Select an `ffmpeg_stack` deliberately and retest fixtures before changing it, because encoder and filter behavior can vary between releases.

Codec control is rarely an all-or-nothing choice between a fixed preset and a raw FFmpeg command. Transloadit lets a Template start from maintained video or audio presets, replace individual supported FFmpeg options, or use the empty preset when the workflow must define the output settings itself.

## In this guide

1. [Start with the compatibility target, not a favorite codec](#control-video-audio-codecs-section-1)
2. [Layer supported overrides on a maintained preset](#control-video-audio-codecs-section-2)
3. [Control video codec and rate-control settings deliberately](#control-video-audio-codecs-section-3)
4. [Build an explicit audio output with the empty preset](#control-video-audio-codecs-section-4)
5. [Use FFmpeg flexibility inside the managed safety boundary](#control-video-audio-codecs-section-5)
6. [Create codec variants as independent Assembly Steps](#control-video-audio-codecs-section-6)
7. [Version the Template and test the produced media](#control-video-audio-codecs-section-7)

## What matters most

* Use `preset: "empty"` with the explicit `ffmpeg_stack: "v7"` selector recommended by the Robot documentation for encoding settings without preset defaults.
* Create alternate codecs as independent Steps, then validate the produced metadata and playback instead of treating a successful encode as acceptance.

## Start with the compatibility target, not a favorite codec

Write down where the result must play or be edited before choosing parameters. A browser delivery file, an archival master, a podcast download, and an interchange file have different requirements even when they begin with the same source. Record the required container, video and audio codecs, profiles, channel layout, dimensions, frame rate, sample rate, captions, and maximum delivery size for each target.

Keep containers and codecs separate in that matrix. MP4, WebM, Ogg, and MOV describe how streams and metadata are packaged; H.264, HEVC, VP9, AAC, Opus, and FLAC describe how individual streams are represented. A player can recognize a container and still reject one of its streams, so extension-only testing cannot establish compatibility.

### Playback target

Name the actual browsers, devices, editors, or distribution specifications that decide whether an output is acceptable.

### Stream policy

Specify video and audio requirements independently so a valid container does not hide an unsupported stream.

### Acceptance evidence

Combine metadata inspection with playback on representative clients instead of approving a file from its extension alone.

## Layer supported overrides on a maintained preset

A preset is a versioned collection of encoding settings for a common target. `/video/encode` and `/audio/encode` merge the entries in the `ffmpeg` object over the selected preset, so an explicit option replaces the corresponding preset value. This is usually the smallest maintainable policy: inherit the established baseline and record only the codec, profile, rate control, filter, or container behavior the product has intentionally changed.

Do not copy every resolved preset option into a Template merely to make it look explicit. That creates a private preset that the application must understand and maintain. Instead, name the preset, keep the override object focused, and inspect the result. If the workflow needs to avoid preset-supplied FFmpeg defaults, choose `preset: "empty"`, keep the documentation’s recommended `ffmpeg_stack: "v7"` selector explicit, and supply the required format and codecs.

### Preset baseline

Provides a documented starting point for a common output without requiring the Template to restate every FFmpeg option.

### Override object

Records only the supported settings that deliberately differ, and those values take precedence over the preset.

### Empty preset

Makes the deliberate absence of preset-supplied encoding defaults visible to future maintainers instead of relying on an omitted default.

## Control video codec and rate-control settings deliberately

The video example begins with the `web/mp4/1080p` preset, selects the recommended `v7` stack line, and then overrides the H.264 `level` constraint plus the product-specific rate-control settings in `ffmpeg`. JSON keys omit the command-line dash: `level`, `crf`, `maxrate`, and `bufsize` become FFmpeg output options. The preset continues to provide its H.264 (`libx264`) video codec, `high` profile, `yuv420p` pixel format, AAC (`libfdk_aac`) audio encoder, MP4 container, and `movflags: "+faststart"`; those inherited values do not need to be restated.

The values are an example policy, not universal quality recommendations. CRF, a bitrate cap, encoder profile, pixel format, source complexity, and playback constraints interact. Test text, animation, grain, motion, dark scenes, and ordinary talking-head material at the intended resolution. Inspect the output for both visual quality and decoder compatibility before promoting the settings.

Start from a web preset and override selected FFmpeg options

```
{
  "steps": {
    ":original": {
      "robot": "/upload/handle"
    },
    "web_video": {
      "use": ":original",
      "robot": "/video/encode",
      "ffmpeg_stack": "v7",
      "preset": "web/mp4/1080p",
      "ffmpeg": {
        "level": "4.1",
        "crf": 21,
        "maxrate": "5M",
        "bufsize": "10M"
      },
      "result": true
    }
  }
}
```

### Rate control and conformance

CRF targets a quality level while `maxrate` and `bufsize` cap the bitrate and smooth bursts; useful values depend on the encoder and delivery target. `level` is separate: it sets an H.264 decoder-conformance ceiling that bounds frame size, macroblock rate, and a maximum bitrate, but it is not the target or cap bitrate set through CRF, maxrate, and bufsize.

### Preset compatibility baseline

The selected preset supplies the profile and pixel format, which can matter as much as the codec name to older hardware and browser decoders.

### Inherited fast start

The preset supplies the MP4 movflags option for progressive download, but that does not replace adaptive streaming or a CDN.

## Build an explicit audio output with the empty preset

The audio example uses `preset: "empty"` with the documentation’s recommended `ffmpeg_stack: "v7"` selector, so the Template supplies its encoding settings instead of inheriting them from a preset. Keep that selector explicit rather than relying on the implicit `v6` runtime fallback. The example chooses the Ogg container, the Opus encoder, a target bitrate, a 48 kHz sample rate, two output channels, and a simple high-pass filter. `/audio/encode` accepts audio files and video files that contain an audio stream, which makes the same Step useful for audio-only uploads and soundtrack extraction workflows.

The empty preset removes preset-supplied encoding defaults rather than every Robot-added argument. `/audio/encode` still adds a default stream map so embedded cover art is not encoded as audio, and it derives the format or bitrate from the input when either value is omitted. This example states both values explicitly in the `ffmpeg` object with `f` and `b:a`.

This audio example uses the `ffmpeg` object because it demonstrates explicit empty-preset encoding settings. For simple conversions that only change bitrate or sample rate, prefer the documented top-level `bitrate` and `sample_rate` Robot parameters instead. They take integers in bits per second and Hertz—for example, `256000` and `48000`—while `ffmpeg.b:a` accepts strings such as `"128k"`. The Audio Encode Robot applies the top-level values after the `ffmpeg` merge, so they override conflicting `b:a` or `ar` values. Avoid specifying the same concern in both places: one authoritative value is easier to review, test, and change.

Define an Ogg Opus output without inherited preset options

```
{
  "steps": {
    ":original": {
      "robot": "/upload/handle"
    },
    "podcast_audio": {
      "use": ":original",
      "robot": "/audio/encode",
      "ffmpeg_stack": "v7",
      "preset": "empty",
      "ffmpeg": {
        "f": "ogg",
        "codec:a": "libopus",
        "b:a": "128k",
        "ar": 48000,
        "ac": 2,
        "af": "highpass=f=80"
      },
      "result": true
    }
  }
}
```

### Explicit container

The `f` option selects the output container format through its muxer, independently from `codec:a`, so the container and audio codec are chosen separately.

### Channel policy

The `ac` option makes the requested output channel count visible instead of inheriting an unexpected source layout.

### Audio filter

An `af` chain can apply supported FFmpeg filters, but each filter still needs representative listening and level checks.

## Use FFmpeg flexibility inside the managed safety boundary

The `ffmpeg` value is a structured option object, not a shell command. Transloadit turns its keys and values into arguments for the selected managed FFmpeg stack. That boundary provides substantial control without exposing the worker host. It also means a Template cannot install a different FFmpeg build, add an unavailable encoder library, or assume every option from the newest upstream documentation exists in every stack.

User-controlled options pass through safety checks. Filter scripts and filter directives that read local files are rejected, including file-based subtitle, font, and text inputs. Use dedicated Robot parameters and Robots where available, such as `watermark_url`, `/video/subtitle`, or inline `drawtext` text with an available font family. Treat a rejection as a boundary to redesign around, not a reason to hide another command inside a filter string.

### No shell syntax

Pass option names and values as JSON so quoting, interpolation, and validation stay within Assembly Instructions.

### Stack capabilities

An encoder, muxer, or filter must be compiled into the selected managed stack before an otherwise valid option can work.

### Safe file handling

Use declared inputs and purpose-built Robot parameters instead of asking an FFmpeg filter to open worker-local paths.

## Create codec variants as independent Assembly Steps

One source Step can feed several independent encoding Steps. The example creates H.264/MP4 and VP9/WebM video renditions plus AAC and Opus audio outputs. These branches do not need application-side loops or repeated uploads: their shared `use` value declares the dependency, and each encode can run after the source file is available.

Give each Step a name that describes the output contract rather than an implementation detail likely to change. An application may care about `browser_fallback` and `modern_web` more than today’s encoder names. Mark only intentional outputs as results, export every durable rendition, and retain the Assembly ID so an operator can connect a rejected file to the exact Step and settings that produced it.

Create four independent codec outputs from one source

```
{
  "steps": {
    ":original": {
      "robot": "/upload/handle"
    },
    "browser_fallback": {
      "use": ":original",
      "robot": "/video/encode",
      "ffmpeg_stack": "v7",
      "preset": "web/mp4/720p",
      "result": true
    },
    "modern_web": {
      "use": ":original",
      "robot": "/video/encode",
      "ffmpeg_stack": "v7",
      "preset": "web/webm/720p",
      "result": true
    },
    "download_aac": {
      "use": ":original",
      "robot": "/audio/encode",
      "ffmpeg_stack": "v7",
      "preset": "aac",
      "result": true
    },
    "download_opus": {
      "use": ":original",
      "robot": "/audio/encode",
      "ffmpeg_stack": "v7",
      "preset": "opus",
      "result": true
    }
  }
}
```

### Shared source

Sibling Steps read the same uploaded or imported file without requiring the application to transfer it again.

### Independent results

Each codec branch has its own status, metadata, error, and result entries for precise application handling.

### Purpose-based names

Stable contract names let the underlying codec policy evolve without forcing every consumer to rename its field.

## Version the Template and test the produced media

Keep the preset, FFmpeg overrides, and `ffmpeg_stack` in a saved Template so production jobs use one reviewable policy. Production Robot documentation recommends the `v7` major selector, while a request that reaches the API runtime without a selector uses its implicit `v6` fallback, so the examples save `v7` explicitly. A major selector resolves to the highest available build within that major—for example, `v7` resolves to a `v7` build rather than `v8`. The deprecated `v5` selector is no longer a valid stack and now resolves to `v6`. Exercise stack, preset, or override changes beside the current policy before routing all new jobs to them.

A successful Assembly proves that the command completed, not that the output meets the product contract. Read result metadata for codecs, dimensions, duration, frame rate, channel count, and sample rate, then play the files on representative clients. Keep known-good and difficult fixtures, compare file size and processing cost, check audio/video synchronization and seeking, and preserve a fallback while a changed policy rolls out.

### Controlled Template

Keeps the codec policy together and prevents individual callers from drifting into undocumented combinations.

### Fixture matrix

Covers the source codecs, resolutions, frame rates, channels, metadata, and damaged inputs production actually receives.

### Playback checks

Prove decoding, seeking, timing, and quality on target clients instead of relying on an encoder exit status.

## Technical details worth knowing

* The `ffmpeg` parameter is an object whose entries are merged on top of the selected preset; values supplied in that object take precedence over corresponding preset options.
* When a caller supplies an `ffmpeg` object without naming a preset, the default video or audio preset is not applied, which avoids inheriting settings that would then need to be overridden.
* The explicit `empty` preset removes preset-supplied encoding defaults, but the Robot may still add stream selection or input-derived fallbacks when required values are omitted.
* The `ffmpeg_stack` major selector resolves within the requested major: `v7` selects the highest available `v7` build and never jumps to `v8`. Supported majors are `v6`, `v7`, and `v8`.
* Production Robot documentation currently recommends `v7`, so every example sets `ffmpeg_stack: "v7"` explicitly; a request that reaches the API runtime without a selector falls back to `v6` instead.
* The deprecated `v5` selector is accepted for backward compatibility but is no longer a runtime stack, so a request naming it is transparently upgraded to `v6`; `v6`, `v7`, and `v8` each run within their requested major.
* A container option such as `f: "mp4"` or `f: "ogg"` does not choose every stream codec; video and audio codecs are controlled independently. The stream-codec options accept either the long form (`codec:v`, `codec:a`) or the equivalent short FFmpeg aliases (`c:v`, `c:a`); maintained presets use the short forms, and the Robot treats the two spellings as interchangeable.
* The Audio Encode Robot also exposes top-level integer `bitrate` and `sample_rate` parameters, measured in bits per second and Hertz. The `ffmpeg` object covers codec, format, channel, filter, and other supported options, and accepts values such as `"128k"` for `b:a`.
* FFmpeg option names are JSON keys without a leading dash, so the command-line option `-movflags +faststart` is represented as `"movflags": "+faststart"` inside the object.
* Transloadit validates user-controlled FFmpeg options against a managed safety policy; the selected stack must also contain the requested encoder, muxer, and filter.
* Independent Steps that use the same uploaded or imported file can encode different codec variants without another upload, and each Step appears separately in Assembly Status and results.

## A practical approach

1. 1\
   Define the players, devices, editors, or distribution systems that every output must support.
2. 2\
   Choose the closest preset and record only the supported FFmpeg overrides needed for that target.
3. 3\
   Run a fixture matrix that covers real input codecs, channels, frame rates, dimensions, and damaged files.
4. 4\
   Store the Template, stack choice, acceptance checks, and approved outputs as one controlled release.

A four-stage media workflow

## When Transloadit is useful

Use `/video/encode` and `/audio/encode` when a workflow needs a documented preset, selected codec and container controls, filters, or several renditions from one source. Run authorization and output-approval logic in your application, not inside the encode Step, and export approved results to durable storage.

## Architecture boundary

The `ffmpeg` parameter exposes supported FFmpeg options inside managed encoding Robots; it is not shell access, a way to install another encoder build, or a guarantee that every option from every upstream FFmpeg release is available. Options outside the managed safety boundary are rejected.

## Frequently asked questions

### Can I pass any FFmpeg option through the `ffmpeg` object?

No. The object accepts supported FFmpeg options, but Transloadit validates them before execution and blocks forms outside the managed safety boundary. The selected stack must also contain the requested encoder, muxer, and filter. Test the exact option set against representative inputs rather than assuming an example for another FFmpeg build will transfer unchanged.

### When should I use a preset instead of `preset: "empty"`?

Use a named preset when it provides the intended container, codec family, dimensions, and compatibility baseline. Add a small `ffmpeg` object when only a few choices differ. Use `preset: "empty"` when inheriting preset behavior would obscure or conflict with an explicit encoding policy, and keep the documentation’s recommended `ffmpeg_stack: "v7"` selector explicit.

### How should I choose an `ffmpeg_stack`?

Use the current recommended stack unless the workflow requires another supported major selector, and keep that choice in the saved Template. Production Robot documentation recommends `v7`, so the examples select it explicitly instead of depending on the API runtime’s implicit `v6` fallback. Before moving a production workflow to another stack, run the same source fixtures, compare output metadata and playback, and deploy the changed Template as a controlled release.

### Does choosing MP4 or Ogg also choose the codecs?

No. A container packages streams, while codecs define how those video and audio streams are encoded. An MP4 file can still carry a codec that a target player does not support. Specify and inspect the video codec, audio codec, profile, pixel format, and other playback requirements separately from the container.

### How do I create several codec variants from one input?

Create sibling `/video/encode` or `/audio/encode` Steps that all use the same source Step. Give each Step a stable purpose-based name, mark the selected outputs as results or export them, and validate each independent variant against its own playback target.

## Build the workflow

Move from the concept to a tested Assembly with Robot documentation and working demos.

### Relevant Robots

* [/video/encode](/docs/robots/video-encode.md)
* [/audio/encode](/docs/robots/audio-encode.md)
* [Configure the Video Encode Robot](/docs/robots/video-encode.md)
* [Configure the Audio Encode Robot](/docs/robots/audio-encode.md)
* [Browse video presets](/docs/presets/video.md)
* [Browse audio presets](/docs/presets/audio.md)
* [Read the FFmpeg option reference⁠](https://ffmpeg.org/ffmpeg-doc.html)
* [Read the API documentation](/docs.md)
* [Explore working demos](/demos.md)
* [Create a free workspace](/c/signup/)

Video processing and creation

## Continue with related guides

* [A beginner’s guide to FFmpeg with Python](/guides/ffmpeg-with-python.md)\
  A safe introduction to running FFmpeg from Python and deciding when to move the workload to a managed media pipeline.
* [HTML video in production: 10 practical checks](/guides/html-video-production-checklist.md)\
  Ten production checks for HTML video, from source selection and captions to poster images and preprocessing.
* [Media automation: from upload to reliable output](/guides/media-automation.md)\
  Automate repeatable media intake, transformation, validation, and export while preserving observability and control.
* [AVI vs. MOV: how to choose the right container](/guides/avi-vs-mov.md)\
  Compare AVI and MOV by codec support, metadata, editing workflows, compatibility, and delivery goals.
* [22 types of marketing videos and how to produce them well](/guides/marketing-video-types.md)\
  A practical map of 22 marketing video formats and the production decisions they share.
* [A practical architecture for video auto-tagging](/guides/video-auto-tagging.md)\
  Understand video auto-tagging as a sampled analysis workflow with explicit taxonomies, confidence, and human review.
