Media Settings & Presets¶
K-Pipe describes every image, video and audio output once, in a DCC-agnostic way. The kpipe.media package (packages/core/src/kpipe/media/) holds the vocabulary: media types, format and codec registries, settings objects, named presets, OCIO color roles, frame ranges and file-type detection. DCC integrations never define their own formats or codecs; they only translate validated settings into their own parameters:
This means a pipeline TD configures an output once (for example "16-bit half EXR, DWAA 45, scene-linear") and every DCC writes it the same way. Unsupported combinations fail early with a clear message, before anything is rendered.
The core is pure Python. OpenImageIO and PyOpenColorIO are used only when they can be imported (for file probing and OCIO role resolution).
| Media type | Settings class | Used for |
|---|---|---|
image |
ImageSettings |
Single images and image sequences |
multilayer_image |
ImageSettings with channels = "all" |
Multilayer / multi-part EXR (render passes, AOVs) |
video |
VideoSettings |
Movie files |
audio |
AudioSettings |
Audio files, or the audio track of a video |
Media presets¶
A preset is a named set of media settings. K-Pipe ships with built-in presets; config bundles add their own or replace built-ins under [media.presets.<name>] in kpipe.toml:
[media.presets.fs_review]
media_type = "video"
container = "mov"
codec = "h264"
quality = "medium"
color = "display:sRGB/Standard"
# Inherit from another preset and change single fields
[media.presets.fs_plate]
base = "exr_dwaa"
channels = "rgb"
| Key | Description |
|---|---|
media_type |
image, multilayer_image, video or audio. Required, unless inherited via base |
base |
Name of another preset (built-in or config) to inherit from. Chains are allowed; cycles are an error |
description |
Optional, informational only |
| any settings field | See the field reference for the media type |
A config preset with the same name as a built-in replaces it completely. Use base to extend a preset instead.
How settings are resolved¶
When an action or output asks for settings, they are resolved in this order. Later steps win:
- The preset's
basechain, oldest ancestor first. - The preset itself (a config preset replaces a built-in preset of the same name).
- Field overrides from the action step, the publish settings or the output entry. Empty values (
"") are ignored, so unset override fields never clear a preset value. - Format defaults fill in whatever is still unset (e.g. EXR →
16fanddwaa:45).
The result is validated against the format's capabilities. Invalid settings raise MediaSettingsError, which publish actions report as a PublishError:
Codec 'prores' is not valid for container 'webm' — expected one of: vp9, av1
JPEG has no alpha channel — use channels = 'rgb'
Compression level 5000 for 'dwaa' is out of range 0-1000
Unknown image setting(s) compresion — expected: bit_depth, channels, color, compression, file_format, quality
Note
Changing the format through an override resets the fields that only make sense for the old format. For example, file_format = "png" on top of exr_dwaa drops the EXR bit depth and compression. Changing container to one that can't hold the current codec also switches to the container's default codec.
Value conventions¶
| Value | Convention |
|---|---|
| Bit depth | "8", "10", "12", "16" (integer) and "16f", "32f" (float). Numbers are accepted, too: 16 becomes "16f" for EXR (which has no 16-bit integer), "16" for PNG |
| Compression | "<name>" or "<name>:<level>", using OpenImageIO/OpenEXR names, e.g. "zip", "dwaa:45", "zip:9". Names are case-insensitive |
| Quality | A level, "low", "medium", "high" or "lossless", or an explicit number 0–100. Each format/codec maps levels to its own parameters (see quality mapping) |
| Channels | "rgb", "rgba", or "all". "all" writes every channel/layer, making the image a multilayer EXR |
| Color | A ColorSpec string, e.g. "role:scene_linear" or "display:sRGB/Standard" |
| Frame rate | A number or fraction. NTSC rates are stored exactly: 23.976, "23.976" and "24000/1001" all become 24000/1001 |
Field reference¶
| Field | Default | Description |
|---|---|---|
file_format |
"exr" |
Image format id or any of its extensions (tiff → tif) |
bit_depth |
format default | See image formats |
compression |
format default | <name>[:<level>] |
quality |
— | Level or 0–100. Formats without a quality setting (PNG, DPX, TIFF…) ignore levels but reject numbers |
channels |
"rgba" |
rgb, rgba, all |
color |
derived | Empty → role:scene_linear for float data, role:srgb_texture otherwise |
| Field | Default | Description |
|---|---|---|
container |
"mov" |
Container id or extension |
codec |
container default | First codec listed for the container |
profile |
from quality or codec default | ProRes/DNxHR profile, e.g. hq, 4444, hqx |
quality |
high |
Level or 0–100. For ProRes/DNxHR, levels select a profile and numbers are rejected |
bit_depth |
profile/codec default | 8, 10, 12 (16 for FFV1) |
chroma |
profile/codec default | 420, 422, 444 (4:2:2 is accepted, too) |
alpha |
false |
Needs a codec/profile with alpha, e.g. ProRes 4444, VP9, FFV1. ProRes picks a 4444 profile automatically |
bitrate |
— | Constant bitrate such as "20M", replacing constant-quality encoding. Long-GOP codecs only |
keyframe_interval |
encoder default | Frames between key frames; 1 = all-intra (smooth scrubbing in editing). Long-GOP codecs only |
fps |
— | Usually taken from the frame range or project config by the action |
audio |
— | A table of audio fields; validated against the container's audio codecs |
color |
role:srgb_texture |
| Field | Default | Description |
|---|---|---|
file_format |
"wav" |
Ignored when the audio is embedded in a video container |
codec |
format/container default | pcm, flac, aac, opus, mp3, vorbis |
sample_rate |
48000 |
|
bit_depth |
codec default | Lossless codecs only: 16, 24, 32f (PCM) |
channels |
2 |
|
bitrate |
codec default | Lossy codecs only, e.g. "256k" |
Built-in presets¶
Defined in BUILTIN_PRESETS (packages/core/src/kpipe/media/presets.py). Values shown are fully resolved.
Images
| Preset | Format | Bit depth | Compression / quality | Channels | Color |
|---|---|---|---|---|---|
exr_dwaa |
EXR | 16f | dwaa:45 |
rgba | role:scene_linear |
exr_dwab |
EXR | 16f | dwab:45 |
rgba | role:scene_linear |
exr_zip |
EXR | 16f | zip |
rgba | role:scene_linear |
exr_piz |
EXR | 16f | piz |
rgba | role:scene_linear |
exr_32_zip |
EXR | 32f | zip |
rgba | role:scene_linear |
exr_multilayer |
EXR (multilayer) | 16f | dwaa:45 |
all | role:scene_linear |
exr_multilayer_lossless |
EXR (multilayer) | 32f | zip |
all | role:scene_linear |
png_8 |
PNG | 8 | zip:6 |
rgba | role:srgb_texture |
png_8_rgb |
PNG | 8 | zip:6 |
rgb | role:srgb_texture |
png_16 |
PNG | 16 | zip:6 |
rgba | role:srgb_texture |
jpg_review |
JPEG | 8 | quality high |
rgb | role:srgb_texture |
tif_16 |
TIFF | 16 | zip |
rgba | role:srgb_texture |
dpx_10_log |
DPX | 10 | — | rgb | role:compositing_log |
webp_review |
WebP | 8 | quality high |
rgba | role:srgb_texture |
Video
| Preset | Container | Codec | Profile / quality | Bit depth | Chroma |
|---|---|---|---|---|---|
prores_proxy |
mov | ProRes | proxy |
10 | 422 |
prores_lt |
mov | ProRes | lt |
10 | 422 |
prores_422 |
mov | ProRes | standard |
10 | 422 |
prores_hq |
mov | ProRes | hq |
10 | 422 |
prores_4444 |
mov | ProRes | 4444 |
12 | 444 |
prores_4444_alpha |
mov | ProRes | 4444 + alpha |
12 | 444 |
prores_4444xq |
mov | ProRes | 4444xq |
12 | 444 |
dnxhr_sq |
mxf | DNxHR | sq |
8 | 422 |
dnxhr_hq |
mxf | DNxHR | hq |
8 | 422 |
dnxhr_hqx |
mxf | DNxHR | hqx |
10 | 422 |
dnxhr_444 |
mxf | DNxHR | 444 |
10 | 444 |
h264_review |
mp4 | H.264 | medium |
8 | 420 |
h264_high |
mp4 | H.264 | high |
8 | 420 |
h265_review |
mp4 | H.265 | medium |
10 | 420 |
h265_high |
mp4 | H.265 | high |
10 | 420 |
h265_proxy |
mov | H.265 | medium, all-intra (keyframe_interval = 1) — editing proxy |
10 | 420 |
av1_web |
mp4 | AV1 | medium |
8 | 420 |
vp9_web |
webm | VP9 | medium |
8 | 420 |
ffv1_archive |
mkv | FFV1 | lossless | 10 | 444 |
All video presets use role:srgb_texture unless overridden.
Audio
| Preset | Format | Codec | Bit depth / bitrate | Sample rate |
|---|---|---|---|---|
wav_24 |
wav | PCM | 24 | 48000 |
wav_16 |
wav | PCM | 16 | 48000 |
flac_24 |
flac | FLAC | 24 | 48000 |
aac_256 |
m4a | AAC | 256k | 48000 |
Format reference¶
These tables mirror the registries in media/image.py, media/video.py and media/audio.py. Whether a specific DCC can write a combination is listed under DCC support and limitations.
Image formats¶
| Format | Extensions | Bit depths (default) | Compressions (default) | Quality | Alpha |
|---|---|---|---|---|---|
exr |
exr, sxr, mxr | 16f, 32f (16f) | none, rle, zips, zip, piz, pxr24, b44, b44a, dwaa, dwab, htj2k (dwaa:45) |
levels select a compression | yes, multilayer |
png |
png | 8, 16 (8) | zip (zip:6) |
— | yes |
jpg |
jpg, jpeg, jpe, jfif | 8 | — | 70 / 85 / 95 | no |
tif |
tif, tiff | 8, 16, 32f (16) | none, lzw, zip, packbits (zip) |
— | yes |
dpx |
dpx | 8, 10, 12, 16 (10) | — | — | yes |
tga |
tga, tpic | 8 | none, rle (rle) |
— | yes |
webp |
webp | 8 | — | 50 / 75 / 90, lossless | yes |
avif |
avif | 8, 10, 12 (8) | — | 50 / 70 / 85 | yes |
jxl |
jxl | 8, 16, 32f (8) | — | 70 / 85 / 95, lossless | yes |
Compression levels: dwaa/dwab take 0–1000 (OpenEXR's DWA level, 45 ≈ visually lossless), EXR zip/zips take 1–9, PNG zip takes 0–9.
Read-only formats, recognised for detection and loading but never written: hdr (hdr, rgbe), cin, psd (psd, psb), bmp (bmp, dib), gif, tx, jp2 (jp2, j2k, j2c).
Containers and codecs¶
| Container | Extensions | Video codecs (first = default) | Audio codecs |
|---|---|---|---|
mov |
mov, qt | prores, dnxhr, h264, h265 | pcm, aac |
mp4 |
mp4, m4v | h264, h265, av1 | aac, opus |
mxf |
mxf | dnxhr, prores | pcm |
mkv |
mkv | ffv1, h264, h265, av1, vp9, prores | pcm, flac, aac, opus |
webm |
webm | vp9, av1 | opus, vorbis |
Read-only containers: avi, mpg (mpg, mpeg, m2v), mts (mts, m2ts, ts), wmv, flv, ogv, 3gp, r3d, braw.
| Codec | Bit depths | Chroma | Alpha | Notes |
|---|---|---|---|---|
prores |
10, 12 | 422, 444 | 4444 profiles | Intra-frame. Profiles below |
dnxhr |
8, 10, 12 | 422, 444 | no | Intra-frame. Profiles below |
h264 |
8 | 420 (default), 422, 444 | no | |
h265 |
8, 10 | 420 (default), 422, 444 | no | |
av1 |
8, 10 | 420 (default), 444 | no | |
vp9 |
8, 10 | 420 (default), 444 | yes | |
ffv1 |
8, 10 (default), 12, 16 | 420, 422, 444 (default) | yes | Lossless only |
| Profile | Chroma | Bit depths | Alpha |
|---|---|---|---|
ProRes proxy, lt, standard, hq (default) |
422 | 10 | no |
ProRes 4444, 4444xq |
444 | 10, 12 | yes |
DNxHR lb, sq, hq (default) |
422 | 8 | no |
DNxHR hqx |
422 | 10, 12 | no |
DNxHR 444 |
444 | 10, 12 | no |
Quality mapping¶
| Target | low |
medium |
high |
lossless |
|---|---|---|---|---|
EXR (sets the compression unless compression is given) |
dwaa:200 |
dwaa:100 |
dwaa:45 |
zip |
| JPEG / JPEG XL | 70 | 85 | 95 | — / lossless |
| WebP | 50 | 75 | 90 | lossless |
| AVIF | 50 | 70 | 85 | — |
| ProRes (selects the profile) | proxy |
standard |
hq |
— |
| DNxHR (selects the profile) | lb |
sq |
hq |
— |
| H.264 / H.265 / AV1 / VP9 | translated to CRF by each adapter (e.g. ffmpeg H.264: 25 / 21 / 17) | — |
Audio formats¶
| Format | Extensions | Codecs |
|---|---|---|
wav |
wav, wave, bwf | pcm |
aif |
aif, aiff | pcm |
flac |
flac | flac |
m4a |
m4a | aac |
opus |
opus | opus |
mp3, ogg |
mp3 / ogg, oga | read-only |
Color¶
Media settings never store config-specific colorspace names. They store a ColorSpec, written as a string:
| Form | Example | Meaning |
|---|---|---|
role:<role> |
role:scene_linear |
An OCIO role or pipeline role, resolved per DCC |
<role> |
data |
Same as role:data |
colorspace:<name> |
colorspace:ACEScg |
An explicit colorspace name, used as-is |
display:<display>/<view> |
display:sRGB/Standard |
A display/view transform (display-referred output) |
Supported roles are the standard OCIO roles: scene_linear, rendering, compositing_log, color_timing, color_picking, data, texture_paint, matte_paint, default, reference, aces_interchange and cie_xyz_d65_interchange. Next to those are the DCC roles default_byte, default_float and default_sequencer, and the pipeline role srgb_texture for sRGB-encoded textures and display-referred stills (OCIO has no standard role for it).
When color is empty, image settings default to role:scene_linear for float data (EXR, 32f TIFF…) and role:srgb_texture otherwise; video defaults to role:srgb_texture.
How roles are resolved¶
When a DCC applies a ColorSpec, resolve_colorspace(spec, dcc) (media/color.py) picks the first match:
- An explicit
colorspace:name. - A per-DCC alias:
[color.roles.<dcc>]. - A global alias:
[color.roles]. - The OCIO config in
$OCIO(via PyOpenColorIO, which both launchers provide), else the project's[config] ocio_config_compat. It tries the role itself, then candidate names; OCIO 2 resolves colorspace aliases too. - A fallback name (
srgb_texture→sRGB), else the role name itself.
Candidate names tried in the OCIO config:
| Role | Candidates |
|---|---|
srgb_texture |
srgb_texture, srgb_tx, sRGB - Texture, Utility - sRGB - Texture, sRGB |
data |
raw, Raw, Non-Color, Utility - Raw |
scene_linear |
ACEScg, lin_ap1, Linear Rec.709, linear |
compositing_log |
ACEScct, AgX Log, Cineon |
Config aliases are only needed when the OCIO config doesn't define a role, or a DCC needs a different name:
[color.roles]
srgb_texture = "sRGB - Texture"
[color.roles.blender]
srgb_texture = "sRGB"
# Display names used in display:<display>/<view> specs
[color.displays]
sRGB = "sRGB - Display"
[color.displays.blender]
sRGB = "sRGB"
Note
Without $OCIO, roles fall back to their names, which Nuke accepts directly. Blender falls back to its bundled OCIO config.
Formats¶
Formats are the project-specific part of media output: resolutions under generic keys (working, delivery, review, proxy by convention, any key allowed), so pipeline configs stay project independent. Defined in the bundle's kpipe.toml:
[formats.delivery]
name = "Doris 2K" # label artists see (Nuke format name, setting dropdowns)
resolution = [2048, 1024] # optionally [w, h, pixel_aspect]
[formats.working]
name = "Doris 2K Overscan"
base = "delivery" # inherit the other keys
overscan = [102, 51] # pixels added per side → 2252x1126
[formats.review]
name = "Doris 1K"
resolution = [1920, 960]
| Key | Description |
|---|---|
name |
Display name; defaults to the key |
resolution |
[width, height] or [width, height, pixel_aspect] — the image area |
overscan |
[x, y] pixels added on each side; written images have the full size, Nuke registers the image area as the format box |
pixel_aspect |
Default 1.0 |
base |
Another format key to inherit from |
Actions writing or scaling images take a format field (a key, empty = keep the source resolution):
| Action | Behaviour |
|---|---|
ConfigBlenderRenderOutput |
Sets the render resolution (at 100 %, incl. overscan and pixel aspect), restored after the publish |
ExtractFfmpegSequenceToVideo, ExtractFfmpegVideoToSequence |
Scales with ffmpeg; fit = fit (letterbox/pillarbox, default), fill (cover and crop) or distort |
ExtractBurnIn |
Scales (same fit modes) before the burn-ins, so text sizes are relative to the target |
ConfigNukeWriteOutput |
No resizing: the publish fails if the image reaching the Write node has another size |
SetBlenderFormat (command) |
Sets the scene's render resolution, e.g. to working |
A format setting shows the formats by name in publish UIs:
format = { text = "Format", type = "format", default_value = "review", allow_empty = true, empty_label = "Scene resolution", target = "ConfigBlenderRenderOutput.format" }
In code, kpipe.get_format(key) returns a Format (name, resolution, overscan, full_resolution); kpipe.get_formats() lists them, and kpipe.media.fit_rect(src, dst, mode) computes where an image lands on a format.
Replaces outputs and products
[[attributes.outputs]], [[attributes.products]] and [[attributes.formats]] are gone. Pipelines name a preset (plus overrides) and a format per step, and set their product types themselves (product_type = "{task}_review").
Using presets in pipelines¶
Every action that writes media follows the same convention. It has a preset field plus flat override fields named like the settings fields. Empty override fields are ignored, so a step only lists what differs from the preset:
# Multilayer EXR render from Blender
[[pipeline.render_blender_cycles.config]]
action = "ConfigBlenderRenderOutput"
preset = "exr_multilayer"
# Review movie from rendered frames, written as .mov instead of .mp4
[[pipeline.blender_render_review_video.extract]]
action = "ExtractFfmpegSequenceToVideo"
slot_in = "frames"
preset = "h264_review"
container = "mov"
# Conform a plate video into a 16-bit half EXR sequence
[[pipeline.plate_conform.extract]]
action = "ExtractFfmpegVideoToSequence"
preset = "exr_zip"
channels = "rgb"
Actions using this convention:
| Action | Media type | Default preset | Override fields |
|---|---|---|---|
| ConfigBlenderRenderOutput | image | png_8 |
file_format, bit_depth, compression, quality, channels, color, format |
| ExtractBlenderSequencerFrames | image | png_8 |
file_format, bit_depth, compression, quality, channels |
| ExtractFfmpegSequenceToVideo | video | h264_high |
container, codec, profile, quality, bit_depth, chroma, bitrate, keyframe_interval, fps, format, fit |
| ExtractFfmpegVideoToSequence | image | png_8 |
file_format, bit_depth, compression, quality, channels, fps, format, fit |
Letting artists choose¶
Because the fields are flat, publish settings can target them directly. An enum setting is the usual way to offer a fixed choice of presets or quality levels:
[pipeline.blender_render_review_video.settings.video_format]
type = "enum"
text = "Format"
default_value = "h264_review"
values = [["h264_review", "H.264 review (mp4)"], ["prores_hq", "ProRes 422 HQ (mov)"], ["prores_4444", "ProRes 4444 (mov)"]]
target = "ExtractFfmpegSequenceToVideo.preset"
[pipeline.blender_render_review_video.settings.video_quality]
type = "enum"
text = "Quality"
default_value = ""
values = [["", "Preset default"], ["low", "Low"], ["medium", "Medium"], ["high", "High"]]
target = "ExtractFfmpegSequenceToVideo.quality"
A setting of type = "media_preset" (optionally with media_type = "video") lists all presets instead of a hand-written values list. The empty "" value keeps the preset's own quality. If the artist's choices form an invalid combination (for example quality = "low" with a lossless-only codec), the action fails with the validation message before anything is encoded.
Published metadata¶
Integrating a sequence or video stores the media settings with the publish in kpipe-metadata.toml, as a media table inside the version entry:
[[publish.review.main]]
file-type = "sequence"
version = 3
file-format = "png"
frame-start = 1001
frame-end = 1100
[publish.review.main.media]
file_format = "png"
bit_depth = "8"
channels = "rgba"
color = "display:sRGB/Standard"
compression = "zip:6"
Texture sets store one ColorSpec per map in colorspaces (e.g. basecolor = "role:srgb_texture", normal = "role:data"). Load actions read these: LoadBlenderMedia applies media.color to the image datablock, and LoadBlenderTextureSet resolves each map's ColorSpec through OCIO.
DCC support and limitations¶
The registries describe what a format allows. Each writer supports a subset, and adapters raise MediaSettingsError for combinations they can't write rather than silently writing something else. The mappings below were checked against the DCCs' own documentation and source.
Blender (kpipe_blender/media_blender.py)¶
Based on the Blender 5.2 Python API (ImageFormatSettings, FFmpegSettings) and Blender's writers in source/blender/imbuf.
- Images: EXR (single and multilayer), PNG, JPEG, TIFF (8/16 bit), DPX, Targa, WebP and AVIF (8/10/12 bit). Every EXR compression in the registry is supported: none, RLE, ZIPS, ZIP, PIZ, PXR24, B44, B44A, DWAA, DWAB and HTJ2K.
- DWA level: Blender has no level field; it derives the OpenEXR DWA level from
image_settings.qualityaslevel = (100 − quality) × 4.5. The adapter setsquality = 100 − level / 4.5, sodwaa:45becomes quality 90 (Blender's default). - Targa:
compression = "rle"writesTARGA,nonewritesTARGA_RAW. - Video: containers mov, mp4, mkv, webm (no MXF). ProRes profiles map to
422_PROXY,422_LT,422_STD,422_HQ,4444,4444_XQ. Quality levels map to Blender's CRF presets; numeric quality usesCUSTOMwithcustom_constant_rate_factor. - Color: display/view specs set
color_management = "OVERRIDE"with display and view; float formats override the linear colorspace; byte formats follow the scene's view transform. - Restore: all changes go through a
SettingsRecorder. Actions restore the original scene settings incleanup().
Not supported in Blender
DNxHR (Blender's DNxHD codec has no profile setting), MXF, JPEG XL, and 32-bit float TIFF.
Nuke (kpipe_nuke/kpipe_nuke_ext/media_nuke.py)¶
EXR, PNG and DPX knob values come from Foundry's NDK writer sources; knob names from the Nuke 17 Write node reference.
- Images: EXR, PNG, JPEG, TIFF, DPX, Targa. EXR compressions none, ZIPS, ZIP, PIZ, RLE, B44, B44A, DWAA, DWAB, with
dw_compression_level(0–500). - Video: mov and mp4 with ProRes (all profiles) and H.264 (quality levels).
- Color:
transformTypecolorspace with the resolved name, or display withocioDisplay/ocioView.
Not supported in Nuke
EXR PXR24 and HTJ2K, DWA levels above 500, DNxHR/MXF (Nuke supports DNxHR, but its profile values aren't documented yet), and WebP/AVIF/JPEG XL.
Note
Display/view output on Write nodes has known Nuke issues: the view can reset on script reload (Foundry ID 606354), and OCIO 2 display views are reported not to be usable directly on Write. Rendering through an OCIODisplay node with a colorspace: / raw Write is a workaround.
ffmpeg (kpipe_imgtools/ffmpeg_media.py)¶
Options and pixel formats checked with ffmpeg -h encoder=<name> (ffmpeg 9.0).
- Video: every codec in the registry:
prores_ks,dnxhd(DNxHR profiles),libx264,libx265,libaom-av1,libvpx-vp9,ffv1. Quality levels map to CRF, e.g. H.264 25 / 21 / 17, H.265 28 / 24 / 20, AV1 40 / 30 / 20, VP9 40 / 33 / 24. - Images: PNG, EXR, JPEG, TIFF, DPX, Targa, WebP, AVIF, JPEG XL.
- Colour:
ExtractFfmpegSequenceToVideoconverts RGB frames to YUV with the BT.709 matrix in limited range (ffmpeg's default is BT.601) and tags the video from the sequence's colour (color_filter()/color_tags()): always the BT.709 matrix, plus BT.709 primaries and transfer for Rec.709/Rec.1886 displays (sRGB transfer for sRGB). Scene-referred and log colour (e.g. ACEScct) stays untagged; the product'smedia.coloris authoritative. Tags are set withsetparamsin the filter chain, since encoder options like-color_primariesare overridden by frame properties.
ffmpeg limitations
- ffmpeg's EXR encoder only writes none, RLE, ZIPS and ZIP. DWAA, DWAB and PIZ can be read but not written, so they fall back to lossless ZIP with a warning. Use OIIO to write DWA-compressed EXRs.
- 10- and 12-bit DPX and AVIF can't be written with alpha.
- The DNxHR encoder takes at most 10-bit input; 12-bit settings are encoded as 10 bit with a warning.
OpenImageIO (kpipe_imgtools/oiio_media.py)¶
OIIO uses the same compression names and bit depths as kpipe.media, so all writable image settings pass straight through, including DWA levels: apply_image_spec(spec, settings) sets the output attributes and image_spec_type(settings) returns the pixel type. oiiotool_output_args(settings) returns the same settings as oiiotool arguments (-d, --compression, --attrib); ExtractBurnIn uses it to keep the sequence's settings when it rewrites frames.
ExtractOiioColorConvert (kpipe_imgtools/actions/extract_oiio_convert.py) converts a sequence with oiiotool in float: optional resize to a format (in the source colour), then the colour transform from the sequence's color to the target color — --colorconvert for roles/colorspaces (role:compositing_log → ACEScct), --ociodisplay for display specs (display:Rec.1886/ACES 2.0) — and writes it with an image preset (default tif_16) into the render folder of its product_type. It always uses the project's compat OCIO config (--colorconfig).
Writing media-aware actions¶
Custom actions follow the same pattern as the built-ins:
- Expose
presetplus flat override fields with empty defaults. - Resolve them with
resolve_image_settings()/resolve_video_settings()/resolve_audio_settings()and re-raiseMediaSettingsErrorasPublishError. - Apply the settings through an adapter. Never hard-code DCC enum values in actions.
- Store the settings and color on the pipeline data (
SequencePipelineData.image,VideoPipelineData.video,.color) so integrate actions write them to the publish metadata. - Write changed data back with
ctx.set_slot().get_slot()returns pipeline data (MediaPipelineDatasubclasses,LayeredSequencePipelineData,FilePipelineData) as a deep copy, so changes to the returned object never reach the slot on their own — and never leak into other slots holding the same data. Other slot contents, such as DCC objects, are returned as is.
from dataclasses import dataclass
import bpy
from kpipe.media import MediaSettingsError, resolve_image_settings
from kpipe.pipeline import ConfigAction, PipelineContext, PublishError
from kpipe_blender.media_blender import SettingsRecorder, apply_image_settings
@dataclass
class ConfigBlenderStillFormat(ConfigAction):
"""Sets the scene's output image format from a media preset."""
description: str = "Apply image format"
slot_in: str = "main"
preset: str = "png_8"
file_format: str = ""
bit_depth: str = ""
def execute(self, ctx: PipelineContext) -> None:
try:
settings = resolve_image_settings(self.preset, file_format=self.file_format, bit_depth=self.bit_depth)
self._recorder = SettingsRecorder()
ext = apply_image_settings(bpy.context.scene.render.image_settings, settings, self._recorder)
except MediaSettingsError as e:
raise PublishError(f"ConfigBlenderStillFormat: {e}") from e
data = ctx.get_slot(self.slot_in)
data.file_format, data.image, data.color = ext, settings, settings.resolved_color()
ctx.set_slot(self.slot_in, data)
def cleanup(self, ctx: PipelineContext) -> None:
if hasattr(self, "_recorder"):
self._recorder.restore()
| Adapter | Entry points |
|---|---|
| Blender | apply_image_settings(image_settings, settings, recorder), apply_video_settings(render, settings, recorder), blender_colorspace(spec), blender_image_source(path, product) |
| Nuke | apply_write_settings(node, settings), nuke_colorspace(spec) |
| ffmpeg | video_output_args(settings), image_output_args(settings), audio_output_args(settings); each returns ffmpeg.output() kwargs |
| OIIO | apply_image_spec(spec, settings), image_spec_type(settings), image_spec_attributes(settings) |
Useful helpers from kpipe.media:
| Helper | Purpose |
|---|---|
is_image_file, is_video_file, is_audio_file, get_media_type |
Detection by extension; works with sequence paths (shot.####.exr) |
probe(path) |
Reads real properties (size, channels, bit depth, compression, fps) via OIIO or ffprobe |
FrameRange, parse_fps, Resolution |
Frame ranges with exact NTSC rates, resolutions from config |
parse_sequence_name, sequence_pattern, replace_frame_placeholder |
Sequence file naming |
resolve_colorspace, resolve_display_view |
ColorSpec → DCC names |
Sources¶
The DCC limitations above were verified against:
- Blender API: ImageFormatSettings, FFmpegSettings, Exr Codec Items, Image Type Items, Supported Image Formats
- Blender source: openexr_api.cpp, movie_write.cc
- Nuke Write node reference, NDK exrGeneral.h
- Foundry ID 606354, ACES Central: Write node display views
ffmpeg -h encoder=<name>(ffmpeg 9.0.1)