FFmpeg
Loading...
Searching...
No Matches
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 "filters.h"
23#include "formats.h"
25#include "libavutil/opt.h"
26#include "libavutil/pixdesc.h"
27#include "libavutil/time.h"
28#include "ocio_wrapper.hpp"
29#include "video.h"
30
31typedef struct {
32 const AVClass *class;
36 char *display;
37 char *view;
42 char *out_format_string; // e.g. "rgb48le" which is converted to AVPixelFormat
43 // as output_format
44 int channels; // 3 or 4 depending on pixfmt
47
48typedef struct ThreadData {
49 AVFrame *in, *out;
51
52static int ocio_filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
53{
54 OCIOContext *s = ctx->priv;
55 ThreadData *td = arg;
56 AVFrame *in = td->in;
57 AVFrame *out = td->out;
58 const int height = out->height;
59 const int slice_start = ff_slice_pos(height, jobnr, nb_jobs);
60 const int slice_end = ff_slice_pos(height, jobnr + 1, nb_jobs);
61 const int slice_h = slice_end - slice_start;
62
63 return ocio_apply(ctx, s->ocio, in, out, slice_start, slice_h);
64}
65
82
83static int config_props(AVFilterLink *inlink)
84{
85 AVFilterContext *ctx = inlink->dst;
86 OCIOContext *s = ctx->priv;
88
89 if (!desc) {
90 av_log(ctx, AV_LOG_ERROR, "Invalid pixel format\n");
91 return AVERROR(EINVAL);
92 }
93
94 int is_half =
95 (desc->comp[0].depth == 16 && desc->flags & AV_PIX_FMT_FLAG_FLOAT);
96 if (s->output_format == AV_PIX_FMT_NONE) {
97 // Need to set the output format now, if not known.
98 if (is_half) {
99 // If its half-float, we output float, due to a bug in ffmpeg with
100 // half-float frames
101 s->output_format = AV_PIX_FMT_GBRAPF32;
102 } else {
103 // If output format not set, use same as input
104 s->output_format = inlink->format;
105 }
106 }
107
108 s->channels = desc->nb_components; // 3 or 4
109
111 "Configuring OCIO for %s (bit depth: %d, channels: %d), output "
112 "format: (%s)\n",
113 av_get_pix_fmt_name(inlink->format), desc->comp[0].depth, s->channels,
114 av_get_pix_fmt_name(s->output_format));
115
116 // Now finalize the OCIO processor with the correct bit depth
117 int ret = ocio_finalize_processor(ctx, s->ocio, inlink->format, s->output_format);
118 if (ret < 0) {
120 "Failed to finalize OCIO processor for bit depth\n");
121 return AVERROR_EXTERNAL;
122 }
123
124 return 0;
125}
126
128{
129 OCIOContext *s = ctx->priv;
130 if (s->out_format_string != NULL)
131 s->output_format = av_get_pix_fmt(s->out_format_string);
132 else
133 s->output_format = AV_PIX_FMT_NONE; // Default to same as input format (see later).
134
135 if (s->filetransform && strlen(s->filetransform) > 0) {
137 ctx, s->filetransform, s->inverse);
139 "Creating OCIO processor with FileTransform: %s, Inverse: %d\n",
140 s->filetransform, s->inverse);
141 } else if (s->output_space && strlen(s->output_space) > 0) {
143 ctx, s->config_path, s->input_space, s->output_space, s->context_params);
145 "Creating OCIO processor with config: %s, input: %s, output: %s\n",
146 s->config_path, s->input_space, s->output_space);
147 } else {
149 ctx, s->config_path, s->input_space, s->display, s->view, s->inverse, s->context_params);
151 "Creating OCIO processor with config: %s, input: %s, display: %s, "
152 "view: %s, Inverse: %d\n",
153 s->config_path, s->input_space, s->display, s->view, s->inverse);
154 }
155 if (!s->ocio) {
156 av_log(ctx, AV_LOG_ERROR, "Failed to create OCIO processor.\n");
157 return AVERROR(EINVAL);
158 }
159
160 return 0;
161}
162
164{
165 AVFilterContext *ctx = inlink->dst;
166 OCIOContext *s = ctx->priv;
168
169 if (!desc)
170 return AVERROR(EINVAL);
171
172 int ret;
174 ThreadData td;
175
176 if (s->output_format == inlink->format) {
177 /* No pixel-format conversion needed. If the input frame is
178 * writable we can apply OCIO in-place, otherwise allocate a
179 * separate output frame to avoid mutating shared buffers. */
182 } else {
184 if (!output_frame) {
186 return AVERROR(ENOMEM);
187 }
188 output_frame->format = s->output_format;
189 output_frame->width = frame->width;
190 output_frame->height = frame->height;
192
193 if (ret < 0) {
196 return ret;
197 }
199 }
200 } else {
201 // Allocate new output frame
203 if (!output_frame) {
205 return AVERROR(ENOMEM);
206 }
207 output_frame->format = s->output_format;
208 output_frame->width = frame->width;
209 output_frame->height = frame->height;
211
212 if (ret < 0) {
215 return ret;
216 }
218 }
219
220 td.in = frame;
221 td.out = output_frame;
222
223 // Use threads from context if set, otherwise let ffmpeg decide based on global settings or defaults
224 // Note: ctx->graph->nb_threads is usually the global thread count.
225 // ff_filter_get_nb_threads(ctx) gives the number of threads available for this filter.
226
227 int nb_jobs = ff_filter_get_nb_threads(ctx);
228
229 ret = ff_filter_execute(ctx, ocio_filter_slice, &td, NULL, FFMIN(output_frame->height, nb_jobs));
230
231 if (frame != output_frame)
233
234 if (ret < 0) {
235 av_log(ctx, AV_LOG_ERROR, "OCIO apply failed.\n");
236 return AVERROR(EINVAL);
237 }
238
239 return ff_filter_frame(ctx->outputs[0], output_frame);
240}
241
243{
244 OCIOContext *s = ctx->priv;
245 if (s->ocio) {
247 s->ocio = NULL;
248 }
249}
250
251#define OFFSET(x) offsetof(OCIOContext, x)
252#define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM
253
254static const AVOption ocio_options[] = {
255 { "config", "OCIO config path, overriding OCIO environment variable.", OFFSET(config_path), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
256 { "input", "Input color space", OFFSET(input_space), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
257 { "output", "Output color space", OFFSET(output_space), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
258 { "filetransform", "Specify a File Transform", OFFSET(filetransform), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
259 { "display", "Output display, used instead of output color space.", OFFSET(display), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
260 { "view", "View, output view transform, used in combination with display.", OFFSET(view), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
261 { "inverse", "Invert output display/view transform.", OFFSET(inverse), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS },
262 { "format", "Output video format", OFFSET(out_format_string), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
263 { "context_params", "OCIO context parameters", OFFSET(context_params), AV_OPT_TYPE_DICT, { .str = NULL }, 0, 0, FLAGS },{ NULL }
264};
265
267
268static const AVFilterPad inputs[] = {
269 {
270 .name = "default",
271 .type = AVMEDIA_TYPE_VIDEO,
272 .filter_frame = filter_frame,
273 .config_props = config_props,
274 },
275};
276
277static const AVFilterPad outputs[] = {
278 {.name = "default", .type = AVMEDIA_TYPE_VIDEO}};
279
281 .p.name = "ocio",
282 .p.description = NULL_IF_CONFIG_SMALL("Apply OCIO Display/View transform"),
283 .p.priv_class = &ocio_class,
285 .priv_size = sizeof(OCIOContext),
286 .init = init,
287 .uninit = uninit,
static int query_formats(const AVFilterContext *ctx, AVFilterFormatsConfig **cfg_in, AVFilterFormatsConfig **cfg_out)
Definition aeval.c:246
static const AVFilterPad inputs[]
Definition af_aap.c:299
static const AVFilterPad outputs[]
Definition af_aap.c:310
inverse
const FFFilter ff_vf_ocio
static FILE * out
static AVFormatContext * ctx
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition avfilter.c:1068
int ff_filter_execute(AVFilterContext *ctx, avfilter_action_func *func, void *arg, int *ret, int nb_jobs)
Definition avfilter.c:1696
int ff_filter_get_nb_threads(AVFilterContext *ctx)
Get number of threads for current filter instance.
Definition avfilter.c:846
Main libavfilter public API header.
#define s(width, name)
Definition cbs_vp9.c:198
#define FLAGS
Definition cmdutils.c:598
#define NULL
Definition coverity.c:32
static AVFrame * frame
static int filter_frame(DBEDecodeContext *s, AVFrame *frame)
Definition dolby_e.c:1067
int(* init)(AVBSFContext *ctx)
Definition dts2pts.c:608
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:963
av_warn_unused_result AVFilterFormats * ff_make_format_list(const int *fmts)
Create a list of supported formats.
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
@ AV_OPT_TYPE_DICT
Underlying C type is AVDictionary*.
Definition opt.h:289
@ 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:275
#define AVFILTER_FLAG_SLICE_THREADS
The filter supports multithreading by splitting frames into multiple parts and processing them concur...
Definition avfilter.h:166
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition error.h:59
#define AVERROR(e)
Definition error.h:45
int av_frame_is_writable(AVFrame *frame)
Check if the frame data is writable.
Definition frame.c:535
int av_frame_get_buffer(AVFrame *frame, int align)
Allocate new buffer(s) for audio or video data.
Definition frame.c:206
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition frame.c:64
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition frame.c:599
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
Definition frame.c:52
#define AV_LOG_INFO
Standard information.
Definition log.h:221
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
static av_cold void uninit(AVBitStreamFilterContext *ctx)
static int output_frame(H264Context *h, AVFrame *dst, H264Picture *srcp)
Definition h264dec.c:860
const char * arg
Definition jacosubdec.c:65
#define FILTER_INPUTS(array)
Definition filters.h:264
#define FILTER_OUTPUTS(array)
Definition filters.h:265
static int ff_slice_pos(int total, int jobnr, int nb_jobs)
Compute the boundary index for a slice when work of size total is split into nb_jobs slices.
Definition filters.h:763
#define FILTER_QUERY_FUNC(func)
Definition filters.h:238
#define AVFILTER_DEFINE_CLASS(fname)
Definition filters.h:478
#define av_cold
Definition attributes.h:117
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
static enum AVPixelFormat pix_fmts[]
Definition libkvazaar.c:296
const char * desc
Definition libsvtav1.c:83
#define FFMIN(a, b)
Definition macros.h:49
static int slice_end(AVCodecContext *avctx, AVFrame *pict, int *got_output)
Handle slice ends.
Definition mpeg12dec.c:1697
void ocio_destroy_processor(AVFilterContext *ctx, OCIOHandle handle)
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)
OCIOHandle ocio_create_output_colorspace_processor(AVFilterContext *ctx, const char *config_path, const char *input_color_space, const char *output_color_space, AVDictionary *params)
int ocio_finalize_processor(AVFilterContext *ctx, OCIOHandle handle, int input_format, int output_format)
OCIOHandle ocio_create_file_transform_processor(AVFilterContext *ctx, const char *file_transform, int inverse)
int ocio_apply(AVFilterContext *ctx, OCIOHandle handle, AVFrame *input_frame, AVFrame *output_frame, int y_start, int height)
void * OCIOHandle
AVOptions.
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:3380
enum AVPixelFormat av_get_pix_fmt(const char *name)
Return the pixel format corresponding to name.
Definition pixdesc.c:3392
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition pixdesc.c:3460
#define AV_PIX_FMT_FLAG_FLOAT
The pixel format contains IEEE-754 floating point values.
Definition pixdesc.h:158
#define AV_PIX_FMT_GBRAP12
Definition pixfmt.h:569
#define AV_PIX_FMT_GBRPF32
Definition pixfmt.h:584
#define AV_PIX_FMT_GBRP10
Definition pixfmt.h:564
#define AV_PIX_FMT_GBRAPF16
Definition pixfmt.h:583
#define AV_PIX_FMT_RGBA64
Definition pixfmt.h:535
#define AV_PIX_FMT_GBRP12
Definition pixfmt.h:565
#define AV_PIX_FMT_RGB48
Definition pixfmt.h:531
AVPixelFormat
Pixel format.
Definition pixfmt.h:71
@ AV_PIX_FMT_NONE
Definition pixfmt.h:72
@ AV_PIX_FMT_RGB24
packed RGB 8:8:8, 24bpp, RGBRGB...
Definition pixfmt.h:75
@ AV_PIX_FMT_RGBA
packed RGBA 8:8:8:8, 32bpp, RGBARGBA...
Definition pixfmt.h:100
#define AV_PIX_FMT_GBRPF16
Definition pixfmt.h:582
#define AV_PIX_FMT_GBRAP10
Definition pixfmt.h:568
#define AV_PIX_FMT_GBRAPF32
Definition pixfmt.h:585
static int config_props(AVBitStreamFilterLink *link)
Definition source.c:181
Describe the class of an AVClass context structure.
Definition log.h:76
An instance of a filter.
Definition avfilter.h:273
A filter pad used for either input or output.
Definition filters.h:40
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
AVOption.
Definition opt.h:428
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition pixdesc.h:69
char * config_path
char * input_space
AVDictionary * context_params
char * filetransform
char * output_space
OCIOHandle ocio
char * out_format_string
Used for passing data between threads.
Definition dsddec.c:71
AVFrame * out
#define av_log(a,...)
#define height
Definition dsp.h:89
static int query_formats(AVFilterContext *ctx)
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
static const AVOption ocio_options[]
static int config_props(AVFilterLink *inlink)
static av_cold void uninit(AVFilterContext *ctx)
#define OFFSET(x)
static int ocio_filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
static int slice_start(SliceContext *sc, VVCContext *s, VVCFrameContext *fc, const CodedBitstreamUnit *unit, const int is_first_slice)
Definition dec.c:844