Refactoring pmoflac - factorisation des erreurs

This commit is contained in:
2025-10-27 22:47:59 +01:00
parent d07dae3783
commit e15eaf9b53
6 changed files with 73 additions and 139 deletions

View File

@@ -20,39 +20,15 @@ use tokio::{
use crate::{
common::ChannelReader,
decoder_common::{spawn_ingest_task, spawn_writer_task, CHANNEL_CAPACITY, DUPLEX_BUFFER_SIZE},
decoder_common::{
spawn_ingest_task, spawn_writer_task, DecoderError, CHANNEL_CAPACITY, DUPLEX_BUFFER_SIZE,
},
pcm::StreamInfo,
stream::ManagedAsyncReader,
};
/// Errors that can occur while decoding AIFF data.
#[derive(thiserror::Error, Debug, Clone)]
pub enum AiffError {
#[error("I/O error ({kind:?}): {message}")]
Io {
kind: io::ErrorKind,
message: String,
},
#[error("AIFF decode error: {0}")]
Decode(String),
#[error("internal channel closed unexpectedly")]
ChannelClosed,
}
impl From<io::Error> for AiffError {
fn from(err: io::Error) -> Self {
AiffError::Io {
kind: err.kind(),
message: err.to_string(),
}
}
}
impl From<String> for AiffError {
fn from(value: String) -> Self {
AiffError::Decode(value)
}
}
pub type AiffError = DecoderError;
/// Streaming reader that buffers bytes as they arrive and exposes convenience helpers.
struct StreamingAiffReader<E>

View File

@@ -23,17 +23,17 @@ pub enum DecodeAudioError {
#[error("unknown or unsupported audio format")]
UnknownFormat,
#[error("FLAC decode error: {0}")]
Flac(#[from] FlacError),
Flac(FlacError),
#[error("MP3 decode error: {0}")]
Mp3(#[from] Mp3Error),
Mp3(Mp3Error),
#[error("Ogg/Vorbis decode error: {0}")]
Vorbis(OggError),
#[error("Ogg/Opus decode error: {0}")]
Opus(OggOpusError),
#[error("WAV decode error: {0}")]
Wav(#[from] WavError),
Wav(WavError),
#[error("AIFF decode error: {0}")]
Aiff(#[from] AiffError),
Aiff(AiffError),
}
pub async fn decode_audio_stream<R>(reader: R) -> Result<DecodedAudioStream, DecodeAudioError>
@@ -59,11 +59,15 @@ where
let stream = match format {
DetectedFormat::Flac => {
let stream = decode_flac_stream(prefixed).await?;
let stream = decode_flac_stream(prefixed)
.await
.map_err(DecodeAudioError::Flac)?;
DecodedAudioStream::Flac(stream)
}
DetectedFormat::Mp3 => {
let stream = decode_mp3_stream(prefixed).await?;
let stream = decode_mp3_stream(prefixed)
.await
.map_err(DecodeAudioError::Mp3)?;
DecodedAudioStream::Mp3(stream)
}
DetectedFormat::OggVorbis => {
@@ -79,11 +83,15 @@ where
DecodedAudioStream::OggOpus(stream)
}
DetectedFormat::Wav => {
let stream = decode_wav_stream(prefixed).await?;
let stream = decode_wav_stream(prefixed)
.await
.map_err(DecodeAudioError::Wav)?;
DecodedAudioStream::Wav(stream)
}
DetectedFormat::Aiff => {
let stream = decode_aiff_stream(prefixed).await?;
let stream = decode_aiff_stream(prefixed)
.await
.map_err(DecodeAudioError::Aiff)?;
DecodedAudioStream::Aiff(stream)
}
};
@@ -114,16 +122,16 @@ impl DecodedAudioStream {
pub async fn wait(self) -> Result<(), DecodeAudioError> {
match self {
DecodedAudioStream::Flac(inner) => inner.wait().await.map_err(DecodeAudioError::from),
DecodedAudioStream::Mp3(inner) => inner.wait().await.map_err(DecodeAudioError::from),
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::from),
DecodedAudioStream::Aiff(inner) => inner.wait().await.map_err(DecodeAudioError::from),
DecodedAudioStream::Wav(inner) => inner.wait().await.map_err(DecodeAudioError::Wav),
DecodedAudioStream::Aiff(inner) => inner.wait().await.map_err(DecodeAudioError::Aiff),
}
}
@@ -186,12 +194,12 @@ pub enum DecodedReader {
impl DecodedReader {
pub async fn wait(self) -> Result<(), DecodeAudioError> {
match self {
DecodedReader::Flac(inner) => inner.wait().await.map_err(DecodeAudioError::from),
DecodedReader::Mp3(inner) => inner.wait().await.map_err(DecodeAudioError::from),
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::from),
DecodedReader::Aiff(inner) => inner.wait().await.map_err(DecodeAudioError::from),
DecodedReader::Wav(inner) => inner.wait().await.map_err(DecodeAudioError::Wav),
DecodedReader::Aiff(inner) => inner.wait().await.map_err(DecodeAudioError::Aiff),
}
}
}

View File

@@ -12,6 +12,41 @@ use tokio::{
task::JoinHandle,
};
/// Generic error type shared by the streaming decoders.
#[derive(thiserror::Error, Debug, Clone)]
pub enum DecoderError {
#[error("I/O error ({kind:?}): {message}")]
Io {
kind: io::ErrorKind,
message: String,
},
#[error("{0}")]
Decode(String),
#[error("internal channel closed unexpectedly")]
ChannelClosed,
}
impl From<io::Error> for DecoderError {
fn from(err: io::Error) -> Self {
DecoderError::Io {
kind: err.kind(),
message: err.to_string(),
}
}
}
impl From<String> for DecoderError {
fn from(value: String) -> Self {
DecoderError::Decode(value)
}
}
impl From<&str> for DecoderError {
fn from(value: &str) -> Self {
DecoderError::Decode(value.to_owned())
}
}
/// Size of chunks when reading input data.
///
/// This size balances between efficient I/O operations and memory usage.

View File

@@ -101,41 +101,15 @@ use tokio::{
use crate::{
common::ChannelReader,
decoder_common::{spawn_ingest_task, spawn_writer_task, CHANNEL_CAPACITY, DUPLEX_BUFFER_SIZE},
decoder_common::{
spawn_ingest_task, spawn_writer_task, DecoderError, CHANNEL_CAPACITY, DUPLEX_BUFFER_SIZE,
},
pcm::StreamInfo,
stream::ManagedAsyncReader,
};
/// Errors that can occur while decoding MP3 data.
#[derive(thiserror::Error, Debug, Clone)]
pub enum Mp3Error {
#[error("I/O error ({kind:?}): {message}")]
Io {
kind: io::ErrorKind,
message: String,
},
#[error("MP3 decode error: {0}")]
Decode(String),
#[error("internal channel closed unexpectedly")]
ChannelClosed,
#[error("{role} task failed: {details}")]
TaskJoin { role: &'static str, details: String },
}
impl From<io::Error> for Mp3Error {
fn from(err: io::Error) -> Self {
Mp3Error::Io {
kind: err.kind(),
message: err.to_string(),
}
}
}
impl From<String> for Mp3Error {
fn from(msg: String) -> Self {
Mp3Error::Decode(msg)
}
}
pub type Mp3Error = DecoderError;
/// An async stream that decodes MP3 audio into PCM samples.
///

View File

@@ -25,7 +25,7 @@ use std::{
io::{self, Read},
};
use crate::common::ChannelReader;
use crate::{common::ChannelReader, decoder_common::DecoderError};
/// Maximum number of bytes to scan when searching for Ogg sync pattern.
///
@@ -60,42 +60,7 @@ impl Default for OggReaderOptions {
}
}
/// Shared error type for Ogg container parsing.
#[derive(thiserror::Error, Debug, Clone)]
pub enum OggContainerError {
#[error("I/O error ({kind:?}): {message}")]
Io {
kind: io::ErrorKind,
message: String,
},
#[error("ogg container error: {0}")]
Decode(String),
#[error("internal channel closed unexpectedly")]
ChannelClosed,
#[error("{role} task failed: {details}")]
TaskJoin { role: &'static str, details: String },
}
impl From<io::Error> for OggContainerError {
fn from(err: io::Error) -> Self {
OggContainerError::Io {
kind: err.kind(),
message: err.to_string(),
}
}
}
impl From<String> for OggContainerError {
fn from(value: String) -> Self {
OggContainerError::Decode(value)
}
}
impl From<&str> for OggContainerError {
fn from(value: &str) -> Self {
OggContainerError::Decode(value.into())
}
}
pub type OggContainerError = DecoderError;
/// Streaming Ogg packet reader that assembles packets from Ogg pages.
///

View File

@@ -18,39 +18,15 @@ use tokio::{
use crate::{
common::ChannelReader,
decoder_common::{spawn_ingest_task, spawn_writer_task, CHANNEL_CAPACITY, DUPLEX_BUFFER_SIZE},
decoder_common::{
spawn_ingest_task, spawn_writer_task, DecoderError, CHANNEL_CAPACITY, DUPLEX_BUFFER_SIZE,
},
pcm::StreamInfo,
stream::ManagedAsyncReader,
};
/// Errors that can occur while decoding WAV data.
#[derive(thiserror::Error, Debug, Clone)]
pub enum WavError {
#[error("I/O error ({kind:?}): {message}")]
Io {
kind: io::ErrorKind,
message: String,
},
#[error("WAV decode error: {0}")]
Decode(String),
#[error("internal channel closed unexpectedly")]
ChannelClosed,
}
impl From<io::Error> for WavError {
fn from(err: io::Error) -> Self {
WavError::Io {
kind: err.kind(),
message: err.to_string(),
}
}
}
impl From<String> for WavError {
fn from(value: String) -> Self {
WavError::Decode(value)
}
}
pub type WavError = DecoderError;
/// Streaming WAV reader state.
struct StreamingWavReader<E>