Opened 3 weeks ago

Closed 6 days ago

Last modified 26 hours ago

#10987 closed defect (fixed)

Blend video filter *_expr options don't handle store and load functions correctly.

Reported by: Fallan Owned by:
Priority: normal Component: avfilter
Version: git-master Keywords: "blend filter" "store and load" st() ld()
Cc: Fallan, Michael Koch Blocked By:
Blocking: Reproduced by developer: no
Analyzed by developer: no

Description

For example make a local gradient over normalized height 0.2 starting at 0.5, which will be used to transition between 2 input videos. E.G. the inputs could be Big Buck Bunny variations, the first having been self-modified by blend-glow and the second by blend-burn:
ffmpeg -i B3.mov -vf "split[F][G];[F][G]blend=all_mode=glow" -c:v ffv1 -an glow.mkv
and similarly burn.mkv with blend=c0_mode=burn. Now I should be able to do
ffmpeg -i burn.mkv -i glow.mkv -filter_complex "[0:v][1:v]blend=all_expr=st(0,clip((Y/H-0.5)/0.2\,0.0\,1.0))\;A*ld(0)+B*(1.0-ld(0))" -c:v libx265 -crf 16 -preset slow -an output.mkv
But the result has random snow obscuring the image. It works fine if I omit st() and replace the 2 ld()'s with clip()'s but that takes longer to run.
I am using ffmpeg version "2024-04-25-git-cae0f2bc55-full_build-www.gyan.dev" and it happens in older versions too.
I suspect this is known behavior which has been described in other context as "stored variables not having global scope" and they don't transmit between color planes but here we have the same color plane and frame, just different streams that are already in the same scope in avfilter. It sure would be beneficial if this worked.

Change History (9)

comment:1 by Michael Koch, 3 weeks ago

I can reproduce the problem.

This does not work:
ffmpeg -f lavfi -i color=red -f lavfi -i color=yellow -lavfi blend=all_expr='st(0,clip((Y/H-0.5)/0.2,0,1));A*ld(0)+B*(1.0-ld(0))' -t 5 -y out.mp4

But the same expression without ld() and st() does work:
ffmpeg -f lavfi -i color=red -f lavfi -i color=yellow -lavfi blend=all_expr='A*clip((Y/H-0.5)/0.2,0,1)+B*(1.0-clip((Y/H-0.5)/0.2,0,1))' -t 5 -y out.mp4

comment:2 by Michael Koch, 3 weeks ago

Simplified examples for reproducing:

This does not work:
ffmpeg -f lavfi -i color=red -f lavfi -i color=yellow -lavfi blend=all_expr='st(0,Y/H);A*ld(0)+B*(1-ld(0))' -frames 1 -y out1.png

This does work:
ffmpeg -f lavfi -i color=red -f lavfi -i color=yellow -lavfi blend=all_expr='A*Y/H+B*(1-Y/H)' -frames 1 -y out2.png

It seems the memory for variable 0 is overwritten by something else in most cases (but not always).

comment:3 by Michael Koch, 3 weeks ago

Component: avutilavfilter

comment:4 by Fallan, 3 weeks ago

Some more information that might be useful: I ran Michael Koch's simplified example using st and ld (with "-frames=25" to avoid error message) for ffmpeg versions 6.1, 5.1 and 4.4 and found the same kind of problematic noisy result but not exactly the same. Also, I ran the recent version 7 master several times and got different random noise each time; feeding the outputs into hstack makes an easy comparison.

So the "overwriting" of the stored variable is not systematic, it's as if its memory is mistakenly freed and used for some other things at random.

Last edited 3 weeks ago by Fallan (previous) (diff)

comment:5 by Michael Koch, 2 weeks ago

Cc: Michael Koch added
Type: enhancementdefect

comment:6 by Marton Balint, 2 weeks ago

This is kind of expected if you are using multiple threads with the blend filter. If you specify threads=1 as an additional parameter of blend, it will work as expected.

in reply to:  6 comment:7 by Michael Koch, 2 weeks ago

It does indeed work with threads=1 as an additional parameter of blend.

But this parameter is missing in official documentation.

Why doesn't it work if the option -filter_threads 1 is used instead? I did already try that.

Why does the geq filter work just fine with many ld() and st() in the expressions, without requiring any limitations to the number of threads?

comment:8 by Marton Balint, 6 days ago

Resolution: fixed
Status: newclosed

Fixed in 64330e365b97d9b8304a9d3d306581b6c14a773b.

Now blend filter will use a separate expression state for each slice thread, similar to the geq filter.

comment:9 by Fallan, 26 hours ago

Thank you for fixing this.

Note: See TracTickets for help on using tickets.