[Fork] FFV1 Go Encoder / Decoder https://github.com/dwbuiten/go-ffv1
Find a file
Robin Eklind 0db0364132
ffv1: remove pred{16,32}.go and use type parameterized deriveBorders
Also, remove the now unused genhbd Perl script.
2024-09-20 17:40:15 +02:00
ffv1 ffv1: remove pred{16,32}.go and use type parameterized deriveBorders 2024-09-20 17:40:15 +02:00
COPYING Add license 2019-10-21 22:57:18 +01:00
go.mod ffv1: remove pred{16,32}.go and use type parameterized deriveBorders 2024-09-20 17:40:15 +02:00
README.md 16-bit RGB Support 2019-12-31 19:26:24 +00:00

FFV1 Decoder in Go

This repo contains an FFV1 Version 3 decoder implemented from draft-ietf-cellar-ffv1.

The reason for this project was to test how good the specification was, and indeed, during the development of this, several issues were unearthed. The secondary goal was to write a readable, commented, good reference; obviousness and spec-similarity over speed, struct members that match the spec, etc.

... Well, at least the former goal was met.

As such, there's a lot of non-idiomatic Go in here, and a lot of gross argument passing that should be refactored out into the contexts, stuff that should be interfaces instead of code generation etc... one day. Any day now. If we keep waiting, it may happen. Surely.

TODO

  • Fix Alpha Support

API

You can read the API godoc at godoc.org/github.com/dwbuiten/go-ffv1/ffv1.

Example of Decoding FFV1 in Matroska

package main

import (
	"encoding/binary"
	"fmt"
	"io"
	"log"
	"os"
	"strings"

	"github.com/dwbuiten/go-ffv1/ffv1"
	"github.com/dwbuiten/matroska"
)

func main() {
	f, err := os.Open("input.mkv")
	if err != nil {
		log.Fatalln(err)
	}
	defer f.Close()

	mat, err := matroska.NewDemuxer(f)
	if err != nil {
		log.Fatalln(err)
	}
	defer mat.Close()

	// Assuming track 0 is video because lazy.
	ti, err := mat.GetTrackInfo(0)
	if err != nil {
		log.Fatalln(err)
	}

	fmt.Printf("Encode is %dx%d\n", ti.Video.PixelWidth, ti.Video.PixelHeight)

	extradata := ti.CodecPrivate
	if strings.Contains(ti.CodecID, "VFW") {
		extradata = extradata[40:] // As per Matroska spec for VFW CodecPrivate
	}

	d, err := ffv1.NewDecoder(extradata, ti.Video.PixelWidth, ti.Video.PixelHeight)
	if err != nil {
		log.Fatalln(err)
	}

	file, err := os.Create("test.raw")
	if err != nil {
		log.Fatalln(err)
	}
	defer file.Close()

	for {
		packet, err := mat.ReadPacket()
		if err == io.EOF {
			break
		} else if err != nil {
			log.Fatalln(err)
		}

		fmt.Printf("extradata = %d packet = %d track = %d\n", len(extradata), len(packet.Data), packet.Track)
		if packet.Track != 0 {
			continue
		}

		frame, err := d.DecodeFrame(packet.Data)
		if err != nil {
			log.Fatalln(err)
		}
		fmt.Printf("Frame decoded at %dx%d\n", frame.Width, frame.Height)

		if frame.BitDepth == 8 {
			err = binary.Write(file, binary.LittleEndian, frame.Buf[0])
			if err != nil {
				log.Fatalln(err)
			}
			err = binary.Write(file, binary.LittleEndian, frame.Buf[1])
			if err != nil {
				log.Fatalln(err)
			}
			err = binary.Write(file, binary.LittleEndian, frame.Buf[2])
			if err != nil {
				log.Fatalln(err)
			}
		} else {
			err = binary.Write(file, binary.LittleEndian, frame.Buf16[0])
			if err != nil {
				log.Fatalln(err)
			}
			err = binary.Write(file, binary.LittleEndian, frame.Buf16[1])
			if err != nil {
				log.Fatalln(err)
			}
			err = binary.Write(file, binary.LittleEndian, frame.Buf16[2])
			if err != nil {
				log.Fatalln(err)
			}
		}
	}
	fmt.Println("Done.")
}