Documentation
¶
Overview ¶
Package ogg implements Ogg Opus container reading and writing.
It provides low-level page, packet, OpusHead, and OpusTags primitives for RFC 7845 (Ogg Encapsulation for the Opus Audio Codec) streams on top of the Ogg framing rules from RFC 3533. The package verifies Ogg CRC-32 checksums, preserves packet boundaries across lacing segments, and leaves codec encode/decode work to the top-level gopus API.
Reading ¶
Reader consumes an io.Reader, parses the mandatory OpusHead identification header and OpusTags comment header up front, then yields one Opus packet per ReadPacket call. Packets that span multiple pages via the lacing table are reassembled transparently. When the underlying reader is also an io.ReadSeeker, SeekGranule provides sample-accurate positioning.
Writing ¶
Writer emits a well-formed Ogg Opus stream: a beginning-of-stream page with OpusHead, a comment page with OpusTags, one audio page per packet, and a terminal end-of-stream page on Close. Files produced this way play back in standard tools (VLC, FFmpeg, browsers).
Buffer ownership and partial reads ¶
ParsePage, ParseOpusHead, and ParseOpusTags copy the bytes they retain, so the caller may reuse or modify the input slice afterwards. Reader.ReadPacket likewise returns a freshly allocated slice the caller owns. The lower-level Page.Packets and Page.Payload, by contrast, are sub-slices of the page and share its backing array.
Error handling ¶
Parsing functions reject malformed input with the sentinel errors declared in this package (for example ErrInvalidPage, ErrBadCRC, ErrInvalidHeader) rather than panicking, and Reader.ReadPacket reports end of stream with io.EOF. All parse paths are bounds-checked and treat any byte slice as untrusted input.
Example (RoundTripOgg) ¶
package main
import (
"bytes"
"fmt"
"log"
"math"
"github.com/thesyncim/gopus"
"github.com/thesyncim/gopus/container/ogg"
)
func main() {
// Complete Ogg Opus round-trip: encode to file, decode from file
var buf bytes.Buffer
// Encode to Ogg
w, err := ogg.NewWriter(&buf, uint32(48000), uint8(2))
if err != nil {
log.Fatal(err)
}
enc, err := gopus.NewEncoder(gopus.EncoderConfig{SampleRate: 48000, Channels: 2, Application: gopus.ApplicationAudio})
if err != nil {
log.Fatal(err)
}
// Encode 10 frames
for i := range 10 {
pcm := make([]float32, 960*2)
for j := range pcm {
pcm[j] = float32(math.Sin(float64(i*960*2+j) * 0.005))
}
packetBuf := make([]byte, 4000)
nPacket, err := enc.Encode(pcm, packetBuf)
if err != nil {
log.Fatal(err)
}
if err := w.WritePacket(packetBuf[:nPacket], 960); err != nil {
log.Fatal(err)
}
}
if err := w.Close(); err != nil {
log.Fatal(err)
}
// Decode from Ogg
r, err := ogg.NewReader(bytes.NewReader(buf.Bytes()))
if err != nil {
log.Fatal(err)
}
cfg := gopus.DefaultDecoderConfig(48000, 2)
dec, err := gopus.NewDecoder(cfg)
if err != nil {
log.Fatal(err)
}
pcmOut := make([]float32, cfg.MaxPacketSamples*cfg.Channels)
totalSamples := 0
for {
packet, _, err := r.ReadPacket()
if err != nil {
break
}
n, _ := dec.Decode(packet, pcmOut)
totalSamples += n
}
fmt.Printf("Round-trip: wrote 10 frames, decoded %d samples\n", totalSamples)
}
Output: Round-trip: wrote 10 frames, decoded 9600 samples
Example (WriteOggFile) ¶
package main
import (
"bytes"
"fmt"
"log"
"math"
"github.com/thesyncim/gopus"
"github.com/thesyncim/gopus/container/ogg"
)
func main() {
// Demonstrate creating an Ogg Opus file
var buf bytes.Buffer
// Create writer
w, err := ogg.NewWriter(&buf, uint32(48000), uint8(2))
if err != nil {
log.Fatal(err)
}
// Create encoder
enc, err := gopus.NewEncoder(gopus.EncoderConfig{SampleRate: 48000, Channels: 2, Application: gopus.ApplicationAudio})
if err != nil {
log.Fatal(err)
}
if err := enc.SetBitrate(128000); err != nil {
log.Fatal(err)
}
// Write 1 second of audio (50 frames of 20ms each)
for i := range 50 {
pcm := make([]float32, 960*2)
// Generate stereo sine wave
for j := range 960 {
t := float64(i*960+j) / 48000.0
pcm[j*2] = float32(math.Sin(2 * math.Pi * 440 * t)) // Left: 440 Hz
pcm[j*2+1] = float32(math.Sin(2 * math.Pi * 554 * t)) // Right: 554 Hz
}
packetBuf := make([]byte, 4000)
nPacket, err := enc.Encode(pcm, packetBuf)
if err != nil {
log.Fatal(err)
}
if err := w.WritePacket(packetBuf[:nPacket], 960); err != nil {
log.Fatal(err)
}
}
if err := w.Close(); err != nil {
log.Fatal(err)
}
fmt.Printf("Created 1-second Ogg Opus stream with %d frames\n", 50)
}
Output: Created 1-second Ogg Opus stream with 50 frames
Index ¶
- Constants
- Variables
- func BuildSegmentTable(packetLen int) []byte
- func ParseSegmentTable(segments []byte) []int
- type OpusHead
- func DefaultOpusHead(sampleRate uint32, channels uint8) *OpusHead
- func DefaultOpusHeadMultistream(sampleRate uint32, channels uint8, streams, coupled uint8, mapping []byte) *OpusHead
- func DefaultOpusHeadMultistreamWithFamily(sampleRate uint32, channels uint8, mappingFamily, streams, coupled uint8, ...) *OpusHead
- func ParseOpusHead(data []byte) (*OpusHead, error)
- type OpusTags
- type Page
- type Reader
- func (or *Reader) Channels() uint8
- func (or *Reader) EOF() bool
- func (or *Reader) GranulePos() uint64
- func (or *Reader) PreSkip() uint16
- func (or *Reader) ReadPacket() (packet []byte, granulePos uint64, err error)
- func (or *Reader) ReadPacketInto(dst []byte) (n int, granulePos uint64, err error)
- func (or *Reader) SampleRate() uint32
- func (or *Reader) SeekGranule(target uint64) error
- func (or *Reader) Serial() uint32
- type Writer
- type WriterConfig
Examples ¶
Constants ¶
const ( // MappingFamilyRTP is for mono/stereo with implicit channel order (RTP). MappingFamilyRTP = 0 // MappingFamilyVorbis is for 1-8 channels with Vorbis channel order. MappingFamilyVorbis = 1 // MappingFamilyAmbisonics is for ambisonics ACN/SN3D channel mapping. MappingFamilyAmbisonics = 2 // MappingFamilyProjection is for projection-based ambisonics mapping. MappingFamilyProjection = 3 // MappingFamilyDiscrete is for N channels with no defined relationship. MappingFamilyDiscrete = 255 )
MappingFamily values per RFC 7845.
const ( // PageFlagContinuation indicates this page contains data from a packet // that began on a previous page. PageFlagContinuation = 0x01 // PageFlagBOS (Beginning of Stream) indicates this is the first page // of a logical bitstream. PageFlagBOS = 0x02 // PageFlagEOS (End of Stream) indicates this is the last page of a // logical bitstream. PageFlagEOS = 0x04 )
Page header flag constants.
const ( // DefaultPreSkip is the standard Opus encoder lookahead at 48kHz. // This is the number of samples to discard at the beginning of decode. DefaultPreSkip = 312 )
Opus header constants per RFC 7845.
Variables ¶
var ( // ErrNilReader indicates a nil io.Reader was supplied to NewReader. ErrNilReader = errors.New("ogg: nil reader") // ErrNilWriter indicates a nil io.Writer was supplied to NewWriter. ErrNilWriter = errors.New("ogg: nil writer") // ErrInvalidPage indicates the page structure is malformed. // This includes missing "OggS" magic, invalid version, or truncated data. ErrInvalidPage = errors.New("ogg: invalid page structure") // ErrInvalidHeader indicates an Opus header (OpusHead or OpusTags) is malformed. // This includes wrong magic signature, unsupported version, or truncated data. ErrInvalidHeader = errors.New("ogg: invalid Opus header") // ErrBadCRC indicates the page CRC checksum does not match the computed value. // This typically indicates data corruption. ErrBadCRC = errors.New("ogg: CRC mismatch") // ErrUnexpectedEOS indicates the stream ended unexpectedly. // This occurs when a page is truncated or data ends mid-packet. ErrUnexpectedEOS = errors.New("ogg: unexpected end of stream") // ErrPacketTooLarge indicates the packet does not fit in the provided buffer. ErrPacketTooLarge = errors.New("ogg: packet too large for buffer") // ErrNotSeekable indicates the reader does not support seeking. ErrNotSeekable = errors.New("ogg: reader is not seekable") )
Package-level errors for Ogg parsing and encoding.
Functions ¶
func BuildSegmentTable ¶
BuildSegmentTable creates a segment table for a packet of the given length. Packets larger than 255 bytes span multiple segments (each 255 bytes except the final segment which contains the remainder).
func ParseSegmentTable ¶
ParseSegmentTable reconstructs packet lengths from an Ogg lacing table (RFC 3533 §6). Each lacing value of 255 means the packet continues into the next segment; the first value below 255 terminates a packet whose length is the running sum of its segments. The returned slice holds one entry per completed packet.
A table that ends on a 255 value describes a packet continued on the next page; that incomplete trailing packet is not included in the result. An empty table returns nil.
Types ¶
type OpusHead ¶
type OpusHead struct {
// Version is the format version (must be 1).
Version uint8
// Channels is the output channel count (1-255).
Channels uint8
// PreSkip is the number of samples to discard at the start (at 48kHz).
// Typically 312 for standard Opus encoder lookahead.
PreSkip uint16
// SampleRate is the original input sample rate (informational only).
// Opus always operates at 48kHz internally.
SampleRate uint32
// OutputGain is the gain to apply in Q7.8 dB format.
// Positive values amplify, negative values attenuate.
OutputGain int16
// MappingFamily specifies the channel mapping:
// 0: Mono/stereo (implicit order)
// 1: Surround 1-8 channels (Vorbis order)
// 2: Ambisonics ACN/SN3D
// 3: Projection-based ambisonics
// 255: Discrete (no defined relationship)
MappingFamily uint8
// StreamCount is the number of Opus streams in the packet.
StreamCount uint8
// CoupledCount is the number of coupled (stereo) streams.
CoupledCount uint8
// ChannelMapping maps output channels to decoder channels.
// For mapping family 0, this is implicit (not stored).
// For family 1/2/255, length equals Channels.
ChannelMapping []byte
// DemixingMatrix stores RFC 8486 family-3 demixing metadata.
// Size is 2*Channels*(StreamCount+CoupledCount) bytes in S16LE format.
DemixingMatrix []byte
}
OpusHead is the identification header for Opus in Ogg. This appears in the first Ogg page (BOS) and describes the stream format.
func DefaultOpusHead ¶
DefaultOpusHead returns an OpusHead with standard settings. sampleRate is the original input sample rate (informational). channels is 1 for mono, 2 for stereo.
func DefaultOpusHeadMultistream ¶
func DefaultOpusHeadMultistream(sampleRate uint32, channels uint8, streams, coupled uint8, mapping []byte) *OpusHead
DefaultOpusHeadMultistream returns an OpusHead for multistream with mapping family 1. This is for surround configurations (1-8 channels).
func DefaultOpusHeadMultistreamWithFamily ¶
func DefaultOpusHeadMultistreamWithFamily(sampleRate uint32, channels uint8, mappingFamily, streams, coupled uint8, mapping []byte) *OpusHead
DefaultOpusHeadMultistreamWithFamily returns an OpusHead for multistream mappings.
func ParseOpusHead ¶
ParseOpusHead parses an OpusHead identification header (RFC 7845 §5.1) from data. The returned header owns copies of its channel-mapping table and demixing matrix, so data may be reused afterwards.
It returns ErrInvalidHeader when data is too short, lacks the "OpusHead" magic, declares a version other than 1, has a zero channel count, or carries a mapping family whose required fields (stream/coupled counts, channel mapping, or RFC 8486 demixing matrix) are missing, truncated, or internally inconsistent (for example coupled streams exceeding total streams, a mapping index outside the decoded streams, or more than two channels for mapping family 0).
type OpusTags ¶
type OpusTags struct {
// Vendor is the encoder name (e.g., "gopus").
Vendor string
// Comments is a map of user comments (key=value pairs).
// Common keys: TITLE, ARTIST, ALBUM, DATE, TRACKNUMBER, etc.
Comments map[string]string
}
OpusTags is the comment header for Opus in Ogg. This appears in the second Ogg page and contains metadata.
func DefaultOpusTags ¶
func DefaultOpusTags() *OpusTags
DefaultOpusTags returns an OpusTags with gopus vendor string.
func ParseOpusTags ¶
ParseOpusTags parses an OpusTags comment header (RFC 7845 §5.2, Vorbis comment layout) from data. The vendor string and comments are copied into the returned struct, so data may be reused afterwards.
Comments are returned as a key=value map split on the first '=' in each entry; an entry with no '=' is skipped. The length fields are unsigned 32-bit and bounds-checked against the remaining input, so an over-long vendor or comment length yields ErrInvalidHeader rather than reading past the buffer.
It returns ErrInvalidHeader when data is too short, lacks the "OpusTags" magic, or declares a vendor, comment count, or comment length that extends past the end of data. Trailing bytes after the declared comments (RFC 7845 framing padding) are ignored.
type Page ¶
type Page struct {
// Version is the stream structure version (always 0).
Version byte
// HeaderType contains page flags (continuation, BOS, EOS).
HeaderType byte
// GranulePos is the granule position, representing the number of
// samples decoded (including this page) at the page's end.
// For Opus, this is the sample count at 48kHz.
GranulePos uint64
// SerialNumber identifies the logical bitstream.
SerialNumber uint32
// PageSequence is the page sequence number within the bitstream.
PageSequence uint32
// Segments contains the segment table entries.
// Each entry is the size of a segment (0-255).
Segments []byte
// Payload contains the concatenated packet data.
Payload []byte
}
Page represents a single Ogg page.
func ParsePage ¶
ParsePage parses a single Ogg page (RFC 3533 §6) from the front of data. It returns the parsed page, the number of bytes consumed (the full page length, header plus segment table plus payload), and any error.
data may contain more than one page or trailing bytes; only the first page is consumed and callers should advance by the returned count. The returned Page owns copies of its segment table and payload, so the input slice may be reused or overwritten afterwards.
Errors:
- ErrInvalidPage if data is shorter than a page header, the "OggS" capture pattern is missing, or the declared segment table or payload extends past the end of data (a truncated or incomplete page).
- ErrBadCRC if the page CRC-32 does not match the bytes on the wire, indicating corruption.
A truncated page yields ErrInvalidPage with a zero consumed count; the caller can distinguish "need more data" from genuine corruption by buffering more input and retrying.
func (*Page) Encode ¶
Encode serializes the page to bytes with proper CRC. The output format is:
- 27-byte header
- Segment table
- Payload
The CRC is computed over the entire page (with CRC field zeroed).
func (*Page) IsContinuation ¶
IsContinuation returns true if this page continues a packet from a previous page.
func (*Page) PacketLengths ¶
PacketLengths extracts packet lengths from the segment table. This is equivalent to ParseSegmentTable(p.Segments).
func (*Page) Packets ¶
Packets splits the page payload into individual packet byte slices using the lacing lengths from PacketLengths.
The returned slices alias p.Payload; they remain valid only as long as the page is not modified. A packet whose final lacing value is 255 continues onto the next page and is not returned here. If the payload is shorter than the segment table claims, the final packet is truncated to the bytes actually present and parsing stops, so a malformed page never produces a slice outside p.Payload.
type Reader ¶
type Reader struct {
Header *OpusHead // Parsed ID header (set after NewReader)
Tags *OpusTags // Parsed comment header (set after NewReader)
// contains filtered or unexported fields
}
Reader reads Opus packets from an Ogg container. It parses the Ogg stream and extracts Opus packets for decoding.
func NewReader ¶
NewReader creates a Reader over r and parses the Ogg Opus headers up front: it reads the beginning-of-stream page carrying OpusHead and the following page(s) carrying OpusTags, exposing them via the Header and Tags fields. The comment header may span multiple pages and is reassembled before returning.
It returns ErrNilReader if r is nil, ErrInvalidPage or ErrBadCRC if the leading pages are malformed or corrupt, and ErrInvalidHeader if the OpusHead or OpusTags packets are not well-formed. If r also implements io.ReadSeeker, the Reader records the offset of the first audio page so SeekGranule can be used later.
Example ¶
package main
import (
"bytes"
"fmt"
"log"
"github.com/thesyncim/gopus"
"github.com/thesyncim/gopus/container/ogg"
)
func main() {
// First create some Ogg Opus data
var buf bytes.Buffer
w, err := ogg.NewWriter(&buf, uint32(48000), uint8(2))
if err != nil {
log.Fatal(err)
}
enc, err := gopus.NewEncoder(gopus.EncoderConfig{SampleRate: 48000, Channels: 2, Application: gopus.ApplicationAudio})
if err != nil {
log.Fatal(err)
}
pcm := make([]float32, 960*2)
packetBuf := make([]byte, 4000)
nPacket, err := enc.Encode(pcm, packetBuf)
if err != nil {
log.Fatal(err)
}
if err := w.WritePacket(packetBuf[:nPacket], 960); err != nil {
log.Fatal(err)
}
if err := w.Close(); err != nil {
log.Fatal(err)
}
// Now read it back
r, err := ogg.NewReader(bytes.NewReader(buf.Bytes()))
if err != nil {
log.Fatal(err)
}
fmt.Printf("Ogg Opus: %d channels, %d Hz\n", r.Channels(), r.SampleRate())
}
Output: Ogg Opus: 2 channels, 48000 Hz
func (*Reader) GranulePos ¶
GranulePos returns the granule position of the last read packet.
func (*Reader) PreSkip ¶
PreSkip returns the pre-skip value from the OpusHead header. This is the number of samples to discard at the start of decode.
func (*Reader) ReadPacket ¶
ReadPacket reads the next Opus packet, reassembling packets that span page boundaries via the lacing table. It returns the packet bytes, the granule position attributed to that packet, and any error.
The returned slice is freshly allocated and owned by the caller; it is not overwritten by later reads. Packets from logical bitstreams whose serial number differs from the first stream are skipped. ReadPacket returns io.EOF once the end-of-stream page has been consumed; a malformed or truncated page surfaces the underlying parse error. To avoid the per-packet allocation, use ReadPacketInto.
Example ¶
package main
import (
"bytes"
"fmt"
"log"
"math"
"github.com/thesyncim/gopus"
"github.com/thesyncim/gopus/container/ogg"
)
func main() {
// Create Ogg data with multiple packets
var buf bytes.Buffer
w, err := ogg.NewWriter(&buf, uint32(48000), uint8(1))
if err != nil {
log.Fatal(err)
}
enc, err := gopus.NewEncoder(gopus.EncoderConfig{SampleRate: 48000, Channels: 1, Application: gopus.ApplicationAudio})
if err != nil {
log.Fatal(err)
}
for i := range 5 {
pcm := make([]float32, 960)
for j := range pcm {
pcm[j] = float32(math.Sin(float64(i*960+j) * 0.01))
}
packetBuf := make([]byte, 4000)
nPacket, err := enc.Encode(pcm, packetBuf)
if err != nil {
log.Fatal(err)
}
if err := w.WritePacket(packetBuf[:nPacket], 960); err != nil {
log.Fatal(err)
}
}
if err := w.Close(); err != nil {
log.Fatal(err)
}
// Read packets back
r, err := ogg.NewReader(bytes.NewReader(buf.Bytes()))
if err != nil {
log.Fatal(err)
}
cfg := gopus.DefaultDecoderConfig(48000, 1)
dec, err := gopus.NewDecoder(cfg)
if err != nil {
log.Fatal(err)
}
pcmOut := make([]float32, cfg.MaxPacketSamples*cfg.Channels)
packetCount := 0
for {
packet, _, err := r.ReadPacket()
if err != nil {
break
}
_, _ = dec.Decode(packet, pcmOut)
packetCount++
}
fmt.Printf("Decoded %d packets\n", packetCount)
}
Output: Decoded 5 packets
func (*Reader) ReadPacketInto ¶
ReadPacketInto reads the next Opus packet into dst, allocating nothing when the packet fits. It returns the number of bytes written and the packet's granule position.
If the packet is larger than dst it returns ErrPacketTooLarge with n == 0; the packet has already been consumed in that case, so dst should be sized to the largest expected packet. Serial-mismatched bitstreams are skipped and io.EOF is returned at end of stream.
func (*Reader) SampleRate ¶
SampleRate returns the original sample rate from the OpusHead header. Note: Opus always operates at 48kHz internally; this is informational only.
func (*Reader) SeekGranule ¶
SeekGranule rewinds a seekable stream to the first packet at or after target.
This is a correctness-first linear scan from the first audio page, which keeps the API small and deterministic for in-memory or file-backed readers. Later optimizations can replace the linear walk with a true bisection search.
type Writer ¶
type Writer struct {
// contains filtered or unexported fields
}
Writer writes Opus packets to an Ogg container. Files created by Writer are playable by standard players (VLC, FFmpeg, browsers).
func NewWriter ¶
NewWriter creates a new OggWriter with default configuration. sampleRate is the original input sample rate (informational only). channels is 1 for mono or 2 for stereo. Returns an error if channels is 0 or greater than 2 (use NewWriterWithConfig for multistream).
Example ¶
package main
import (
"bytes"
"fmt"
"log"
"github.com/thesyncim/gopus/container/ogg"
)
func main() {
// Create a buffer to write Ogg Opus data
var buf bytes.Buffer
// Create writer for 48kHz stereo
w, err := ogg.NewWriter(&buf, uint32(48000), uint8(2))
if err != nil {
log.Fatal(err)
}
defer w.Close()
fmt.Println("Ogg Opus writer created")
}
Output: Ogg Opus writer created
func NewWriterWithConfig ¶
func NewWriterWithConfig(w io.Writer, config WriterConfig) (*Writer, error)
NewWriterWithConfig creates a new OggWriter with explicit configuration. This supports all multistream mapping families (1/2/3/255).
func (*Writer) Close ¶
Close writes the EOS page and marks the stream as closed. The writer should not be used after Close.
Example ¶
package main
import (
"bytes"
"fmt"
"log"
"github.com/thesyncim/gopus"
"github.com/thesyncim/gopus/container/ogg"
)
func main() {
var buf bytes.Buffer
w, err := ogg.NewWriter(&buf, uint32(48000), uint8(1))
if err != nil {
log.Fatal(err)
}
// Write some audio
enc, err := gopus.NewEncoder(gopus.EncoderConfig{SampleRate: 48000, Channels: 1, Application: gopus.ApplicationAudio})
if err != nil {
log.Fatal(err)
}
pcm := make([]float32, 960)
packetBuf := make([]byte, 4000)
nPacket, err := enc.Encode(pcm, packetBuf)
if err != nil {
log.Fatal(err)
}
if err := w.WritePacket(packetBuf[:nPacket], 960); err != nil {
log.Fatal(err)
}
// Close writes the EOS page
err = w.Close()
if err != nil {
log.Fatal(err)
}
fmt.Println("Stream closed with EOS page")
}
Output: Stream closed with EOS page
func (*Writer) GranulePos ¶
GranulePos returns the current granule position (samples at 48kHz).
func (*Writer) WritePacket ¶
WritePacket writes an Opus packet to the stream. samples is the number of PCM samples at 48kHz represented by this packet (typically 960 for 20ms frames). Updates the granule position accordingly.
Example ¶
package main
import (
"bytes"
"fmt"
"log"
"github.com/thesyncim/gopus"
"github.com/thesyncim/gopus/container/ogg"
)
func main() {
var buf bytes.Buffer
w, err := ogg.NewWriter(&buf, uint32(48000), uint8(2))
if err != nil {
log.Fatal(err)
}
// Create an encoder
enc, err := gopus.NewEncoder(gopus.EncoderConfig{SampleRate: 48000, Channels: 2, Application: gopus.ApplicationAudio})
if err != nil {
log.Fatal(err)
}
// Encode and write a frame
pcm := make([]float32, 960*2) // 20ms stereo
packetBuf := make([]byte, 4000)
nPacket, err := enc.Encode(pcm, packetBuf)
if err != nil {
log.Fatal(err)
}
// Write packet with sample count
err = w.WritePacket(packetBuf[:nPacket], 960)
if err != nil {
log.Fatal(err)
}
if err := w.Close(); err != nil {
log.Fatal(err)
}
r, err := ogg.NewReader(bytes.NewReader(buf.Bytes()))
if err != nil {
log.Fatal(err)
}
packet, granule, err := r.ReadPacket()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Stored packet at granule %d: %t\n", granule, len(packet) > 0)
}
Output: Stored packet at granule 960: true
type WriterConfig ¶
type WriterConfig struct {
// SampleRate is the original input sample rate (informational only).
// Opus always operates at 48kHz internally.
SampleRate uint32
// Channels is the output channel count (1-255).
Channels uint8
// PreSkip is the number of samples to discard at the start (at 48kHz).
// Default is 312 for standard Opus encoder lookahead.
PreSkip uint16
// OutputGain is the gain to apply in Q7.8 dB format.
// Positive values amplify, negative values attenuate.
OutputGain int16
// MappingFamily specifies the channel mapping:
// 0: Mono/stereo (implicit order) - for 1-2 channels
// 1: Surround 1-8 channels (Vorbis order)
// 2: Ambisonics ACN/SN3D
// 3: Projection-based ambisonics
// 255: Discrete (no defined relationship)
MappingFamily uint8
// StreamCount is the number of Opus streams in the packet (for non-RTP mappings).
StreamCount uint8
// CoupledCount is the number of coupled (stereo) streams (for non-RTP mappings).
CoupledCount uint8
// ChannelMapping maps output channels to decoder channels (for family 1/2/255).
ChannelMapping []byte
// DemixingMatrix stores RFC 8486 family-3 demixing metadata.
// If empty for family 3, libopus default projection matrices are emitted
// when (channels,streams,coupled) matches a valid projection layout;
// otherwise an identity matrix is emitted.
DemixingMatrix []byte
}
WriterConfig configures the OggWriter.