Pure Go Opus codec for Go applications.
gopus implements Opus (RFC 6716) and Ogg Opus (RFC 7845) in pure Go. It is built for real-time use: no cgo, no C toolchain, and caller-owned buffers on the main encode/decode hot path.
gopus is usable today, but it is still pre-v1.
- Recommended starting surface:
Encoder,Decoder,MultistreamEncoder,MultistreamDecoder,Reader,Writer, andcontainer/ogg. - The main API target is the zero-allocation caller-owned path:
func (d *Decoder) Decode(data []byte, pcm []float32) (int, error)func (e *Encoder) Encode(pcm []float32, data []byte) (int, error)
- The default build intentionally does not support every optional libopus build-time extension. It supports
SetDNNBlob(...); QEXT and DRED are tag-gated, and OSCE BWE remains quarantine-only. See Optional Extensions for the supported, tag-gated, and unsupported matrix. - Low-level packages such as
celt,silk,hybrid,rangecoding, andplcare implementation detail, not a stable public contract yet. - Validation and parity work is pinned against libopus 1.6.1.
- No tagged release has been published yet. If you adopt
gopusbeforev0.1.0, pin the exact version you validate.
- Released version: none yet.
docs/releases/v0.1.0.mdis prepared release notes, butv0.1.0is not a release until the tag and GitHub Release are both published. - Latest release evidence: after publication, inspect the
release-evidence-v0.1.0.mdsummary andrelease-evidence-v0.1.0.tar.gzbundle attached to the GitHub Release; before then, runmake release-evidencelocally. - CI guardrails: required checks and branch protection.
- Security policy: private reporting and supported versions.
- Release process: release checklist.
- Supply chain: Dependabot, Scorecard, action review, and release provenance plan.
- Downstream import check: external consumer smoke test, run by
make test-consumer-smoke.
go get github.com/thesyncim/gopusRequirements:
- Go 1.25+
- No cgo or external C toolchain for normal builds
Official RFC 8251 test-vector decode benchmarks use pinned libopus 1.6.1 as the baseline, with the same preloaded packets, reset cadence, and 48 kHz stereo output. Current checked-in results were measured on Apple M4 Max with Go 1.26.0 and Go PGO profile default.pgo; the full report uses median-of-3 runs at 200ms, 1s, and 5s minimum run times. The table below highlights the 5s row. Ratios above 1.00x mean gopus is slower than libopus.
| Path | gopus ns/sample | libopus ns/sample | gopus/libopus | gopus allocs/op |
|---|---|---|---|---|
| Float32 decode | 19.78 | 19.36 | 1.02x | 0 |
| Int16 decode | 20.07 | 19.52 | 1.03x | 0 |
See the full Markdown report in Official Test Vector Decode Performance. Reproduce it with BENCH_TESTVECTORS_COMPARE_CASES=aggregate BENCH_TESTVECTORS_COMPARE_PATHS=all BENCH_TESTVECTORS_COMPARE_TIMES=200,1000,5000 BENCH_TESTVECTORS_COMPARE_COUNT=3 make bench-testvectors-compare.
Use caller-owned buffers in real-time paths.
package main
import (
"log"
"github.com/thesyncim/gopus"
)
func main() {
const sampleRate = 48000
const channels = 2
enc, err := gopus.NewEncoder(gopus.EncoderConfig{
SampleRate: sampleRate,
Channels: channels,
Application: gopus.ApplicationAudio,
})
if err != nil {
log.Fatal(err)
}
decCfg := gopus.DefaultDecoderConfig(sampleRate, channels)
dec, err := gopus.NewDecoder(decCfg)
if err != nil {
log.Fatal(err)
}
pcmIn := make([]float32, 960*channels)
packetBuf := make([]byte, 4000)
pcmOut := make([]float32, decCfg.MaxPacketSamples*channels)
nPacket, err := enc.Encode(pcmIn, packetBuf)
if err != nil {
log.Fatal(err)
}
if nPacket == 0 {
return
}
nSamples, err := dec.Decode(packetBuf[:nPacket], pcmOut)
if err != nil {
log.Fatal(err)
}
decoded := pcmOut[:nSamples*channels]
_ = decoded
}Packet loss concealment uses dec.Decode(nil, pcmOut). If you prefer convenience over zero-allocation behavior, allocating helpers such as EncodeFloat32 and EncodeInt16Slice are also available.
| Area | Status | Notes |
|---|---|---|
| Mono/stereo encode/decode | Supported | Encoder / Decoder with caller-owned buffers |
| Multistream encode/decode | Supported | Default mappings for 1-8 channels; explicit mappings up to 255 channels |
| Ogg Opus container | Supported | container/ogg reader/writer |
| Streaming facade | Supported | Reader / Writer |
| Allocating convenience helpers | Supported | Simpler to use, but not zero-allocation |
| Low-level codec packages | Experimental | May change before v1 |
| Optional libopus build-time extensions | Mixed | Default builds support SetDNNBlob(...) only. QEXT requires -tags gopus_qext; DRED control/standalone surfaces require -tags gopus_dred; OSCE BWE remains unsupported outside quarantine builds. See Optional Extensions |
Environment and codec expectations:
| Topic | Current expectation |
|---|---|
| Go versions | Go 1.25+ is required; scheduled safety automation also exercises Go 1.26 |
| CI platforms | Linux, macOS, and Windows |
| Optimized architectures | amd64 and arm64 have tuned assembly kernels; other architectures use pure Go fallbacks |
| Sample rates | 8000, 12000, 16000, 24000, 48000 Hz |
| Frame durations | 2.5 ms to 120 ms, depending on mode |
| Channels | Encoder / Decoder: 1-2; default multistream: 1-8; explicit multistream: up to 255 channels |
Encoder, Decoder, MultistreamEncoder, and MultistreamDecoder are not safe for concurrent use. Use one instance per goroutine.
If you want to evaluate or contribute to the codec, these are the main entry points:
go test ./...make test-qualitymake bench-guardmake bench-testvectorsmake bench-testvectors-comparemake verify-production
make ensure-libopus bootstraps the pinned libopus 1.6.1 reference used by parity and quality checks. Some verification paths also expect ffmpeg and opusdec to be available.
- Go package docs
- Official test-vector performance
- Optional extension policy
- Examples guide
- Release notes
- Contributing guide
- Security policy
- Code of conduct
- Maintainer docs
- Assembly notes
BSD 3-Clause. See LICENSE.