package vp8
import (
"errors"
"image"
"io"
)
type limitReader struct {
r io .Reader
n int
}
func (r *limitReader ) ReadFull (p []byte ) error {
if len (p ) > r .n {
return io .ErrUnexpectedEOF
}
n , err := io .ReadFull (r .r , p )
r .n -= n
return err
}
type FrameHeader struct {
KeyFrame bool
VersionNumber uint8
ShowFrame bool
FirstPartitionLen uint32
Width int
Height int
XScale uint8
YScale uint8
}
const (
nSegment = 4
nSegmentProb = 3
)
type segmentHeader struct {
useSegment bool
updateMap bool
relativeDelta bool
quantizer [nSegment ]int8
filterStrength [nSegment ]int8
prob [nSegmentProb ]uint8
}
const (
nRefLFDelta = 4
nModeLFDelta = 4
)
type filterHeader struct {
simple bool
level int8
sharpness uint8
useLFDelta bool
refLFDelta [nRefLFDelta ]int8
modeLFDelta [nModeLFDelta ]int8
perSegmentLevel [nSegment ]int8
}
type mb struct {
pred [4 ]uint8
nzMask uint8
nzY16 uint8
}
type Decoder struct {
r limitReader
scratch [8 ]byte
img *image .YCbCr
mbw, mbh int
frameHeader FrameHeader
segmentHeader segmentHeader
filterHeader filterHeader
fp partition
op [8 ]partition
nOP int
quant [nSegment ]quant
tokenProb [nPlane ][nBand ][nContext ][nProb ]uint8
useSkipProb bool
skipProb uint8
filterParams [nSegment ][2 ]filterParam
perMBFilterParams []filterParam
segment int
leftMB mb
upMB []mb
nzDCMask, nzACMask uint32
usePredY16 bool
predY16 uint8
predC8 uint8
predY4 [4 ][4 ]uint8
coeff [1 *16 *16 + 2 *8 *8 + 1 *4 *4 ]int16
ybr [1 + 16 + 1 + 8 ][32 ]uint8
}
func NewDecoder () *Decoder {
return &Decoder {}
}
func (d *Decoder ) Init (r io .Reader , n int ) {
d .r = limitReader {r , n }
}
func (d *Decoder ) DecodeFrameHeader () (fh FrameHeader , err error ) {
b := d .scratch [:3 ]
if err = d .r .ReadFull (b ); err != nil {
return
}
d .frameHeader .KeyFrame = (b [0 ] & 1 ) == 0
d .frameHeader .VersionNumber = (b [0 ] >> 1 ) & 7
d .frameHeader .ShowFrame = (b [0 ]>>4 )&1 == 1
d .frameHeader .FirstPartitionLen = uint32 (b [0 ])>>5 | uint32 (b [1 ])<<3 | uint32 (b [2 ])<<11
if !d .frameHeader .KeyFrame {
return d .frameHeader , nil
}
b = d .scratch [:7 ]
if err = d .r .ReadFull (b ); err != nil {
return
}
if b [0 ] != 0x9d || b [1 ] != 0x01 || b [2 ] != 0x2a {
err = errors .New ("vp8: invalid format" )
return
}
d .frameHeader .Width = int (b [4 ]&0x3f )<<8 | int (b [3 ])
d .frameHeader .Height = int (b [6 ]&0x3f )<<8 | int (b [5 ])
d .frameHeader .XScale = b [4 ] >> 6
d .frameHeader .YScale = b [6 ] >> 6
d .mbw = (d .frameHeader .Width + 0x0f ) >> 4
d .mbh = (d .frameHeader .Height + 0x0f ) >> 4
d .segmentHeader = segmentHeader {
prob : [3 ]uint8 {0xff , 0xff , 0xff },
}
d .tokenProb = defaultTokenProb
d .segment = 0
return d .frameHeader , nil
}
func (d *Decoder ) ensureImg () {
if d .img != nil {
p0 , p1 := d .img .Rect .Min , d .img .Rect .Max
if p0 .X == 0 && p0 .Y == 0 && p1 .X >= 16 *d .mbw && p1 .Y >= 16 *d .mbh {
return
}
}
m := image .NewYCbCr (image .Rect (0 , 0 , 16 *d .mbw , 16 *d .mbh ), image .YCbCrSubsampleRatio420 )
d .img = m .SubImage (image .Rect (0 , 0 , d .frameHeader .Width , d .frameHeader .Height )).(*image .YCbCr )
d .perMBFilterParams = make ([]filterParam , d .mbw *d .mbh )
d .upMB = make ([]mb , d .mbw )
}
func (d *Decoder ) parseSegmentHeader () {
d .segmentHeader .useSegment = d .fp .readBit (uniformProb )
if !d .segmentHeader .useSegment {
d .segmentHeader .updateMap = false
return
}
d .segmentHeader .updateMap = d .fp .readBit (uniformProb )
if d .fp .readBit (uniformProb ) {
d .segmentHeader .relativeDelta = !d .fp .readBit (uniformProb )
for i := range d .segmentHeader .quantizer {
d .segmentHeader .quantizer [i ] = int8 (d .fp .readOptionalInt (uniformProb , 7 ))
}
for i := range d .segmentHeader .filterStrength {
d .segmentHeader .filterStrength [i ] = int8 (d .fp .readOptionalInt (uniformProb , 6 ))
}
}
if !d .segmentHeader .updateMap {
return
}
for i := range d .segmentHeader .prob {
if d .fp .readBit (uniformProb ) {
d .segmentHeader .prob [i ] = uint8 (d .fp .readUint (uniformProb , 8 ))
} else {
d .segmentHeader .prob [i ] = 0xff
}
}
}
func (d *Decoder ) parseFilterHeader () {
d .filterHeader .simple = d .fp .readBit (uniformProb )
d .filterHeader .level = int8 (d .fp .readUint (uniformProb , 6 ))
d .filterHeader .sharpness = uint8 (d .fp .readUint (uniformProb , 3 ))
d .filterHeader .useLFDelta = d .fp .readBit (uniformProb )
if d .filterHeader .useLFDelta && d .fp .readBit (uniformProb ) {
for i := range d .filterHeader .refLFDelta {
d .filterHeader .refLFDelta [i ] = int8 (d .fp .readOptionalInt (uniformProb , 6 ))
}
for i := range d .filterHeader .modeLFDelta {
d .filterHeader .modeLFDelta [i ] = int8 (d .fp .readOptionalInt (uniformProb , 6 ))
}
}
if d .filterHeader .level == 0 {
return
}
if d .segmentHeader .useSegment {
for i := range d .filterHeader .perSegmentLevel {
strength := d .segmentHeader .filterStrength [i ]
if d .segmentHeader .relativeDelta {
strength += d .filterHeader .level
}
d .filterHeader .perSegmentLevel [i ] = strength
}
} else {
d .filterHeader .perSegmentLevel [0 ] = d .filterHeader .level
}
d .computeFilterParams ()
}
func (d *Decoder ) parseOtherPartitions () error {
const maxNOP = 1 << 3
var partLens [maxNOP ]int
d .nOP = 1 << d .fp .readUint (uniformProb , 2 )
n := 3 * (d .nOP - 1 )
partLens [d .nOP -1 ] = d .r .n - n
if partLens [d .nOP -1 ] < 0 {
return io .ErrUnexpectedEOF
}
if n > 0 {
buf := make ([]byte , n )
if err := d .r .ReadFull (buf ); err != nil {
return err
}
for i := 0 ; i < d .nOP -1 ; i ++ {
pl := int (buf [3 *i +0 ]) | int (buf [3 *i +1 ])<<8 | int (buf [3 *i +2 ])<<16
if pl > partLens [d .nOP -1 ] {
return io .ErrUnexpectedEOF
}
partLens [i ] = pl
partLens [d .nOP -1 ] -= pl
}
}
if 1 <<24 <= partLens [d .nOP -1 ] {
return errors .New ("vp8: too much data to decode" )
}
buf := make ([]byte , d .r .n )
if err := d .r .ReadFull (buf ); err != nil {
return err
}
for i , pl := range partLens {
if i == d .nOP {
break
}
d .op [i ].init (buf [:pl ])
buf = buf [pl :]
}
return nil
}
func (d *Decoder ) parseOtherHeaders () error {
firstPartition := make ([]byte , d .frameHeader .FirstPartitionLen )
if err := d .r .ReadFull (firstPartition ); err != nil {
return err
}
d .fp .init (firstPartition )
if d .frameHeader .KeyFrame {
d .fp .readBit (uniformProb )
d .fp .readBit (uniformProb )
}
d .parseSegmentHeader ()
d .parseFilterHeader ()
if err := d .parseOtherPartitions (); err != nil {
return err
}
d .parseQuant ()
if !d .frameHeader .KeyFrame {
return errors .New ("vp8: Golden / AltRef frames are not implemented" )
}
d .fp .readBit (uniformProb )
d .parseTokenProb ()
d .useSkipProb = d .fp .readBit (uniformProb )
if d .useSkipProb {
d .skipProb = uint8 (d .fp .readUint (uniformProb , 8 ))
}
if d .fp .unexpectedEOF {
return io .ErrUnexpectedEOF
}
return nil
}
func (d *Decoder ) DecodeFrame () (*image .YCbCr , error ) {
d .ensureImg ()
if err := d .parseOtherHeaders (); err != nil {
return nil , err
}
for mbx := 0 ; mbx < d .mbw ; mbx ++ {
d .upMB [mbx ] = mb {}
}
for mby := 0 ; mby < d .mbh ; mby ++ {
d .leftMB = mb {}
for mbx := 0 ; mbx < d .mbw ; mbx ++ {
skip := d .reconstruct (mbx , mby )
fs := d .filterParams [d .segment ][btou (!d .usePredY16 )]
fs .inner = fs .inner || !skip
d .perMBFilterParams [d .mbw *mby +mbx ] = fs
}
}
if d .fp .unexpectedEOF {
return nil , io .ErrUnexpectedEOF
}
for i := 0 ; i < d .nOP ; i ++ {
if d .op [i ].unexpectedEOF {
return nil , io .ErrUnexpectedEOF
}
}
if d .filterHeader .level != 0 {
if d .filterHeader .simple {
d .simpleFilter ()
} else {
d .normalFilter ()
}
}
return d .img , nil
}
The pages are generated with Golds v0.8.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .