lumen-engine-ffmpeg 0.2.2

FFmpeg integration for media decode, encode, muxing, and GPU interop in Lumen.
Documentation
use crate::gpu::GpuBackend;
use crate::video::VideoCodec;

use super::codec::encoder_by_name;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GpuTextureEncodeSupport {
    pub backend: GpuBackend,
    pub codec: VideoCodec,
    pub encoder_name: Option<&'static str>,
    pub available: bool,
    pub direct_texture_path: bool,
    pub reason: Option<String>,
}

pub(crate) fn hardware_texture_encoder_name(
    backend: GpuBackend,
    codec: VideoCodec,
) -> Option<&'static str> {
    match (backend, codec) {
        (GpuBackend::Cuda, VideoCodec::H264) => Some("h264_nvenc"),
        (GpuBackend::Cuda, VideoCodec::Hevc) => Some("hevc_nvenc"),
        (GpuBackend::Cuda, VideoCodec::Av1) => Some("av1_nvenc"),
        (GpuBackend::Metal, VideoCodec::H264) => Some("h264_videotoolbox"),
        (GpuBackend::Metal, VideoCodec::Hevc) => Some("hevc_videotoolbox"),
        _ => None,
    }
}

pub fn gpu_texture_encode_support(
    codec: VideoCodec,
    backend: GpuBackend,
) -> GpuTextureEncodeSupport {
    let encoder_name = hardware_texture_encoder_name(backend, codec);
    let texture_path_wired = matches!(backend, GpuBackend::Cuda | GpuBackend::Metal);
    let encoder_available =
        texture_path_wired && encoder_name.is_some_and(|name| encoder_by_name(name).is_ok());
    let reason = if !encoder_available {
        Some(match (texture_path_wired, encoder_name) {
            (false, _) => format!("{backend:?} hardware texture encode is not available yet"),
            (true, Some(name)) => format!("FFmpeg encoder `{name}` is unavailable"),
            (true, None) => format!("{backend:?} texture encode is unavailable for {codec:?}"),
        })
    } else {
        None
    };

    GpuTextureEncodeSupport {
        backend,
        codec,
        encoder_name,
        available: encoder_available,
        direct_texture_path: false,
        reason,
    }
}