videotoolbox 0.16.0

Safe Rust bindings for Apple's VideoToolbox framework — hardware H.264/HEVC/ProRes encode and decode on macOS
Documentation
//! Video codecs supported by `VideoToolbox`.

use core::ffi::c_void;
use core::ptr;

use apple_cf::cf::{CFDictionary, CFType};

use crate::error::VTError;
use crate::ffi;

/// Video codec families. Maps to `CMVideoCodecType` four-character codes.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Codec {
    /// H.264 / AVC
    H264,
    /// H.265 / HEVC
    HEVC,
    /// Apple `ProRes` 422 (Standard Definition)
    ProRes422,
    /// Apple `ProRes` 422 HQ
    ProRes422HQ,
    /// Apple `ProRes` 422 LT
    ProRes422LT,
    /// Apple `ProRes` 422 Proxy
    ProRes422Proxy,
    /// Apple `ProRes` 4444 (with alpha)
    ProRes4444,
}

impl Codec {
    /// Lower into a `CMVideoCodecType` for the FFI boundary.
    #[must_use]
    pub const fn as_cm_codec_type(self) -> ffi::CMVideoCodecType {
        match self {
            Self::H264 => ffi::kCMVideoCodecType_H264,
            Self::HEVC => ffi::kCMVideoCodecType_HEVC,
            Self::ProRes422 => ffi::kCMVideoCodecType_AppleProRes422,
            Self::ProRes422HQ => ffi::kCMVideoCodecType_AppleProRes422HQ,
            Self::ProRes422LT => ffi::kCMVideoCodecType_AppleProRes422LT,
            Self::ProRes422Proxy => ffi::kCMVideoCodecType_AppleProRes422Proxy,
            Self::ProRes4444 => ffi::kCMVideoCodecType_AppleProRes4444,
        }
    }
}

pub(crate) unsafe fn copy_property(
    session: *mut c_void,
    key: ffi::CFStringRef,
) -> Result<Option<CFType>, VTError> {
    let mut value: *mut c_void = ptr::null_mut();
    // SAFETY: `VTSessionCopyProperty` is a standard Apple SDK function.
    // The session pointer is validated by the caller. The key is a valid CFStringRef.
    // The value pointer is properly initialized on return.
    let status = unsafe {
        ffi::VTSessionCopyProperty(
            session,
            key,
            ffi::kCFAllocatorDefault,
            (&raw mut value).cast(),
        )
    };
    if status != 0 {
        return Err(VTError::ApiFailed {
            api: "VTSessionCopyProperty",
            status,
        });
    }
    Ok(CFType::from_raw(value))
}

pub(crate) unsafe fn copy_supported_property_dictionary(
    session: *mut c_void,
) -> Result<CFDictionary, VTError> {
    let mut out: ffi::CFDictionaryRef = ptr::null();
    // SAFETY: `VTSessionCopySupportedPropertyDictionary` is a standard Apple SDK function.
    // The session pointer is validated by the caller. The out pointer is properly initialized.
    let status = unsafe { ffi::VTSessionCopySupportedPropertyDictionary(session, &mut out) };
    if status != 0 || out.is_null() {
        return Err(VTError::ApiFailed {
            api: "VTSessionCopySupportedPropertyDictionary",
            status,
        });
    }
    CFDictionary::from_raw(out.cast_mut().cast()).ok_or(VTError::ApiFailed {
        api: "VTSessionCopySupportedPropertyDictionary",
        status,
    })
}

pub(crate) unsafe fn copy_serializable_properties(
    session: *mut c_void,
) -> Result<CFDictionary, VTError> {
    let mut out: ffi::CFDictionaryRef = ptr::null();
    // SAFETY: `VTSessionCopySerializableProperties` is a standard Apple SDK function.
    // The session pointer is validated by the caller. The out pointer is properly initialized.
    let status = unsafe {
        ffi::VTSessionCopySerializableProperties(session, ffi::kCFAllocatorDefault, &mut out)
    };
    if status != 0 || out.is_null() {
        return Err(VTError::ApiFailed {
            api: "VTSessionCopySerializableProperties",
            status,
        });
    }
    CFDictionary::from_raw(out.cast_mut().cast()).ok_or(VTError::ApiFailed {
        api: "VTSessionCopySerializableProperties",
        status,
    })
}

pub(crate) unsafe fn set_properties(
    session: *mut c_void,
    properties: &CFDictionary,
) -> Result<(), VTError> {
    let status = unsafe { ffi::VTSessionSetProperties(session, properties.as_ptr().cast()) };
    if status == 0 {
        Ok(())
    } else {
        Err(VTError::ApiFailed {
            api: "VTSessionSetProperties",
            status,
        })
    }
}