79bbbd4956
Processors now emit a ProcessedAudio plan with a streamed writer instead of a whole-file AudioBinary; vault writes stream via RegisterResourceStreamingAsync. Header parsing is bounded. Wave 2 (waveform/Opus) still re-reads the full file by design.
59 lines
2.5 KiB
C#
59 lines
2.5 KiB
C#
namespace DeepDrftContent.Processors;
|
|
|
|
/// <summary>
|
|
/// Bounded-buffer streaming primitives shared by the audio processors on the store path. None of
|
|
/// these hold the whole file in memory: copies move a fixed window at a time, and the header read
|
|
/// caps its allocation regardless of file size.
|
|
/// </summary>
|
|
internal static class AudioStoreStream
|
|
{
|
|
private const int CopyBufferSize = 81920; // 80 KB — matches the controller staging copy.
|
|
|
|
/// <summary>
|
|
/// Bounded disk-to-disk copy of <paramref name="sourcePath"/> into <paramref name="destination"/>.
|
|
/// Used for passthrough formats whose stored bytes equal the source bytes. Hand-rolled rather than
|
|
/// <see cref="Stream.CopyToAsync(Stream)"/> because <c>FileStream</c>'s override writes in 128 KB
|
|
/// blocks; this keeps every write at or below <see cref="CopyBufferSize"/>, so peak managed memory
|
|
/// is provably O(buffer), never O(filesize).
|
|
/// </summary>
|
|
public static async Task CopyFileAsync(string sourcePath, Stream destination, CancellationToken ct)
|
|
{
|
|
await using var src = new FileStream(
|
|
sourcePath, FileMode.Open, FileAccess.Read, FileShare.Read,
|
|
bufferSize: CopyBufferSize, useAsync: true);
|
|
|
|
var buffer = new byte[CopyBufferSize];
|
|
int read;
|
|
while ((read = await src.ReadAsync(buffer, ct)) > 0)
|
|
{
|
|
await destination.WriteAsync(buffer.AsMemory(0, read), ct);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads at most <paramref name="cap"/> bytes from the start of <paramref name="path"/> — enough
|
|
/// for header/metadata parsing without loading the (potentially ~GB) body. Bounds the allocation
|
|
/// at <c>min(cap, fileLength)</c>. Size-based metadata (e.g. average bitrate) must use the true
|
|
/// file length, supplied separately, not the prefix length.
|
|
/// </summary>
|
|
public static async Task<byte[]> ReadPrefixAsync(string path, long cap, CancellationToken ct)
|
|
{
|
|
await using var fs = new FileStream(
|
|
path, FileMode.Open, FileAccess.Read, FileShare.Read,
|
|
bufferSize: CopyBufferSize, useAsync: true);
|
|
|
|
var length = (int)Math.Min(cap, fs.Length);
|
|
var buffer = new byte[length];
|
|
var total = 0;
|
|
while (total < length)
|
|
{
|
|
var read = await fs.ReadAsync(buffer.AsMemory(total, length - total), ct);
|
|
if (read == 0)
|
|
break;
|
|
total += read;
|
|
}
|
|
|
|
return total == length ? buffer : buffer[..total];
|
|
}
|
|
}
|