moq-ffi 0.2.21

Media over QUIC, UniFFI bindings
Documentation
use std::collections::HashMap;

#[derive(Clone, uniffi::Record)]
pub struct MoqDimensions {
	pub width: u32,
	pub height: u32,
}

#[derive(Clone, uniffi::Enum)]
pub enum Container {
	Legacy,
	Cmaf { init: Vec<u8> },
	Loc,
}

impl From<hang::catalog::Container> for Container {
	fn from(container: hang::catalog::Container) -> Self {
		match container {
			hang::catalog::Container::Legacy => Self::Legacy,
			hang::catalog::Container::Cmaf { init, .. } => Self::Cmaf { init: init.to_vec() },
			hang::catalog::Container::Loc => Self::Loc,
		}
	}
}

impl From<Container> for hang::catalog::Container {
	fn from(container: Container) -> Self {
		match container {
			Container::Legacy => Self::Legacy,
			Container::Cmaf { init } => Self::Cmaf {
				init: init.into(),
				timescale: None,
				track_id: None,
			},
			Container::Loc => Self::Loc,
		}
	}
}

#[derive(uniffi::Record)]
pub struct MoqCatalog {
	pub video: HashMap<String, MoqVideo>,
	pub audio: HashMap<String, MoqAudio>,
	pub display: Option<MoqDimensions>,
	pub rotation: Option<f64>,
	pub flip: Option<bool>,
}

#[derive(Clone, uniffi::Record)]
pub struct MoqVideo {
	pub codec: String,
	pub description: Option<Vec<u8>>,
	pub coded: Option<MoqDimensions>,
	pub display_ratio: Option<MoqDimensions>,
	pub bitrate: Option<u64>,
	pub framerate: Option<f64>,
	pub container: Container,
}

#[derive(Clone, uniffi::Record)]
pub struct MoqAudio {
	pub codec: String,
	pub description: Option<Vec<u8>>,
	pub sample_rate: u32,
	pub channel_count: u32,
	pub bitrate: Option<u64>,
	pub container: Container,
}

/// A media frame.
#[derive(uniffi::Record)]
pub struct MoqFrame {
	pub payload: Vec<u8>,
	pub timestamp_us: u64,
	pub keyframe: bool,
}

pub(crate) fn convert_catalog(catalog: &moq_mux::catalog::hang::Catalog) -> MoqCatalog {
	let video = catalog
		.video
		.renditions
		.iter()
		.map(|(name, config)| {
			(
				name.clone(),
				MoqVideo {
					codec: config.codec.to_string(),
					description: config.description.as_ref().map(|d| d.to_vec()),
					coded: match (config.coded_width, config.coded_height) {
						(Some(w), Some(h)) => Some(MoqDimensions { width: w, height: h }),
						_ => None,
					},
					display_ratio: match (config.display_ratio_width, config.display_ratio_height) {
						(Some(w), Some(h)) => Some(MoqDimensions { width: w, height: h }),
						_ => None,
					},
					bitrate: config.bitrate,
					framerate: config.framerate,
					container: config.container.clone().into(),
				},
			)
		})
		.collect();

	let audio = catalog
		.audio
		.renditions
		.iter()
		.map(|(name, config)| {
			(
				name.clone(),
				MoqAudio {
					codec: config.codec.to_string(),
					description: config.description.as_ref().map(|d| d.to_vec()),
					sample_rate: config.sample_rate,
					channel_count: config.channel_count,
					bitrate: config.bitrate,
					container: config.container.clone().into(),
				},
			)
		})
		.collect();

	let display = catalog.video.display.as_ref().map(|d| MoqDimensions {
		width: d.width,
		height: d.height,
	});

	MoqCatalog {
		video,
		audio,
		display,
		rotation: catalog.video.rotation,
		flip: catalog.video.flip,
	}
}