Skip to content

Instantly share code, notes, and snippets.

@lucaspar
Last active August 26, 2024 23:25
Show Gist options
  • Save lucaspar/27f5e108b80524b315be10b2a9049817 to your computer and use it in GitHub Desktop.
Save lucaspar/27f5e108b80524b315be10b2a9049817 to your computer and use it in GitHub Desktop.
Installation script of CUDA-accelerated `ffmpeg` with NVIDIA Encoder
#!/bin/bash
# =========================================================================
# Source: https://gist.github.com/lucaspar/27f5e108b80524b315be10b2a9049817
# =========================================================================
# This script will compile and install a static FFmpeg build with
# support for NVENC in Ubuntu. Developed in Ubuntu 23.10,
# with NVIDIA Drivers v535.129.03 and CUDA v12.2 with a GPU
# with CUDA capability 8.6 (RTX 3080) (see ccap below).
# It assumes NVIDA drivers are installed and that you have a
# CUDA-compatible GPU. You can check installed drivers with:
# $ apt list *nvidia-driver-* | grep installed
# $ nvidia-smi
# =========================================================================
set -e
# Variables you might want to change
DIR_USR_BIN="${HOME}/.local/bin" # user-writable binaries, where to install ffmpeg
DIR_INSTALL_ROOT="${XDG_STATE_HOME:-${HOME}/.local/state}" # location to clone repos and build artifacts
DIR_FFMPEG_BUILD="${DIR_INSTALL_ROOT}/ffmpeg-build" # where to build ffmpeg
DIR_FFMPEG_SOURCES="${DIR_INSTALL_ROOT}/ffmpeg-sources" # ffmpeg source code
# CUDA compute capability: check yours at https://developer.nvidia.com/cuda-gpus, or run:
# nvidia-smi --query-gpu=compute_cap --format=csv
ccap=86
# NASM (Netwide Assembler) version to install (most recent version at the time of writing)
# https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu#NASM
NASM_VERSION=2.16.01
# ffmpeg version to install (most recent version at the time of writing)
# check versions at https://github.com/FFmpeg/FFmpeg/branches/active
# check changelog at https://github.com/FFmpeg/FFmpeg/blob/master/Changelog
FFMPEG_VERSION=6.1
# Install required things from apt
install_libs() {
echo " ๐Ÿš€ Installing prerequisites"
sudo apt-get update
sudo apt-get -y install autoconf automake build-essential \
libass-dev libfreetype6-dev libsdl1.2-dev libtheora-dev \
libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev \
libxcb-xfixes0-dev pkg-config texi2html zlib1g-dev libopus-dev libunistring-dev \
libavdevice-dev libfdk-aac-dev libmp3lame-dev libx264-dev libavcodec-dev \
libgnutls28-dev libx265-dev libnuma-dev libaom-dev libaom3
}
# Install NVENC SDK
install_nvenc_sdk() {
echo " ๐Ÿš€ Installing the NVIDIA NVENC SDK."
cd "${DIR_FFMPEG_SOURCES}" || exit 1
dir_nvcodec="${DIR_FFMPEG_SOURCES}/nv-codec-headers"
if [ ! -d "${dir_nvcodec}" ]; then
git clone https://git.videolan.org/git/ffmpeg/nv-codec-headers.git "${dir_nvcodec}"
fi
cd "${dir_nvcodec}" || exit 1
make
sudo make install
}
# Compile NASM
compile_nasm() {
echo " ๐Ÿš€ Compiling nasm"
dir_nasm="${DIR_FFMPEG_SOURCES}/nasm-${NASM_VERSION}"
if [ ! -d "${dir_nasm}" ]; then
cd "${DIR_FFMPEG_SOURCES}" || exit 1
wget "https://www.nasm.us/pub/nasm/releasebuilds/${NASM_VERSION}/nasm-${NASM_VERSION}.tar.gz"
tar xzvf nasm-${NASM_VERSION}.tar.gz && rm -f nasm-${NASM_VERSION}.tar.gz
fi
cd "${dir_nasm}" || exit 1
./configure --prefix="${DIR_FFMPEG_BUILD}" --bindir="${DIR_USR_BIN}"
make -j"${NUM_CORES}" || true # too many false positives, ignoring errors
make -j"${NUM_CORES}" install || true # too many false positives, ignoring errors
make -j"${NUM_CORES}" distclean
}
# Compile libvpx
compile_libvpx() {
echo " ๐Ÿš€ Compiling libvpx"
dir_vpx="${DIR_FFMPEG_SOURCES}/libvpx"
if [ ! -d "${dir_vpx}" ]; then
cd "${DIR_FFMPEG_SOURCES}" || exit 1
git clone https://chromium.googlesource.com/webm/libvpx "${dir_vpx}"
fi
cd "${dir_vpx}" || exit 1
PATH="${DIR_USR_BIN}:${PATH}" ./configure --prefix="${DIR_FFMPEG_BUILD}" --disable-examples \
--enable-runtime-cpu-detect --enable-vp9 --enable-vp8 \
--enable-postproc --enable-vp9-postproc --enable-multi-res-encoding \
--enable-webm-io --enable-better-hw-compatibility --enable-vp9-highbitdepth \
--enable-onthefly-bitpacking --enable-realtime-only --enable-pic \
--cpu=native --as=nasm
PATH="${DIR_USR_BIN}:${PATH}" make -j"${NUM_CORES}"
make -j"${NUM_CORES}" install
make -j"${NUM_CORES}" clean
}
# Compile ffmpeg and install it
compile_and_install_ffmpeg() {
echo " ๐Ÿš€ Compiling ffmpeg"
dir_ffmpeg="${DIR_FFMPEG_SOURCES}/ffmpeg-repo"
if [ ! -d "${dir_ffmpeg}" ]; then
cd "${DIR_FFMPEG_SOURCES}" || exit 1
git clone https://github.com/FFmpeg/FFmpeg.git -b master "${dir_ffmpeg}"
fi
cd "${dir_ffmpeg}" || exit 1
git fetch --tags
git switch "release/${FFMPEG_VERSION}"
PATH="${DIR_USR_BIN}:${PATH}" PKG_CONFIG_PATH="${DIR_FFMPEG_BUILD}/lib/pkgconfig" ./configure \
--pkg-config-flags="--static" \
--prefix="${DIR_FFMPEG_BUILD}" \
--extra-cflags="-I${DIR_FFMPEG_BUILD}/include" \
--extra-ldflags="-L${DIR_FFMPEG_BUILD}/lib" \
--extra-cflags="-I/usr/local/cuda/include/" \
--extra-ldflags=-L/usr/local/cuda/lib64/ \
--nvccflags="-gencode arch=compute_${ccap},code=sm_${ccap} -O2" \
--bindir="${DIR_USR_BIN}" \
--enable-static \
--enable-cuda-nvcc \
--enable-cuvid \
--enable-decoder=aac \
--enable-decoder=h264 \
--enable-decoder=h264_cuvid \
--enable-demuxer=mov \
--enable-filter=scale \
--enable-gnutls \
--enable-gpl \
--enable-libass \
--enable-libfdk-aac \
--enable-libfreetype \
--enable-libmp3lame \
--enable-libnpp \
--enable-libopus \
--enable-libtheora \
--enable-libvorbis \
--enable-libvpx \
--enable-libx264 \
--enable-libx265 \
--enable-libaom \
--enable-nonfree \
--enable-nvdec \
--enable-nvenc \
--enable-pic \
--enable-protocol=file \
--enable-protocol=https \
--enable-vaapi
PATH="${DIR_USR_BIN}:${PATH}" make -j"${NUM_CORES}"
make -j"${NUM_CORES}" install
make -j"${NUM_CORES}" distclean
hash -r
}
# check if nvidia-smi succeeds
check_nvidia_smi() {
if ! nvidia-smi &>/dev/null; then
echo -e "\tnvidia-smi not found or failed to run."
echo -e "\tMake sure NVIDIA drivers are installed e.g.:"
echo -e "\t\tsudo apt install nvidia-driver-545"
exit 1
fi
}
# link installed binaries to user bin
link_installed() {
echo "Linking installed binaries to ${DIR_USR_BIN}"
cd "${DIR_USR_BIN}" || exit 1
declare -a arr=("ffmpeg" "ffprobe" "ffplay" "ffserver" "ffprobe")
for f in "${arr[@]}"; do
f_path="${DIR_FFMPEG_BUILD}/bin/${f}"
if [ -f "${f_path}" ]; then
ldd "${f_path}"
fi
done
}
# main things
function main() {
check_nvidia_smi
# set number of cores to use
num_proc="$(nproc)"
NUM_CORES=$((num_proc - 2))
echo "Using up to ${NUM_CORES} cores"
# create directories
DIR_PREV="$(pwd)"
cd "${DIR_INSTALL_ROOT}" || exit 1
mkdir -p "${DIR_FFMPEG_SOURCES}"
install_libs
install_nvenc_sdk
compile_nasm
compile_libvpx
compile_and_install_ffmpeg
cd "${DIR_PREV}" || exit 1
link_installed
wrap_up
benchmark
}
function wrap_up() {
echo " ๐Ÿ Complete!"
echo -e " ๐Ÿ’ก Some commands to try:\n"
echo "which ffmpeg ffprobe"
echo "whereis ffmpeg ffprobe # to run the correct binary in case your system has others"
echo "ffmpeg 2>/dev/null -version | grep -i 'nv[a-z]*|nvidia|cuda|nvenc'"
echo "ffprobe 2>/dev/null -decoders | grep -i 'nvidia|cuda|nvenc'"
echo "ffprobe 2>/dev/null -encoders | grep -i 'nvidia|cuda|nvenc'"
echo "ffprobe 2>/dev/null -filters | grep -i 'nvidia|cuda|nvenc'"
echo -e "\n\n\t\t EXAMPLE OF NVENC USAGE"
echo -e "\n ๐Ÿ‘‰ Get a sample video to work with:\n"
echo -e "wget https://delamain.s3.amazonaws.com/public/samples/video-sample-720p.mp4 -O input.mp4"
echo -e "\n ๐Ÿ‘‰ GPU encoding test:\n"
echo -e "time ffmpeg -y -hwaccel cuda -hwaccel_output_format cuda -extra_hw_frames 4 -i input.mp4 -c:v h264_nvenc output-nvenc.mp4"
echo -e "\n ๐Ÿ‘‰ CPU encoding test:\n"
echo -e "time ffmpeg -y -i input.mp4 -c:v h264 output-cpu.mp4"
echo -e "\n ๐Ÿ‘‰ Wall times for tested run:\t15.695s (CPU, 24 cores) vs. (CUDA, RTX 3080) 4.952s"
echo -e "\nIf everything works, you can mark the following directories for deletion:\n\n ๐Ÿงน ${DIR_FFMPEG_SOURCES}\n ๐Ÿงน ${DIR_FFMPEG_BUILD}\n"
}
function benchmark() {
if ! [ -f input.mp4 ]; then
echo "Downloading sample video"
wget https://delamain.s3.amazonaws.com/public/samples/video-sample-720p.mp4 -O input.mp4
fi
echo "Benchmarking x264 (MPEG-4/H.264) CPU vs. NVENC"
time ffmpeg -y -i input.mp4 -c:v h264 output-cpu-h264.mp4
time ffmpeg -y -hwaccel cuda -hwaccel_output_format cuda -extra_hw_frames 4 -i input.mp4 -c:v h264_nvenc output-nvenc-h264.mp4
echo "Benchmarking x265 (HEVC/H.265) CPU vs. NVENC"
time ffmpeg -y -i input.mp4 -c:v hevc output-cpu-hevc.mp4
time ffmpeg -y -hwaccel cuda -hwaccel_output_format cuda -extra_hw_frames 4 -i input.mp4 -c:v hevc_nvenc output-nvenc-hevc.mp4
}
main
@lucaspar
Copy link
Author

lucaspar commented Dec 20, 2023

Changelog

2023-01-24

  • Enabled AV1 support*.

* Hardware accelerated encoding using NVIDIA cards (av1_nvenc codec) is only available for 40 series onwards (Ada architecture). See matrix.

2023-12-20

  • Updated script from an Ubuntu 22.04 base to 23.10.
  • Added HEVC (x265) encoders.
  • Added benchmark function as usage example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment