Skip to content

Hardware Acceleration Guide

Since: 0.11.0

Overview

Phlix Media Server supports hardware-accelerated transcoding via GPU encoders. The hardware acceleration system automatically detects available encoders (NVENC, VAAPI, QSV, VideoToolbox, AMF, V4L2) and provides a unified interface for selecting the best encoder for a given codec.

Architecture

Components

  1. HwaccelCapability — Value object representing a hardware accelerator's capabilities
  2. HwaccelProbe — Runs vendor-specific detection probes
  3. HwaccelRegistry — Singleton holding probed capabilities
  4. Vendor Probes — Each vendor (NVENC, VAAPI, etc.) has its own probe class

Vendor Priority

Hardware vendors are prioritized for fallback selection. Lower values = higher priority:

php
vendor_priority => [
    'nvenc' => 0,        // NVIDIA GPU (fastest, best quality)
    'vaapi' => 1,         // Linux VAAPI (Intel/AMD)
    'qsv' => 2,          // Intel Quick Sync
    'videotoolbox' => 3, // macOS VideoToolbox
    'amf' => 4,          // AMD GPU
    'v4l2' => 5,         // Video4Linux2 (limited)
]

HwaccelCapability Fields

FieldTypeDescription
vendorstringVendor identifier (e.g., 'nvenc', 'vaapi')
encoderstringFFmpeg encoder name (e.g., 'h264_nvenc')
decoderstringFFmpeg decoder name (e.g., 'hevc_cuvid')
supports_hdr_tone_mappingboolWhether HDR tone mapping is supported
supported_codecsstring[]List of supported codecs
supported_profilesstring[]List of supported profiles
max_resolution_wintMaximum width in pixels
max_resolution_hintMaximum height in pixels
max_bitrateintMaximum bitrate in bits per second
extra_argsarrayVendor-specific additional FFmpeg args

Usage

Automatic Encoder Selection

php
use Phlix\Media\Transcoding\Hwaccel\HwaccelRegistry;

// Get the best encoder for a codec
$capability = HwaccelRegistry::getInstance()->getEncoder('h264');

if ($capability !== null) {
    echo "Using encoder: " . $capability->encoder;
    echo "Vendor: " . $capability->vendor;
}

With FfmpegRunner

php
use Phlix\Media\Transcoding\FfmpegRunner;

// Probe hardware acceleration at startup
$runner = new FfmpegRunner();
$runner->probeHardwareAcceleration();

// Build a hardware-accelerated transcode command
$cmd = $runner->buildHwaccelCommand(
    inputPath: '/path/to/input.mkv',
    outputPath: '/path/to/output.mp4',
    codec: 'h264',
    params: ['crf' => 23]
);

HDR Transcoding

php
// Get encoder with HDR tone mapping support
$capability = HwaccelRegistry::getInstance()->getEncoder(
    'hevc',
    require_hdr_tone_map: true
);

if ($capability !== null && $capability->supports_hdr_tone_mapping) {
    // Use this encoder for HDR content
}

Adding a New Vendor

  1. Create a new class implementing VendorProbeInterface in src/Media/Transcoding/Hwaccel/VendorProbe/
  2. Implement the 5 methods: getVendorName(), isAvailable(), probe(), runAcceptanceTest()
  3. Register the probe in HwaccelProbe::__construct()
  4. Add vendor priority in config/hwaccel.php

Example:

php
namespace Phlix\Media\Transcoding\Hwaccel\VendorProbe;

use Phlix\Media\Transcoding\Hwaccel\HwaccelCapability;
use Phlix\Media\Transcoding\Hwaccel\VendorProbeInterface;
use Psr\Log\LoggerInterface;

class NewVendorProbe implements VendorProbeInterface
{
    private const VENDOR_NAME = 'newvendor';

    public function getVendorName(): string
    {
        return self::VENDOR_NAME;
    }

    public function isAvailable(): bool
    {
        // Detection logic
        return file_exists('/some/vendor/device');
    }

    public function probe(string $ffmpeg_path, ?LoggerInterface $logger = null): ?HwaccelCapability
    {
        if (!$this->isAvailable()) {
            return null;
        }

        // Return capability based on detection
        return new HwaccelCapability(
            vendor: self::VENDOR_NAME,
            encoder: 'h264_newvendor',
            decoder: 'hevc_newvendor',
            supports_hdr_tone_mapping: true,
            supported_codecs: ['h264', 'hevc'],
            supported_profiles: ['main', 'high'],
            max_resolution_w: 3840,
            max_resolution_h: 2160,
            max_bitrate: 40000000,
        );
    }

    public function runAcceptanceTest(string $ffmpeg_path, string $test_clip_path, ?LoggerInterface $logger = null): bool
    {
        // Run actual encode test
        return true;
    }
}

Encoding Profiles

Each hardware vendor has a dedicated encoder profile class implementing HwaccelEncoderProfileInterface. These profiles map abstract quality levels to concrete FFmpeg encoder flags.

Profile Classes

VendorClassEncoderNotes
NVIDIANvencProfileh264_nvenc, hevc_nvencPreset p1-p7, zerolatency tune
VAAPIVaapiProfileh264_vaapi, hevc_vaapiCQP/VBR rate control
QSVQsvProfileh264_qsv, hevc_qsv, av1_qsvLook-ahead support
VideoToolboxVideoToolboxProfileh264_videotoolbox, hevc_videotoolboxmacOS only
AMFAmfProfileh264_amf, hevc_amfAMD GPUs
V4L2V4L2Profileh264_v4l2m2m, hevc_v4l2m2mLinux kernel API
SoftwareSoftwareProfilelibx264, libx265CPU fallback

Quality Level Mapping

Each profile supports four quality levels with associated bitrate and preset settings:

php
// Example: NVENC quality levels
'ultra'  => ['bitrate' => 8000000, 'preset' => 'p3', 'bframes' => 0],
'high'    => ['bitrate' => 5000000, 'preset' => 'p4', 'bframes' => 0],
'medium'  => ['bitrate' => 2500000, 'preset' => 'p5', 'bframes' => 0],
'low'     => ['bitrate' => 1000000, 'preset' => 'p6', 'bframes' => 0],

Using the Profile Factory

php
use Phlix\Media\Transcoding\Hwaccel\HwaccelRegistry;
use Phlix\Media\Transcoding\Hwaccel\HwaccelProfileFactory;

// Get the best profile for a vendor+codec combination
$registry = HwaccelRegistry::getInstance();
$factory = new HwaccelProfileFactory($registry);

$profile = $factory->getProfile('nvenc', 'h264');
$builder = $factory->createCommandBuilder('nvenc', 'h264', 'high');

Using the Command Builder

php
use Phlix\Media\Transcoding\Hwaccel\HwaccelCommandBuilder;
use Phlix\Media\Transcoding\Hwaccel\Profiles\NvencProfile;
use Phlix\Media\Transcoding\Hwaccel\HwaccelCapability;

$capability = new HwaccelCapability(
    vendor: 'nvenc',
    encoder: 'h264_nvenc',
    decoder: 'h264_cuvid',
    supports_hdr_tone_mapping: true,
    supported_codecs: ['h264', 'hevc'],
    supported_profiles: ['baseline', 'main', 'high'],
    max_resolution_w: 3840,
    max_resolution_h: 2160,
    max_bitrate: 50000000,
);

$cmd = (new HwaccelCommandBuilder(new NvencProfile(), $capability, 'high'))
    ->setInput('/input.mkv')
    ->setOutput('/output.mp4')
    ->setVideoCodec('h264')
    ->setBitrate(5000000)
    ->setResolution(1920, 1080)
    ->build();

Max Concurrent Encodes

VendorMax ConcurrentNotes
NVENC3Per GPU
VAAPI4Per GPU
QSV6Per GPU
VideoToolbox0Unlimited (Apple Silicon)
AMF2Per GPU
V4L21Limited hardware
Software0CPU-bound

Configuration

See config/hwaccel.php for configuration options:

  • enabled — Enable/disable hardware acceleration
  • prefer_hardware — Prefer hardware over software
  • vendor_priority — Vendor fallback order
  • probe_timeout — Timeout for probe operations
  • test_clip_path — Path for acceptance test clip
  • fallback_to_software — Allow software fallback

Detection Methods by Vendor

VendorDetection Method
NVENCnvidia-smi command
VAAPI/dev/dri devices + vainfo
QSVvainfo with Intel GPU
VideoToolboxmacOS + system_profiler
AMFvainfo with AMD GPU
V4L2/dev/media* devices
SoftwareAlways available (libx264)

Requirements

HDR Tone-Mapping (Since 0.11.0)

When transcoding HDR (High Dynamic Range) content to SDR (Standard Dynamic Range), the system applies tone-mapping to preserve visual quality while converting the extended luminance range to displayable SDR levels.

HDR Metadata Detection

The system detects HDR content via ffprobe color metadata:

  • color_transfer: smpte2084 (PQ) or arib-std-b67 (HLG) indicates HDR
  • color_space: Typically bt2020nc for HDR content
  • color_primaries: Typically bt2020 for HDR content
  • max_luminance: Extracted from mastering_display_luminance tag (e.g., 1000, 4000 nits)
  • avg_luminance: Extracted from ambient_luminance tag

Tone-Mapping Architecture

HwaccelToneMapper
├── Detects HDR from ffprobe results
├── Selects appropriate vendor tone mapper
└── Generates vendor-specific filter chain

Vendor-Specific Tone Mapping

VendorHardware SupportFilter ChainNotes
NVENC✅ Yeshwupload, tonemap_cuda, scale_cudaPrimary: CUDA-based
VAAPI✅ Yeshwupload, tonemap_vaapi, scale_vaapiPrimary: VAAPI built-in
QSV✅ Yeshwupload, vpp_tonemap, scale_qsvVPP filmic mode
VideoToolbox❌ Nozscale, formatCPU fallback
AMF✅ Yeshwupload, tonemap_amfAMD GPU
V4L2❌ Nozscale, formatV4L2 request API limitation
Software❌ Nozscale, formatCPU fallback

Tone-Mapping Filter Parameters

NVENC (tonemap_cuda)

tonemap_cuda=transfer=smpte2084:primaries=bt2020:tonemap=hable:desat=0.5:peak=10.0
  • transfer: Source transfer function (smpte2084 for PQ, arib-std-b67 for HLG)
  • primaries: Color primaries (bt2020)
  • tonemap: Tone mapping curve (hable, mobius, linear)
  • desat: Desaturation threshold for bright colors
  • peak: Reference peak luminance

VAAPI (tonemap_vaapi)

tonemap_vaapi=transfer=bt2020:primaries=bt2020:tonemap=hable:desat=0.5

QSV (vpp_tonemap)

vpp_tonemap=mode=1:desat=0.5:peak=10.0
  • mode: 1 = filmic, 2 = fixed, 3 = linear
  • desat: Desaturation parameter
  • peak: Peak luminance for tone mapping

Software Fallback (zscale)

zscale=transfer=bt709:min_luminance=2.0:max_luminance=10.0:param1=0.18:param2=0.14
  • transfer: Target transfer function (bt709 for SDR)
  • min_luminance: Minimum luminance reference
  • max_luminance: Maximum luminance reference
  • param1/param2: Tone mapping curve parameters

Usage Example

php
use Phlix\Media\Transcoding\Hwaccel\HwaccelCommandBuilder;
use Phlix\Media\Transcoding\Hwaccel\HwaccelRegistry;
use Phlix\Media\Transcoding\Hwaccel\ToneMapping\HdrMetadata;
use Phlix\Media\Transcoding\Hwaccel\ToneMapping\HwaccelToneMapper;
use Phlix\Media\Transcoding\Hwaccel\Profiles\NvencProfile;

// Get HDR metadata from ffprobe
$probeResult = $ffmpegRunner->probe('/path/to/hdr/video.mkv');
$colorMeta = $ffmpegRunner->extractColorMetadata($probeResult);

if ($colorMeta['color_transfer'] === 'smpte2084' || $colorMeta['color_transfer'] === 'arib-std-b67') {
    $hdr = new HdrMetadata(
        color_space: $colorMeta['color_space'],
        color_transfer: $colorMeta['color_transfer'],
        color_primaries: $colorMeta['color_primaries'],
        max_luminance: $colorMeta['max_luminance'],
        avg_luminance: $colorMeta['avg_luminance']
    );

    // Build HDR transcode command with tone mapping
    $registry = HwaccelRegistry::getInstance();
    $capability = $registry->getEncoder('hevc', require_hdr_tone_map: true);
    $profile = new NvencProfile();

    $cmd = (new HwaccelCommandBuilder($profile, $capability, 'high'))
        ->setInput('/path/to/hdr/video.mkv')
        ->setOutput('/path/to/sdr/output.mp4')
        ->setVideoCodec('hevc')
        ->setHdrMetadata($hdr)
        ->build();
}

Tone Mapping Classes

ClassDescription
HdrMetadataValue object for HDR source metadata
ToneMapFilterChainResult container for generated filter chains
HwaccelToneMapperMain orchestrator for tone mapping
HwaccelToneMapperInterfaceInterface for vendor tone mappers
ToneMapperFactoryFactory for creating vendor tone mappers
NvencToneMapperNVIDIA NVENC implementation
VaapiToneMapperVAAPI implementation
QsvToneMapperIntel QSV implementation
VideoToolboxToneMapperApple VideoToolbox implementation (software fallback)
AmfToneMapperAMD AMF implementation
V4L2ToneMapperV4L2 implementation (software fallback)
SoftwareToneMapperCPU-based zscale implementation

BSD-3-Clause