gopeg banner # 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: ```sh # 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 `[]float32` interleaved 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 build` for all supported platforms ## Quick start ```go 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 ```go // 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 ```go 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: ```sh # 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 ``` ```sh 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.