diff --git a/src/StreamShorts.Console/Commands/DefaultCommand.cs b/src/StreamShorts.Console/Commands/DefaultCommand.cs
index 6fe0ea1..3780956 100644
--- a/src/StreamShorts.Console/Commands/DefaultCommand.cs
+++ b/src/StreamShorts.Console/Commands/DefaultCommand.cs
@@ -52,7 +52,7 @@ internal class DefaultCommand(
.Spinner(Spinner.Known.Dots)
.StartAsync("Extracting audio...", async ctx =>
{
- audioStream = await _audioExtractor.ExtractMp3FromMp4Async(videoStream).ConfigureAwait(false);
+ audioStream = await _audioExtractor.ExtractMp3FromMp4Async(videoStream);
});
if (audioStream is null)
diff --git a/src/StreamShorts.Library/Media/IAudioService.cs b/src/StreamShorts.Library/Media/IAudioService.cs
index fee0713..8711fe7 100644
--- a/src/StreamShorts.Library/Media/IAudioService.cs
+++ b/src/StreamShorts.Library/Media/IAudioService.cs
@@ -11,6 +11,9 @@ internal interface IAudioService
///
/// The input MP3 stream.
/// The output WAV stream.
+ /// Thrown when the MP3 stream is null.
+ /// Thrown when the MP3 stream is not readable or seekable.
+ /// The method will preserve the passed MP3 stream's data and position.
@@ -19,6 +22,9 @@ internal interface IAudioService
/// param name="wavStream">The input WAV stream.
/// The duration of each segment.
/// The number of segments.
+ /// Thrown when the WAV stream is null.
+ /// Thrown when the WAV stream is not readable or seekable.
+ /// The method will preserve the passed WAV stream's data and position.
int GetNumberOfWavSegments(Stream wavStream, TimeSpan segmentDuration);
///
@@ -28,5 +34,8 @@ internal interface IAudioService
/// The segment number to retrieve.
/// The duration of each segment.
/// The segment stream.
+ /// Thrown when the WAV stream is null.
+ /// Thrown when the WAV stream is not readable or seekable.
+ /// The method will preserve the passed WAV stream's data and position.
Stream GetWavSegment(Stream wavStream, int segmentNumber, TimeSpan segmentDuration);
}
\ No newline at end of file
diff --git a/src/StreamShorts.Library/Media/NAudioService.cs b/src/StreamShorts.Library/Media/NAudioService.cs
index e715c5e..431ef32 100644
--- a/src/StreamShorts.Library/Media/NAudioService.cs
+++ b/src/StreamShorts.Library/Media/NAudioService.cs
@@ -11,35 +11,77 @@ internal class NAudioService : IAudioService
{
public Stream ConvertMp3ToWav16(Stream mp3)
{
- using var reader = new Mp3FileReader(mp3);
- var outFormat = new WaveFormat(16000, reader.WaveFormat.Channels);
- using var resampler = new MediaFoundationResampler(reader, outFormat);
- var waveStream = new MemoryStream();
- WaveFileWriter.WriteWavFileToStream(waveStream, resampler);
- waveStream.Position = 0;
- return waveStream;
+ return UseStream(mp3, stream =>
+ {
+ using var reader = new Mp3FileReader(mp3);
+ var outFormat = new WaveFormat(16000, reader.WaveFormat.Channels);
+ using var resampler = new MediaFoundationResampler(reader, outFormat);
+ var waveStream = new MemoryStream();
+ WaveFileWriter.WriteWavFileToStream(waveStream, resampler);
+ waveStream.Position = 0;
+ return waveStream;
+ });
}
public int GetNumberOfWavSegments(Stream wavStream, TimeSpan segmentDuration)
{
- using var waveReader = new WaveFileReader(wavStream);
- var totalDuration = waveReader.TotalTime;
- var segmentCount = (int)Math.Ceiling(totalDuration.TotalMilliseconds / segmentDuration.TotalMilliseconds);
- wavStream.Position = 0;
- return segmentCount;
+ return UseStream(wavStream, stream =>
+ {
+ using var waveReader = new WaveFileReader(wavStream);
+ var totalDuration = waveReader.TotalTime;
+ var segmentCount = (int)Math.Ceiling(totalDuration.TotalMilliseconds / segmentDuration.TotalMilliseconds);
+ return segmentCount;
+ });
}
public Stream GetWavSegment(Stream wavStream, int segmentNumber, TimeSpan segmentDuration)
{
- using var segmentWaveReader = new WaveFileReader(wavStream);
- var segment = segmentWaveReader.ToSampleProvider()
- .Skip(segmentNumber * segmentDuration)
- .Take(segmentDuration);
- var segmentProvider = segment.ToWaveProvider16();
- var segmentStream = new MemoryStream();
- WaveFileWriter.WriteWavFileToStream(segmentStream, segmentProvider);
- segmentStream.Position = 0;
- wavStream.Position = 0;
- return segmentStream;
+ return UseStream(wavStream, stream =>
+ {
+ using var segmentWaveReader = new WaveFileReader(wavStream);
+ var segment = segmentWaveReader.ToSampleProvider()
+ .Skip(segmentNumber * segmentDuration)
+ .Take(segmentDuration);
+ var segmentProvider = segment.ToWaveProvider16();
+ var segmentStream = new MemoryStream();
+ WaveFileWriter.WriteWavFileToStream(segmentStream, segmentProvider);
+ segmentStream.Position = 0;
+ return segmentStream;
+ });
+ }
+
+ private static T UseStream(Stream stream, Func action)
+ {
+ ValidateStream(stream);
+
+ var originalPosition = stream.Position;
+
+ try
+ {
+ stream.Position = 0;
+ return action(stream);
+ }
+ finally
+ {
+ stream.Position = originalPosition;
+ }
+ }
+
+ private static void ValidateStream(Stream stream)
+ {
+ if (stream == null)
+ {
+ throw new ArgumentNullException(nameof(stream), $"{nameof(stream)} cannot be null");
+ }
+
+ if (stream.CanRead is false)
+ {
+ throw new ArgumentException($"{nameof(stream)} must be readable", nameof(stream));
+ }
+
+ if (stream.CanSeek is false)
+ {
+ throw new ArgumentException($"{nameof(stream)} must be seekable", nameof(stream));
+ }
}
}
\ No newline at end of file