user8071576
user8071576

Reputation:

Calculate VMAF score while encoding a video with FFmpeg

I have an ffmpeg version built with VMAF library. I can use it to calculate the VMAF scores of a distorted video against a reference video using commands like this:

ffmpeg -i distorted.mp4 -i original.mp4 -filter_complex "[0:v]scale=640:480:flags=bicubic[main];[main][1:v]libvmaf=model_path=model/vmaf_v0.6.1.json:log_path=log.json" -f null -

Now, I remember there was a way to get VMAF scores while performing regular ffmpeg encoding. How can I do that at the same time?

I want to encode a video like this, while also calulate the VMAF of the output file:

ffmpeg -i original.mp4 -crf 27 -s 640x480 out.mp4

Upvotes: 1

Views: 1339

Answers (2)

TheAudiophile
TheAudiophile

Reputation: 86

This is how I would do this:

ffmpeg -i original.mp4 -c:a copy -c:s copy -c:v <encoding arguments> -f rawvideo - -f matroska distorted.mkv | ffmpeg -r 24 -i pipe:0 -r 24 -i original.mp4 -lavfi "[0:v]setpts=PTS-STARTPTS[dist];[1:v]setpts=PTS-STARTPTS[ref];[dist][ref]libvmaf=log_fmt=json:log_path='vmaf_scores.json'" -f null -

Replace <encoding arguments> with the encoding arguments you want to use e.g. -c:v libx264 -crf 27

The -r X specifies the input framerate, this can be any value as long as you set the same input rate for both files. More details here.

You can call the output file whatever you want, distorted.mkv is just an example filename.

I specified -f matroska and .mkv as the file extension for the output file as Matroska is a flexible container that supports various audio and video codecs, but if you're encoding to a format supported by the MPEG-4 container e.g. H.264 (as is the case when using libx264) or H.265, you can replace -f matroska distorted.mkv with -f mp4 distorted.mp4

Upvotes: 0

kesh
kesh

Reputation: 5523

[edited]

Alright, scratch what I said earlier...

You should be able to use [the `tee` muxer](http://ffmpeg.org/ffmpeg-formats.html#tee-1) to save the file and pipe the encoded frames to another ffmpeg process. Something like this should work for you:
ffmpeg -i original.mp4 -crf 27 -s 640x480 -f tee "out.mp4 | [f=mp4]-" \
  | ffmpeg -i - -i original.mp4 -filter_complex ...

(make them into 2 lines and remove \ for Windows)

Here is what works on my Windows PC (thanks to @Rotem for his help)

ffmpeg -i in.mp4 -vcodec libx264 -crf 27 -f nut pipe: 
| 
ffmpeg -i in.mp4 -f nut -i pipe: 
  -filter_complex "[0:v][1:v]libvmaf=log_fmt=json:log_path=log.json,nullsink" 
  -map 1 -c copy out.mp4

The main issue that @Rotem and I missed is that we need to terminate the libvmaf's output. Also, h264 raw format does not carry header info, and using `nut alleviates that issue.

There are a couple caveats:

  1. testing with the testsrc example that @Rotem suggested in the comment below does not produce any libvmaf log, at least as far as I can see, but in debug mode, you can see the filter is getting initialized.

  2. You'll likely see [nut @ 0000026b123afb80] Thread message queue blocking; consider raising the thread_queue_size option (current value: 8) message in the log. This just means that the frames are piped in faster than the 2nd ffmpeg is processing. FFmpeg does block on both ends, so no info should be lost.

For the full disclosure, I posted my Python test script on GitHub. It just runs the shell command, so it should be easy to follow even if you don't do Python.

Upvotes: 0

Related Questions