// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie // 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")); }