I use ffmpeg in a VM instance running Ubuntu to encode some videos I downloaded from various streaming sites to HEVC. Currently I use a bash script to convert all these files in a directory. Overall Bitrate of these videos varies between 300 kb/s to 1500 kb/s. In my test using the same CRF for all these videos produced either bigger output file in the case of high bitrate videos or low quality video in the case of low bitrate. Currently I manually check the bitrate and move similar bitrate files to a directory and change CRF accordingly each and everytime. This is the command I use to retrieve the bitrate:

find . -name "*.mp4" -print0 | xargs -0 -i{} sh -c " echo -n '{} ' && ffmpeg -i '{}' 2>&1 | sed -n -e 's/^.*bitrate: //p' "

This is the bash script.

#!/bin/bash -e
for i in ~/ffmpeg/*.mp4;    
ffmpeg -i "$i" -c:v libx265 -crf 26 -c:a libopus -b:a 48k -vbr on -compression_level 10 -frame_duration 60 -application audio "${i%.*}.mkv"    
mv "${i%.*}.mkv" ~/ffmpeg/hevc    
rm -f -- "$i"    

How can I change the CRF dynamically based on a range of bitrates in this bash script? Like, if overall Bit Rate above 950 = crf 26, if Overall Bit Rate between 750 to 949 = crf 24, if Overall Bit Rate between 500 to 749 = crf 22, if overall Bit Rate below 499 = crf 18.


Here is - your ideas combined into a single script:


# Script name: /usr/local/bin/ffscript.sh
# Usage:       go into the source directory and execute `ffscript.sh`  
for file in *.mp4
        # Use command substitution `$()` in order to get the bit rate of the mp4 file as variable
        BIT_RATE=$(ffmpeg -i "$file" 2>&1 | sed -n -e 's/^.*bitrate: //p' | sed -r 's#([0-9]+)\skb/s#\1#')
        echo; echo "$BIT_RATE"


        if   (( BIT_RATE > 950 )); then CFR='26'
        elif (( BIT_RATE > 750 )); then CFR='24'
        elif (( BIT_RATE > 500 )); then CFR='22'
        else CFR='18'; fi

        echo "$CFR";

        # Remove the leading echo below in order to do the actions
        echo ffmpeg -i "$SRC_FILE" -c:v libx265 -crf "$CFR" -c:a libopus -b:a 48k -vbr on -compression_level 10 -frame_duration 60 -application audio "$NEW_FILE"
        echo mv "$NEW_FILE" ~/ffmpeg/hevc/
        echo rm -f -- "$SRC_FILE"
  • Thank you so much. I am testing it now and so far it is encoding only one file in a directory and ends. I am new to bash and linux in general, I don't get it how -crf changes in ffmpeg command since the -crf is declared as 26. – Metallizer Jul 23 '20 at 15:02
  • Hi, @Metallizer, I've changed the script. The problem in this case was the find command - reference. – pa4080 Jul 23 '20 at 15:40
  • @Metallizer, the "$CFR"/26 typo is fixed. – pa4080 Jul 23 '20 at 15:53
  • Now it is encoding every file. Changed -crf "$CFR" and else CFR='18'; fi Thank you very much for taking time to answer this. – Metallizer Jul 23 '20 at 15:57
  • @Metallizer, you are welcome :) – pa4080 Jul 23 '20 at 16:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.