FFmpeg
vf_opencolorio.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2026 Sam Richards
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include "avfilter.h"
22 #include "formats.h"
23 #include "libavutil/half2float.h"
24 #include "libavutil/opt.h"
25 #include "libavutil/pixdesc.h"
26 #include "libavutil/time.h"
27 #include "ocio_wrapper.hpp"
28 #include "video.h"
29 
30 typedef struct {
31  const AVClass *class;
32  char *config_path;
33  char *input_space;
34  char *output_space;
35  char *display;
36  char *view;
38  int inverse;
41  char *out_format_string; // e.g. "rgb48le" which is converted to AVPixelFormat
42  // as output_format
43  int channels; // 3 or 4 depending on pixfmt
45 } OCIOContext;
46 
47 typedef struct ThreadData {
48  AVFrame *in, *out;
49 } ThreadData;
50 
51 static int ocio_filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
52 {
53  OCIOContext *s = ctx->priv;
54  ThreadData *td = arg;
55  AVFrame *in = td->in;
56  AVFrame *out = td->out;
57  const int height = out->height;
58  const int slice_start = (height * jobnr) / nb_jobs;
59  const int slice_end = (height * (jobnr + 1)) / nb_jobs;
60  const int slice_h = slice_end - slice_start;
61 
62  return ocio_apply(ctx, s->ocio, in, out, slice_start, slice_h);
63 }
64 
66  static const enum AVPixelFormat pix_fmts[] = {
67  // 8-bit
69  // 16-bit
71  // 10-bit
73  // 12-bit
75  // Half-float and float
77  // Float
80 }
81 
83 {
84  AVFilterContext *ctx = inlink->dst;
85  OCIOContext *s = ctx->priv;
87 
88  if (!desc) {
89  av_log(ctx, AV_LOG_ERROR, "Invalid pixel format\n");
90  return AVERROR(EINVAL);
91  }
92 
93  int is_half =
94  (desc->comp[0].depth == 16 && desc->flags & AV_PIX_FMT_FLAG_FLOAT);
95  if (s->output_format == AV_PIX_FMT_NONE) {
96  // Need to set the output format now, if not known.
97  if (is_half) {
98  // If its half-float, we output float, due to a bug in ffmpeg with
99  // half-float frames
100  s->output_format = AV_PIX_FMT_GBRAPF32;
101  } else {
102  // If output format not set, use same as input
103  s->output_format = inlink->format;
104  }
105  }
106 
107  s->channels = desc->nb_components; // 3 or 4
108 
110  "Configuring OCIO for %s (bit depth: %d, channels: %d), output "
111  "format: (%s)\n",
112  av_get_pix_fmt_name(inlink->format), desc->comp[0].depth, s->channels,
113  av_get_pix_fmt_name(s->output_format));
114 
115  // Now finalize the OCIO processor with the correct bit depth
116  int ret = ocio_finalize_processor(ctx, s->ocio, inlink->format, s->output_format);
117  if (ret < 0) {
119  "Failed to finalize OCIO processor for bit depth\n");
120  return AVERROR_EXTERNAL;
121  }
122 
123  return 0;
124 }
125 
127 {
128  OCIOContext *s = ctx->priv;
129  if (s->out_format_string != NULL)
130  s->output_format = av_get_pix_fmt(s->out_format_string);
131  else
132  s->output_format = AV_PIX_FMT_NONE; // Default to same as input format (see later).
133 
134  if (s->filetransform && strlen(s->filetransform) > 0) {
136  ctx, s->filetransform, s->inverse);
138  "Creating OCIO processor with FileTransform: %s, Inverse: %d\n",
139  s->filetransform, s->inverse);
140  } else if (s->output_space && strlen(s->output_space) > 0) {
142  ctx, s->config_path, s->input_space, s->output_space, s->context_params);
144  "Creating OCIO processor with config: %s, input: %s, output: %s\n",
145  s->config_path, s->input_space, s->output_space);
146  } else {
148  ctx, s->config_path, s->input_space, s->display, s->view, s->inverse, s->context_params);
150  "Creating OCIO processor with config: %s, input: %s, display: %s, "
151  "view: %s, Inverse: %d\n",
152  s->config_path, s->input_space, s->display, s->view, s->inverse);
153  }
154  if (!s->ocio) {
155  av_log(ctx, AV_LOG_ERROR, "Failed to create OCIO processor.\n");
156  return AVERROR(EINVAL);
157  }
158 
159  return 0;
160 }
161 
163 {
164  AVFilterContext *ctx = inlink->dst;
165  OCIOContext *s = ctx->priv;
167 
168  if (!desc)
169  return AVERROR(EINVAL);
170 
171  int ret;
173  ThreadData td;
174 
175  if (s->output_format == inlink->format) {
176  /* No pixel-format conversion needed. If the input frame is
177  * writable we can apply OCIO in-place, otherwise allocate a
178  * separate output frame to avoid mutating shared buffers. */
181  } else {
183  if (!output_frame) {
185  return AVERROR(ENOMEM);
186  }
187  output_frame->format = s->output_format;
188  output_frame->width = frame->width;
189  output_frame->height = frame->height;
191 
192  if (ret < 0) {
195  return ret;
196  }
198  }
199  } else {
200  // Allocate new output frame
202  if (!output_frame) {
204  return AVERROR(ENOMEM);
205  }
206  output_frame->format = s->output_format;
207  output_frame->width = frame->width;
208  output_frame->height = frame->height;
210 
211  if (ret < 0) {
214  return ret;
215  }
217  }
218 
219  td.in = frame;
220  td.out = output_frame;
221 
222  // Use threads from context if set, otherwise let ffmpeg decide based on global settings or defaults
223  // Note: ctx->graph->nb_threads is usually the global thread count.
224  // ff_filter_get_nb_threads(ctx) gives the number of threads available for this filter.
225 
226  int nb_jobs = ff_filter_get_nb_threads(ctx);
227 
228  ret = ff_filter_execute(ctx, ocio_filter_slice, &td, NULL, FFMIN(output_frame->height, nb_jobs));
229 
230  if (frame != output_frame)
232 
233  if (ret < 0) {
234  av_log(ctx, AV_LOG_ERROR, "OCIO apply failed.\n");
235  return AVERROR(EINVAL);
236  }
237 
238  return ff_filter_frame(ctx->outputs[0], output_frame);
239 }
240 
242 {
243  OCIOContext *s = ctx->priv;
244  if (s->ocio) {
245  ocio_destroy_processor(ctx, s->ocio);
246  s->ocio = NULL;
247  }
248 }
249 
250 #define OFFSET(x) offsetof(OCIOContext, x)
251 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM
252 
253 static const AVOption ocio_options[] = {
254  { "config", "OCIO config path, overriding OCIO environment variable.", OFFSET(config_path), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
255  { "input", "Input color space", OFFSET(input_space), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
256  { "output", "Output color space", OFFSET(output_space), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
257  { "filetransform", "Specify a File Transform", OFFSET(filetransform), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
258  { "display", "Output display, used instead of output color space.", OFFSET(display), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
259  { "view", "View, output view transform, used in combination with display.", OFFSET(view), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
260  { "inverse", "Invert output display/view transform.", OFFSET(inverse), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS },
261  { "format", "Output video format", OFFSET(out_format_string), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
262  { "context_params", "OCIO context parameters", OFFSET(context_params), AV_OPT_TYPE_DICT, { .str = NULL }, 0, 0, FLAGS },{ NULL }
263 };
264 
266 
267 static const AVFilterPad inputs[] = {
268  {
269  .name = "default",
270  .type = AVMEDIA_TYPE_VIDEO,
271  .filter_frame = filter_frame,
272  .config_props = config_props,
273  },
274 };
275 
276 static const AVFilterPad outputs[] = {
277  {.name = "default", .type = AVMEDIA_TYPE_VIDEO}};
278 
280  .p.name = "ocio",
281  .p.description = NULL_IF_CONFIG_SMALL("Apply OCIO Display/View transform"),
282  .p.priv_class = &ocio_class,
283  .p.flags = AVFILTER_FLAG_SLICE_THREADS,
284  .priv_size = sizeof(OCIOContext),
285  .init = init,
286  .uninit = uninit,
OCIOContext::ocio
OCIOHandle ocio
Definition: vf_opencolorio.c:39
AVPixelFormat
AVPixelFormat
Pixel format.
Definition: pixfmt.h:71
OCIOContext::view
char * view
Definition: vf_opencolorio.c:36
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
opt.h
out
static FILE * out
Definition: movenc.c:55
av_frame_get_buffer
int av_frame_get_buffer(AVFrame *frame, int align)
Allocate new buffer(s) for audio or video data.
Definition: frame.c:206
inverse
inverse
Definition: af_crystalizer.c:122
ocio_filter_slice
static int ocio_filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_opencolorio.c:51
ff_filter_frame
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1067
av_pix_fmt_desc_get
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:3456
inlink
The exact code depends on how similar the blocks are and how related they are to the and needs to apply these operations to the correct inlink or outlink if there are several Macros are available to factor that when no extra processing is inlink
Definition: filter_design.txt:212
AV_PIX_FMT_FLAG_FLOAT
#define AV_PIX_FMT_FLAG_FLOAT
The pixel format contains IEEE-754 floating point values.
Definition: pixdesc.h:158
av_frame_free
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:64
FILTER_INPUTS
#define FILTER_INPUTS(array)
Definition: filters.h:264
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:427
pixdesc.h
ff_vf_ocio
const FFFilter ff_vf_ocio
Definition: vf_opencolorio.c:279
AVOption
AVOption.
Definition: opt.h:429
FLAGS
#define FLAGS
Definition: vf_opencolorio.c:251
ocio_apply
int ocio_apply(AVFilterContext *ctx, OCIOHandle handle, AVFrame *input_frame, AVFrame *output_frame, int y_start, int height)
Definition: ocio_wrapper.cpp:275
AVDictionary
Definition: dict.c:32
inputs
static const AVFilterPad inputs[]
Definition: vf_opencolorio.c:267
AVFilter::name
const char * name
Filter name.
Definition: avfilter.h:220
ThreadData::out
AVFrame * out
Definition: af_adeclick.c:526
video.h
ThreadData::in
AVFrame * in
Definition: af_adecorrelate.c:155
config_props
static int config_props(AVFilterLink *inlink)
Definition: vf_opencolorio.c:82
formats.h
slice_end
static int slice_end(AVCodecContext *avctx, AVFrame *pict, int *got_output)
Handle slice ends.
Definition: mpeg12dec.c:1693
AV_PIX_FMT_GBRP10
#define AV_PIX_FMT_GBRP10
Definition: pixfmt.h:558
ocio_wrapper.hpp
AVFilterPad
A filter pad used for either input or output.
Definition: filters.h:40
av_frame_alloc
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
Definition: frame.c:52
ocio_create_output_colorspace_processor
OCIOHandle ocio_create_output_colorspace_processor(AVFilterContext *ctx, const char *config_path, const char *input_color_space, const char *output_color_space, AVDictionary *params)
Definition: ocio_wrapper.cpp:99
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
outputs
static const AVFilterPad outputs[]
Definition: vf_opencolorio.c:276
av_cold
#define av_cold
Definition: attributes.h:106
ff_set_common_formats
int ff_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats)
A helper for query_formats() which sets all links to the same list of formats.
Definition: formats.c:962
FFFilter
Definition: filters.h:267
AV_PIX_FMT_GBRAP10
#define AV_PIX_FMT_GBRAP10
Definition: pixfmt.h:562
uninit
static av_cold void uninit(AVFilterContext *ctx)
Definition: vf_opencolorio.c:241
s
#define s(width, name)
Definition: cbs_vp9.c:198
AV_PIX_FMT_GBRAP12
#define AV_PIX_FMT_GBRAP12
Definition: pixfmt.h:563
OCIOContext::output_space
char * output_space
Definition: vf_opencolorio.c:34
ff_make_format_list
av_warn_unused_result AVFilterFormats * ff_make_format_list(const int *fmts)
Create a list of supported formats.
pix_fmts
static enum AVPixelFormat pix_fmts[]
Definition: libkvazaar.c:296
ctx
static AVFormatContext * ctx
Definition: movenc.c:49
OCIOContext::display
char * display
Definition: vf_opencolorio.c:35
FILTER_OUTPUTS
#define FILTER_OUTPUTS(array)
Definition: filters.h:265
AV_PIX_FMT_RGBA
@ AV_PIX_FMT_RGBA
packed RGBA 8:8:8:8, 32bpp, RGBARGBA...
Definition: pixfmt.h:100
arg
const char * arg
Definition: jacosubdec.c:65
AV_PIX_FMT_RGBA64
#define AV_PIX_FMT_RGBA64
Definition: pixfmt.h:529
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:76
NULL
#define NULL
Definition: coverity.c:32
av_frame_copy_props
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition: frame.c:599
OCIOContext::out_format_string
char * out_format_string
Definition: vf_opencolorio.c:41
AV_OPT_TYPE_DICT
@ AV_OPT_TYPE_DICT
Underlying C type is AVDictionary*.
Definition: opt.h:290
AVFILTER_DEFINE_CLASS
AVFILTER_DEFINE_CLASS(ocio)
OCIOContext::inverse
int inverse
Definition: vf_opencolorio.c:38
time.h
AV_PIX_FMT_GBRPF16
#define AV_PIX_FMT_GBRPF16
Definition: pixfmt.h:576
AV_PIX_FMT_RGB24
@ AV_PIX_FMT_RGB24
packed RGB 8:8:8, 24bpp, RGBRGB...
Definition: pixfmt.h:75
query_formats
static int query_formats(AVFilterContext *ctx)
Definition: vf_opencolorio.c:65
NULL_IF_CONFIG_SMALL
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:94
OCIOContext::config_path
char * config_path
Definition: vf_opencolorio.c:32
height
#define height
Definition: dsp.h:89
AV_PIX_FMT_GBRPF32
#define AV_PIX_FMT_GBRPF32
Definition: pixfmt.h:578
OCIOContext::channels
int channels
Definition: vf_opencolorio.c:43
AV_PIX_FMT_RGB48
#define AV_PIX_FMT_RGB48
Definition: pixfmt.h:525
av_frame_is_writable
int av_frame_is_writable(AVFrame *frame)
Check if the frame data is writable.
Definition: frame.c:535
OCIOContext::filetransform
char * filetransform
Definition: vf_opencolorio.c:37
ocio_create_file_transform_processor
OCIOHandle ocio_create_file_transform_processor(AVFilterContext *ctx, const char *file_transform, int inverse)
Definition: ocio_wrapper.cpp:183
output_frame
static int output_frame(H264Context *h, AVFrame *dst, H264Picture *srcp)
Definition: h264dec.c:861
AVERROR_EXTERNAL
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition: error.h:59
OCIOContext::output_format
int output_format
Definition: vf_opencolorio.c:40
OCIOContext::context_params
AVDictionary * context_params
Definition: vf_opencolorio.c:44
ocio_create_display_view_processor
OCIOHandle ocio_create_display_view_processor(AVFilterContext *ctx, const char *config_path, const char *input_color_space, const char *display, const char *view, int inverse, AVDictionary *params)
Definition: ocio_wrapper.cpp:137
half2float.h
AV_LOG_INFO
#define AV_LOG_INFO
Standard information.
Definition: log.h:221
filter_frame
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
Definition: vf_opencolorio.c:162
AV_PIX_FMT_GBRP12
#define AV_PIX_FMT_GBRP12
Definition: pixfmt.h:559
ff_filter_get_nb_threads
int ff_filter_get_nb_threads(AVFilterContext *ctx)
Get number of threads for current filter instance.
Definition: avfilter.c:845
ThreadData
Used for passing data between threads.
Definition: dsddec.c:71
FFMIN
#define FFMIN(a, b)
Definition: macros.h:49
AVFilterPad::name
const char * name
Pad name.
Definition: filters.h:46
slice_start
static int slice_start(SliceContext *sc, VVCContext *s, VVCFrameContext *fc, const CodedBitstreamUnit *unit, const int is_first_slice)
Definition: dec.c:844
ret
ret
Definition: filter_design.txt:187
frame
these buffered frames must be flushed immediately if a new input produces new the filter must not call request_frame to get more It must just process the frame or queue it The task of requesting more frames is left to the filter s request_frame method or the application If a filter has several the filter must be ready for frames arriving randomly on any input any filter with several inputs will most likely require some kind of queuing mechanism It is perfectly acceptable to have a limited queue and to drop frames when the inputs are too unbalanced request_frame For filters that do not use the this method is called when a frame is wanted on an output For a it should directly call filter_frame on the corresponding output For a if there are queued frames already one of these frames should be pushed If the filter should request a frame on one of its repeatedly until at least one frame has been pushed Return or at least make progress towards producing a frame
Definition: filter_design.txt:265
av_get_pix_fmt
enum AVPixelFormat av_get_pix_fmt(const char *name)
Return the pixel format corresponding to name.
Definition: pixdesc.c:3388
ff_filter_execute
int ff_filter_execute(AVFilterContext *ctx, avfilter_action_func *func, void *arg, int *ret, int nb_jobs)
Definition: avfilter.c:1691
AV_PIX_FMT_NONE
@ AV_PIX_FMT_NONE
Definition: pixfmt.h:72
AV_OPT_TYPE_INT
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition: opt.h:259
avfilter.h
AV_PIX_FMT_GBRAPF32
#define AV_PIX_FMT_GBRAPF32
Definition: pixfmt.h:579
AV_PIX_FMT_GBRAPF16
#define AV_PIX_FMT_GBRAPF16
Definition: pixfmt.h:577
AVFilterContext
An instance of a filter.
Definition: avfilter.h:274
OCIOHandle
void * OCIOHandle
Definition: ocio_wrapper.hpp:28
AVFILTER_FLAG_SLICE_THREADS
#define AVFILTER_FLAG_SLICE_THREADS
The filter supports multithreading by splitting frames into multiple parts and processing them concur...
Definition: avfilter.h:167
desc
const char * desc
Definition: libsvtav1.c:78
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:200
FFFilter::p
AVFilter p
The public AVFilter.
Definition: filters.h:271
AVPixFmtDescriptor
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition: pixdesc.h:69
ocio_destroy_processor
void ocio_destroy_processor(AVFilterContext *ctx, OCIOHandle handle)
Definition: ocio_wrapper.cpp:309
ocio_options
static const AVOption ocio_options[]
Definition: vf_opencolorio.c:253
init
static av_cold int init(AVFilterContext *ctx)
Definition: vf_opencolorio.c:126
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
OFFSET
#define OFFSET(x)
Definition: vf_opencolorio.c:250
AV_OPT_TYPE_STRING
@ AV_OPT_TYPE_STRING
Underlying C type is a uint8_t* that is either NULL or points to a C string allocated with the av_mal...
Definition: opt.h:276
OCIOContext
Definition: vf_opencolorio.c:30
FILTER_QUERY_FUNC
#define FILTER_QUERY_FUNC(func)
Definition: filters.h:238
OCIOContext::input_space
char * input_space
Definition: vf_opencolorio.c:33
ocio_finalize_processor
int ocio_finalize_processor(AVFilterContext *ctx, OCIOHandle handle, int input_format, int output_format)
Definition: ocio_wrapper.cpp:214
av_get_pix_fmt_name
const char * av_get_pix_fmt_name(enum AVPixelFormat pix_fmt)
Return the short name for a pixel format, NULL in case pix_fmt is unknown.
Definition: pixdesc.c:3376