#![forbid(unsafe_code)]
use async_trait::async_trait;
use oximedia_core::OxiResult;
use crate::{ContainerFormat, Packet, StreamInfo};
#[derive(Clone, Debug, Default)]
pub struct OutputFormat {
pub format: Option<ContainerFormat>,
pub seekable: bool,
pub max_cluster_duration_ms: Option<u32>,
pub max_cluster_size: Option<u32>,
}
impl OutputFormat {
#[must_use]
pub const fn new() -> Self {
Self {
format: None,
seekable: true,
max_cluster_duration_ms: None,
max_cluster_size: None,
}
}
#[must_use]
pub const fn with_format(mut self, format: ContainerFormat) -> Self {
self.format = Some(format);
self
}
#[must_use]
pub const fn with_seekable(mut self, seekable: bool) -> Self {
self.seekable = seekable;
self
}
#[must_use]
pub const fn with_max_cluster_duration_ms(mut self, duration_ms: u32) -> Self {
self.max_cluster_duration_ms = Some(duration_ms);
self
}
#[must_use]
pub const fn with_max_cluster_size(mut self, size: u32) -> Self {
self.max_cluster_size = Some(size);
self
}
}
#[derive(Clone, Debug, Default)]
pub struct MuxerConfig {
pub title: Option<String>,
pub muxing_app: Option<String>,
pub writing_app: Option<String>,
pub output_format: OutputFormat,
pub write_duration: bool,
pub write_cues: bool,
}
impl MuxerConfig {
#[must_use]
pub fn new() -> Self {
Self {
title: None,
muxing_app: Some("OxiMedia".into()),
writing_app: Some("OxiMedia".into()),
output_format: OutputFormat::new(),
write_duration: true,
write_cues: true,
}
}
#[must_use]
pub fn with_title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into());
self
}
#[must_use]
pub fn with_muxing_app(mut self, app: impl Into<String>) -> Self {
self.muxing_app = Some(app.into());
self
}
#[must_use]
pub fn with_writing_app(mut self, app: impl Into<String>) -> Self {
self.writing_app = Some(app.into());
self
}
#[must_use]
pub fn with_output_format(mut self, format: OutputFormat) -> Self {
self.output_format = format;
self
}
#[must_use]
pub const fn with_write_duration(mut self, write: bool) -> Self {
self.write_duration = write;
self
}
#[must_use]
pub const fn with_write_cues(mut self, write: bool) -> Self {
self.write_cues = write;
self
}
}
#[async_trait]
pub trait Muxer: Send {
fn add_stream(&mut self, info: StreamInfo) -> OxiResult<usize>;
async fn write_header(&mut self) -> OxiResult<()>;
async fn write_packet(&mut self, packet: &Packet) -> OxiResult<()>;
async fn write_trailer(&mut self) -> OxiResult<()>;
fn streams(&self) -> &[StreamInfo];
fn config(&self) -> &MuxerConfig;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_output_format_default() {
let format = OutputFormat::new();
assert!(format.format.is_none());
assert!(format.seekable);
}
#[test]
fn test_output_format_builder() {
let format = OutputFormat::new()
.with_format(ContainerFormat::Matroska)
.with_seekable(false)
.with_max_cluster_duration_ms(5000)
.with_max_cluster_size(1024 * 1024);
assert_eq!(format.format, Some(ContainerFormat::Matroska));
assert!(!format.seekable);
assert_eq!(format.max_cluster_duration_ms, Some(5000));
assert_eq!(format.max_cluster_size, Some(1024 * 1024));
}
#[test]
fn test_muxer_config_default() {
let config = MuxerConfig::new();
assert!(config.title.is_none());
assert!(config.muxing_app.is_some());
assert!(config.writing_app.is_some());
assert!(config.write_duration);
assert!(config.write_cues);
}
#[test]
fn test_muxer_config_builder() {
let config = MuxerConfig::new()
.with_title("Test Video")
.with_muxing_app("TestApp")
.with_writing_app("TestWriter")
.with_write_duration(false)
.with_write_cues(false);
assert_eq!(config.title, Some("Test Video".into()));
assert_eq!(config.muxing_app, Some("TestApp".into()));
assert_eq!(config.writing_app, Some("TestWriter".into()));
assert!(!config.write_duration);
assert!(!config.write_cues);
}
}