Opened 4 years ago

Closed 2 years ago

Last modified 7 months ago

#6924 closed defect (invalid)

ffmpeg Video Levels

Reported by: chris319 Owned by:
Priority: normal Component: undetermined
Version: unspecified Keywords:
Cc: Blocked By:
Blocking: Reproduced by developer: no
Analyzed by developer: no


This is a lengthy post.

I have been doing quite a bit of work with video levels and have some concerns regarding ffmpeg.

I have documented these concerns using a waveform monitor program. The x axis of the waveform monitor corresponds to the x axis of the image. The values on y axis under the "DIG" column represent all of the pixel intensities at a given x coordinate. The range is 0 - 255. Here is how the unaltered video of a Kodak Gray Card Plus appears. The video was taken with a Canon Vixia HF R800.

Note that there is no chrominance information in the right-hand 2/3 of the chart. I use a C-language program to read a single frame of video from the mp4 file and place the samples into an array. The C program creates input and output pipes to ffmpeg which is used to read and write a frame of video. Here is the code used to read a frame of video into ffmpeg, showing the command line ffmpeg receives:

FILE *pipein = popen("ffmpeg -i KodakChart.mp4 -f image2pipe -vcodec rawvideo -pix_fmt yuv420p -", "r");

Here is the code used to write a video frame to ffmpeg, also showing the ffmpeg command line:

FILE *pipeout = popen("ffmpeg -y -f rawvideo -vcodec rawvideo -pix_fmt yuv420p -s 1280x720 -r 29.97 -i - -f mp4 -q:v 5 -vf scale=out_range=tv cliptest.mp4", "w");

Once the YUV samples are in the array, I use the C code below to force the luminance component of all pixels to a specific value. In the example below, 235 is chosen, as this is the maximum video level specified in ITU BT.709.
for (y=0; y<H ; y++)


for (x=0; x<W ; x++)


lum[y*W+x] = 235;




Below are several waveform shots of video levels at various vales.

In the first waveform shot, all luminance components have been forced to digital 255.

Here is a waveform shot with all luminance components forced to digital 110.

Below, all luminance components forced to digital 180. As we can see, the video levels are actually slightly higher than 180.

In the next scope display, all luminance components have been forced to digital 235.

This scope display is especially noteworthy because the video levels reach 255 despite having been forced to 235.

It is possible to force the luminance component to a specific value by reading and writing a frame of video using ffmpeg. The example which shows luma forced to 110 works fairly well. As we increase the video level, ffmpeg is applying gain to the levels. Finally, when luma is forced to 235, ffmpeg is boosting the levels to 255. Note that the code for the output pipe contains the following video filter:

-vf scale=out_range=tv

It appears that this filter is not functioning as documented, i.e. it is not confining the output to the range of 16 - 235. This could be problematic if a user needs 16 - 235 output. For example, the U.S. broadcast TV station I work for would never broadcast any of the examples shown in this post having video levels above digital 235.

Change History (9)

comment:1 by Carl Eugen Hoyos, 4 years ago

Component: avcodecundetermined

Please provide the FFmpeg command line you tested together with the complete, uncut console output on stderr and the used input file to make this a valid ticket.

Last edited 4 years ago by Carl Eugen Hoyos (previous) (diff)

comment:2 by Elon Musk, 4 years ago

Resolution: invalid
Status: newclosed

If you wish to limit pixels like that use -vf scale=in_range=pc:out_range=tv
Otherwise, command will do nothing.

BTW ffmpeg have its own waveform filter.
If you just want to clamp pixels, use lut or limiter filter.

comment:3 by chris319, 4 years ago

Resolution: invalid
Status: closedreopened

Here is the command line which converts the video to TV levels:

bin\ffmpeg -i "D:\Videos\KodakChart.MP4" -y -vf "scale=in_range=pc:out_range=tv" -c:a copy output.mp4

As you can see there are still excursions above 235. The video is not being clipped to 235.

comment:4 by Kieran Kunhya, 4 years ago

So do what the previous poster says and clamp using a downstream filter. All processes might produce excursions outside TV range, that's the entire point of TV range. And modern practice is actually to leave the odd excursion in as described here:

comment:5 by Elon Musk, 4 years ago

Resolution: wontfix
Status: reopenedclosed

There is nothing to fix.

comment:6 by Carl Eugen Hoyos, 4 years ago

Resolution: wontfixinvalid

comment:7 by NNois, 2 years ago

Resolution: invalid
Status: closedreopened

Im having too the same problem but that's working in some case but not on some others...

If I took a png then limiting it to a jpg it works
but if I took a jpg to limiting it to tv it doesn't work
I've got the same for the targa format (I'm using it for my broadcast work and I can't find a way to limit the range on them...)

example who doesn't work... the black/whites levels are the sames
ffmpeg -i testalpha_%04d.jpg -c:v mjpeg -q:v 1 -filter:v "scale=in_range=pc:out_range=tv" testalpha191220_@25p_jpg-1_%05d.jpg

comment:8 by Carl Eugen Hoyos, 2 years ago

Resolution: invalid
Status: reopenedclosed

comment:9 by Balling, 7 months ago

The valid range for limited range 8 bit is 1-254 in HDMI and 0-255 in file. Please stop with that. With full range no limitation, that is both R'G'B' and Y'CbCr.

Preferred range in EBU R103 is just for HDR.

P.S. You can use this

-vf lutyuv=y='clip(val,22,222)',lutyuv=u='clip(val,16,232)',lutyuv=v='clip(val,16,232)'

if you really need it.

Note: See TracTickets for help on using tickets.