Video Encoding Presets
Video Service supports FFmpeg argument templates and HandBrake preset files. A video job selects ffmpeg or handbrake, provides at least one input and output URI, and names a configuration file with config_name and config_source.
FFmpeg configuration
An FFmpeg configuration is JSON with a name, description, and ordered argument array:
{
"name": "h264-standard",
"description": "H.264 with AAC audio",
"template": [
"-i", "{{ input 0 }}",
"-c:v", "libx264",
"-crf", "23",
"-c:a", "aac",
"-movflags", "+faststart",
"{{ output 0 }}"
]
}The array contains arguments only; do not include the ffmpeg executable. Each array element is parsed and rendered independently, preserving argument boundaries.
Go template functions
Each template array element uses Go text/template syntax. Video Service renders every element independently after it stages the job's input and output files, then passes the rendered values to FFmpeg as separate arguments. Template expressions can nest function calls, such as using a smart tag as the chapter number.
FFmpeg templates provide these four functions:
| Function | Syntax | Returns |
|---|---|---|
input | {{ input 0 }} | The staged input path at a zero-based index. |
output | {{ output 0 }} | The staged output path at a zero-based index. |
tag | {{ tag "title" }} | The value for a key in the queued job's smart-tag snapshot. |
chapter | {{ chapter 0 2 }} | The selected input's one-based chapter start as an FFmpeg offset in seconds. |
input and output indexes follow the order of the job's input_source and output_source arrays. They refer to staged temporary paths, not the source or destination URIs. The former .Input0 and .Output0 fields are obsolete.
Rendering fails before FFmpeg runs when an input or output index is missing, a requested tag is absent, or a chapter cannot be resolved. A template parse or execution error identifies the argument template that failed.
Multiple inputs and outputs
FFmpeg accepts one or more inputs and outputs:
{
"name": "combine-video-audio",
"description": "Copy video from input 0 and audio from input 1",
"template": [
"-i", "{{ input 0 }}",
"-i", "{{ input 1 }}",
"-map", "0:v:0",
"-map", "1:a:0",
"-c", "copy",
"{{ output 0 }}"
]
}Video Service stages every source, creates temporary output files with the requested extensions, runs FFmpeg, then writes each completed temporary file to its output provider.
Smart tags inside FFmpeg
Use the tag function in preset arguments:
{
"name": "metadata",
"description": "Copy streams and set title metadata",
"template": [
"-i", "{{ input 0 }}",
"-map", "0",
"-c", "copy",
"-metadata", "title={{ tag \"title\" }}",
"{{ output 0 }}"
]
}See Smart Tags for the difference between job-parameter substitution and FFmpeg rendering.
Chapter-aware trimming
The chapter function has the form {{ chapter inputIndex chapterNumber }}. The input index is zero-based; the chapter number is one-based and must be a positive integer. It runs ffprobe against the selected staged input and returns the matching chapter's start as a non-negative seconds offset that FFmpeg accepts. For example, a start time of 900.000000 renders as 900.
The chapter number may be a literal or the output of another template function. This preset starts at the chapter stored in the queued start_chapter smart tag:
{
"name": "from-selected-chapter",
"description": "Start at the chapter stored in start_chapter",
"template": [
"-ss", "{{ chapter 0 (tag \"start_chapter\") }}",
"-i", "{{ input 0 }}",
"-c", "copy",
"{{ output 0 }}"
]
}Rendering fails if the input index is invalid, the chapter number is not a positive integer, ffprobe cannot run or returns invalid chapter data, the input has no chapter metadata, or the requested chapter is out of range. If the chapter number comes from tag, a missing tag also fails rendering.
HandBrake configuration
HandBrake uses an exported HandBrake preset JSON file. The worker invokes HandBrakeCLI with the selected preset name.
HandBrake jobs require exactly one input and one output. They do not use the FFmpeg argument-template functions.
Video job fields
{
"input_source": ["file:///media/source.mkv"],
"output_source": ["file:///media/output.mp4"],
"converter_type": "ffmpeg",
"config_name": "h264-standard",
"config_source": "file:///etc/mediamoth/presets/h264-standard.json"
}converter_type is case-sensitive and must be ffmpeg or handbrake. All five user-visible fields are required by the current video parameter schema.