fix: maintain passed in streams position
This commit is contained in:
@@ -52,7 +52,7 @@ internal class DefaultCommand(
|
|||||||
.Spinner(Spinner.Known.Dots)
|
.Spinner(Spinner.Known.Dots)
|
||||||
.StartAsync("Extracting audio...", async ctx =>
|
.StartAsync("Extracting audio...", async ctx =>
|
||||||
{
|
{
|
||||||
audioStream = await _audioExtractor.ExtractMp3FromMp4Async(videoStream).ConfigureAwait(false);
|
audioStream = await _audioExtractor.ExtractMp3FromMp4Async(videoStream);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (audioStream is null)
|
if (audioStream is null)
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ internal interface IAudioService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mp3">The input MP3 stream.</param>
|
/// <param name="mp3">The input MP3 stream.</param>
|
||||||
/// <returns>The output WAV stream.</returns>
|
/// <returns>The output WAV stream.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown when the MP3 stream is null.</exception>
|
||||||
|
/// <exception cref="ArgumentException">Thrown when the MP3 stream is not readable or seekable.</exception>
|
||||||
|
/// <remarks>The method will preserve the passed MP3 stream's data and position.</remarks
|
||||||
Stream ConvertMp3ToWav16(Stream mp3);
|
Stream ConvertMp3ToWav16(Stream mp3);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -19,6 +22,9 @@ internal interface IAudioService
|
|||||||
/// param name="wavStream">The input WAV stream.</param>
|
/// param name="wavStream">The input WAV stream.</param>
|
||||||
/// <param name="segmentDuration">The duration of each segment.</param>
|
/// <param name="segmentDuration">The duration of each segment.</param>
|
||||||
/// <returns>The number of segments.</returns>
|
/// <returns>The number of segments.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown when the WAV stream is null.</exception>
|
||||||
|
/// <exception cref="ArgumentException">Thrown when the WAV stream is not readable or seekable.</exception>
|
||||||
|
/// <remarks>The method will preserve the passed WAV stream's data and position.</remarks>
|
||||||
int GetNumberOfWavSegments(Stream wavStream, TimeSpan segmentDuration);
|
int GetNumberOfWavSegments(Stream wavStream, TimeSpan segmentDuration);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -28,5 +34,8 @@ internal interface IAudioService
|
|||||||
/// <param name="segmentNumber">The segment number to retrieve.</param>
|
/// <param name="segmentNumber">The segment number to retrieve.</param>
|
||||||
/// <param name="segmentDuration">The duration of each segment.</param>
|
/// <param name="segmentDuration">The duration of each segment.</param>
|
||||||
/// <returns>The segment stream.</returns>
|
/// <returns>The segment stream.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown when the WAV stream is null.</exception>
|
||||||
|
/// <exception cref="ArgumentException">Thrown when the WAV stream is not readable or seekable.</exception>
|
||||||
|
/// <remarks>The method will preserve the passed WAV stream's data and position.</remarks>
|
||||||
Stream GetWavSegment(Stream wavStream, int segmentNumber, TimeSpan segmentDuration);
|
Stream GetWavSegment(Stream wavStream, int segmentNumber, TimeSpan segmentDuration);
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,8 @@ namespace StreamShorts.Library.Media;
|
|||||||
internal class NAudioService : IAudioService
|
internal class NAudioService : IAudioService
|
||||||
{
|
{
|
||||||
public Stream ConvertMp3ToWav16(Stream mp3)
|
public Stream ConvertMp3ToWav16(Stream mp3)
|
||||||
|
{
|
||||||
|
return UseStream(mp3, stream =>
|
||||||
{
|
{
|
||||||
using var reader = new Mp3FileReader(mp3);
|
using var reader = new Mp3FileReader(mp3);
|
||||||
var outFormat = new WaveFormat(16000, reader.WaveFormat.Channels);
|
var outFormat = new WaveFormat(16000, reader.WaveFormat.Channels);
|
||||||
@@ -18,18 +20,23 @@ internal class NAudioService : IAudioService
|
|||||||
WaveFileWriter.WriteWavFileToStream(waveStream, resampler);
|
WaveFileWriter.WriteWavFileToStream(waveStream, resampler);
|
||||||
waveStream.Position = 0;
|
waveStream.Position = 0;
|
||||||
return waveStream;
|
return waveStream;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetNumberOfWavSegments(Stream wavStream, TimeSpan segmentDuration)
|
public int GetNumberOfWavSegments(Stream wavStream, TimeSpan segmentDuration)
|
||||||
|
{
|
||||||
|
return UseStream(wavStream, stream =>
|
||||||
{
|
{
|
||||||
using var waveReader = new WaveFileReader(wavStream);
|
using var waveReader = new WaveFileReader(wavStream);
|
||||||
var totalDuration = waveReader.TotalTime;
|
var totalDuration = waveReader.TotalTime;
|
||||||
var segmentCount = (int)Math.Ceiling(totalDuration.TotalMilliseconds / segmentDuration.TotalMilliseconds);
|
var segmentCount = (int)Math.Ceiling(totalDuration.TotalMilliseconds / segmentDuration.TotalMilliseconds);
|
||||||
wavStream.Position = 0;
|
|
||||||
return segmentCount;
|
return segmentCount;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream GetWavSegment(Stream wavStream, int segmentNumber, TimeSpan segmentDuration)
|
public Stream GetWavSegment(Stream wavStream, int segmentNumber, TimeSpan segmentDuration)
|
||||||
|
{
|
||||||
|
return UseStream(wavStream, stream =>
|
||||||
{
|
{
|
||||||
using var segmentWaveReader = new WaveFileReader(wavStream);
|
using var segmentWaveReader = new WaveFileReader(wavStream);
|
||||||
var segment = segmentWaveReader.ToSampleProvider()
|
var segment = segmentWaveReader.ToSampleProvider()
|
||||||
@@ -39,7 +46,42 @@ internal class NAudioService : IAudioService
|
|||||||
var segmentStream = new MemoryStream();
|
var segmentStream = new MemoryStream();
|
||||||
WaveFileWriter.WriteWavFileToStream(segmentStream, segmentProvider);
|
WaveFileWriter.WriteWavFileToStream(segmentStream, segmentProvider);
|
||||||
segmentStream.Position = 0;
|
segmentStream.Position = 0;
|
||||||
wavStream.Position = 0;
|
|
||||||
return segmentStream;
|
return segmentStream;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T UseStream<T>(Stream stream, Func<Stream, T> 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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user