A managed C# reader and decoder for Ogg streams containing Theora video and Vorbis audio.
TheoraSharp is a pure C# implementation for reading Ogg bitstreams and decoding:
- Theora video into packed 32-bit pixel buffers
- Vorbis audio into interleaved floating-point PCM samples
- Multiplexed Ogg streams through a small pull-based API
The project began as a C# port of the Java Theora decoder used by the Cortado web video player applet.
Important
The public API is still being refined. A NuGet package is planned, but it will be published only after the current refactoring and performance work is complete.
- Managed Theora video decoding
- Managed Vorbis audio decoding
- Ogg page and packet parsing
- File,
Stream, andbyte[]input - Frame width, height, and frame-rate metadata
- Audio channel count, sample rate, sample positions, and timing metadata
- Optional recovery from corrupted Ogg pages
- No native decoder dependency in the library project
There is no NuGet package yet. Clone the repository and reference the library project directly:
git clone https://github.com/DJYar/TheoraSharp.gitdotnet add YourProject.csproj reference path/to/TheoraSharp/TheoraSharp/TheoraSharp.csprojOr add the project reference manually:
<ItemGroup>
<ProjectReference Include="path/to/TheoraSharp/TheoraSharp/TheoraSharp.csproj" />
</ItemGroup>A package will be published after the decoder API has been refactored and the main decoding paths have been optimized.
The future installation command will be:
dotnet add package TheoraSharpThis command is shown for reference only. The package is not available yet.
using TheoraSharp;
var source = new OggVideoReader("video.ogv");
using var frames = source.StartReading<uint>(
throwOnCorruptedPacket: false);
while (frames.MoveNext())
{
uint[] pixels = frames.Current;
Console.WriteLine(
$"Decoded {source.Width}x{source.Height} frame at {source.Fps:0.##} FPS");
// pixels.Length == source.Width * source.Height
// Each pixel is packed as 0xAARRGGBB.
}The stream headers are decoded while enumeration advances, so dimensions and frame-rate metadata become available during reading.
using TheoraSharp;
// From an existing stream
using Stream input = File.OpenRead("video.ogv");
var fromStream = new OggVideoReader(input);
// From an in-memory buffer
byte[] data = await File.ReadAllBytesAsync("video.ogv");
var fromMemory = new OggVideoReader(data);An optional buffer size can be supplied to any constructor:
var source = new OggVideoReader("video.ogv", bufferSize: 16 * 1024);StartReading<uint>() returns frames as one-dimensional uint[] buffers.
using var frames = source.StartReading<uint>();
while (frames.MoveNext())
{
uint[] pixels = frames.Current;
for (var y = 0; y < source.Height; y++)
{
for (var x = 0; x < source.Width; x++)
{
uint argb = pixels[y * source.Width + x];
byte a = (byte)(argb >> 24);
byte r = (byte)(argb >> 16);
byte g = (byte)(argb >> 8);
byte b = (byte)argb;
// Upload to a texture, copy into an image, or process directly.
}
}
}The current decoder produces opaque pixels with an alpha value of 255.
Vorbis audio is decoded while the Ogg stream is enumerated. New chunks are appended to AudioChunks.
using var frames = source.StartReading<uint>(
throwOnCorruptedPacket: false);
var audioCursor = 0;
while (frames.MoveNext())
{
uint[] pixels = frames.Current;
while (audioCursor < source.AudioChunks.Count)
{
DecodedAudioChunk chunk = source.AudioChunks[audioCursor++];
float[] samples = chunk.Samples;
Console.WriteLine(
$"{chunk.Channels} channels, " +
$"{chunk.SampleRate} Hz, " +
$"{chunk.SampleCount} samples per channel, " +
$"starts at {chunk.StartTimeSeconds:0.000}s");
// Samples are interleaved:
// L, R, L, R, ... for stereo audio.
}
}Each DecodedAudioChunk exposes:
| Property | Description |
|---|---|
Samples |
Interleaved floating-point PCM samples |
Channels |
Number of audio channels |
SampleRate |
Sample rate in Hz |
SampleCount |
Number of samples per channel |
StartSample |
Absolute starting sample index |
StartTimeSeconds |
Chunk start time |
DurationSeconds |
Chunk duration |
GranulePosition |
Ogg granule position |
PacketNumber |
Source Ogg packet number |
Reader-level audio metadata is also available through:
source.AudioChannels;
source.AudioSampleRate;
source.AudioSampleCount;
source.AudioDecoders;
source.AudioChunks;By default, malformed Ogg page data causes decoding to fail:
using var frames = source.StartReading<uint>(
throwOnCorruptedPacket: true);For best-effort playback, corrupted pages can be skipped:
using var frames = source.StartReading<uint>(
throwOnCorruptedPacket: false);This option applies to damaged Ogg page data. Decoder errors inside valid packets may still throw.
Build the complete solution:
dotnet build TheoraSharp.slnRun the terminal demo with an .ogv file:
dotnet run --project Decoder/Decoder.csproj -- path/to/video.ogvThe demo renders decoded frames in the terminal. On Windows, it also plays decoded Vorbis audio through WinMM.
The decoder works, but the repository should currently be treated as a development snapshot rather than a stable package.
Before the first NuGet release, the main goals are:
- Refactor and simplify the public API
- Profile and optimize decoding hot paths
- Reduce unnecessary allocations
- Improve validation and compatibility coverage
- Finalize package metadata and documentation
Breaking API changes may happen before the first packaged release.
TheoraSharp/
├── Decoder/ # Terminal playback and decoding example
├── TheoraSharp/
│ ├── Ogg/ # Ogg bitstream parsing
│ ├── Theora/ # Theora decoder implementation
│ ├── Vorbis/ # Vorbis decoder implementation
│ ├── OggVideoReader.cs # High-level Ogg reader
│ ├── TheoraDec.cs # Video decoder adapter
│ └── VorbisAudioDec.cs # Audio decoder adapter
└── TheoraSharp.sln
Bug reports, test files, profiling results, and focused pull requests are welcome.
When reporting a decoding problem, include:
- The container and codec details
- The expected and actual result
- The exception or corrupted-frame symptoms
- A minimal reproducible sample, when redistribution is permitted
Please avoid committing copyrighted media that cannot legally be redistributed.
TheoraSharp is based on the Java Theora decoder from Cortado, the historical web video player applet.
The project also builds on the specifications and ecosystem around: