ffmpeg - how to apply filters without losing quality

Viewed 11435

Here is a simple request, it has an input, output, and two watermarks. From what I gathered I can't apply -codec copy because I'm using a filter.

ffmpeg -i input.mp4 -i wm-bl.png -i wm-br.png -filter_complex "overlay=x=0:y=H-h,overlay=x=W-w:y=H-h" output.mp4

This does the trick, as far as watermarking is concerned, but the output is compressed into half the original file size.

Is it possible to watermark without losing video quality?

4 Answers

Unfortunately it is not possible when using the overlay filter (at least with FFmpeg version 4.1.6-1~deb10u1 that I uses), even you use a lossless encoder for the final output file. Because the overlay filter itself uses a lossy encoding during the process, the image quality will certainly degrade after overlay; however this degradation usually won't be noticeable, and this filter is well suitable for most videos.

But if your input files are already in lossless formats, and if you absolutely want a lossless result, then don't use the overlay filter. I don't have a good solution for this, but here's my workaround.

  • Extract the original video, frame by frame, into PNG files:
mkdir frames
ffmpeg -i input.mp4 frames/%d.png
  • Add watermarks or anything you want to overlay onto the video; GraphicsMagick is used in this example:
for f in frames/*.png; do gm composite -geometry +0+640 wm-bl.png "$f" "$f"; gm composite -geometry +1100+640 wm-br.png "$f" "$f"; done
  • Reassemble frames into a new video file, using a lossless encoding format; also remember to specify the same framerate as original video:
ffmpeg -framerate 30 -i frames/%d.png -qp 0 -pix_fmt yuv444p output.mp4

It is obvious that this workaround is very inefficient -- modifying a large number of PNG files one by one would take a very long time, especially the original video is long; storing these temporary PNG files will take a lot of space on the file system, and the progress would take even longer if the file system isn't fast enough on accessing that many files.

But it worked for me, I think sharing it may just help other people with a similar issue.

Try adding format=auto to the details of the -filter_complex parameter.

Or if the value auto isn't suitable in your case, try another - they are listed in the overlay section of FFmpeg documentation.

See the specific example command in my comment of Skinkie's answer of my question "How to losslessly replace a specific area in a range of frames".

In case anyone could benefit from it:

I have been trying to find a solution to keep video quality after adding drawtext (has Same quality issue as adding filter) on Android (using FFMpegKit lib)

Adding -crf 0, -preset speed or any other common suggestions didn't help.

Using the original video bitrate have helped to solve it:

  1. get bitrate:

val mediaInformation = FFprobeKit.getMediaInformation(input.path)

val bitrate = mediaInformation.mediaInformation.bitrate.toLong()/1000L

  1. add it to your FFmpeg command:

"-y -i input.mp4 -vcodec mpeg4 (as example) -b:v ${bitrate}k -acodec copy -vp (whatever filter) output.mp4"

Related