ffmpeg-pipeline 0.1.0

Composable FFmpeg decoding, encoding, remuxing, and media I/O for Rust
Documentation
use super::*;
use ffmpeg_next::{codec::context::Context, media::Type as MediaType, Codec, Rational, Stream};

pub(crate) fn decoder_channel_layout(decoder: &ffmpeg_next::decoder::Audio) -> ChannelLayout {
    let layout = decoder.channel_layout();
    if layout.is_empty() {
        ChannelLayout::default(decoder.channels().into())
    } else {
        layout
    }
}

#[derive(Clone)]
pub struct AudioSpec {
    pub(super) sample_rate: u32,
    pub(super) time_base: Rational,
    pub(super) format: Sample,
    pub(super) channel_layout: ChannelLayout,
    pub(super) codec: Option<Codec>,
    pub(super) frame_size: u32,
}

impl AudioSpec {
    pub fn new(channel_layout: ChannelLayout, format: Sample, sample_rate: u32) -> Self {
        Self {
            channel_layout,
            format,
            frame_size: 0,
            sample_rate,
            time_base: (1, sample_rate as i32).into(),
            codec: None,
        }
    }

    pub fn with_codec(mut self, codec: Option<Codec>) -> Self {
        self.codec = codec;
        self
    }

    pub fn with_frame_size(mut self, frame_size: u32) -> Self {
        self.frame_size = frame_size;
        self
    }

    pub fn with_time_base(mut self, time_base: Rational) -> Self {
        self.time_base = time_base;
        self
    }
}

impl TryFrom<&AudioSpec> for AudioSpec {
    type Error = FFmpegError;
    fn try_from(a: &AudioSpec) -> Result<Self, Self::Error> {
        Ok(a.clone())
    }
}

impl TryFrom<&Decoder<'_>> for AudioSpec {
    type Error = FFmpegError;
    fn try_from(decoder: &Decoder<'_>) -> Result<Self, Self::Error> {
        match decoder.get_decoder() {
            StreamDecoder::Audio(decoder) => Ok(Self::new(
                decoder_channel_layout(decoder),
                decoder.format(),
                decoder.rate(),
            )
            .with_codec(decoder.codec())
            .with_frame_size(decoder.frame_size())
            .with_time_base(decoder.time_base())),
            _ => Err(FFmpegError::InvalidStreamType("Video".into())),
        }
    }
}

impl TryFrom<&Encoder<'_>> for AudioSpec {
    type Error = FFmpegError;
    fn try_from(encoder: &Encoder<'_>) -> Result<Self, Self::Error> {
        match encoder.get_encoder() {
            StreamEncoder::Audio(encoder) => {
                Ok(
                    Self::new(encoder.channel_layout(), encoder.format(), encoder.rate())
                        .with_codec(encoder.codec())
                        .with_frame_size(encoder.frame_size())
                        .with_time_base(encoder.time_base()),
                )
            }
            _ => Err(FFmpegError::InvalidStreamType("Video".into())),
        }
    }
}

impl TryFrom<&Stream<'_>> for AudioSpec {
    type Error = FFmpegError;
    fn try_from(stream: &Stream) -> Result<Self, Self::Error> {
        let codec = Context::from_parameters(stream.parameters())?;
        if codec.medium() == MediaType::Audio {
            let decoder = codec.decoder().audio()?;
            Ok(AudioSpec::new(
                decoder_channel_layout(&decoder),
                decoder.format(),
                decoder.rate(),
            )
            .with_codec(decoder.codec())
            .with_frame_size(decoder.frame_size())
            .with_time_base(decoder.time_base()))
        } else {
            Err(FFmpegError::CodecNotFound(stream.parameters().id()))
        }
    }
}