use std::collections::HashMap;
use std::io::{Read, Write};
use rff_core::{
CodecId, ColorRange, Error, MediaType, Packet, PixelFormat, Rational, Result, SampleFormat,
};
#[derive(Debug, Clone)]
pub struct Stream {
pub index: usize,
pub codec_id: CodecId,
pub media_type: MediaType,
pub time_base: Rational,
pub width: u32,
pub height: u32,
pub pixel_format: Option<PixelFormat>,
pub color_range: ColorRange,
pub sample_rate: u32,
pub channels: u16,
pub sample_format: Option<SampleFormat>,
pub extradata: Vec<u8>,
}
impl Stream {
pub fn new(index: usize, codec_id: CodecId) -> Stream {
Stream {
index,
codec_id,
media_type: codec_id.media_type(),
time_base: Rational::new(1, 1000),
width: 0,
height: 0,
pixel_format: None,
color_range: ColorRange::Unspecified,
sample_rate: 0,
channels: 0,
sample_format: None,
extradata: Vec::new(),
}
}
}
pub type Input = Box<dyn Read + Send>;
pub type Output = Box<dyn Write + Send>;
pub trait Demuxer: Send {
fn read_header(&mut self) -> Result<Vec<Stream>>;
fn read_packet(&mut self) -> Result<Packet>;
}
pub trait Muxer: Send {
fn write_header(&mut self, streams: &[Stream]) -> Result<()>;
fn write_packet(&mut self, packet: &Packet) -> Result<()>;
fn write_trailer(&mut self) -> Result<()>;
}
pub type DemuxerFactory = fn(Input) -> Box<dyn Demuxer>;
pub type MuxerFactory = fn(Output) -> Box<dyn Muxer>;
pub type ProbeFn = fn(&[u8]) -> i32;
pub struct Format {
pub name: &'static str,
pub long_name: &'static str,
pub extensions: &'static [&'static str],
pub demuxer: Option<DemuxerFactory>,
pub muxer: Option<MuxerFactory>,
pub probe: Option<ProbeFn>,
}
impl Format {
pub fn can_demux(&self) -> bool {
self.demuxer.is_some()
}
pub fn can_mux(&self) -> bool {
self.muxer.is_some()
}
}
#[derive(Default)]
pub struct FormatRegistry {
by_name: HashMap<&'static str, Format>,
}
impl FormatRegistry {
pub fn new() -> FormatRegistry {
FormatRegistry::default()
}
pub fn register(&mut self, format: Format) {
self.by_name.insert(format.name, format);
}
pub fn by_name(&self, name: &str) -> Option<&Format> {
self.by_name.get(name)
}
pub fn by_extension(&self, ext: &str) -> Option<&Format> {
let ext = ext.trim_start_matches('.').to_ascii_lowercase();
self.by_name
.values()
.find(|f| f.extensions.iter().any(|e| e.eq_ignore_ascii_case(&ext)))
}
pub fn probe(&self, data: &[u8]) -> Option<&Format> {
self.by_name
.values()
.filter_map(|f| f.probe.map(|p| (p(data), f)))
.filter(|(score, _)| *score > 0)
.max_by_key(|(score, _)| *score)
.map(|(_, f)| f)
}
pub fn open_demuxer(&self, name: &str, input: Input) -> Result<Box<dyn Demuxer>> {
self.by_name
.get(name)
.and_then(|f| f.demuxer)
.map(|factory| factory(input))
.ok_or_else(|| Error::DemuxerNotFound(name.to_string()))
}
pub fn open_muxer(&self, name: &str, output: Output) -> Result<Box<dyn Muxer>> {
self.by_name
.get(name)
.and_then(|f| f.muxer)
.map(|factory| factory(output))
.ok_or_else(|| Error::MuxerNotFound(name.to_string()))
}
pub fn iter(&self) -> impl Iterator<Item = &Format> {
self.by_name.values()
}
pub fn len(&self) -> usize {
self.by_name.len()
}
pub fn is_empty(&self) -> bool {
self.by_name.is_empty()
}
}