Recursive AI

OneMedia

All onemedia docs

Containers, metadata, subtitles; no decoding. A managed .NET library for media containers, with no dependency beyond the .NET base class library. OneMedia is unaffiliated with other products or companies named OneMedia.

The goal is the depth of libavformat, GPAC, mkvtoolnix, MediaInfo and ExifTool in one library you can call from C#: demux and mux, lossless tag editing, evidence-backed capture time and place, subtitles and captions, repair, and HLS/DASH packaging. It never decodes or encodes an audio or video sample; audio decoding is handed to OneAudio through the RecursiveAI.OneMedia.Audio adapter.

Status

Preview, version 0.1.0-preview.1: reading MP4/MOV/3GP (including fragmented MP4) and Matroska/WebM. Writing, tag editing, every other container, subtitles, repair and packaging are planned and not in this release. The table below lists what works and how it was checked; gaps are listed, not hidden.

C#
using RecursiveAI.OneMedia;
using RecursiveAI.OneMedia.Audio;   // package RecursiveAI.OneMedia.Audio
 
using var media = Media.Open("clip.mov");
Console.WriteLine($"{media.Duration} {media.Video?.Codec} {media.Audio?.Codec}");
Console.WriteLine($"{media.Tags.Title} {media.CaptureTime.Value} {media.Location.Value}");
foreach (var candidate in media.CaptureTime.Candidates) Console.WriteLine(candidate);
 
using var pcm = media.Decode();   // the audio track, decoded by OneAudio

onemedia info <file> (or --json) prints tracks, codecs (RFC 6381 strings), tags and facts with their candidate sources.

Support (0.1)

FormatReadWriteTag editPackets vs ffprobe 7.0.2Probe + metadata I/O (local reads), ours vs ffprobe / MediaInfo / ExifToolDocumented deviations
MP4, MOV, 3GP/3G2, M4A/M4V, fragmented MP4✓ demux, codec headers, native tags (udta, ilst, mdta, 3GPP, XMP, Nero chapters), common tags, basic facts——121 corpus files, 744,306 packets, 0 unexplained4.38 GB file with the index at the end: 165,681 B in 34 reads vs 1,741,689 B / 32 (ffprobe), 23,628,223 B / 138 (MediaInfo), 1,103,868 B / 139 (ExifTool)docs/isobmff.md: edit-list repeats and drops, last-packet durations, negative composition offsets, fragmented durations are structural (no fragment scan)
Matroska, WebM (MKV, MKA, MK3D)✓ demux (seekable and live/forward-only), codec headers, tags with targets, attachments, basic facts——92 corpus files, 464,934 packets, 0 unexplained4.45 GB file: 69,806 B in 20 reads vs 4,877,532 B / 16 (ffprobe), 72,168,087 B / 493 (MediaInfo), 79,600 B / 11 (ExifTool)docs/matroska-differential.md: attachments are not streams, RealAudio is not de-interleaved, RealVideo timestamps as stored

Bytes and request counts for all 213 corpus files, over HTTP range requests and local reads, are in docs/io-results.txt (method in docs/harness.md). Audio tracks of MP4/3GP equal OneAudio's own MP4 reader packet for packet (250,469 packets over 64 files). Tags and facts agree with ExifTool 13.60 and MediaInfo 26.10 on 116 files, with every difference explained in docs/tags.md; the field mapping is docs/tag-mapping.md. The audio adapter is described in docs/audio-adapter.md, the comparison method in docs/harness.md, and the benchmarks in docs/bench.md.

Speed against the fastest managed library per task (ratio = OneMedia time ÷ theirs, steady state; lower is better). Full tables, allocations, memory, first-call times and the native tools' times are in docs/bench-results; the method is in docs/bench.md.

TaskCompared withx64 (Ryzen 9 7950X3D) geomean / worst filearm64 (Apple M2) geomean / worst file
MP4 demux, every packetSharpMP40.10× / 0.21×0.09× / 0.20×
MP4 audio track onlyOneAudio Mp4PacketSource0.53× / 0.82×0.49× / 0.61×
MP4 probe (tracks, duration)MetadataExtractor, TagLib#, ATL.NET0.10× / 0.40×0.12× / 0.28×
Matroska demuxMatroska (NEbml)0.26× / 0.31×0.25× / 0.41×
Matroska probeNEbml element reader0.69× / 0.83×0.60× / 0.76×

The first open in a process includes JIT time: about 17–23 ms for MP4 and 12–16 ms for Matroska, against 10–14 ms and about 3 ms for those libraries.

Every parser is fuzzed; damaged input is reported as typed defects with byte offsets and never surfaces as an untyped exception. Reading pulls packets with zero allocations per packet and constant memory at any file size.

Packages

PackageWhat it is
RecursiveAI.OneMediathe library; depends on nothing but .NET
RecursiveAI.OneMedia.Audioadapter that hands audio tracks to OneAudio; depends on the core package and RecursiveAI.OneAudio
RecursiveAI.OneMedia.Toolthe onemedia command-line tool (dotnet tool install)

Target framework is net10.0. The library is NativeAOT, trimming and browser-WebAssembly clean.

Building

dotnet build -c Release
dotnet test -c Release
dotnet pack -c Release

Build output goes to artifacts/ in the repository root (ignored by git). To build somewhere else, set ONEMEDIA_SCRATCH to a directory; output then goes to $ONEMEDIA_SCRATCH/artifacts. Test files are written under the system temporary directory (TMPDIR).

smoke/OneMedia.AotSmoke is published with NativeAOT and fails on any trim or AOT warning; smoke/OneMedia.WasmSmoke runs the same checks under the browser-wasm runtime (wasm-tools workload; run the built AppBundle/main.mjs with node). tools/ holds the differential and I/O harness and the corpus scripts, fuzz/ the fuzz targets and bench/ the benchmarks; none of them ship.

Contributing notes

  • Clean room: the code is written from published specifications and from observing the behaviour of other tools as black boxes. The source of other media libraries and tools is not consulted.
  • Every public type and member has an XML doc comment; warnings are errors.
  • unsafe code, Unsafe.*, MemoryMarshal and intrinsics are not used.

Licence

MIT OR Apache-2.0, at your option. Copyright (c) Recursive AI, LLC. See LICENSE-MIT and LICENSE-APACHE. Sample media notices are in THIRD-PARTY-NOTICES.