Files
pmomusic/pmoflac/src/autodetect.rs

293 lines
9.8 KiB
Rust
Executable File

use std::{
io,
pin::Pin,
task::{Context, Poll},
};
use tokio::io::{AsyncRead, AsyncReadExt, ReadBuf};
use crate::{
decode_aiff_stream, decode_flac_stream, decode_mp3_stream, decode_ogg_opus_stream,
decode_ogg_vorbis_stream, decode_wav_stream, pcm::StreamInfo, prefixed_reader::PrefixedReader,
AiffDecodedStream, AiffError, FlacDecodedStream, FlacError, Mp3DecodedStream, Mp3Error,
OggDecodedStream, OggError, OggOpusDecodedStream, OggOpusError, WavDecodedStream, WavError,
};
const MAX_SNIFF_BYTES: usize = 64 * 1024;
const READ_CHUNK: usize = 4096;
#[derive(thiserror::Error, Debug)]
pub enum DecodeAudioError {
#[error("I/O error: {0}")]
Io(#[from] io::Error),
#[error("unknown or unsupported audio format")]
UnknownFormat,
#[error("FLAC decode error: {0}")]
Flac(FlacError),
#[error("MP3 decode error: {0}")]
Mp3(Mp3Error),
#[error("Ogg/Vorbis decode error: {0}")]
Vorbis(OggError),
#[error("Ogg/Opus decode error: {0}")]
Opus(OggOpusError),
#[error("WAV decode error: {0}")]
Wav(WavError),
#[error("AIFF decode error: {0}")]
Aiff(AiffError),
}
pub async fn decode_audio_stream<R>(reader: R) -> Result<DecodedAudioStream, DecodeAudioError>
where
R: AsyncRead + Unpin + Send + 'static,
{
let mut reader = reader;
let mut initial = Vec::new();
let mut tmp = vec![0u8; READ_CHUNK];
let mut detected = detect_format(&initial);
while detected.is_none() && initial.len() < MAX_SNIFF_BYTES {
let read = reader.read(&mut tmp).await?;
if read == 0 {
break;
}
initial.extend_from_slice(&tmp[..read]);
detected = detect_format(&initial);
}
let format = detected.ok_or(DecodeAudioError::UnknownFormat)?;
let prefixed = PrefixedReader::new(initial, reader);
let stream = match format {
DetectedFormat::Flac => {
let stream = decode_flac_stream(prefixed)
.await
.map_err(DecodeAudioError::Flac)?;
DecodedAudioStream::Flac(stream)
}
DetectedFormat::Mp3 => {
let stream = decode_mp3_stream(prefixed)
.await
.map_err(DecodeAudioError::Mp3)?;
DecodedAudioStream::Mp3(stream)
}
DetectedFormat::OggVorbis => {
let stream = decode_ogg_vorbis_stream(prefixed)
.await
.map_err(DecodeAudioError::Vorbis)?;
DecodedAudioStream::OggVorbis(stream)
}
DetectedFormat::OggOpus => {
let stream = decode_ogg_opus_stream(prefixed)
.await
.map_err(DecodeAudioError::Opus)?;
DecodedAudioStream::OggOpus(stream)
}
DetectedFormat::Wav => {
let stream = decode_wav_stream(prefixed)
.await
.map_err(DecodeAudioError::Wav)?;
DecodedAudioStream::Wav(stream)
}
DetectedFormat::Aiff => {
let stream = decode_aiff_stream(prefixed)
.await
.map_err(DecodeAudioError::Aiff)?;
DecodedAudioStream::Aiff(stream)
}
};
Ok(stream)
}
pub enum DecodedAudioStream {
Flac(FlacDecodedStream),
Mp3(Mp3DecodedStream),
OggVorbis(OggDecodedStream),
OggOpus(OggOpusDecodedStream),
Wav(WavDecodedStream),
Aiff(AiffDecodedStream),
}
impl DecodedAudioStream {
pub fn info(&self) -> &StreamInfo {
match self {
DecodedAudioStream::Flac(inner) => inner.info(),
DecodedAudioStream::Mp3(inner) => inner.info(),
DecodedAudioStream::OggVorbis(inner) => inner.info(),
DecodedAudioStream::OggOpus(inner) => inner.info(),
DecodedAudioStream::Wav(inner) => inner.info(),
DecodedAudioStream::Aiff(inner) => inner.info(),
}
}
pub async fn wait(self) -> Result<(), DecodeAudioError> {
match self {
DecodedAudioStream::Flac(inner) => inner.wait().await.map_err(DecodeAudioError::Flac),
DecodedAudioStream::Mp3(inner) => inner.wait().await.map_err(DecodeAudioError::Mp3),
DecodedAudioStream::OggVorbis(inner) => {
inner.wait().await.map_err(DecodeAudioError::Vorbis)
}
DecodedAudioStream::OggOpus(inner) => {
inner.wait().await.map_err(DecodeAudioError::Opus)
}
DecodedAudioStream::Wav(inner) => inner.wait().await.map_err(DecodeAudioError::Wav),
DecodedAudioStream::Aiff(inner) => inner.wait().await.map_err(DecodeAudioError::Aiff),
}
}
pub fn into_reader(self) -> (StreamInfo, DecodedReader) {
match self {
DecodedAudioStream::Flac(inner) => {
let (info, reader) = inner.into_parts();
(info, DecodedReader::Flac(reader))
}
DecodedAudioStream::Mp3(inner) => {
let (info, reader) = inner.into_parts();
(info, DecodedReader::Mp3(reader))
}
DecodedAudioStream::OggVorbis(inner) => {
let (info, reader) = inner.into_parts();
(info, DecodedReader::OggVorbis(reader))
}
DecodedAudioStream::OggOpus(inner) => {
let (info, reader) = inner.into_parts();
(info, DecodedReader::OggOpus(reader))
}
DecodedAudioStream::Wav(inner) => {
let (info, reader) = inner.into_parts();
(info, DecodedReader::Wav(reader))
}
DecodedAudioStream::Aiff(inner) => {
let (info, reader) = inner.into_parts();
(info, DecodedReader::Aiff(reader))
}
}
}
}
impl AsyncRead for DecodedAudioStream {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
match self.get_mut() {
DecodedAudioStream::Flac(inner) => Pin::new(inner).poll_read(cx, buf),
DecodedAudioStream::Mp3(inner) => Pin::new(inner).poll_read(cx, buf),
DecodedAudioStream::OggVorbis(inner) => Pin::new(inner).poll_read(cx, buf),
DecodedAudioStream::OggOpus(inner) => Pin::new(inner).poll_read(cx, buf),
DecodedAudioStream::Wav(inner) => Pin::new(inner).poll_read(cx, buf),
DecodedAudioStream::Aiff(inner) => Pin::new(inner).poll_read(cx, buf),
}
}
}
pub enum DecodedReader {
Flac(crate::stream::ManagedAsyncReader<FlacError>),
Mp3(crate::stream::ManagedAsyncReader<Mp3Error>),
OggVorbis(crate::stream::ManagedAsyncReader<OggError>),
OggOpus(crate::stream::ManagedAsyncReader<OggOpusError>),
Wav(crate::stream::ManagedAsyncReader<WavError>),
Aiff(crate::stream::ManagedAsyncReader<AiffError>),
}
impl DecodedReader {
pub async fn wait(self) -> Result<(), DecodeAudioError> {
match self {
DecodedReader::Flac(inner) => inner.wait().await.map_err(DecodeAudioError::Flac),
DecodedReader::Mp3(inner) => inner.wait().await.map_err(DecodeAudioError::Mp3),
DecodedReader::OggVorbis(inner) => inner.wait().await.map_err(DecodeAudioError::Vorbis),
DecodedReader::OggOpus(inner) => inner.wait().await.map_err(DecodeAudioError::Opus),
DecodedReader::Wav(inner) => inner.wait().await.map_err(DecodeAudioError::Wav),
DecodedReader::Aiff(inner) => inner.wait().await.map_err(DecodeAudioError::Aiff),
}
}
}
impl AsyncRead for DecodedReader {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
match self.get_mut() {
DecodedReader::Flac(inner) => Pin::new(inner).poll_read(cx, buf),
DecodedReader::Mp3(inner) => Pin::new(inner).poll_read(cx, buf),
DecodedReader::OggVorbis(inner) => Pin::new(inner).poll_read(cx, buf),
DecodedReader::OggOpus(inner) => Pin::new(inner).poll_read(cx, buf),
DecodedReader::Wav(inner) => Pin::new(inner).poll_read(cx, buf),
DecodedReader::Aiff(inner) => Pin::new(inner).poll_read(cx, buf),
}
}
}
fn detect_format(bytes: &[u8]) -> Option<DetectedFormat> {
if bytes.len() >= 4 && &bytes[..4] == b"fLaC" {
return Some(DetectedFormat::Flac);
}
if bytes.len() >= 12 && &bytes[..4] == b"RIFF" && &bytes[8..12] == b"WAVE" {
return Some(DetectedFormat::Wav);
}
if bytes.len() >= 12
&& &bytes[..4] == b"FORM"
&& (&bytes[8..12] == b"AIFF" || &bytes[8..12] == b"AIFC")
{
return Some(DetectedFormat::Aiff);
}
if let Some(fmt) = detect_ogg(bytes) {
return Some(fmt);
}
if is_mp3(bytes) {
return Some(DetectedFormat::Mp3);
}
None
}
fn detect_ogg(bytes: &[u8]) -> Option<DetectedFormat> {
if bytes.len() < 27 || &bytes[..4] != b"OggS" {
return None;
}
let segment_count = bytes[26] as usize;
let header_len = 27 + segment_count;
if bytes.len() < header_len {
return None;
}
let mut packet_len = 0usize;
for lace in &bytes[27..27 + segment_count] {
packet_len += *lace as usize;
if *lace < 255 {
break;
}
}
if bytes.len() < header_len + packet_len {
return None;
}
let packet = &bytes[header_len..header_len + packet_len];
if packet.starts_with(b"OpusHead") {
Some(DetectedFormat::OggOpus)
} else if packet.starts_with(b"\x01vorbis") {
Some(DetectedFormat::OggVorbis)
} else {
None
}
}
fn is_mp3(bytes: &[u8]) -> bool {
if bytes.len() >= 3 && &bytes[..3] == b"ID3" {
return true;
}
if bytes.len() >= 2 && bytes[0] == 0xFF && (bytes[1] & 0xE0) == 0xE0 {
return true;
}
false
}
enum DetectedFormat {
Flac,
Mp3,
OggVorbis,
OggOpus,
Wav,
Aiff,
}