How to Dynamically Adjust Exposure in FFmpeg

This guide explains how to dynamically adjust video exposure using FFmpeg's exposure filter. Because the filter accepts constant floating-point values for exposure stops (-3.0 to 3.0 EV), dynamic variations across playback are achieved by using timeline expressions via the enable option, chaining staged filters, or interpolating between streams using the blend filter. Below are the precise syntax rules and command examples for time-based triggers, stepped exposure changes, and smooth continuous exposure transitions.


The exposure Filter Syntax

The exposure filter modifies the exposure and black level of a video stream:

  • exposure: Sets the exposure correction in EV (exposure value) stops. The valid range is from -3.0 to 3.0. Default is 0.0.
  • black: Sets the black level correction. The valid range is from -1.0 to 1.0. Default is 0.0.

Because the exposure parameter accepts a fixed float rather than an evaluated mathematical string expression per frame, dynamic adjustments rely on FFmpeg's timeline evaluation or frame blending.


Method 1: Time-Based Activation Using enable

FFmpeg filters support the common enable option, which evaluates expressions on every frame. You can use standard timeline variables like t (current timestamp in seconds) or n (frame number) to turn an exposure adjustment on or off at specific timestamps.

Example: Apply Exposure Correction for a Specific Interval

To boost exposure by 1.5 EV between second 3 and second 8:

ffmpeg -i input.mp4 -vf "exposure=exposure=1.5:enable='between(t,3,8)'" -c:a copy output.mp4

Method 2: Stepped Exposure Transitions

To create stepped adjustments across multiple sections of a video, chain multiple instances of the exposure filter, each constrained to a different time window using enable.

Example: Multi-Stage Brightness Adjustment

ffmpeg -i input.mp4 -vf "\
exposure=exposure=0.5:enable='between(t,2,4)',\
exposure=exposure=1.0:enable='between(t,4,6)',\
exposure=exposure=1.5:enable='between(t,6,8)'" \
-c:a copy output.mp4

In this command:

  • From 0 to 2 seconds: Original exposure (0 EV).
  • From 2 to 4 seconds: Exposure increases by +0.5 EV.
  • From 4 to 6 seconds: Exposure increases by +1.0 EV.
  • From 6 to 8 seconds: Exposure increases by +1.5 EV.
  • After 8 seconds: Reverts to original exposure.

Method 3: Smooth, Continuous Exposure Ramps

To smoothly ramp exposure up or down over time rather than jumping abruptly, split the video into two streams, apply the target exposure to one stream, and use the blend filter with a time-based expression to interpolate between them.

Example: Linear Exposure Ramp Over 5 Seconds

To smoothly transition from standard exposure (0 EV) to +2.0 EV over the first 5 seconds:

ffmpeg -i input.mp4 -filter_complex "\
[0:v]split=2[base][target]; \
[target]exposure=exposure=2.0[bright]; \
[base][bright]blend=all_expr='if(lte(T,5), A*(1-T/5)+B*(T/5), B)'[v]" \
-map "[v]" -map 0:a? -c:a copy output.mp4

How this works:

  • split=2 duplicates the source video into [base] and [target].
  • exposure=exposure=2.0 elevates the exposure of [target] by 2 stops.
  • blend=all_expr=... uses T (current time in seconds). For the first 5 seconds (lte(T,5)), it calculates a linear interpolation between stream A (original) and stream B (exposed). After 5 seconds, it holds stream B completely.

Example: Pulsing Exposure (Oscillation)

To make the exposure continuously oscillate between normal and +1.5 EV using a sine wave:

ffmpeg -i input.mp4 -filter_complex "\
[0:v]split=2[base][target]; \
[target]exposure=exposure=1.5[bright]; \
[base][bright]blend=all_expr='A*(0.5+0.5*cos(2*PI*T/3))+B*(0.5-0.5*cos(2*PI*T/3))'[v]" \
-map "[v]" -map 0:a? -c:a copy output.mp4

This configuration completes a full exposure pulse cycle every 3 seconds.