videotoolbox 0.10.0

Safe Rust bindings for Apple's VideoToolbox framework — hardware H.264/HEVC/ProRes encode and decode on macOS
Documentation
# videotoolbox

Safe, zero-runtime-dependency Rust bindings for Apple's [VideoToolbox](https://developer.apple.com/documentation/videotoolbox) framework — hardware-accelerated H.264, HEVC, and ProRes codecs on macOS.

> **Status:** experimental. Encoder is functional; decoder, pixel transfer, and multi-pass support are planned.

## Features

- **Hardware-accelerated encoding** — H.264, HEVC, and `ProRes` 422/4444
- **Direct `IOSurface` input** — encode zero-copy from screencapturekit / camera output via [`apple-cf::iosurface`]https://github.com/doom-fish/apple-cf-rs
- **Builder pattern** — fluent configuration of bitrate, frame rate, keyframe interval, real-time mode
- **Pure C bindings** — no Swift bridge, no `bindgen`, no procedural macros
- **Single dependency** — only [`apple-cf`]https://github.com/doom-fish/apple-cf-rs for shared types

## Why not bindgen?

The full `VideoToolbox` C surface is ~200 symbols, but the useful set for an encoder is closer to 15. Hand-writing those declarations gives us:

- No build-time dependency on `clang`
- Type-safe Rust enums for codec types (instead of raw `u32` four-character codes)
- Builder APIs that map ergonomically to VT's `CFDictionary` property bag

## Requirements

- macOS 13.0+
- Apple Silicon or Intel Mac with hardware video encoder

## Quick start

```rust,no_run
use videotoolbox::prelude::*;
use apple_cf::iosurface::{IOSurface, IOSurfaceLockOptions};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Allocate a 1920×1080 BGRA IOSurface.
    let surface = IOSurface::create(1920, 1080, u32::from_be_bytes(*b"BGRA"), 4)
        .ok_or("failed to allocate")?;

    // Build a real-time H.264 encoder.
    let encoder = CompressionSession::builder(1920, 1080, Codec::H264)
        .with_real_time(true)
        .with_average_bit_rate(8_000_000)
        .with_expected_frame_rate(60.0)
        .with_max_keyframe_interval(120)
        .build()?;

    // Encode one frame and inspect the resulting CMSampleBuffer.
    let encoded = encoder.encode(&surface, (0, 60))?;
    println!("Got {} bytes of H.264", encoded.data.len());

    if let Some(sb) = encoded.cm_sample_buffer() {
        // Hand `sb` straight to avassetwriter::Writer::append_sample for
        // zero-copy muxing — no raw pointer hand-off needed.
        let _ = sb.is_valid();
    }

    Ok(())
}
```

## Composes with the rest of the doom-fish stack

```text
screencapturekit-rs ──► IOSurface ──► videotoolbox-rs ──► H.264 bytes
                                              ↓
                                        avassetwriter-rs (future)
                                              ↓
                                          .mp4 file
```

## Roadmap

- [x] `VTCompressionSession` (encoder)
- [x] `VTDecompressionSession` (decoder)
- [x] `VTPixelTransferSession` (pixel format / colour space conversion)
- [x] `VTPixelRotationSession`
- [x] `VTMultiPassStorage` + `VTFrameSilo` (two-pass encoding)
- [x] `VTHDRPerFrameMetadataGenerationSession` (Dolby Vision metadata)
- [x] `VTFrameProcessor` capability queries (super-resolution / optical flow detection)
- [x] `VTFrameProcessor` pipeline (super-resolution + motion blur + temporal noise + frame-rate conversion + optical flow + 2 low-latency variants)
- [x] `VTMotionEstimationSession`
- [x] `VTRAWProcessingSession` (with parameter introspection)
- [x] `VTProfessionalVideoWorkflow` decoder/encoder registration
- [x] `VTCreateCGImageFromCVPixelBuffer`
- [x] HEVC profile-level helpers
- [ ] Async encode API via `VTCompressionSessionEncodeFrameWithOutputHandler`

## License

Licensed under either of [Apache-2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT) at your option.