Opened 7 years ago

Closed 7 years ago

#6127 closed defect (fixed)

non-intuitive behavior of random() function used in zoompan filter's expressions

Reported by: Peter Basista Owned by:
Priority: normal Component: avfilter
Version: git-master Keywords: zoompan
Cc: Blocked By:
Blocking: Reproduced by developer: no
Analyzed by developer: no

Description

Summary of the bug:

when random() function is used in a naive way (i.e. "random(0)", without previously setting the internal variable 0) inside an expression for zoompan filter's options, it always returns the *same* number which is very close to zero:

$ /usr/bin/ffmpeg -loop 1 -i alley.png -filter "zoompan='s=1920x1200:fps=60:z=3:y=y+print(random(0)*1000000000):x=x+print(random(1)*1000000000)'" -frames 600 output.hevc -y
ffmpeg version 3.2.2-2 Copyright (c) 2000-2016 the FFmpeg developers
  built with gcc 6.3.0 (Debian 6.3.0-4) 20170121
  configuration: --prefix=/usr --extra-version=2 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --enable-gpl --disable-stripping --enable-avresample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libebur128 --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libmp3lame --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-omx --enable-openal --enable-opengl --enable-sdl2 --enable-libdc1394 --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libopencv --enable-libx264 --enable-shared
  libavutil      55. 34.100 / 55. 34.100
  libavcodec     57. 64.101 / 57. 64.101
  libavformat    57. 56.100 / 57. 56.100
  libavdevice    57.  1.100 / 57.  1.100
  libavfilter     6. 65.100 /  6. 65.100
  libavresample   3.  1.  0 /  3.  1.  0
  libswscale      4.  2.100 /  4.  2.100
  libswresample   2.  3.100 /  2.  3.100
  libpostproc    54.  1.100 / 54.  1.100
[png_pipe @ 0x557e77849f00] Stream #0: not enough frames to estimate rate; consider increasing probesize
Input #0, png_pipe, from 'alley.png':
  Duration: N/A, bitrate: N/A
    Stream #0:0: Video: png, rgb24(pc), 7360x4912, 25 tbr, 25 tbn, 25 tbc
x265 [info]: HEVC encoder version 0.0
x265 [info]: build info [Linux][GCC 6.2.0][64 bit] 8bit+10bit+12bit
x265 [info]: using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX AVX2 FMA3 LZCNT BMI2
x265 [info]: Main 4:4:4 profile, Level-5 (Main tier)
x265 [info]: Thread pool created using 8 threads
x265 [info]: Slices                              : 1
x265 [info]: frame threads / pool features       : 3 / wpp(19 rows)
x265 [info]: Coding QT: max CU size, min CU size : 64 / 8
x265 [info]: Residual QT: max TU size, max depth : 32 / 1 inter / 1 intra
x265 [info]: ME / range / subpel / merge         : hex / 57 / 2 / 2
x265 [info]: Keyframe min / max / scenecut       : 25 / 250 / 40
x265 [info]: Cb/Cr QP Offset                     : 6 / 6
x265 [info]: Lookahead / bframes / badapt        : 20 / 4 / 2
x265 [info]: b-pyramid / weightp / weightb       : 1 / 1 / 0
x265 [info]: References / ref-limit  cu / depth  : 3 / on / on
x265 [info]: AQ: mode / str / qg-size / cu-tree  : 1 / 1.0 / 32 / 1
x265 [info]: Rate Control / qCompress            : CRF-28.0 / 0.60
x265 [info]: tools: rd=3 psy-rd=2.00 rskip signhide tmvp strong-intra-smoothing
x265 [info]: tools: lslices=7 deblock sao
Output #0, hevc, to 'output.hevc':
  Metadata:
    encoder         : Lavf57.56.100
    Stream #0:0: Video: hevc (libx265), gbrp, 1920x1200, q=2-31, 60 fps, 60 tbn, 60 tbc
    Metadata:
      encoder         : Lavc57.64.101 libx265
Stream mapping:
  Stream #0:0 -> #0:0 (png (native) -> hevc (libx265))
Press [q] to stop, [?] for help
0.054964
    Last message repeated 1 times
0.054964  1 fps=0.0 q=0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x
    Last message repeated 19 times
0.054964 11 fps=8.5 q=0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x
    Last message repeated 15 times
[swscaler @ 0x557e79781500] Warning: data is not aligned! This can lead to a speedloss
0.054964
    Last message repeated 5 times
0.054964 22 fps= 12 q=0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x
    Last message repeated 15 times
0.054964 30 fps= 13 q=-0.0 size=       6kB time=-00:00:00.01 bitrate=N/A speed=N/A
    Last message repeated 15 times
0.054964 38 fps= 13 q=-0.0 size=       7kB time=00:00:00.11 bitrate= 501.0kbits/s speed=0.04x
    Last message repeated 17 times
0.054964 47 fps= 14 q=-0.0 size=       9kB time=00:00:00.26 bitrate= 261.3kbits/s speed=0.077x
    Last message repeated 17 times
0.054964 56 fps= 14 q=-0.0 size=      10kB time=00:00:00.41 bitrate= 190.8kbits/s speed=0.104x
    Last message repeated 17 times
0.054964 65 fps= 14 q=-0.0 size=      11kB time=00:00:00.56 bitrate= 152.7kbits/s speed=0.125x
    Last message repeated 15 times
0.054964 73 fps= 14 q=-0.0 size=      12kB time=00:00:00.70 bitrate= 141.1kbits/s speed=0.139x
    Last message repeated 17 times
0.054964 82 fps= 15 q=-0.0 size=      13kB time=00:00:00.85 bitrate= 127.0kbits/s speed=0.152x
    Last message repeated 15 times

...

The output above is from Debian's version of ffmpeg, but the same behavior can be experienced in git-master version as of today.

Explanation:

zoompan filter uses the internal formula evaluator to *parse* and evaluate the expressions for its options. The parsing is done for *every* frame (see function output_single_frame in libavfilter/vf_zoompan.c, line 135). This means that for every frame, the evaluator initializes the parser's internal variables 0-9 for each of the parsed expressions to zero (see function av_expr_parse in libavutil/eval.c, line 667). Therefore, no function is able to store its internal state into one of the internal variables and use it during the next evaluation (which is typically for the next frame), because there will always be only a single evaluation for every parsed expression.

This breaks the intuitive behavior of functions which require internal state, like random(). It uses an internal variable to store its current "seed" from which it calculates the next pseudorandom number (see function eval_expr in libavutil/eval.c, line 220). When the seed value is always the same (i.e. zero), the generated "pseudorandom" number is always the same as well.

Suggestion:

This non-intuitive behavior is caused by the fact that the expressions are parsed for every frame. If the expressions were parsed only during the filter's initialization and evaluated for every frame, the internal variables would be preserved and the above described problem would not exist. Some other filters, like the crop filter, use this technique and I believe it would be better if zoompan used it as well.

Change History (1)

comment:1 by Elon Musk, 7 years ago

Resolution: fixed
Status: newclosed
Note: See TracTickets for help on using tickets.