Documentation
¶
Overview ¶
Package oggopus muxes Opus packets into an Ogg-Opus stream.
It is a pure-Go muxer and never decodes, re-encodes or inspects audio: packets handed to Writer.WritePacket are written through byte-identically, framed by the OpusHead and OpusTags pages the Opus-in-Ogg mapping requires. That is what makes it usable in a CGO_ENABLED=0 build with no libopus present.
Granule positions are always 48 kHz ¶
Ogg granule positions for Opus are defined in 48 kHz units regardless of the stream's real capture rate, so an 8 kHz recording advances its granule exactly as a 48 kHz one does for the same wall-clock duration. The capture rate is recorded in OpusHead for players to report, and is not used for timing.
WritePacket takes milliseconds, not sample counts ¶
The duration of a packet is not derivable from its bytes without decoding it, and the codec header's frame-size field cannot be trusted: on real networks it disagrees with the stream's actual packet_duration. WritePacket therefore takes the duration the protocol reported for that packet, in milliseconds, and scales it to granules itself. Feed it the protocol's packet_duration, never a value derived from the codec header.
Recovering a file whose writer died ¶
Probe reads a file's duration back without decoding it, from the granule position of the last intact page. It does not need an end-of-stream page, so it works on a recording that a crash interrupted — which is the case an unattended recorder actually has to survive. Writer.Flush bounds how much audio such a crash can lose, and how much of it Probe can see.
A disk that fills is handled the same way round: the first failed page write stops the Writer permanently rather than carrying on and leaving a gap in the page sequence, so the file ends cleanly at its last complete page and Probe still reads it. See Writer for the details.
Index ¶
Constants ¶
This section is empty.
Variables ¶
ErrNoOggPage reports a file with no recognisable Ogg page in its tail.
Functions ¶
func Probe ¶
Probe returns the audio duration of an Ogg-Opus file by reading the granule position of the last complete page in its tail. It works on files that were never closed (no EOS page), which is how a recording interrupted by a crash is recovered without decoding audio.
A candidate page is believed only once its CRC checks out, because the "OggS" capture pattern also occurs by chance inside audio data. One consequence is deliberate: in a file truncated mid-page the final partial page fails its CRC and is skipped, so Probe reports the last *intact* page's granule. A truncated page's granule claims packets that are not fully present, so this makes Probe a lower bound on the real duration rather than an occasionally optimistic guess.
func ReadPackets ¶
ReadPackets walks an Ogg-Opus stream and returns the audio packets in order — everything after the OpusHead and OpusTags packets — tolerating a truncated final page (a crash mid-write), which is dropped. It is what the archive's Replay reads a file back into frames with.
Types ¶
type Writer ¶
type Writer struct {
// contains filtered or unexported fields
}
Writer muxes Opus packets into an Ogg-Opus stream.
Packets are written through unmodified; the Writer never decodes or re-encodes audio. Not safe for concurrent use.
A write failure is permanent ¶
Once a page write fails the Writer is failed for good: WritePacket, Flush and Close all return that first error and emit nothing further. Resuming would be worse than stopping. A decoder reads a jump in page sequence numbers as a hole in the stream, and a Write that failed part-way through a page has already put bytes on disk that re-emitting the same packets would duplicate. So a recorder whose disk fills leaves a file that simply ends at its last complete page, which Probe can still read, instead of one that is subtly corrupt.
func NewWriter ¶
NewWriter starts an Ogg-Opus stream, writing the OpusHead and OpusTags pages immediately. sampleRate is the original capture rate recorded in OpusHead; channels must be 1 or 2 (channel mapping family 0).
func (*Writer) Close ¶
Close flushes buffered packets, marks end of stream and is idempotent. It does not close the underlying writer.
On a failed Writer it writes no end-of-stream page and returns the latched error instead of nil, every time it is called.
func (*Writer) Duration ¶
Duration reports the audio duration accumulated so far.
granule only ever advances by whole milliseconds (durationMs * granulesPerMs), so dividing first is exact. Doing it the other way round — `time.Duration(granule) * time.Second / 48000` — overflows int64 once granule passes ~9.2e9, i.e. after about 53 hours of accumulated audio, and silently returns a wrapped value.
func (*Writer) Flush ¶
Flush writes any buffered packets as a page. It bounds how much audio a crash can lose, and makes everything written so far visible to Probe. Close flushes implicitly.
Up to maxPageBody of audio can otherwise sit in memory — seconds of it at 8 kHz — so an unattended recorder that never calls this loses that much on every crash, and Probe cannot see it either.
Source Files
¶
- crc.go
- doc.go
- page.go
- probe.go
- reader.go
- writer.go