API Reference
IMediaEncoder
The core abstraction, implemented by NativeEncoder.
public interface IMediaEncoder
{
Task<ProbeResult> Probe(string filePath);
Task ConvertFile(string sourceFilePath, string destFilePath);
Task CutFile(string sourceFilePath, string destFilePath, int startInSeconds, int endInSeconds);
}
ProbeResult
Only fields this library can genuinely compute are populated — no ffprobe-style placeholders (e.g. no probe_score, no disposition flags).
public class ProbeResult
{
public string FormatName { get; init; } // e.g. "wav", "flac", "mp3", "aac", "asf", "mov", "mp4"
public string FormatLongName { get; init; } // e.g. "WAV / WAVE (Waveform Audio)"
public long SizeBytes { get; init; }
public double DurationSeconds { get; init; }
public string CodecType { get; init; } // "audio" or "video"
public string? CodecName { get; init; } // e.g. "pcm_s16le", "flac", "mp3", "aac", "wmav2"
public string? CodecLongName { get; init; }
public int? SampleRate { get; init; }
public int? Channels { get; init; }
public string? ChannelLayout { get; init; } // "mono", "stereo", or "{n} channels"
public int? BitsPerSample { get; init; }
public int? BitRate { get; init; }
public bool? IsVariableBitRate { get; init; } // MP3 only
public long? DurationInSamples { get; init; }
public string? TimeBase { get; init; } // e.g. "1/44100"
public int? Width { get; init; } // set for video containers (MOV/MP4), null for audio
public int? Height { get; init; }
public IReadOnlyList<double>? Waveform { get; init; } // normalized peak windows in [0,1], or null
}
NativeEncoder
Namespace: EggEncoder. Dispatches to a codec under Codecs/ by file extension. No external process.
public class NativeEncoder : IMediaEncoder
{
public NativeEncoder(ILogger<NativeEncoder> logger);
}
Supported extensions for Probe: .wav, .flac, .mp3, .aac, .wma, .mov, .mp4. Unrecognized extensions throw NotSupportedException.
ServiceCollectionExtensions
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddEggEncoder(this IServiceCollection services);
// registers IMediaEncoder -> NativeEncoder (scoped). No options.
}
AudioCutter
Namespace: EggEncoder.Codecs. Format-dispatching convert/cut used internally by NativeEncoder; also usable directly.
public static class AudioCutter
{
public static void Convert(string sourceFilePath, string destFilePath);
public static bool Cut(string sourceFilePath, string destFilePath, int startInSeconds, int endInSeconds);
}
Cut decodes any supported source (.wav, .flac, .mp3, .aac, .wma) and writes any supported destination extension — source and destination don't need to match, so it can transcode while it trims. Returns false (and writes no file) if the requested range is entirely outside the source's duration.
WaveformCalculator
Namespace: EggEncoder.Waveform.
public sealed class WaveformCalculator
{
public const int DefaultWindowCount = 200;
public WaveformCalculator(long totalSamplesPerChannel, int channels, int bitsPerSample, int windowCount = DefaultWindowCount);
public void AddBlock(ReadOnlySpan<int> interleavedSamples);
public List<double> GetNormalizedWindows();
}
Codecs
AAC — EggEncoder.Codecs.Aac
public static class AacDecoder
{
public static AacStreamInfo Decode(string aacFilePath, AudioBlockDecodedCallback onBlockDecoded);
}
public static class AacEncoder
{
public static void Encode(string destFilePath, short[] samples, int channels, int sampleRate);
}
FLAC — EggEncoder.Codecs.Flac
public static class FlacDecoder
{
public static FlacStreamInfo Decode(string flacFilePath, AudioBlockDecodedCallback onBlockDecoded);
}
public static class FlacEncoder
{
public const uint DefaultCompressionLevel = 5;
public static void Encode(string sourceWavFilePath, string destFlacFilePath, uint compressionLevel = DefaultCompressionLevel);
}
MP3 — EggEncoder.Codecs.Mp3
public static class Mp3Decoder // backed by NLayer
{
public static Mp3StreamInfo Decode(string mp3FilePath, AudioBlockDecodedCallback onBlockDecoded);
}
public static class Mp3Encoder // backed by native libmp3lame
{
public const int DefaultBitRateKbps = 320;
public static void Encode(string sourceWavFilePath, string destMp3FilePath, int bitRateKbps = DefaultBitRateKbps);
}
public static class Mp3Probe // manual frame-header parsing, no native call
{
public static Mp3ProbeResult Probe(string mp3FilePath);
}
WAV — EggEncoder.Codecs.Wav
public sealed class WavReader : IDisposable
{
public static WavReader Open(string filePath);
public int Channels { get; }
public int SampleRate { get; }
public int BitsPerSample { get; }
public long TotalSamples { get; }
public bool IsFloatFormat { get; }
public int ReadInterleavedSamples(int[] buffer, int maxSamplesPerChannel);
}
public sealed class WavWriter : IDisposable
{
public static WavWriter Create(string destFilePath, int channels, int sampleRate, int bitsPerSample, long totalFrames);
public void WriteInterleavedSamples(int[] buffer, int frameCount);
}
Supports 8-bit unsigned, 16/24/32-bit signed integer, and (read-only) 32-bit IEEE float PCM.
WMA — EggEncoder.Codecs.Wma
public static class WmaDecoder
{
public static WmaStreamInfo Decode(string wmaFilePath, AudioBlockDecodedCallback onBlockDecoded);
}
public static class WmaEncoder
{
public static void Encode(string destFilePath, IReadOnlyList<short> interleavedSamples, int channels, int sampleRate);
}
Mono or independently-coded stereo only. Mid/side stereo coding (used by most real-world WMAv2 encoders, including ffmpeg's) is not supported for decode and throws NotSupportedException; WmaEncoder never produces mid/side output, so this library's own encoder/decoder round-trip always works.
MOV/MP4 — EggEncoder.Codecs.Mov
public static class MovProbe
{
public static MovProbeResult Probe(string filePath);
}
public class MovProbeResult
{
public int DurationInSeconds { get; init; }
public int? Width { get; init; }
public int? Height { get; init; }
public string? CodecFourCc { get; init; }
}
Decode Callback Signature
Every codec's Decode method streams blocks through the same callback shape rather than returning the full signal in memory. It's a plain delegate rather than a generic Action<> because ReadOnlySpan<int> can't be used as a generic type argument on net8.0:
namespace EggEncoder.Codecs;
public delegate void AudioBlockDecodedCallback(ReadOnlySpan<int> block, int channels, int sampleRate, int bitsPerSample, long totalSamplesPerChannel);
block is interleaved samples for whatever chunk the decoder just produced — accumulate it yourself (into a List<int>, a WaveformCalculator, an IAudioSink) as needed.