use std::collections::HashMap;
use std::io::{Read, Seek, SeekFrom, Write};
use crate::{CodecResolver, Error, Packet, Result, StreamInfo};
pub trait Demuxer: Send {
fn format_name(&self) -> &str;
fn streams(&self) -> &[StreamInfo];
fn next_packet(&mut self) -> Result<Packet>;
fn set_active_streams(&mut self, _indices: &[u32]) {}
fn seek_to(&mut self, _stream_index: u32, _pts: i64) -> Result<i64> {
Err(Error::unsupported("this demuxer does not support seeking"))
}
fn metadata(&self) -> &[(String, String)] {
&[]
}
fn duration_micros(&self) -> Option<i64> {
None
}
fn attached_pictures(&self) -> &[crate::AttachedPicture] {
&[]
}
}
pub trait Muxer: Send {
fn format_name(&self) -> &str;
fn write_header(&mut self) -> Result<()>;
fn write_packet(&mut self, packet: &Packet) -> Result<()>;
fn write_trailer(&mut self) -> Result<()>;
}
pub type OpenDemuxerFn =
fn(input: Box<dyn ReadSeek>, codecs: &dyn CodecResolver) -> Result<Box<dyn Demuxer>>;
pub type OpenMuxerFn =
fn(output: Box<dyn WriteSeek>, streams: &[StreamInfo]) -> Result<Box<dyn Muxer>>;
pub struct ProbeData<'a> {
pub buf: &'a [u8],
pub ext: Option<&'a str>,
}
pub type ProbeScore = u8;
pub const MAX_PROBE_SCORE: ProbeScore = 100;
pub const PROBE_SCORE_EXTENSION: ProbeScore = 25;
pub type ContainerProbeFn = fn(probe: &ProbeData) -> ProbeScore;
pub trait ReadSeek: Read + Seek + Send {}
impl<T: Read + Seek + Send> ReadSeek for T {}
pub trait WriteSeek: Write + Seek + Send {}
impl<T: Write + Seek + Send> WriteSeek for T {}
#[derive(Default)]
pub struct ContainerRegistry {
demuxers: HashMap<String, OpenDemuxerFn>,
muxers: HashMap<String, OpenMuxerFn>,
extensions: HashMap<String, String>,
probes: HashMap<String, ContainerProbeFn>,
}
impl ContainerRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register_demuxer(&mut self, name: &str, open: OpenDemuxerFn) {
self.demuxers.insert(name.to_owned(), open);
}
pub fn register_muxer(&mut self, name: &str, open: OpenMuxerFn) {
self.muxers.insert(name.to_owned(), open);
}
pub fn register_extension(&mut self, ext: &str, container_name: &str) {
self.extensions
.insert(ext.to_lowercase(), container_name.to_owned());
}
pub fn register_probe(&mut self, container_name: &str, probe: ContainerProbeFn) {
self.probes.insert(container_name.to_owned(), probe);
}
pub fn demuxer_names(&self) -> impl Iterator<Item = &str> {
self.demuxers.keys().map(|s| s.as_str())
}
pub fn muxer_names(&self) -> impl Iterator<Item = &str> {
self.muxers.keys().map(|s| s.as_str())
}
pub fn open_demuxer(
&self,
name: &str,
input: Box<dyn ReadSeek>,
codecs: &dyn CodecResolver,
) -> Result<Box<dyn Demuxer>> {
let open = self
.demuxers
.get(name)
.ok_or_else(|| Error::FormatNotFound(name.to_owned()))?;
open(input, codecs)
}
pub fn open_muxer(
&self,
name: &str,
output: Box<dyn WriteSeek>,
streams: &[StreamInfo],
) -> Result<Box<dyn Muxer>> {
let open = self
.muxers
.get(name)
.ok_or_else(|| Error::FormatNotFound(name.to_owned()))?;
open(output, streams)
}
pub fn container_for_extension(&self, ext: &str) -> Option<&str> {
self.extensions.get(&ext.to_lowercase()).map(|s| s.as_str())
}
pub fn probe_input(&self, input: &mut dyn ReadSeek, ext_hint: Option<&str>) -> Result<String> {
const PROBE_BUF_SIZE: usize = 256 * 1024;
let saved_pos = input.stream_position()?;
input.seek(SeekFrom::Start(0))?;
let mut buf = vec![0u8; PROBE_BUF_SIZE];
let mut got = 0;
while got < buf.len() {
match input.read(&mut buf[got..]) {
Ok(0) => break,
Ok(n) => got += n,
Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
Err(e) => {
let _ = input.seek(SeekFrom::Start(saved_pos));
return Err(e.into());
}
}
}
buf.truncate(got);
input.seek(SeekFrom::Start(saved_pos))?;
let ext_lower = ext_hint.map(|s| s.to_ascii_lowercase());
let probe_data = ProbeData {
buf: &buf,
ext: ext_lower.as_deref(),
};
let mut best: Option<(&str, ProbeScore)> = None;
for (name, probe) in &self.probes {
let score = probe(&probe_data);
if score == 0 {
continue;
}
match best {
Some((_, prev)) if score <= prev => {}
_ => best = Some((name.as_str(), score)),
}
}
if let Some((name, _)) = best {
return Ok(name.to_owned());
}
if let Some(ext) = ext_hint {
if let Some(name) = self.container_for_extension(ext) {
let _ = PROBE_SCORE_EXTENSION; return Ok(name.to_owned());
}
}
Err(Error::FormatNotFound(
"no registered demuxer recognises this input".into(),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
struct DummyDemuxer;
impl Demuxer for DummyDemuxer {
fn format_name(&self) -> &str {
"dummy"
}
fn streams(&self) -> &[StreamInfo] {
&[]
}
fn next_packet(&mut self) -> Result<Packet> {
Err(Error::Eof)
}
}
#[test]
fn default_seek_to_is_unsupported() {
let mut d = DummyDemuxer;
match d.seek_to(0, 0) {
Err(Error::Unsupported(_)) => {}
other => panic!(
"expected default seek_to to return Unsupported, got {:?}",
other
),
}
}
}