main
gopeg
Go video decoding library with no runtime dependencies. FFmpeg is statically linked — import the package and get a single binary, no system installs required.
Installation
go get m8sh.su/x/gopeg
Requires CGO and a C compiler:
# arch/manjaro
sudo pacman -S gcc
# debian/ubuntu
sudo apt install gcc
Platforms
| Platform | Arch | Status |
|---|---|---|
linux/amd64 |
x86_64 | ✅ Supported |
linux/arm64 |
AArch64 | ✅ Supported |
windows/amd64 |
x86_64 | ✅ Supported |
darwin/amd64 |
x86_64 | ✅ Supported |
darwin/arm64 |
Apple Silicon | ✅ Supported |
web/wasm |
wasm32 | 🚧 Coming soon |
Supported formats
| Codecs | |
|---|---|
| Video | H.264, H.265/HEVC, AV1, VP9, VP8, MPEG-4 |
| Audio | AAC, MP3, Opus, Vorbis, FLAC, PCM |
| Containers | MP4, MKV, WebM, AVI, FLV, OGG, IVF, MPEGTS |
Features
- Decode video frames as
image.NRGBA— standard Go image type, no conversion needed - Decode audio frames as
[]float32interleaved PCM - Read from any
io.Reader— files, in-memory buffers, HTTP streams, pipes - Seeking supported when the reader implements
io.Seeker; works without it too - Zero runtime dependencies — all FFmpeg and dav1d libs statically linked
- Single
go buildfor all supported platforms
Quick start
package main
import (
"fmt"
"image/png"
"os"
"m8sh.su/x/gopeg"
)
func main() {
f, _ := os.Open("video.mp4")
defer f.Close()
dec, err := gopeg.NewDecoder(f)
if err != nil {
panic(err)
}
defer dec.Close()
meta := dec.Meta()
fmt.Printf("duration: %.2fs\n", meta.Duration.Seconds())
fmt.Printf("video: %dx%d %s @ %d/%d fps\n",
meta.Video.Width, meta.Video.Height,
meta.Video.CodecName,
meta.Video.FPSNum, meta.Video.FPSDen)
if meta.Audio != nil {
fmt.Printf("audio: %dHz %dch %s\n",
meta.Audio.SampleRate,
meta.Audio.Channels,
meta.Audio.CodecName)
}
for {
frame, err := dec.DecodeFrame()
if err != nil {
panic(err)
}
if frame == nil {
break // EOF
}
if frame.Video != nil {
f, _ := os.Create("frame.png")
png.Encode(f, frame.Video.Img)
f.Close()
}
if frame.Audio != nil {
fmt.Printf("audio pts=%v samples=%d\n",
frame.Audio.PTS,
len(frame.Audio.Samples)/frame.Audio.Channels)
}
}
}
API
// Open a decoder from any reader. If r also implements io.Seeker,
// FFmpeg will use it for random access where the format requires it.
func NewDecoder(r io.Reader) (*Decoder, error)
// Returns duration, video/audio stream info. Safe to call before decoding.
func (d *Decoder) Meta() Meta
// Decode the next video or audio frame. Returns nil, nil on EOF.
// Frame.Video and Frame.Audio are mutually exclusive per call.
func (d *Decoder) DecodeFrame() (*Frame, error)
// Release all FFmpeg resources. Safe to call multiple times.
func (d *Decoder) Close()
Types
type Meta struct {
Duration time.Duration
Video *VideoMeta // nil if no video stream
Audio *AudioMeta // nil if no audio stream
}
type VideoMeta struct {
Width, Height int
FPSNum, FPSDen int
CodecName string
}
type AudioMeta struct {
SampleRate int
Channels int
CodecName string
}
type Frame struct {
Video *VideoFrame
Audio *AudioFrame
}
type VideoFrame struct {
Img *image.NRGBA
PTS time.Duration
Duration time.Duration
}
type AudioFrame struct {
Samples []float32 // interleaved
Channels int
SampleRate int
PTS time.Duration
}
Building from source
The vendor/ directory contains prebuilt static libraries for all supported platforms. To rebuild from scratch, delete vendor/ and use the build script:
# arch/manjaro
sudo pacman -S gcc meson ninja nasm aarch64-linux-gnu-gcc mingw-w64-gcc
# debian/ubuntu
sudo apt install gcc meson ninja-build nasm gcc-aarch64-linux-gnu gcc-mingw-w64-x86-64
go run cmd/build/main.go
Script clones necessary repositories (default dav1d mirror and FFmpeg mirror), builds them into static libraries for linking with CGO with preset of required parameters and produces a vendor directory.
License
GPLv3 - this is free software. Contributions are welcome, feel free to open an issue.