Wednesday, February 2, 2011

FFmpeg and x264 in Ubuntu

In my last post I have explained how to use MeGUI to convert videos from one format to another. Sadly MeGUI for Linux is not available. We can install ffmpeg from Synaptic Package Manager but the resulting ffmpeg does not support x264. If we need ffmpeg with x264 support then we need to manually compile ffmpeg from its source code.


Instructions:


First we need to download the following:
  • ffmpeg source (you can download stable or daily snapshot).
  • x264 source (you can download stable or daily snapshot).
Extract ffmpeg to ~/ffmpeg and x264 to ~/x264. First you need to uninstall any previous versions of ffmpeg and x264. You can do this from terminal using the command :


sudo apt-get remove ffmpeg x264 libx264-dev


or you can manually remove them from synaptic. For the next step we need to install the dependencies needed for ffmpeg and x264. We can do this by this command:


sudo apt-get update
sudo apt-get install build-essential git-core checkinstall yasm texi2html \
    libfaac-dev libjack-jackd2-dev libmp3lame-dev libopencore-amrnb-dev \
    libopencore-amrwb-dev libsdl1.2-dev libtheora-dev libva-dev libvdpau-dev \
    libvorbis-dev libvpx-dev libx11-dev libxfixes-dev libxvidcore-dev zlib1g-dev


or manually install each package from synaptic.


If you are behind a proxy and have trouble updating through terminal then do the following:
  1. type the following in terminal
    sudo gedit /etc/bash.bashrc 
  2. Then at the end of the file add these lines:
    export http_proxy=http://username:password@proxy.that.you.use:port/
    export ftp_proxy=http://username:password@proxy.that.you.use:port/
  3. Restart the terminal. You should be now able to use proxy in terminal.
If you have trouble installing programs from synaptic then follow the steps given below:
  1. type the following in terminal
    sudo gedit /etc/apt/apt.conf
  2. Then change the contents of the file to this:
    Acquire::http::proxy "http://username:password@proxy.that.you.use:port";
    Acquire::ftp::proxy "ftp://username:password@proxy.that.you.use:port";
    Acquire::https::proxy "https://username:password@proxy.that.you.use:port";
  3. Restart your system. You should be now able download programs from synaptic.
Open a terminal and type the following to install x264 from its source:
cd ~
cd x264
./configure
make
sudo checkinstall --pkgname=x264 --pkgversion="2:0.$(grep X264_BUILD x264.h -m1 | \
    cut -d' ' -f3).$(git rev-list HEAD | wc -l)+git$(git rev-list HEAD -n 1 | \
    head -c 7)" --backup=no --deldoc=yes --fstrans=no --default


doing this will install x264 and it also creates a .deb file for future needs.


Open a terminal and type the following to install ffmpeg from its source:


cd
git clone git://git.ffmpeg.org/ffmpeg.git
cd ffmpeg
./configure --enable-gpl --enable-version3 --enable-nonfree --enable-postproc \
    --enable-libfaac --enable-libmp3lame --enable-libopencore-amrnb \
    --enable-libopencore-amrwb --enable-libtheora --enable-libvorbis \
    --enable-libvpx --enable-libx264 --enable-libxvid --enable-x11grab
make
sudo checkinstall --pkgname=ffmpeg --pkgversion="5:$(./version.sh)" --backup=no \
    --deldoc=yes --fstrans=no --default
hash x264 ffmpeg ffplay ffprobe


You have successfully installed ffmpeg with x264 support.


Additional resources:
There are two main methods to convert videos.


1. One-pass CRF (Constant Rate Factor) using the slow preset. One-pass CRF is good for general encoding and is what I use most often. Adjust -crf to change the quality. Lower numbers mean higher quality and a larger output file size. A sane range is 18 to 28. It is faster than 2 pass encoding but the output files are larger in size compared to 2pass encoding but the quality is great.


Example:
ffmpeg -i input.wmv -acodec libfaac -ab 128k -ac 2 -vcodec libx264 -vpre slow -crf 22 \
    -threads 0 output.mp4
2. Two-Pass encode using the fast presets. Two-pass encoding is used when you are targeting a specific bitrate and/or final output file size. It generally takes a lot of time but gives the best quality and smallest size.
Example:
ffmpeg -i input.avi -pass 1 -vcodec libx264 -vpre fast_firstpass -b 512k -threads 0 \
    -f rawvideo -an -y /dev/null
ffmpeg -i input.avi -pass 2 -acodec libfaac -ab 128k \
    -ac 2 -vcodec libx264 -vpre fast -b 512k -threads 0 output.mp4
I have made a shell script to change all files in a directory recursively. You can get it from here:


First you'll need to install python 2.7 and mplayer. Install it using synaptic or from terminal using the command sudo apt-get install mplayer


renamer.py:
#!/usr/bin/python
import os
print ("use pwd | $renamer")
folder=raw_input()
for R,DIR,FILES in os.walk(folder,topdown=False):
    for file in FILES:
        newfile=file.replace(" ","_")
        newfile=newfile.replace("[","")
        newfile=newfile.replace("]","")
        new_file_name=os.path.join(R,newfile)
        os.rename( os.path.join(R,file) , new_file_name)
    for dir in DIR:
        newdir=dir.replace(" ","_")
        newdir=newdir.replace("[","")
        newdir=newdir.replace("]","")
        new_dir_name=os.path.join(R,newdir)
        os.rename( os.path.join(R,dir) , new_dir_name)



save this file in your home folder and give permission to execute. Then add the line
export renamer=~/renamer.sh
to /etc/bash.bashrc file. You'll need to run this first before attempting to run 2pass because files with spaces or [ or ] in their names cannot be converted by this script.

recursive_2pass.sh:
#!/bin/sh

direc=$(pwd)
scase=0
ext="avi"
vbit=0
vrate=500
abit=0
pass="2"
temp=""

echo $0
if [ $# -eq 6 ]; then
 scase=$1
 ext=$2
 vbit=$3
 vrate=$4
 abit=$5
 pass=$6
else
 echo "Enter input file format (default avi):"
 read temp
 if [ "$temp" != "" ]; then
  ext=$temp
 fi

 echo "Enter video bitrate (press enter to use default value):"
 read temp
 if [ "$temp" = "" ]; then
  scase=2
  echo "Enter default video rate (press enter to use default value(100).):"
  read temp
  if [ "$temp" != "" ]; then
   vrate=$((temp))
   vrate=$(( vrate * 500 / 100 ))
  fi
 else
  vbit=$((temp))
 fi

 echo "Enter audio bitrate (press enter to use the original audio):"
 read temp
 if [ "$temp" = "" ]; then
  scase=$((scase+1))
 else
  abit=$((temp))
 fi
 
 echo "Do you want to use only 1 pass? (press enter to use 2 pass or press anything to use 1 pass)"
 read temp
 if [ "$temp" != "" ]; then
  pass="1" 
 fi
fi
conv=1
#search for files and folders if folder is found go into it else convert the file
for filename in $direc/*.$ext; do
 if [ -f $filename ] ; then
  echo "Going to convert "$filename
  case $scase in
  0)
  #given abit and vbit
   if [ "$pass" = "2" ] ;then
    ffmpeg -y -i "$filename" -an -pass 1 -vcodec libx264 -b "$vbit"k -flags +loop -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8 -flags2 +bpyramid -me_method dia -subq 1 -trellis 0 -refs 1 -bf 16 -directpred 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bt "$vbit"k -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -threads 0 -f rawvideo /dev/null
   
    ffmpeg -y -i "$filename" -pass 2 -acodec libfaac -ab "$abit"k -vcodec libx264 -b "$vbit"k -flags +loop -cmp +chroma -partitions +parti8x8+parti4x4+partp8x8+partp4x4+partb8x8 -flags2 +dct8x8+wpred+bpyramid+mixed_refs -me_method umh -subq 7 -trellis 1 -refs 6 -bf 16 -directpred 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bt "$vbit"k -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -threads 0 "$filename"_converted.mp4
   else
    ffmpeg -y -i "$filename" -acodec libfaac -ab "$abit"k -vcodec libx264 -b "$vbit"k -flags +loop -cmp +chroma -partitions +parti8x8+parti4x4+partp8x8+partp4x4+partb8x8 -flags2 +dct8x8+wpred+bpyramid+mixed_refs -me_method umh -subq 7 -trellis 1 -refs 6 -bf 16 -directpred 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bt "$vbit"k -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -threads 0 "$filename"_converted.mp4
   fi
   conv=0
  ;;
  1)
  #given vbit but not abit
   if [ "$pass" = "2" ] ;then
    ffmpeg -y -i "$filename" -an -pass 1 -vcodec libx264 -b "$vbit"k -flags +loop -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8 -flags2 +bpyramid -me_method dia -subq 1 -trellis 0 -refs 1 -bf 16 -directpred 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bt "$vbit"k -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -threads 0 -f rawvideo /dev/null

    ffmpeg -y -i "$filename" -pass 2 -acodec copy -vcodec libx264 -b "$vbit"k -flags +loop -cmp +chroma -partitions +parti8x8+parti4x4+partp8x8+partp4x4+partb8x8 -flags2 +dct8x8+wpred+bpyramid+mixed_refs -me_method umh -subq 7 -trellis 1 -refs 6 -bf 16 -directpred 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bt "$vbit"k -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -threads 0 "$filename"_converted.mp4
   else
    ffmpeg -y -i "$filename" -acodec copy -vcodec libx264 -b "$vbit"k -flags +loop -cmp +chroma -partitions +parti8x8+parti4x4+partp8x8+partp4x4+partb8x8 -flags2 +dct8x8+wpred+bpyramid+mixed_refs -me_method umh -subq 7 -trellis 1 -refs 6 -bf 16 -directpred 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bt "$vbit"k -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -threads 0 "$filename"_converted.mp4
   fi
   conv=0
  ;;
  2)
  #given abit but not vbit
   width=$(/usr/share/mplayer/midentify.sh $filename | grep "ID_VIDEO_WIDTH=" | sed 's/ID_VIDEO_WIDTH=\([0-9]*\)/\1/')
   height=$(/usr/share/mplayer/midentify.sh $filename | grep "ID_VIDEO_HEIGHT=" | sed 's/ID_VIDEO_HEIGHT=\([0-9]*\)/\1/')
   vbit=$(((width * height * vrate)/(640 * 480)))
   if [ "$pass" = "2" ] ;then
    ffmpeg -y -i "$filename" -an -pass 1 -vcodec libx264 -b "$vbit"k -flags +loop -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8 -flags2 +bpyramid -me_method dia -subq 1 -trellis 0 -refs 1 -bf 16 -directpred 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bt "$vbit"k -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -threads 0 -f rawvideo /dev/null

    ffmpeg -y -i "$filename" -pass 2 -acodec libfaac -ab "$abit"k -vcodec libx264 -b "$vbit"k -flags +loop -cmp +chroma -partitions +parti8x8+parti4x4+partp8x8+partp4x4+partb8x8 -flags2 +dct8x8+wpred+bpyramid+mixed_refs -me_method umh -subq 7 -trellis 1 -refs 6 -bf 16 -directpred 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bt "$vbit"k -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -threads 0 "$filename"_converted.mp4
   else
    ffmpeg -y -i "$filename" -acodec libfaac -ab "$abit"k -vcodec libx264 -b "$vbit"k -flags +loop -cmp +chroma -partitions +parti8x8+parti4x4+partp8x8+partp4x4+partb8x8 -flags2 +dct8x8+wpred+bpyramid+mixed_refs -me_method umh -subq 7 -trellis 1 -refs 6 -bf 16 -directpred 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bt "$vbit"k -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -threads 0 "$filename"_converted.mp4
   fi
   conv=0
  ;;
  3)
  #both are not given
   width=$(/usr/share/mplayer/midentify.sh $filename | grep "ID_VIDEO_WIDTH=" | sed 's/ID_VIDEO_WIDTH=\([0-9]*\)/\1/')
   height=$(/usr/share/mplayer/midentify.sh $filename | grep "ID_VIDEO_HEIGHT=" | sed 's/ID_VIDEO_HEIGHT=\([0-9]*\)/\1/')
   vbit=$(((width * height * vrate)/(640 * 480)))
   if [ "$pass" = "2" ] ;then
    ffmpeg -y -i "$filename" -an -pass 1 -vcodec libx264 -b "$vbit"k -flags +loop -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8 -flags2 +bpyramid -me_method dia -subq 1 -trellis 0 -refs 1 -bf 16 -directpred 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bt "$vbit"k -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -threads 0 -f rawvideo /dev/null

    ffmpeg -y -i "$filename" -pass 2 -acodec copy -vcodec libx264 -b "$vbit"k -flags +loop -cmp +chroma -partitions +parti8x8+parti4x4+partp8x8+partp4x4+partb8x8 -flags2 +dct8x8+wpred+bpyramid+mixed_refs -me_method umh -subq 7 -trellis 1 -refs 6 -bf 16 -directpred 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bt "$vbit"k -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -threads 0 "$filename"_converted.mp4
   else
    ffmpeg -y -i "$filename" -acodec copy -vcodec libx264 -b "$vbit"k -flags +loop -cmp +chroma -partitions +parti8x8+parti4x4+partp8x8+partp4x4+partb8x8 -flags2 +dct8x8+wpred+bpyramid+mixed_refs -me_method umh -subq 7 -trellis 1 -refs 6 -bf 16 -directpred 3 -b_strategy 1 -coder 1 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bt "$vbit"k -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -threads 0 "$filename"_converted.mp4
   fi
   conv=0
  ;;
  esac
 fi
done
for foldername in $direc/*; do
 if [ -d $foldername ] ; then
  if [ -L $foldername ] ; then
   echo $foldername" is a Link"
  else
   cd $foldername
   $0 $scase $ext $vbit $vrate $abit $pass
   cd ..
  fi
 fi
done
if [ $conv -eq 0 ] ; then
 echo "Deleting temporary files"
 rm "$direc"/ffmpeg2pass-0.log
 rm "$direc"/x264_2pass.log
 rm "$direc"/x264_2pass.log.mbtree
 echo "Renaming Files"
 rename -v "\s/."$ext"_converted.mp4$/.mp4/" *.mp4
fi






save this as recursive_2pass.sh in your home folder and make it executable.

Add the line
export recursive_2pass=~/recursive_2pass.sh 
to /etc/bash.bashrc file.

Now you can cd to any location in the terminal and run pwd | $renamer then $recursive_2pass to start auto encoding.

That took care of 2 pass encoding. If you want to use CRF then save the following code and follow instructions.

recursive_crf.sh:
#!/bin/sh

direc=$(pwd)

if [ $# -ne 3 ];then
 echo "Error in number of input parameters should be $0 <format> <crf give a number between 15-28> <audio bit rate or copy>"
else
 ext=$1
 crf=$2
 #lower crf means higher quality you can get a good quality at 18-21 anything less is not necessary.
 A_BIT=$3
 conv=1
 #search for files and folders if folder is found go into it else convert the file
 for filename in $direc/*.$ext; do
  if [ -f $filename ] ; then
   echo "Going to convert "$filename
   if [ "$A_BIT" = "copy" ] ; then
    ffmpeg -y -i "$filename" -acodec copy -vcodec libx264 -vpre medium -crf "$crf" -coder 1 -flags +loop -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8 -me_method hex -subq 6 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -b_strategy 1  -threads 0 "$filename"_converted.mp4
   else
    ffmpeg -y -i "$filename" -acodec libfaac -ab "$A_BIT"k -vcodec libx264 -vpre medium -crf "$crf" -coder 1 -flags +loop -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8 -me_method hex -subq 6 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -b_strategy 1 -threads 0 "$filename"_converted.mp4
   fi
   conv=0
  fi
 done
 for filename in $direc/*; do
  if [ -d $filename ] ; then
   if [ -L $filename ] ; then
    echo $filename" is a Link"
   else
    cd $filename
     $0 $1 $2 $3
    cd ..
   fi
  fi
 done
 if [ $conv -eq 0 ] ; then
  echo "Renaming Files"
  rename -v "\s/."$ext"_converted.mp4$/.mp4/" *.mp4
 fi

fi

save the code as recursive_crf.sh in your home folder and make it executable.
Add the following line to /etc/bash.bashrc
export recursive_crf=~/recursive_crf.sh

You can now cd to the required folder and run pwd | $renamer then $recursive_crf in terminal to start encoding.


Now you have everything assembled to start converting the videos. You can change the code according to your needs.

    No comments:

    Post a Comment