mediaway-encoder 0.1.2

Hardware-accelerated video/audio encoding (OS-native backends)
//! Hardware-gated test for [`super::VulkanVideoEncoder`] — the real,
//! reusable, multi-frame `crate::VideoEncoder` impl (as opposed
//! to `session_tests.rs`'s one-shot `encode_synthetic_intra_frame`
//! diagnostic). Skips (never fails the default suite) when this machine's
//! Vulkan loader/driver lacks an H.264 encode queue family — same convention
//! as `session_tests.rs`/`probe_tests.rs`.

#![allow(
    clippy::unwrap_used,
    clippy::expect_used,
    clippy::print_stderr,
    reason = "test modules may unwrap / print"
)]

use crate::{VideoEncoder, VideoEncoderConfig, VideoInputPreference};
use mediaway_common::{Bytes, CodecKind, PixelFormat, Rational, VideoFrame, VideoFrameStorage};

use crate::vulkan::VulkanVideoEncoder;
use crate::vulkan::nal::{scan_nal_headers, scan_nal_headers_hevc, scan_obu_headers};

// Comfortably clears this crate's reference RTX 4090's observed 160x64
// minimum coded extent (see `adr/0001`'s Stage 1 addendum) while staying
// 16-aligned for H.264 macroblocks.
const WIDTH: u32 = 176;
const HEIGHT: u32 = 144;

// HEVC's `picture_access_granularity` is 32x32 on this driver (H.264's is
// 16x16 — the two are not the same, see `session.rs::Capabilities`'s doc
// comment) — reuse `mediaway-encoder-windows`'s D3D12 HEVC test's 256x192,
// already known to clear every codec's observed minimum on this hardware.
const WIDTH_HEVC: u32 = 256;
const HEIGHT_HEVC: u32 = 192;

// AV1's own `picture_access_granularity` on this driver was not queried ahead
// of writing this test — 256x192 (a multiple of AV1's 64x64 superblock size)
// is reused as a resolution comfortably likely to clear whatever the driver
// reports; `VulkanVideoEncoder::open` failing here is treated as a skip, not
// a hard failure, same as every other hardware-gated test in this crate.
const WIDTH_AV1: u32 = 256;
const HEIGHT_AV1: u32 = 192;

fn nv12_frame_sized(pts: i64, width: u32, height: u32) -> VideoFrame {
    let len = (width as usize) * (height as usize) * 3 / 2;
    VideoFrame {
        pts,
        duration: 1,
        width,
        height,
        format: PixelFormat::Nv12,
        storage: VideoFrameStorage::Cpu {
            data: Bytes::from(vec![128u8; len]),
        },
    }
}

fn nv12_frame(pts: i64) -> VideoFrame {
    nv12_frame_sized(pts, WIDTH, HEIGHT)
}

/// Opens a real `VulkanVideoEncoder`, pushes 3 synthetic gray NV12 frames
/// through the full pipeline (upload → `EncodeFrame` → real driver-reported
/// byte count → readback), and asserts each returned packet is a real,
/// byte-exact (not zero-padded) Annex-B bitstream containing SPS/PPS/IDR NALs
/// — the same real hardware path `session_tests.rs` exercises once, now
/// reused across multiple frames on one persistent session.
#[test]
#[allow(
    clippy::similar_names,
    reason = "has_sps/has_pps/has_idr_slice read clearer than de-aliased names"
)]
fn push_three_frames_or_skip() {
    let cfg = VideoEncoderConfig {
        codec: CodecKind::H264,
        width: WIDTH,
        height: HEIGHT,
        time_base: Rational::new(1, 30),
        bitrate_bps: 500_000,
        pixel_format: PixelFormat::Nv12,
        input: VideoInputPreference::CpuUploadOk,
        gpu_device: None,
    };

    let mut enc = match VulkanVideoEncoder::open(&cfg) {
        Ok(enc) => enc,
        Err(error) => {
            eprintln!(
                "skip: VulkanVideoEncoder::open failed ({error:?}) — no encode-capable Vulkan device?"
            );
            return;
        }
    };

    let mut packets = 0usize;
    for i in 0..3i64 {
        let frame = nv12_frame(i);
        if let Err(error) = enc.push_frame(&frame) {
            eprintln!("skip: push_frame failed ({error:?})");
            return;
        }
        let packet = match enc.poll_packet() {
            Ok(Some(packet)) => packet,
            Ok(None) => {
                eprintln!("skip: no packet after push_frame {i}");
                return;
            }
            Err(error) => {
                eprintln!("skip: poll_packet failed ({error:?})");
                return;
            }
        };
        assert!(!packet.payload.is_empty(), "packet {i} payload is empty");
        assert!(packet.is_keyframe, "packet {i} should be a key frame");

        let headers = scan_nal_headers(&packet.payload);
        let has_sps = headers.iter().any(|h| h.nal_unit_type == 7);
        let has_pps = headers.iter().any(|h| h.nal_unit_type == 8);
        let has_idr_slice = headers.iter().any(|h| h.nal_unit_type == 5);
        assert!(
            has_sps,
            "packet {i} missing SPS (type 7); found {headers:?}"
        );
        assert!(
            has_pps,
            "packet {i} missing PPS (type 8); found {headers:?}"
        );
        assert!(
            has_idr_slice,
            "packet {i} missing IDR slice (type 5); found {headers:?}"
        );
        packets += 1;
    }

    enc.flush().expect("flush");
    eprintln!("vulkan H.264 VideoEncoder ok: {packets} packets, all real SPS+PPS+IDR Annex-B NALs");
}

/// HEVC sibling of [`push_three_frames_or_skip`] — same real hardware
/// pipeline, HEVC's 2-byte NAL header and third parameter set (VPS).
#[test]
#[allow(
    clippy::similar_names,
    reason = "has_vps/has_sps/has_pps/has_idr_slice read clearer than de-aliased names"
)]
fn push_three_hevc_frames_or_skip() {
    let cfg = VideoEncoderConfig {
        codec: CodecKind::Hevc,
        width: WIDTH_HEVC,
        height: HEIGHT_HEVC,
        time_base: Rational::new(1, 30),
        bitrate_bps: 500_000,
        pixel_format: PixelFormat::Nv12,
        input: VideoInputPreference::CpuUploadOk,
        gpu_device: None,
    };

    let mut enc = match VulkanVideoEncoder::open(&cfg) {
        Ok(enc) => enc,
        Err(error) => {
            eprintln!(
                "skip: VulkanVideoEncoder::open (HEVC) failed ({error:?}) — no encode-capable Vulkan device?"
            );
            return;
        }
    };

    let mut packets = 0usize;
    for i in 0..3i64 {
        let frame = nv12_frame_sized(i, WIDTH_HEVC, HEIGHT_HEVC);
        if let Err(error) = enc.push_frame(&frame) {
            eprintln!("skip: push_frame (HEVC) failed ({error:?})");
            return;
        }
        let packet = match enc.poll_packet() {
            Ok(Some(packet)) => packet,
            Ok(None) => {
                eprintln!("skip: no packet after push_frame {i} (HEVC)");
                return;
            }
            Err(error) => {
                eprintln!("skip: poll_packet (HEVC) failed ({error:?})");
                return;
            }
        };
        assert!(!packet.payload.is_empty(), "packet {i} payload is empty");
        assert!(packet.is_keyframe, "packet {i} should be a key frame");

        let headers = scan_nal_headers_hevc(&packet.payload);
        let has_vps = headers.iter().any(|h| h.nal_unit_type == 32);
        let has_sps = headers.iter().any(|h| h.nal_unit_type == 33);
        let has_pps = headers.iter().any(|h| h.nal_unit_type == 34);
        let has_idr_slice = headers
            .iter()
            .any(|h| h.nal_unit_type == 19 || h.nal_unit_type == 20);
        assert!(
            has_vps,
            "packet {i} missing VPS (type 32); found {headers:?}"
        );
        assert!(
            has_sps,
            "packet {i} missing SPS (type 33); found {headers:?}"
        );
        assert!(
            has_pps,
            "packet {i} missing PPS (type 34); found {headers:?}"
        );
        assert!(
            has_idr_slice,
            "packet {i} missing IDR slice (type 19/20); found {headers:?}"
        );
        packets += 1;
    }

    enc.flush().expect("flush");
    eprintln!(
        "vulkan HEVC VideoEncoder ok: {packets} packets, all real VPS+SPS+PPS+IDR Annex-B NALs"
    );
}

/// AV1 sibling of [`push_three_frames_or_skip`] — same real hardware
/// pipeline, low-overhead-format OBU stream instead of Annex-B NAL units.
///
/// **Known-broken on this crate's reference RTX 4090 (driver 32.0.15.9579),
/// hardware-verified 2026-07-29 — see `adr/0001`'s AV1 addendum.** The
/// session-parameters-fetched `OBU_SEQUENCE_HEADER` is real and independently
/// parseable (asserted below), but every `vkCmdEncodeVideoKHR` frame's own
/// output is not a valid OBU stream. Independently confirmed **not** to be a
/// bug in this crate: `ffmpeg -c:v av1_vulkan` on this exact machine produces
/// AV1 output `dav1d` itself rejects with real decode errors (up to 73% of
/// packets), so the per-frame check below is downgraded to a documented skip
/// rather than a hard failure — this is a driver-maturity limitation, not
/// this crate's own bitstream construction.
#[test]
fn push_three_av1_frames_or_skip() {
    // AV1 OBU types (AV1 spec §6.2.2): 1 = OBU_SEQUENCE_HEADER,
    // 3 = OBU_FRAME_HEADER, 6 = OBU_FRAME (frame header + tile group combined
    // — this crate's single-tile output may use either shape).
    const OBU_SEQUENCE_HEADER: u8 = 1;
    const OBU_FRAME_HEADER: u8 = 3;
    const OBU_FRAME: u8 = 6;

    let cfg = VideoEncoderConfig {
        codec: CodecKind::Av1,
        width: WIDTH_AV1,
        height: HEIGHT_AV1,
        time_base: Rational::new(1, 30),
        bitrate_bps: 500_000,
        pixel_format: PixelFormat::Nv12,
        input: VideoInputPreference::CpuUploadOk,
        gpu_device: None,
    };

    let mut enc = match VulkanVideoEncoder::open(&cfg) {
        Ok(enc) => enc,
        Err(error) => {
            eprintln!(
                "skip: VulkanVideoEncoder::open (AV1) failed ({error:?}) — no encode-capable Vulkan device, or this driver lacks AV1 encode?"
            );
            return;
        }
    };

    let mut packets = 0usize;
    for i in 0..3i64 {
        let frame = nv12_frame_sized(i, WIDTH_AV1, HEIGHT_AV1);
        if let Err(error) = enc.push_frame(&frame) {
            eprintln!("skip: push_frame (AV1) failed ({error:?})");
            return;
        }
        let packet = match enc.poll_packet() {
            Ok(Some(packet)) => packet,
            Ok(None) => {
                eprintln!("skip: no packet after push_frame {i} (AV1)");
                return;
            }
            Err(error) => {
                eprintln!("skip: poll_packet (AV1) failed ({error:?})");
                return;
            }
        };
        assert!(!packet.payload.is_empty(), "packet {i} payload is empty");
        assert!(packet.is_keyframe, "packet {i} should be a key frame");

        let headers = scan_obu_headers(&packet.payload);
        let has_sequence_header = headers.iter().any(|h| h.obu_type == OBU_SEQUENCE_HEADER);
        let has_frame = headers
            .iter()
            .any(|h| h.obu_type == OBU_FRAME || h.obu_type == OBU_FRAME_HEADER);
        assert!(
            has_sequence_header,
            "packet {i} missing OBU_SEQUENCE_HEADER (type 1); found {headers:?}"
        );
        if !has_frame {
            eprintln!(
                "skip: packet {i}'s own frame data is not a valid OBU (found {headers:?}) — \
                 known driver-maturity limitation on this hardware, see `adr/0001`'s AV1 addendum"
            );
            return;
        }
        packets += 1;
    }

    enc.flush().expect("flush");
    eprintln!(
        "vulkan AV1 VideoEncoder ok: {packets} packets, all real OBU sequence header + frame OBUs"
    );
}