Something went wrong. Try again.
Reads and writes the Ogg container format in Zig: pages, packets, grouping and chaining, with a page layer that allocates nothing.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
//! Which codec a logical bitstream carries.//!//! Ogg itself does not know: "Ogg does not know any specifics about the codec//! data except that each logical bitstream belongs to a different codec"//! (RFC 3533, Section 4). What it establishes instead is a convention -- "the//! first bytes of the bos page contain magic data that uniquely identifies the//! required codec" -- and leaves each media mapping to choose its own magic and//! to make sure it can be told apart from everyone else's. There is//! deliberately "no fixed way to detect the end of the codec-identifying//! marker", so identifying a stream means comparing against a list, and this is//! that list.//!//! It exists because a container library is the right place to keep it: the//! caller has the first packet of a logical bitstream in hand and wants to know//! which decoder to hand it to. Nothing here parses any of those codecs.
const std = @import("std");
/// The mappings whose identification packets are published and stable. Anything/// else comes back as null from `identify`, which is not the same as the stream/// being invalid -- a mapping this list has never heard of is still a legal Ogg/// stream.pub const Codec = enum { /// Ogg Vorbis, from the Vorbis I specification. vorbis, /// Ogg Opus, RFC 7845. opus, /// Ogg FLAC, from the Ogg FLAC mapping. Not to be confused with a native /// FLAC stream, which begins `fLaC` and has no pages at all. flac, /// Ogg Speex. speex, /// Ogg Theora. theora, /// Ogg Kate, for karaoke and subtitles. kate, /// Dirac video. dirac, /// CMML, the Continuous Media Markup Language. cmml, /// Ogg Skeleton, which describes the other logical bitstreams in the file /// rather than carrying media of its own. skeleton,
/// The bytes a stream of this codec begins its first packet with. pub fn magic(codec: Codec) []const u8 { return switch (codec) { .vorbis => "\x01vorbis", .opus => "OpusHead", .flac => "\x7fFLAC", .speex => "Speex ", .theora => "\x80theora", .kate => "\x80kate\x00\x00\x00", .dirac => "BBCD\x00", .cmml => "CMML\x00\x00\x00\x00", .skeleton => "fishead\x00", }; }
/// What the mapping is for, in a few words, for a tool that has to print /// something. pub fn description(codec: Codec) []const u8 { return switch (codec) { .vorbis => "Vorbis audio", .opus => "Opus audio", .flac => "FLAC audio", .speex => "Speex audio", .theora => "Theora video", .kate => "Kate overlay text", .dirac => "Dirac video", .cmml => "CMML markup", .skeleton => "Skeleton metadata", }; }};
/// Which codec the first packet of a logical bitstream says it is, or null if/// nothing here recognises it.////// No mapping's magic is a prefix of another's, so the order of the comparisons/// does not matter and a match is unambiguous.pub fn identify(first_packet: []const u8) ?Codec { inline for (comptime std.enums.values(Codec)) |codec| { if (std.mem.startsWith(u8, first_packet, codec.magic())) return codec; } return null;}
const testing = std.testing;
test "the bos packets of the golden streams" { // The identification packets at the front of the three files under // src/tests/data, which three different encoders wrote. const flac_stream = @embedFile("tests/data/tone.oga"); const vorbis_stream = @embedFile("tests/data/tone.ogg"); try testing.expectEqual(Codec.flac, identify(flac_stream[28..]).?); try testing.expectEqual(Codec.vorbis, identify(vorbis_stream[28..]).?);}
test "every magic identifies its own codec and nothing else's" { for (std.enums.values(Codec)) |codec| { try testing.expectEqual(codec, identify(codec.magic()).?); // And with a packet's worth of content after it, which is what a real // one looks like. var padded: [64]u8 = @splat(0x5a); @memcpy(padded[0..codec.magic().len], codec.magic()); try testing.expectEqual(codec, identify(&padded).?); }}
test "nothing recognises what it should not" { try testing.expectEqual(@as(?Codec, null), identify("")); try testing.expectEqual(@as(?Codec, null), identify("OggS")); // A native FLAC stream, which is a file of frames rather than of pages. try testing.expectEqual(@as(?Codec, null), identify("fLaC")); // A prefix of a magic is not a match: the packet has to hold the whole of // it. try testing.expectEqual(@as(?Codec, null), identify("\x01vorbi"));}