So far, I’ve mainly used handbrake to encode into x265 (that’s more or less the same thing as HEVC, more specifically HEVC or H.265 is the current kickass video standard, and x265 is one of the main – and best – HEVC encoders), because I don’t encode much and their GUI is comfortable. However, they lack Opus support and tend to be slow to upgrade the included x265 library, even in their nightly builds. So I looked for another solution, and eventually settled for FFmpeg.
FFmpeg is a well-known media encoding software, with support for more than 100 codecs, which are very regularly updated to their current version. Its only but big weakness is that it doesn’t have a GUI and I find the documentation a bit lacking. That steep learning curve is what prevented me from using it until now, but I eventually went ahead and took the time to figure out all I needed to. And here it goes.
Without further ado, here is the command I now use (detailed explanations follow):
for %%A in (*.mkv) do ffmpeg -i "%%A" -vf scale=400:300 -ac 2 -metadata:s:s:0 language=eng -disposition:s:0 default -codec:a libopus -b:a 48k -vbr on -compression_level 10 -frame_duration 60 -application audio -codec:v libx265 -preset veryfast -x265-params crf=23 -codec:s copy "out/%%A"
The for ... do
part and the %%A
thing are for Windows bash: I look for all files in the current folder named (something).mkv, I process them and I put the output file into an “out” subfolder, keeping the filename intact. Here is what the command looks like without the bash loop, with this time an AVI video as input:
ffmpeg -i "inputvideo.avi" -vf scale=400:300 -ac 2 -metadata:s:s:0 language=eng -disposition:s:0 default -codec:a libopus -b:a 48k -vbr on -compression_level 10 -frame_duration 60 -application audio -codec:v libx265 -preset veryfast -x265-params crf=23 -codec:s copy "outputvideo.mkv"
-i "inputvideo.avi"
is our input file… not much to say about this.
-vf scale=400:300
means rescale the video to 400 px width x 300 px height. You can also set only the height or the width, and set the other one to “-1” so that FFmpeg will set it to the proper value to keep the original ratio. I picked those values for the example, you’ll probably want to set yours quite higher, except for testing where a tiny resolution with an ultrafast preset helps save time. To read more about scaling, see Scaling (resizing) with ffmpeg on the FFmpeg wiki.
-ac 2
means convert the audio to stereo. I use this to get a smaller audio compression, obviously you don’t have to if you want to keep 5.1 or something. To read more about changing the number of audio channels, see Manipulating audio channels with ffmpeg on the FFmpeg wiki
-metadata:s:s:0 language=eng
means add a metadata that says the first subtitles track (numbered 0) is in English. The first s: is to access all streams, the second s: is to access all subtitles streams (and then obviously 0 is the index of the first and only stream in my case). See also Set a subtitle language using ffmpeg on Stackoverflow.
-disposition:s:0 default
means set the first subtitles track as default. Not that unlike the previous option, only one s: is needed, as the first one mentioning streams wouldn’t make sense (disposition is always for streams). See also ffmpeg set subtitles track as default on Stackoverflow.
-codec:a libopus -b:a 48k -vbr on -compression_level 10 -frame_duration 60 -application audio
is the block of parameters for the audio codec. We use libopus (Opus), with a bitrate (-b:a 48k
) of 48 kbps (for Opus in stereo, that should be good enough as long as you’re not recording something highly musical), with variable bitrate (VBR) enabled (-vbr on
), with the highest compression level (-compression_level 10
, slower but better compression, this is actually the default), with a frame duration of 60 ms (-frame_duration 60
, this is the highest possible duration value, having a longer frame duration improves quality a bit at low bitrates), with a intended use of generic audio (-application audio
). See also libopus in the ffmpeg-codecs documentation and How to encode audio with Opus codec on Stackoverflow.
-codec:v libx265 -preset veryfast -x265-params crf=23
is the block of parameters for the video codec. We use libx265 (x265), with a veryfast preset (possible values range from placebo to ultrafast, with the default being medium, and are analyzed here), and using constant quality / rate factor (CRF) with a quality of 23. Unless you really need to target a specific size, I strongly advise you to use CRF instead of average bitrate: this will allow you to do only one pass at no quality cost, and will give you the best size for the quality you want. See also libx265 in the ffmpeg-codecs documentation and How to generate an MP4 with H.265 codec using FFmpeg on Stackoverflow.
-codec:s copy
means we copy the subtitle stream. See also How do I add and/or keep subtitles when converting video on AskUbuntu.
Finally, “outputvideo.mkv” is our output file. Note that here we create an MKV file, but to create an MP4 file instead all you have to do is pick the .mp4 extension instead of .mkv.
A little note about multiple streams: I’ve never dealt with multiple streams of the same kind so far, so I don’t know what do to in such cases. My best guess is that there must be a way to pick streams via their index… I’ll update this post if one day I have to deal with such multistream file.
Addendum: use setsar if you want to scale the video to anything you like but maintain the aspect ratio via stretching when playing, for instance:
ffmpeg -i input_video.mp4 -vf “scale=400:300,setsar=1” output_video.mp4