• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1This document is a tutorial/initiation for writing simple filters in
2libavfilter.
3
4Foreword: just like everything else in FFmpeg, libavfilter is monolithic, which
5means that it is highly recommended that you submit your filters to the FFmpeg
6development mailing-list and make sure that they are applied. Otherwise, your filters
7are likely to have a very short lifetime due to more or less regular internal API
8changes, and a limited distribution, review, and testing.
9
10Bootstrap
11=========
12
13Let's say you want to write a new simple video filter called "foobar" which
14takes one frame in input, changes the pixels in whatever fashion you fancy, and
15outputs the modified frame. The most simple way of doing this is to take a
16similar filter.  We'll pick edgedetect, but any other should do. You can look
17for others using the `./ffmpeg -v 0 -filters|grep ' V->V '` command.
18
19 - sed 's/edgedetect/foobar/g;s/EdgeDetect/Foobar/g' libavfilter/vf_edgedetect.c > libavfilter/vf_foobar.c
20 - edit libavfilter/Makefile, and add an entry for "foobar" following the
21   pattern of the other filters.
22 - edit libavfilter/allfilters.c, and add an entry for "foobar" following the
23   pattern of the other filters.
24 - ./configure ...
25 - make -j<whatever> ffmpeg
26 - ./ffmpeg -i http://samples.ffmpeg.org/image-samples/lena.pnm -vf foobar foobar.png
27   Note here: you can obviously use a random local image instead of a remote URL.
28
29If everything went right, you should get a foobar.png with Lena edge-detected.
30
31That's it, your new playground is ready.
32
33Some little details about what's going on:
34libavfilter/allfilters.c:this file is parsed by the configure script, which in turn
35will define variables for the build system and the C:
36
37    --- after running configure ---
38
39    $ grep FOOBAR ffbuild/config.mak
40    CONFIG_FOOBAR_FILTER=yes
41    $ grep FOOBAR config.h
42    #define CONFIG_FOOBAR_FILTER 1
43
44CONFIG_FOOBAR_FILTER=yes from the ffbuild/config.mak is later used to enable
45the filter in libavfilter/Makefile and CONFIG_FOOBAR_FILTER=1 from the config.h
46will be used for registering the filter in libavfilter/allfilters.c.
47
48Filter code layout
49==================
50
51You now need some theory about the general code layout of a filter. Open your
52libavfilter/vf_foobar.c. This section will detail the important parts of the
53code you need to understand before messing with it.
54
55Copyright
56---------
57
58First chunk is the copyright. Most filters are LGPL, and we are assuming
59vf_foobar is as well. We are also assuming vf_foobar is not an edge detector
60filter, so you can update the boilerplate with your credits.
61
62Doxy
63----
64
65Next chunk is the Doxygen about the file. See https://ffmpeg.org/doxygen/trunk/.
66Detail here what the filter is, does, and add some references if you feel like
67it.
68
69Context
70-------
71
72Skip the headers and scroll down to the definition of FoobarContext. This is
73your local state context. It is already filled with 0 when you get it so do not
74worry about uninitialized reads into this context. This is where you put all
75"global" information that you need; typically the variables storing the user options.
76You'll notice the first field "const AVClass *class"; it's the only field you
77need to keep assuming you have a context. There is some magic you don't need to
78care about around this field, just let it be (in the first position) for now.
79
80Options
81-------
82
83Then comes the options array. This is what will define the user accessible
84options. For example, -vf foobar=mode=colormix:high=0.4:low=0.1. Most options
85have the following pattern:
86  name, description, offset, type, default value, minimum value, maximum value, flags
87
88 - name is the option name, keep it simple and lowercase
89 - description are short, in lowercase, without period, and describe what they
90   do, for example "set the foo of the bar"
91 - offset is the offset of the field in your local context, see the OFFSET()
92   macro; the option parser will use that information to fill the fields
93   according to the user input
94 - type is any of AV_OPT_TYPE_* defined in libavutil/opt.h
95 - default value is an union where you pick the appropriate type; "{.dbl=0.3}",
96   "{.i64=0x234}", "{.str=NULL}", ...
97 - min and max values define the range of available values, inclusive
98 - flags are AVOption generic flags. See AV_OPT_FLAG_* definitions
99
100When in doubt, just look at the other AVOption definitions all around the codebase,
101there are tons of examples.
102
103Class
104-----
105
106AVFILTER_DEFINE_CLASS(foobar) will define a unique foobar_class with some kind
107of signature referencing the options, etc. which will be referenced in the
108definition of the AVFilter.
109
110Filter definition
111-----------------
112
113At the end of the file, you will find foobar_inputs, foobar_outputs and
114the AVFilter ff_vf_foobar. Don't forget to update the AVFilter.description with
115a description of what the filter does, starting with a capitalized letter and
116ending with a period. You'd better drop the AVFilter.flags entry for now, and
117re-add them later depending on the capabilities of your filter.
118
119Callbacks
120---------
121
122Let's now study the common callbacks. Before going into details, note that all
123these callbacks are explained in details in libavfilter/avfilter.h, so in
124doubt, refer to the doxy in that file.
125
126init()
127~~~~~~
128
129First one to be called is init(). It's flagged as cold because not called
130often. Look for "cold" on
131http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html for more
132information.
133
134As the name suggests, init() is where you eventually initialize and allocate
135your buffers, pre-compute your data, etc. Note that at this point, your local
136context already has the user options initialized, but you still haven't any
137clue about the kind of data input you will get, so this function is often
138mainly used to sanitize the user options.
139
140Some init()s will also define the number of inputs or outputs dynamically
141according to the user options. A good example of this is the split filter, but
142we won't cover this here since vf_foobar is just a simple 1:1 filter.
143
144uninit()
145~~~~~~~~
146
147Similarly, there is the uninit() callback, doing what the name suggests. Free
148everything you allocated here.
149
150query_formats()
151~~~~~~~~~~~~~~~
152
153This follows the init() and is used for the format negotiation. Basically
154you specify here what pixel format(s) (gray, rgb 32, yuv 4:2:0, ...) you accept
155for your inputs, and what you can output. All pixel formats are defined in
156libavutil/pixfmt.h. If you don't change the pixel format between the input and
157the output, you just have to define a pixel formats array and call
158ff_set_common_formats(). For more complex negotiation, you can refer to other
159filters such as vf_scale.
160
161config_props()
162~~~~~~~~~~~~~~
163
164This callback is not necessary, but you will probably have one or more
165config_props() anyway. It's not a callback for the filter itself but for its
166inputs or outputs (they're called "pads" - AVFilterPad - in libavfilter's
167lexicon).
168
169Inside the input config_props(), you are at a point where you know which pixel
170format has been picked after query_formats(), and more information such as the
171video width and height (inlink->{w,h}). So if you need to update your internal
172context state depending on your input you can do it here. In edgedetect you can
173see that this callback is used to allocate buffers depending on these
174information. They will be destroyed in uninit().
175
176Inside the output config_props(), you can define what you want to change in the
177output. Typically, if your filter is going to double the size of the video, you
178will update outlink->w and outlink->h.
179
180filter_frame()
181~~~~~~~~~~~~~~
182
183This is the callback you are waiting for from the beginning: it is where you
184process the received frames. Along with the frame, you get the input link from
185where the frame comes from.
186
187    static int filter_frame(AVFilterLink *inlink, AVFrame *in) { ... }
188
189You can get the filter context through that input link:
190
191    AVFilterContext *ctx = inlink->dst;
192
193Then access your internal state context:
194
195    FoobarContext *foobar = ctx->priv;
196
197And also the output link where you will send your frame when you are done:
198
199    AVFilterLink *outlink = ctx->outputs[0];
200
201Here, we are picking the first output. You can have several, but in our case we
202only have one since we are in a 1:1 input-output situation.
203
204If you want to define a simple pass-through filter, you can just do:
205
206    return ff_filter_frame(outlink, in);
207
208But of course, you probably want to change the data of that frame.
209
210This can be done by accessing frame->data[] and frame->linesize[].  Important
211note here: the width does NOT match the linesize. The linesize is always
212greater or equal to the width. The padding created should not be changed or
213even read. Typically, keep in mind that a previous filter in your chain might
214have altered the frame dimension but not the linesize. Imagine a crop filter
215that halves the video size: the linesizes won't be changed, just the width.
216
217    <-------------- linesize ------------------------>
218    +-------------------------------+----------------+ ^
219    |                               |                | |
220    |                               |                | |
221    |           picture             |    padding     | | height
222    |                               |                | |
223    |                               |                | |
224    +-------------------------------+----------------+ v
225    <----------- width ------------->
226
227Before modifying the "in" frame, you have to make sure it is writable, or get a
228new one. Multiple scenarios are possible here depending on the kind of
229processing you are doing.
230
231Let's say you want to change one pixel depending on multiple pixels (typically
232the surrounding ones) of the input. In that case, you can't do an in-place
233processing of the input so you will need to allocate a new frame, with the same
234properties as the input one, and send that new frame to the next filter:
235
236    AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
237    if (!out) {
238        av_frame_free(&in);
239        return AVERROR(ENOMEM);
240    }
241    av_frame_copy_props(out, in);
242
243    // out->data[...] = foobar(in->data[...])
244
245    av_frame_free(&in);
246    return ff_filter_frame(outlink, out);
247
248In-place processing
249~~~~~~~~~~~~~~~~~~~
250
251If you can just alter the input frame, you probably just want to do that
252instead:
253
254    av_frame_make_writable(in);
255    // in->data[...] = foobar(in->data[...])
256    return ff_filter_frame(outlink, in);
257
258You may wonder why a frame might not be writable. The answer is that for
259example a previous filter might still own the frame data: imagine a filter
260prior to yours in the filtergraph that needs to cache the frame. You must not
261alter that frame, otherwise it will make that previous filter buggy. This is
262where av_frame_make_writable() helps (it won't have any effect if the frame
263already is writable).
264
265The problem with using av_frame_make_writable() is that in the worst case it
266will copy the whole input frame before you change it all over again with your
267filter: if the frame is not writable, av_frame_make_writable() will allocate
268new buffers, and copy the input frame data. You don't want that, and you can
269avoid it by just allocating a new buffer if necessary, and process from in to
270out in your filter, saving the memcpy. Generally, this is done following this
271scheme:
272
273    int direct = 0;
274    AVFrame *out;
275
276    if (av_frame_is_writable(in)) {
277        direct = 1;
278        out = in;
279    } else {
280        out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
281        if (!out) {
282            av_frame_free(&in);
283            return AVERROR(ENOMEM);
284        }
285        av_frame_copy_props(out, in);
286    }
287
288    // out->data[...] = foobar(in->data[...])
289
290    if (!direct)
291        av_frame_free(&in);
292    return ff_filter_frame(outlink, out);
293
294Of course, this will only work if you can do in-place processing. To test if
295your filter handles well the permissions, you can use the perms filter. For
296example with:
297
298    -vf perms=random,foobar
299
300Make sure no automatic pixel conversion is inserted between perms and foobar,
301otherwise the frames permissions might change again and the test will be
302meaningless: add av_log(0,0,"direct=%d\n",direct) in your code to check that.
303You can avoid the issue with something like:
304
305    -vf format=rgb24,perms=random,foobar
306
307...assuming your filter accepts rgb24 of course. This will make sure the
308necessary conversion is inserted before the perms filter.
309
310Timeline
311~~~~~~~~
312
313Adding timeline support
314(http://ffmpeg.org/ffmpeg-filters.html#Timeline-editing) is often an easy
315feature to add. In the most simple case, you just have to add
316AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC to the AVFilter.flags. You can typically
317do this when your filter does not need to save the previous context frames, or
318basically if your filter just alters whatever goes in and doesn't need
319previous/future information. See for instance commit 86cb986ce that adds
320timeline support to the fieldorder filter.
321
322In some cases, you might need to reset your context somehow. This is handled by
323the AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL flag which is used if the filter
324must not process the frames but still wants to keep track of the frames going
325through (to keep them in cache for when it's enabled again). See for example
326commit 69d72140a that adds timeline support to the phase filter.
327
328Threading
329~~~~~~~~~
330
331libavfilter does not yet support frame threading, but you can add slice
332threading to your filters.
333
334Let's say the foobar filter has the following frame processing function:
335
336    dst = out->data[0];
337    src = in ->data[0];
338
339    for (y = 0; y < inlink->h; y++) {
340        for (x = 0; x < inlink->w; x++)
341            dst[x] = foobar(src[x]);
342        dst += out->linesize[0];
343        src += in ->linesize[0];
344    }
345
346The first thing is to make this function work into slices. The new code will
347look like this:
348
349    for (y = slice_start; y < slice_end; y++) {
350        for (x = 0; x < inlink->w; x++)
351            dst[x] = foobar(src[x]);
352        dst += out->linesize[0];
353        src += in ->linesize[0];
354    }
355
356The source and destination pointers, and slice_start/slice_end will be defined
357according to the number of jobs. Generally, it looks like this:
358
359    const int slice_start = (in->height *  jobnr   ) / nb_jobs;
360    const int slice_end   = (in->height * (jobnr+1)) / nb_jobs;
361    uint8_t       *dst = out->data[0] + slice_start * out->linesize[0];
362    const uint8_t *src =  in->data[0] + slice_start *  in->linesize[0];
363
364This new code will be isolated in a new filter_slice():
365
366    static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ... }
367
368Note that we need our input and output frame to define slice_{start,end} and
369dst/src, which are not available in that callback. They will be transmitted
370through the opaque void *arg. You have to define a structure which contains
371everything you need:
372
373    typedef struct ThreadData {
374        AVFrame *in, *out;
375    } ThreadData;
376
377If you need some more information from your local context, put them here.
378
379In you filter_slice function, you access it like that:
380
381    const ThreadData *td = arg;
382
383Then in your filter_frame() callback, you need to call the threading
384distributor with something like this:
385
386    ThreadData td;
387
388    // ...
389
390    td.in  = in;
391    td.out = out;
392    ctx->internal->execute(ctx, filter_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
393
394    // ...
395
396    return ff_filter_frame(outlink, out);
397
398Last step is to add AVFILTER_FLAG_SLICE_THREADS flag to AVFilter.flags.
399
400For more example of slice threading additions, you can try to run git log -p
401--grep 'slice threading' libavfilter/
402
403Finalization
404~~~~~~~~~~~~
405
406When your awesome filter is finished, you have a few more steps before you're
407done:
408
409 - write its documentation in doc/filters.texi, and test the output with make
410   doc/ffmpeg-filters.html.
411 - add a FATE test, generally by adding an entry in
412   tests/fate/filter-video.mak, add running make fate-filter-foobar GEN=1 to
413   generate the data.
414 - add an entry in the Changelog
415 - edit libavfilter/version.h and increase LIBAVFILTER_VERSION_MINOR by one
416   (and reset LIBAVFILTER_VERSION_MICRO to 100)
417 - git add ... && git commit -m "avfilter: add foobar filter." && git format-patch -1
418
419When all of this is done, you can submit your patch to the ffmpeg-devel
420mailing-list for review.  If you need any help, feel free to come on our IRC
421channel, #ffmpeg-devel on irc.libera.chat.
422