mediaway-device 0.1.1

Device capture: camera/desktop/audio facades + hotplug, with Windows/Linux/Web backends as #[cfg]-gated modules (ADR-0021)
use std::cell::RefCell;
use std::rc::Rc;
use std::time::Duration;

use mediaway_common::{
    Bytes, CodecKind, Rational, StreamInfo, VideoFrame, VideoFrameStorage, VideoGeometry,
};

use super::{
    CameraCapture, CameraCaptureConfig, CaptureError, CaptureOutputPreference, capture_camera_once,
};

struct MockCamera {
    frame: Option<VideoFrame>,
    stream_info: StreamInfo,
    closed: Rc<RefCell<bool>>,
}

impl CameraCapture for MockCamera {
    fn stream_info(&self) -> &StreamInfo {
        &self.stream_info
    }

    fn poll_frame(&mut self) -> Result<Option<VideoFrame>, CaptureError> {
        Ok(self.frame.take())
    }

    fn release_frame(&mut self) -> Result<(), CaptureError> {
        Ok(())
    }

    fn close(&mut self) -> Result<(), CaptureError> {
        *self.closed.borrow_mut() = true;
        Ok(())
    }
}

fn stream_info() -> StreamInfo {
    StreamInfo::Video {
        id: 0,
        codec: CodecKind::RawVideo,
        time_base: Rational::new(1, 30),
        geometry: VideoGeometry {
            width: 1,
            height: 1,
        },
        extra_data: Bytes::new(),
    }
}

fn cpu_frame() -> VideoFrame {
    VideoFrame {
        pts: 0,
        duration: 1,
        width: 1,
        height: 1,
        format: mediaway_common::PixelFormat::Rgba8,
        storage: VideoFrameStorage::Cpu {
            data: Bytes::from_static(&[0u8; 4]),
        },
    }
}

#[test]
fn capture_once_returns_cpu_frame() {
    let closed = Rc::new(RefCell::new(false));
    let result = capture_camera_once(
        || {
            Ok(MockCamera {
                frame: Some(cpu_frame()),
                stream_info: stream_info(),
                closed: Rc::clone(&closed),
            })
        },
        Duration::from_millis(50),
    );
    assert!(result.is_ok());
}

#[test]
fn capture_once_times_out_when_nothing_ready() {
    let closed = Rc::new(RefCell::new(false));
    let result = capture_camera_once(
        || {
            Ok(MockCamera {
                frame: None,
                stream_info: stream_info(),
                closed: Rc::clone(&closed),
            })
        },
        Duration::from_millis(10),
    );
    assert_eq!(result, Err(CaptureError::Timeout));
}

#[test]
fn capture_once_always_closes_the_session() {
    let closed = Rc::new(RefCell::new(false));
    let _ = capture_camera_once(
        || {
            Ok(MockCamera {
                frame: Some(cpu_frame()),
                stream_info: stream_info(),
                closed: Rc::clone(&closed),
            })
        },
        Duration::from_millis(50),
    );
    assert!(*closed.borrow());
}

#[test]
fn default_camera_config_uses_zero_copy_gpu_preference() {
    let config = CameraCaptureConfig::default_camera(Rational::new(1, 30));
    assert_eq!(config.output, CaptureOutputPreference::ZeroCopyGpu);
    assert!(config.gpu_device.is_none());
}