Skip to main content

Convert ffmpeg commands to use hardware acceleration

Project description

ffmpeg-hw-accel

A Python library that converts standard (software) ffmpeg commands into hardware-accelerated equivalents, automatically detecting available accelerators on the current machine and handling all compatibility differences between encoders.

The library only transforms command strings. Executing the command is always left to the caller.


Table of Contents


Features

  • ๐Ÿ” Auto-detection: queries your local ffmpeg binary at startup to discover which hardware encoders are actually available
  • ๐Ÿ”„ Command conversion: rewrites -c:v, -preset, -crf, -tune, and other flags to their hardware-accelerated equivalents
  • ๐Ÿ›ก๏ธ Compatibility enforcement: automatically removes flags that are incompatible with the chosen accelerator (e.g. -tune has no meaning for NVENC)
  • โ†ฉ๏ธ Automatic fallback: if the target accelerator is unavailable or conversion fails, the original command is returned unchanged (configurable)
  • ๐Ÿ—๏ธ Singleton design: one instance per process; detection runs once at initialisation
  • ๐Ÿ”’ Strict typing: all parameters use enums; no magic strings internally
  • ๐ŸŒ Cross-platform: works on Linux, Windows, and macOS; available accelerators depend on the hardware present

Supported Hardware Accelerators

Accelerator Enum Typical Platform Example Codecs
Software (fallback) HWAccelerator.NONE All libx264, libx265
NVIDIA NVENC HWAccelerator.NVENC Windows, Linux h264_nvenc, hevc_nvenc
Intel Quick Sync HWAccelerator.QSV Windows, Linux h264_qsv, hevc_qsv
AMD AMF HWAccelerator.AMF Windows, Linux h264_amf, hevc_amf
VAAPI HWAccelerator.VAAPI Linux h264_vaapi, hevc_vaapi
Apple VideoToolbox HWAccelerator.VIDEOTOOLBOX macOS h264_videotoolbox, hevc_videotoolbox
V4L2 M2M HWAccelerator.V4L2 Linux (Raspberry Pi) h264_v4l2m2m

Requirements

  • Python 3.11+
  • ffmpeg installed and accessible on your PATH (or provide the full path)
  • The hardware driver and SDK corresponding to the accelerator you want to use (e.g. NVIDIA drivers for NVENC)

Installation

pip install ffmpeg-hw-accel

Or with uv:

uv add ffmpeg-hw-accel

Quick Start

from ffmpeg_hw_accel import FFmpegHWAccel, HWAccelerator

# Initialise โ€” detects available hardware at this point
accel = FFmpegHWAccel.get_instance()

# See what was found
print(accel.available_accelerators)
# [<HWAccelerator.NONE: 'none'>, <HWAccelerator.NVENC: 'nvenc'>]

# Convert a command
original = "ffmpeg -i input.mp4 -c:v libx264 -preset slow -crf 22 -c:a aac output.mp4"
converted = accel.convert(original, HWAccelerator.NVENC)

print(converted)
# ffmpeg -hwaccel cuda -i input.mp4 -c:v h264_nvenc -preset p6 -cq 22 -c:a aac -rc:v vbr -b:v 0 output.mp4

Usage Guide

Initialisation

FFmpegHWAccel is a singleton. The first call to get_instance() creates the instance and runs hardware detection. All subsequent calls return the same object.

from ffmpeg_hw_accel import FFmpegHWAccel

# Default โ€” uses "ffmpeg" from PATH, fallback enabled
accel = FFmpegHWAccel.get_instance()

# Custom ffmpeg binary
accel = FFmpegHWAccel.get_instance(
    ffmpeg_binary="/usr/local/bin/ffmpeg",  # full path or name on PATH
    auto_fallback=True,                      # True = return original on any error (default)
)
Parameter Type Default Description
ffmpeg_binary str "ffmpeg" Path or name of the ffmpeg executable
auto_fallback bool True Return the original command unchanged if conversion fails or the accelerator is unavailable

Detecting Available Accelerators

# Full list
print(accel.available_accelerators)
# [<HWAccelerator.NONE: 'none'>, <HWAccelerator.NVENC: 'nvenc'>]

# Check a specific accelerator
accel.is_available(HWAccelerator.NVENC)         # True
accel.is_available(HWAccelerator.VIDEOTOOLBOX)  # False (not macOS)

# List codecs available for a given accelerator
codecs = accel.get_available_codecs(HWAccelerator.NVENC)
# [<VideoCodec.H264_NVENC: 'h264_nvenc'>, <VideoCodec.HEVC_NVENC: 'hevc_nvenc'>]

Converting a Command

Pass any standard ffmpeg command string and a target HWAccelerator:

original = (
    "ffmpeg -i input.mp4 "
    "-c:v libx264 -preset medium -tune film -crf 23 "
    "-c:a aac -b:a 192k output.mp4"
)

# NVENC
print(accel.convert(original, HWAccelerator.NVENC))
# ffmpeg -hwaccel cuda -i input.mp4
#   -c:v h264_nvenc -preset p5 -cq 23
#   -c:a aac -b:a 192k -rc:v vbr -b:v 0 output.mp4
# (-tune film removed โ€” not supported by NVENC)

# QSV
print(accel.convert(original, HWAccelerator.QSV))
# ffmpeg -hwaccel qsv -hwaccel_output_format qsv -i input.mp4
#   -c:v h264_qsv -preset medium -global_quality 23
#   -c:a aac -b:a 192k output.mp4

# VAAPI (Linux)
print(accel.convert(original, HWAccelerator.VAAPI))
# ffmpeg -hwaccel vaapi -hwaccel_output_format vaapi -i input.mp4
#   -c:v h264_vaapi -qp 23
#   -c:a aac -b:a 192k -vf format=nv12|vaapi,hwupload output.mp4
# (-preset and -tune removed โ€” VAAPI has no preset concept)

# VideoToolbox (macOS)
print(accel.convert(original, HWAccelerator.VIDEOTOOLBOX))
# ffmpeg -i input.mp4
#   -c:v h264_videotoolbox -q:v 23
#   -c:a aac -b:a 192k output.mp4
# (-preset, -tune, -crf all removed โ€” not supported)

# AMF (AMD)
print(accel.convert(original, HWAccelerator.AMF))
# ffmpeg -i input.mp4
#   -c:v h264_amf -preset balanced -qp_i 23
#   -c:a aac -b:a 192k output.mp4

Fallback Behaviour

auto_fallback=True (default)

The original command is returned unchanged when:

  • The requested accelerator is not detected on the machine
  • An exception occurs during conversion
accel = FFmpegHWAccel.get_instance(auto_fallback=True)
cmd = "ffmpeg -i input.mp4 -c:v libx264 output.mp4"

# VAAPI not available on this machine โ†’ returns original silently
result = accel.convert(cmd, HWAccelerator.VAAPI)
print(result)
# "ffmpeg -i input.mp4 -c:v libx264 output.mp4"

auto_fallback=False

Raises exceptions instead of silently falling back:

from ffmpeg_hw_accel import FFmpegHWAccel, HWAccelerator

FFmpegHWAccel.reset_instance()
accel = FFmpegHWAccel.get_instance(auto_fallback=False)
cmd = "ffmpeg -i input.mp4 -c:v libx264 output.mp4"

try:
    result = accel.convert(cmd, HWAccelerator.VAAPI)
except ValueError as e:
    print(e)
    # Hardware accelerator 'vaapi' is not available on this machine.
    # Available: ['none', 'nvenc']

Executing the Converted Command

The library only produces a command string. Running it is your responsibility:

import subprocess
import shlex
from ffmpeg_hw_accel import FFmpegHWAccel, HWAccelerator

accel = FFmpegHWAccel.get_instance()
original = "ffmpeg -i input.mp4 -c:v libx264 -preset slow -crf 22 output.mp4"
hw_cmd = accel.convert(original, HWAccelerator.NVENC)

try:
    subprocess.run(shlex.split(hw_cmd), check=True)
    print("Encoding complete.")
except subprocess.CalledProcessError as e:
    print(f"Hardware encoding failed: {e}")
    # Manually fall back to software
    subprocess.run(shlex.split(original), check=True)

Selecting the Best Available Accelerator

from ffmpeg_hw_accel import FFmpegHWAccel, HWAccelerator

def get_best_accel(accel: FFmpegHWAccel) -> HWAccelerator:
    preference = [
        HWAccelerator.NVENC,
        HWAccelerator.QSV,
        HWAccelerator.VIDEOTOOLBOX,
        HWAccelerator.AMF,
        HWAccelerator.VAAPI,
        HWAccelerator.V4L2,
        HWAccelerator.NONE,   # software fallback โ€” always available
    ]
    return next(a for a in preference if accel.is_available(a))


accel = FFmpegHWAccel.get_instance()
best = get_best_accel(accel)

original = "ffmpeg -i input.mp4 -c:v libx264 -preset medium -crf 23 -c:a aac output.mp4"
final_cmd = accel.convert(original, best)

print(f"Selected: {best.value}")
print(f"Command:  {final_cmd}")

Resetting the Singleton

Useful when you need to switch ffmpeg binary or re-run detection (e.g. in tests):

FFmpegHWAccel.reset_instance()

# Next call creates a fresh instance with new detection
accel = FFmpegHWAccel.get_instance(ffmpeg_binary="/opt/homebrew/bin/ffmpeg")

Compatibility Rules

The table below summarises what each accelerator removes or transforms:

Flag NVENC QSV VAAPI VideoToolbox AMF V4L2
-tune โŒ removed โŒ removed โŒ removed โŒ removed โŒ removed โŒ removed
-x264opts โŒ removed โŒ removed โŒ removed โŒ removed โŒ removed โŒ removed
-x264-params โŒ removed โŒ removed โŒ removed โŒ removed โŒ removed โŒ removed
-preset โœ๏ธ remapped (p1โ€“p7) โœ๏ธ remapped โŒ removed โŒ removed โœ๏ธ remapped โŒ removed
-crf โœ๏ธ โ†’ -cq โœ๏ธ โ†’ -global_quality โœ๏ธ โ†’ -qp โŒ removed โœ๏ธ โ†’ -qp_i โŒ removed
-profile:v โœ… kept โœ… kept โŒ removed โŒ removed โœ… kept โœ… kept
-c:v libx264 โœ๏ธ โ†’ h264_nvenc โœ๏ธ โ†’ h264_qsv โœ๏ธ โ†’ h264_vaapi โœ๏ธ โ†’ h264_videotoolbox โœ๏ธ โ†’ h264_amf โœ๏ธ โ†’ h264_v4l2m2m
-c:v libx265 โœ๏ธ โ†’ hevc_nvenc โœ๏ธ โ†’ hevc_qsv โœ๏ธ โ†’ hevc_vaapi โœ๏ธ โ†’ hevc_videotoolbox โœ๏ธ โ†’ hevc_amf โœ๏ธ โ†’ hevc_v4l2m2m

Development

Setup

This project uses uv for dependency management.

# Install uv (if not already installed)
curl -LsSf https://astral.sh/uv/install.sh | sh

# Clone the repository
git clone https://github.com/your-username/ffmpeg-hw-accel.git
cd ffmpeg-hw-accel

# Install dependencies including dev extras
uv sync --extra dev

Running Tests

# Run all tests
uv run pytest

# With coverage report
uv run pytest --cov=src/ffmpeg_hw_accel --cov-report=term-missing

# Run a specific test file
uv run pytest tests/test_converter.py

# Run a specific test case
uv run pytest tests/test_converter.py::TestFFmpegCommandConverter::test_nvenc_replaces_libx264_codec

# Verbose output
uv run pytest -v

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ffmpeg_hw_accel-0.1.0.tar.gz (39.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ffmpeg_hw_accel-0.1.0-py3-none-any.whl (16.4 kB view details)

Uploaded Python 3

File details

Details for the file ffmpeg_hw_accel-0.1.0.tar.gz.

File metadata

  • Download URL: ffmpeg_hw_accel-0.1.0.tar.gz
  • Upload date:
  • Size: 39.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.15 {"installer":{"name":"uv","version":"0.9.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for ffmpeg_hw_accel-0.1.0.tar.gz
Algorithm Hash digest
SHA256 df196ca66969e9fff112715bb89817aebbcb3ba11e703030c401a8432123e6bd
MD5 adf5d3f2b291dc3e4cdb8a14c41c2a77
BLAKE2b-256 e3ae2f85b697b52d17ef39e2f7ec893a6b8583763d97b8d335d71d0e72ffd1a3

See more details on using hashes here.

File details

Details for the file ffmpeg_hw_accel-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: ffmpeg_hw_accel-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 16.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.15 {"installer":{"name":"uv","version":"0.9.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for ffmpeg_hw_accel-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 81c863b653665d224dc3031b5aa6a3a742774fc71593f08fd31ab013e2951ebd
MD5 70160bbbfd947c9b8c0ccefa81530625
BLAKE2b-256 c0a7a377f420f816d9fe3305886f08cb7f7633d850c863803700294b0ddf75a7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page