selene-core 0.9.0-rc.1

Backend for selene-server
Documentation
use std::num::NonZero;

use serde::{Deserialize, Serialize};
use symphonia::core::{codecs::audio::AudioCodecParameters, units::Duration};

use crate::media::{CodecError, ContainerError, codec::Codec, sample_format::SampleFormat};

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Copy)]
pub struct Stream {
    pub id: u32,
    pub codec_params: CodecParameters,
    pub time_base: Option<TimeBase>,
    pub num_frames: Option<u64>,
    pub duration: u64,
    pub start_ts: i64,
    pub delay: Option<u32>,
    pub padding: Option<u32>,
}

impl Stream {
    #[must_use]
    pub fn duration(&self) -> f64 {
        if let Some(frames) = self.num_frames {
            return frames as f64 / f64::from(self.codec_params.sample_rate);
        }

        if let Some(TimeBase { numer, denom }) = self.time_base
            && denom.get() > 0
        {
            return self.duration as f64 * (f64::from(numer.get()) / f64::from(denom.get()));
        }

        0.0
    }
}

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Copy)]
pub struct CodecParameters {
    pub codec: Codec,
    pub sample_rate: u32,
    pub sample_format: Option<SampleFormat>,
    pub bits_per_sample: Option<u32>,
    pub bits_per_coded_sample: Option<u32>,
    pub channels: usize,
    pub max_frames_per_packet: Option<u64>,
    pub frames_per_block: Option<u64>,
}

impl TryFrom<&AudioCodecParameters> for CodecParameters {
    type Error = ContainerError;

    fn try_from(value: &AudioCodecParameters) -> Result<Self, Self::Error> {
        Ok(Self {
            codec: Codec::try_from(value.codec)?,
            sample_rate: value.sample_rate.ok_or(ContainerError::NoSampleRate)?,
            sample_format: value.sample_format.map(SampleFormat::from),
            bits_per_sample: value.bits_per_sample,
            bits_per_coded_sample: value.bits_per_coded_sample,
            channels: value
                .channels
                .as_ref()
                .ok_or(ContainerError::NoChannelCount)?
                .count(),
            max_frames_per_packet: value.max_frames_per_packet,
            frames_per_block: value.frames_per_block,
        })
    }
}

impl TryFrom<&symphonia::core::formats::Track> for Stream {
    type Error = ContainerError;

    fn try_from(value: &symphonia::core::formats::Track) -> Result<Self, Self::Error> {
        let duration = value.duration.map_or(0, Duration::get);

        let codec_params =
            value
                .codec_params
                .as_ref()
                .ok_or(ContainerError::Codec(CodecError::Unknown(
                    "Codec parameters could not be determined".to_owned(),
                )))?;

        Ok(Self {
            id: value.id,
            codec_params: CodecParameters::try_from(
                codec_params
                    .audio()
                    .expect("Only audio tracks are supported"),
            )?,
            time_base: value.time_base.map(Into::into),
            num_frames: value.num_frames,
            duration,
            start_ts: value.start_ts.get(),
            delay: value.delay,
            padding: value.padding,
        })
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct TimeBase {
    pub numer: NonZero<u32>,
    pub denom: NonZero<u32>,
}

impl From<symphonia::core::units::TimeBase> for TimeBase {
    fn from(value: symphonia::core::units::TimeBase) -> Self {
        Self {
            numer: value.numer,
            denom: value.denom,
        }
    }
}

impl From<TimeBase> for symphonia::core::units::TimeBase {
    fn from(value: TimeBase) -> Self {
        Self {
            numer: value.numer,
            denom: value.denom,
        }
    }
}