Answers.Body: <p>I had two errors in the script:</p>
  <ol>
  <li><p>The first <code>between()</code> function should begin not at 0, 6, … and end at 6, 12, …, but have the fade-in duration added to it, i.e. <code>between(t,0+1.5,0+6-2.0)</code> for the first one, and stop at the end minus the fade-out time.</p>
  </li>
  <li><p>The calculation of the offset for the fade-out was wrong. Instead of <code>(-(t-0+6)/2.0)</code>, the offset must be in parentheses, meaning I should use <code>-(t-(0+6))/2.0)</code> (as otherwise the sign is inverted).</p>
  </li>
  </ol>
  <p>Fully functioning code:</p>
  <pre><code>#!/usr/bin/env bash
  
  ffmpeg \
    -y \
    -f lavfi -i testsrc=size=640x480:rate=30:duration=18 \
    -f lavfi -i testsrc=size=640x480:rate=30:duration=18 \
    -f lavfi -i testsrc=size=640x480:rate=30:duration=18 \
    -filter_complex &quot;
      [0:v]
        setpts=PTS-STARTPTS,drawtext=text='vid1 - %{pts}':fontsize=24:fontcolor=white,
        hue=s='clip(between(t,0+1.5,0+6-2.0) + ((t-0)/1.5) * between(t,0,0+1.5) + (-(t-(0+6))/2.0) * between(t,0+6-2.0,0+6),0,1)'
      [vid1];
      [1:v]
        setpts=PTS-STARTPTS,drawtext=text='vid2 - %{pts}':fontsize=24:fontcolor=white,
        hue=s='clip(between(t,6+1.5,6+6-2.0) + ((t-6)/1.5) * between(t,6,6+1.5) + (-(t-(6+6))/2.0) * between(t,6+6-2.0,6+6),0,1)'
      [vid2];
      [2:v]
        setpts=PTS-STARTPTS,drawtext=text='vid3 - %{pts}':fontsize=24:fontcolor=white,
        hue=s='clip(between(t,12+1.5,12+6-2.0) + ((t-12)/1.5) * between(t,12,12+1.5) + (-(t-(12+6))/2.0) * between(t,12+6-2.0,12+6),0,1)'
      [vid3];
      [vid1][vid2][vid3]
        hstack=inputs=3[out]&quot; \
    -map &quot;[out]&quot; -pix_fmt yuv420p -f matroska - | ffplay -
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-05-24T10:07:33.203Z
Answers.LastActivityDate: 2022-05-24T10:07:33.203Z
Answers.UserDisplayName: slhck
Answers.Body: <p>In the expr,</p>
  <p><code>clip(between(t,0,0+6) + ((t - 0)/1.5) * between(t,0,0+1.5) + (-(t-0+6)/2.0) * between(t,0+6-2.0,0+6), 0, 1)</code>,</p>
  <p>the first term will contribute <code>1</code> for the entire interval, and the fade-in clause can only add to that, and then ultimately get clipped to 1, so there's no progression for a fade-in to occur. For the fade-out, before clipping, the expr dips below 0 at once, and gets clipped to 0.</p>
  <p>Use</p>
  <p><code>hue=s=between(t\,0\,0+6) - (between(t\,0\,0+1.5)*(1-((t - 0)/1.5))+between(t\,0+6-2.0\,0+6)*((t-(6-2.0))/2.0))</code></p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-05-24T10:07:29.683Z
Answers.Text: Thanks for spotting this. I actually had not one but two (dumb) translation errors from Python to ffmpeg, that explains the issue (posted another answer with the correct solution based on the original filter).
Answers.UserDisplayName: slhck
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-05-24T09:35:04.870Z
Answers.LastActivityDate: 2022-05-24T09:35:04.870Z
Answers.UserDisplayName: Gyan
Body: <p>I want to fade-in/fade-out the saturation of a video based on the time (<code>t</code>). For three videos, the saturation should fade in and fade out at different points in time, starting with 0, 6, and 12 seconds.</p>
  <p>Here is the script, using 1.5 seconds for fade-in and 2.0 seconds for fade-out. Each video should have saturation only for 6 seconds, with full saturation only for 2.5 seconds (6-2.0-1.5):</p>
  <pre class="lang-bash prettyprint-override"><code>ffmpeg \
    -y \
    -f lavfi -i testsrc=size=640x480:rate=30:duration=18 \
    -f lavfi -i testsrc=size=640x480:rate=30:duration=18 \
    -f lavfi -i testsrc=size=640x480:rate=30:duration=18 \
    -filter_complex &quot;
      [0:v]
        setpts=PTS-STARTPTS,drawtext=text='vid1 - %{pts}':fontsize=24:fontcolor=white,
        hue=s='clip(between(t,0,0+6) + ((t - 0)/1.5) * between(t,0,0+1.5) + (-(t-0+6)/2.0) * between(t,0+6-2.0,0+6), 0, 1)'
      [vid1];
      [1:v]
        setpts=PTS-STARTPTS,drawtext=text='vid2 - %{pts}':fontsize=24:fontcolor=white,
        hue=s='clip(between(t,6,6+6) + ((t - 6)/1.5) * between(t,6,6+1.5) + (-(t-6+6)/2.0) * between(t,6+6-2.0,6+6), 0, 1)'
      [vid2];
      [2:v]
        setpts=PTS-STARTPTS,drawtext=text='vid3 - %{pts}':fontsize=24:fontcolor=white,
        hue=s='clip(between(t,12,12+6) + ((t - 12)/1.5) * between(t,12,12+1.5) + (-(t-12+6)/2.0) * between(t,12+6-2.0,12+6), 0, 1)'
      [vid3];
      [vid1][vid2][vid3]
        hstack=inputs=3[out]&quot; \
    -map &quot;[out]&quot; -pix_fmt yuv420p -f matroska - | ffplay -
  </code></pre>
  <p>The algorithm itself works, as I can use it in a Python program without problems. The issue is that the shift in saturation is instantaneous, i.e., the first video will have full saturation from 0-4, while it should have a fade-in and a fade-out with smooth transitions.</p>
  <p>Why is that, and how can I fix it? It's not that <code>t</code> is integer-based … when I use <code>pts*tb</code> instead of <code>t</code>, the issue persists.</p>
  
Tags: <ffmpeg>
Title: Manual fade-in/fade-out expression in ffmpeg is not smooth
UserDisplayName: slhck
ThreadText: # Manual fade-in/fade-out expression in ffmpeg is not smooth
  
  (Asked by: username_0 on 2022-05-24)
  
  I want to fade-in/fade-out the saturation of a video based on the time (`t`). For three videos, the saturation should fade in and fade out at different points in time, starting with 0, 6, and 12 seconds.
  
  Here is the script, using 1.5 seconds for fade-in and 2.0 seconds for fade-out. Each video should have saturation only for 6 seconds, with full saturation only for 2.5 seconds (6-2.0-1.5):
  
  ```bash
  ffmpeg \
    -y \
    -f lavfi -i testsrc=size=640x480:rate=30:duration=18 \
    -f lavfi -i testsrc=size=640x480:rate=30:duration=18 \
    -f lavfi -i testsrc=size=640x480:rate=30:duration=18 \
    -filter_complex "
      [0:v]
        setpts=PTS-STARTPTS,drawtext=text='vid1 - %{pts}':fontsize=24:fontcolor=white,
        hue=s='clip(between(t,0,0+6) + ((t - 0)/1.5) * between(t,0,0+1.5) + (-(t-0+6)/2.0) * between(t,0+6-2.0,0+6), 0, 1)'
      [vid1];
      [1:v]
        setpts=PTS-STARTPTS,drawtext=text='vid2 - %{pts}':fontsize=24:fontcolor=white,
        hue=s='clip(between(t,6,6+6) + ((t - 6)/1.5) * between(t,6,6+1.5) + (-(t-6+6)/2.0) * between(t,6+6-2.0,6+6), 0, 1)'
      [vid2];
      [2:v]
        setpts=PTS-STARTPTS,drawtext=text='vid3 - %{pts}':fontsize=24:fontcolor=white,
        hue=s='clip(between(t,12,12+6) + ((t - 12)/1.5) * between(t,12,12+1.5) + (-(t-12+6)/2.0) * between(t,12+6-2.0,12+6), 0, 1)'
      [vid3];
      [vid1][vid2][vid3]
        hstack=inputs=3[out]" \
    -map "[out]" -pix_fmt yuv420p -f matroska - | ffplay -
  
  ```
  
  The algorithm itself works, as I can use it in a Python program without problems. The issue is that the shift in saturation is instantaneous, i.e., the first video will have full saturation from 0-4, while it should have a fade-in and a fade-out with smooth transitions.
  
  Why is that, and how can I fix it? It's not that `t` is integer-based … when I use `pts*tb` instead of `t`, the issue persists.
  
  **Tags:** ffmpeg
  
  
  ---
  
  ## 2 answers
  
  ### Answer by username_0 on 2022-05-24. Score: 0 | Accepted answer
  
  I had two errors in the script:
  
  1. The first `between()` function should begin not at 0, 6, … and end at 6, 12, …, but have the fade-in duration added to it, i.e. `between(t,0+1.5,0+6-2.0)` for the first one, and stop at the end minus the fade-out time.
  2. The calculation of the offset for the fade-out was wrong. Instead of `(-(t-0+6)/2.0)`, the offset must be in parentheses, meaning I should use `-(t-(0+6))/2.0)` (as otherwise the sign is inverted).
  
  Fully functioning code:
  
  ```
  #!/usr/bin/env bash
  
  ffmpeg \
    -y \
    -f lavfi -i testsrc=size=640x480:rate=30:duration=18 \
    -f lavfi -i testsrc=size=640x480:rate=30:duration=18 \
    -f lavfi -i testsrc=size=640x480:rate=30:duration=18 \
    -filter_complex "
      [0:v]
        setpts=PTS-STARTPTS,drawtext=text='vid1 - %{pts}':fontsize=24:fontcolor=white,
        hue=s='clip(between(t,0+1.5,0+6-2.0) + ((t-0)/1.5) * between(t,0,0+1.5) + (-(t-(0+6))/2.0) * between(t,0+6-2.0,0+6),0,1)'
      [vid1];
      [1:v]
        setpts=PTS-STARTPTS,drawtext=text='vid2 - %{pts}':fontsize=24:fontcolor=white,
        hue=s='clip(between(t,6+1.5,6+6-2.0) + ((t-6)/1.5) * between(t,6,6+1.5) + (-(t-(6+6))/2.0) * between(t,6+6-2.0,6+6),0,1)'
      [vid2];
      [2:v]
        setpts=PTS-STARTPTS,drawtext=text='vid3 - %{pts}':fontsize=24:fontcolor=white,
        hue=s='clip(between(t,12+1.5,12+6-2.0) + ((t-12)/1.5) * between(t,12,12+1.5) + (-(t-(12+6))/2.0) * between(t,12+6-2.0,12+6),0,1)'
      [vid3];
      [vid1][vid2][vid3]
        hstack=inputs=3[out]" \
    -map "[out]" -pix_fmt yuv420p -f matroska - | ffplay -
  
  ```
  
  
  ---
  
  ### Answer by username_1 on 2022-05-24. Score: 2
  
  In the expr,
  
  `clip(between(t,0,0+6) + ((t - 0)/1.5) * between(t,0,0+1.5) + (-(t-0+6)/2.0) * between(t,0+6-2.0,0+6), 0, 1)`,
  
  the first term will contribute `1` for the entire interval, and the fade-in clause can only add to that, and then ultimately get clipped to 1, so there's no progression for a fade-in to occur. For the fade-out, before clipping, the expr dips below 0 at once, and gets clipped to 0.
  
  Use
  
  `hue=s=between(t\,0\,0+6) - (between(t\,0\,0+1.5)*(1-((t - 0)/1.5))+between(t\,0+6-2.0\,0+6)*((t-(6-2.0))/2.0))`
  
  > **Comments:**
  > **username_0**: Thanks for spotting this. I actually had not one but two (dumb) translation errors from Python to ffmpeg, that explains the issue (posted another answer with the correct solution based on the original filter).  
  
  ---
  
  