use super::{codec::OpusDecoderState, error::DcaError, Codec, Container, Input, Metadata, Reader}; use serde::Deserialize; use std::{ffi::OsStr, mem}; #[cfg(not(feature = "tokio-02-marker"))] use tokio::{fs::File as TokioFile, io::AsyncReadExt}; #[cfg(feature = "tokio-02-marker")] use tokio_compat::{fs::File as TokioFile, io::AsyncReadExt}; /// Creates a streamed audio source from a DCA file. /// Currently only accepts the [DCA1 format](https://github.com/bwmarrin/dca). pub async fn dca>(path: P) -> Result { _dca(path.as_ref()).await } async fn _dca(path: &OsStr) -> Result { let mut reader = TokioFile::open(path).await.map_err(DcaError::IoError)?; let mut header = [0u8; 4]; // Read in the magic number to verify it's a DCA file. reader .read_exact(&mut header) .await .map_err(DcaError::IoError)?; if header != b"DCA1"[..] { return Err(DcaError::InvalidHeader); } let size = reader .read_i32_le() .await .map_err(|_| DcaError::InvalidHeader)?; // Sanity check if size < 2 { return Err(DcaError::InvalidSize(size)); } let mut raw_json = Vec::with_capacity(size as usize); let mut json_reader = reader.take(size as u64); json_reader .read_to_end(&mut raw_json) .await .map_err(DcaError::IoError)?; let reader = json_reader.into_inner().into_std().await; let metadata: Metadata = serde_json::from_slice::(raw_json.as_slice()) .map_err(DcaError::InvalidMetadata)? .into(); let stereo = metadata.channels == Some(2); Ok(Input::new( stereo, Reader::from_file(reader), Codec::Opus(OpusDecoderState::new().map_err(DcaError::Opus)?), Container::Dca { first_frame: (size as usize) + mem::size_of::() + header.len(), }, Some(metadata), )) } #[derive(Debug, Deserialize)] pub(crate) struct DcaMetadata { pub(crate) dca: Dca, pub(crate) opus: Opus, pub(crate) info: Option, pub(crate) origin: Option, pub(crate) extra: Option, } #[derive(Debug, Deserialize)] pub(crate) struct Dca { pub(crate) version: u64, pub(crate) tool: Tool, } #[derive(Debug, Deserialize)] pub(crate) struct Tool { pub(crate) name: String, pub(crate) version: String, pub(crate) url: String, pub(crate) author: String, } #[derive(Debug, Deserialize)] pub(crate) struct Opus { pub(crate) mode: String, pub(crate) sample_rate: u32, pub(crate) frame_size: u64, pub(crate) abr: u64, pub(crate) vbr: u64, pub(crate) channels: u8, } #[derive(Debug, Deserialize)] pub(crate) struct Info { pub(crate) title: Option, pub(crate) artist: Option, pub(crate) album: Option, pub(crate) genre: Option, pub(crate) cover: Option, } #[derive(Debug, Deserialize)] pub(crate) struct Origin { pub(crate) source: Option, pub(crate) abr: Option, pub(crate) channels: Option, pub(crate) encoding: Option, pub(crate) url: Option, } impl From for Metadata { fn from(mut d: DcaMetadata) -> Self { let (title, artist) = d .info .take() .map(|mut m| (m.title.take(), m.artist.take())) .unwrap_or_else(|| (None, None)); let channels = Some(d.opus.channels); let sample_rate = Some(d.opus.sample_rate); Self { title, artist, channels, sample_rate, ..Default::default() } } }