Skip to content

bbeny123/felbaker

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FelBaker

A high-performance VapourSynth port of DoViBaker's core functionality.

This plugin bakes Dolby Vision Profile 7 FEL streams into PQ12 output by processing Base Layer (BL), Enhancement Layer (EL), and RPU metadata.

Features

Installation

Download a binary from Releases or build from source:

core.std.LoadPlugin("/path/to/libfelbaker.dylib")  # .so on Linux, .dll on Windows

API

Bake

core.fel.Bake(bl, el, no_resample=0, dovi_metadata=0, non_parallel=0)
Parameter Default Description
bl DoVi Base Layer clip
el DoVi Enhancement Layer clip
no_resample 0 1disable internal resampling (BL/EL dimensions/chroma must match)
dovi_metadata 0 1 – attach DoVi frame properties
non_parallel 0 1 – force single-threaded mode

Returns: RGB48 when no_resample=0 or BL/EL are 4:4:4; otherwise YUV420P12 (use ToRGB to finalize).

ToRGB

Converts YUV420P12 output from Bake(no_resample=1) to RGB48.

core.fel.ToRGB(baked, non_parallel=0)
Parameter Default Description
baked Bake(no_resample=1) output
non_parallel 0 1 – force single-threaded mode

Returns: RGB48

Examples

Using VapourSynth resizer (recommended — faster, more accurate)
import vapoursynth as vs
core = vs.core

core.std.LoadPlugin("/path/to/libffms2.dylib")     # .so on Linux, .dll on Windows
core.std.LoadPlugin("/path/to/libfelbaker.dylib")  # .so on Linux, .dll on Windows

bl = core.ffms2.Source("BL.hevc", threads=4)
el = core.ffms2.Source("EL.hevc", threads=4)

# Upscale EL to match BL resolution (e.g., 4K)
el_4k = core.resize.Spline16(el, width=3840, height=2160)

# Bake without internal resampling
baked = core.fel.Bake(bl, el_4k, no_resample=1)

# Convert to 4:4:4 before final RGB conversion
baked444 = core.resize.Spline16(baked, format=vs.YUV444P12)

core.fel.ToRGB(baked444).set_output()
Using simplified Spline16 resampler (DoViBaker-compatible)
import vapoursynth as vs
core = vs.core

core.std.LoadPlugin("/path/to/libffms2.dylib")     # .so on Linux, .dll on Windows
core.std.LoadPlugin("/path/to/libfelbaker.dylib")  # .so on Linux, .dll on Windows

bl = core.ffms2.Source("BL.hevc", threads=4)
el = core.ffms2.Source("EL.hevc", threads=4)

core.fel.Bake(bl, el).set_output()
Encoding to ProRes via FFmpeg
vspipe bake.vpy - | ffmpeg -f rawvideo -pixel_format gbrp16le \
  -colorspace rgb -color_primaries bt2020 -color_trc smpte2084 -color_range pc \
  -field_order progressive -video_size 3840x2160 -r 24000/1001 -i - \
  -c:v prores_videotoolbox -profile:v 4 \
  -colorspace bt2020nc -color_primaries bt2020 -color_trc smpte2084 \
  -an output.mov

⚙️ Adjust -video_size and -r to match the source.

Frame Properties

Property Value
_Matrix 0 (RGB)
_ColorRange 0 (Full Range)
_SceneChangePrev 1 for first frame in a scene
DoVi metadata (dovi_metadata=1)
  • _dovi_dynamic_min_pq
  • _dovi_dynamic_max_pq
  • _dovi_dynamic_max_content_light_level
  • _dovi_static_master_display_max_luminance
  • _dovi_static_master_display_min_luminance
  • _dovi_static_max_content_light_level
  • _dovi_static_max_avg_content_light_level
  • _dovi_static_max_pq

💡 Non-zero only when present in RPU

Improvements over DoViBaker

  • Intermediate bit-depth — correctly clamped to 12-bit (spec-compliant) instead of original 16-bit
  • Color range — RGB output _ColorRange is now always 0 (Full Range)
  • MMR mapping — fixed bug where rightmost luma pixels were ignored during chroma mapping

🛠️️ To revert to original behavior, uncomment // COMPAT blocks.

Benchmarks

Results reflect the best of 10 runs, with negligible variance between mean and peak performance.

Platform Plugin Resizer Raw Output FFmpeg Null
Windows x64 (AVX2) DoViBaker Internal 48.2s 49.9s
Windows x64 (AVX2) FelBaker Internal 6.6s 7.8s
Windows x64 (AVX2) FelBaker VapourSynth 6.2s 7.5s
macOS ARM64 (M4) FelBaker Internal 2.5s 3.6s
macOS ARM64 (M4) FelBaker VapourSynth 2.5s 3.6s

🚀 Performance appears RAM-bandwidth limited rather than CPU-bound. When encoding to ProRes or similar, the speed difference should be less pronounced as encoding becomes the bottleneck.

Test systems & methodology
  • Windows 11 (x64) – AMD Ryzen 7 5875U (AVX2)
  • macOS 26 (ARM64) – Mac Mini M4 (Base)

Frame Source: ffms2 with threads=8 (performance stabilized at threads=4+ on both systems).

DoViBaker v0.4.5.1 (AviSynth+ 3.7.5):

LoadPlugin("ffms2.dll")
LoadPlugin("DoViBaker_x64.dll")

bl=FFVideoSource("samples/BL-150.hevc", threads=8)
el=FFVideoSource("samples/EL-150.hevc", threads=8)

DoViBaker(bl,el)
time ./avs2pipemod64.exe -rawvideo bake.avs > /dev/null # Raw output
time ./ffmpeg.exe -i bake.avs -f null -                 # FFmpeg Null

FelBaker (VapourSynth R73):

# Internal resizer
import vapoursynth as vs
core = vs.core

core.std.LoadPlugin('ffms2.dll')     # macOS: libffms2.dylib
core.std.LoadPlugin('felbaker.dll')  # macOS: libfelbaker.dylib

bl = core.ffms2.Source("samples/BL-150.hevc", threads=8)
el = core.ffms2.Source("samples/EL-150.hevc", threads=8)

core.fel.Bake(bl, el, no_resample=0, non_parallel=0, dovi_metadata=1).set_output()
# VapourSynth resizer
import vapoursynth as vs
core = vs.core

core.std.LoadPlugin('ffms2.dll')     # macOS: libffms2.dylib
core.std.LoadPlugin('felbaker.dll')  # macOS: libfelbaker.dylib

bl = core.ffms2.Source("samples/BL-150.hevc", threads=8)
el = core.ffms2.Source("samples/EL-150.hevc", threads=8)

el_4k = core.resize.Spline16(el, width=3840, height=2160, format=vs.YUV420P10)
baked = core.fel.Bake(bl, el_4k, no_resample=1, non_parallel=0, dovi_metadata=1)
bl_4k = core.resize.Spline16(baked, width=3840, height=2160, format=vs.YUV444P12)

core.fel.ToRGB(bl_4k, non_parallel=0).set_output()
# Raw output
time vspipe bake.vpy - > /dev/null

# FFmpeg Null
time vspipe bake.vpy - | ffmpeg -f rawvideo -pixel_format gbrp16le \
  -colorspace rgb -color_primaries bt2020 -color_trc smpte2084 -color_range pc \
  -field_order progressive -video_size 3840x2160 -r 24000/1001 -i - -f null -

Building

Requirements

  • Python 3.12+
  • Meson 1.10+ (older versions should work but are untested)
  • Ninja
  • Rust (with cargo and cargo-c)

💡 For build configuration examples, see .github/workflows/build-release.yml.

Compilation

meson setup <buildDir>
meson compile -C <buildDir>

🚀 FelBaker was developed and optimized using Clang on macOS; To maintain peak performance, Clang is highly recommended on all platforms — benchmarks show that MSVC builds may be over 50% slower.

Options

Configure via meson setup <buildDir> -D<option>=<value>.

Option Values Description
cpu_tier generic (default)
max
native
genericM1 / AVX2 / v8.2
maxM4 / AVX-512 / v9.2
native – auto-detect
tests true
false (default)
Enable and build tests
install_plugin true
false (default)
Install the VapourSynth plugin
win_deterministic true
false (default)
Enable deterministic Windows builds

📋 See meson.options for all available options.

Cleaning

To fully clean the build (including Rust artifacts):

meson compile -C <buildDir> cargo-clean
meson compile -C <buildDir> --clean

Testing

FEL baking integrity is validated via SHA-256 checksums of the RGB output.

⚠️ test/samples are heavily compressed and serve only to validate logic, not visual quality.

meson setup <buildDir> -Dtests=true
meson test -C <buildDir>

🔍 CLion Integration: Tests are detected as pytest cases. Since they depend on the plugin binary, a Meson compile pre-run step should be added to ensure the plugin is rebuilt before execution.

Test Cases

Test cases may be defined in test/tests.toml. Available parameters are documented within that file.

After adding or removing a test case, test/test_integration_generated.py must be regenerated:

meson compile -C <buildDir> refresh-tests

⚠️ The [avisynth] test case (disabled by default) requires uncommenting // COMPAT blocks to pass, as it validates against the original DoViBaker checksums.

Credits

About

DV P7 FEL baking plugin for VapourSynth. High-performance, cross-platform port of DoViBaker with native macOS/Linux/Windows support.

Topics

Resources

License

Stars

Watchers

Forks

Contributors