mirror of https://bitbucket.org/ausocean/av.git
247 lines
6.5 KiB
Go
247 lines
6.5 KiB
Go
/*
|
|
NAME
|
|
parse.go
|
|
|
|
DESCRIPTION
|
|
parse.go provides parsing processes for syntax elements of different
|
|
descriptors specified in 7.2 of ITU-T H.264.
|
|
|
|
AUTHORS
|
|
Saxon Nelson-Milton <saxon@ausocean.org>, The Australian Ocean Laboratory (AusOcean)
|
|
mrmod <mcmoranbjr@gmail.com>
|
|
*/
|
|
|
|
package h264dec
|
|
|
|
import (
|
|
"math"
|
|
|
|
"bitbucket.org/ausocean/av/codec/h264/h264dec/bits"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// mbPartPredMode represents a macroblock partition prediction mode.
|
|
// Modes are defined as consts below. These modes are in section 7.4.5.
|
|
type mbPartPredMode int8
|
|
|
|
const (
|
|
intra4x4 mbPartPredMode = iota
|
|
intra8x8
|
|
intra16x16
|
|
predL0
|
|
predL1
|
|
direct
|
|
biPred
|
|
inter
|
|
naMbPartPredMode
|
|
)
|
|
|
|
// fieldReader provides methods for reading bool and int fields from a
|
|
// bits.BitReader with a sticky error that may be checked after a series of
|
|
// parsing read calls.
|
|
type fieldReader struct {
|
|
e error
|
|
br *bits.BitReader
|
|
}
|
|
|
|
// newFieldReader returns a new fieldReader.
|
|
func newFieldReader(br *bits.BitReader) fieldReader {
|
|
return fieldReader{br: br}
|
|
}
|
|
|
|
// readBitsInt returns an int from reading n bits from br. If we have an error
|
|
// already, we do not continue with the read.
|
|
func (r fieldReader) readBits(n int) uint64 {
|
|
if r.e != nil {
|
|
return 0
|
|
}
|
|
var b uint64
|
|
b, r.e = r.br.ReadBits(n)
|
|
return b
|
|
}
|
|
|
|
// readUe parses a syntax element of ue(v) descriptor, i.e. an unsigned integer
|
|
// Exp-Golomb-coded element using method as specified in section 9.1 of ITU-T
|
|
// H.264 and return as an int. The read does not happen if the fieldReader
|
|
// has a non-nil error.
|
|
func (r fieldReader) readUe() uint64 {
|
|
if r.e != nil {
|
|
return 0
|
|
}
|
|
var i uint64
|
|
i, r.e = readUe(r.br)
|
|
return i
|
|
}
|
|
|
|
// readTe parses a syntax element of te(v) descriptor i.e, truncated
|
|
// Exp-Golomb-coded syntax element using method as specified in section 9.1
|
|
// and returns as an int. The read does not happen if the fieldReader
|
|
// has a non-nil error.
|
|
func (r fieldReader) readTe(x uint) int64 {
|
|
if r.e != nil {
|
|
return 0
|
|
}
|
|
var i int64
|
|
i, r.e = readTe(r.br, x)
|
|
return i
|
|
}
|
|
|
|
// readSe parses a syntax element with descriptor se(v), i.e. a signed integer
|
|
// Exp-Golomb-coded syntax element, using the method described in sections
|
|
// 9.1 and 9.1.1 and returns as int. The read does not happen if the fieldReader
|
|
// has a non-nil error.
|
|
func (r fieldReader) readSe() int {
|
|
if r.e != nil {
|
|
return 0
|
|
}
|
|
var i int
|
|
i, r.e = readSe(r.br)
|
|
return i
|
|
}
|
|
|
|
// readMe parses a syntax element of me(v) descriptor, i.e. mapped
|
|
// Exp-Golomb-coded element, using methods described in sections 9.1 and 9.1.2
|
|
// and returns as int. The read does not happen if the fieldReader has a
|
|
// non-nil error.
|
|
func (r fieldReader) readMe(chromaArrayType uint, mpm mbPartPredMode) int {
|
|
if r.e != nil {
|
|
return 0
|
|
}
|
|
var i uint
|
|
i, r.e = readMe(r.br, chromaArrayType, mpm)
|
|
return int(i)
|
|
}
|
|
|
|
// err returns the fieldReader's error e.
|
|
func (r fieldReader) err() error {
|
|
return r.e
|
|
}
|
|
|
|
// readUe parses a syntax element of ue(v) descriptor, i.e. an unsigned integer
|
|
// Exp-Golomb-coded element using method as specified in section 9.1 of ITU-T H.264.
|
|
//
|
|
// TODO: this should return uint, but rest of code needs to be changed for this
|
|
// to happen.
|
|
func readUe(r *bits.BitReader) (uint64, error) {
|
|
nZeros := -1
|
|
var err error
|
|
for b := uint64(0); b == 0; nZeros++ {
|
|
b, err = r.ReadBits(1)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
}
|
|
rem, err := r.ReadBits(int(nZeros))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return uint64(math.Pow(float64(2), float64(nZeros)) - 1 + float64(rem)), nil
|
|
}
|
|
|
|
// readTe parses a syntax element of te(v) descriptor i.e, truncated
|
|
// Exp-Golomb-coded syntax element using method as specified in section 9.1
|
|
// Rec. ITU-T H.264 (04/2017).
|
|
//
|
|
// TODO: this should also return uint.
|
|
func readTe(r *bits.BitReader, x uint) (int64, error) {
|
|
if x > 1 {
|
|
ue, err := readUe(r)
|
|
return int64(ue), err
|
|
}
|
|
|
|
if x == 1 {
|
|
b, err := r.ReadBits(1)
|
|
if err != nil {
|
|
return 0, errors.Wrap(err, "could not read bit")
|
|
}
|
|
if b == 0 {
|
|
return 1, nil
|
|
}
|
|
return 0, nil
|
|
}
|
|
|
|
return 0, errReadTeBadX
|
|
}
|
|
|
|
var errReadTeBadX = errors.New("x must be more than or equal to 1")
|
|
|
|
// readSe parses a syntax element with descriptor se(v), i.e. a signed integer
|
|
// Exp-Golomb-coded syntax element, using the method described in sections
|
|
// 9.1 and 9.1.1 in Rec. ITU-T H.264 (04/2017).
|
|
func readSe(r *bits.BitReader) (int, error) {
|
|
codeNum, err := readUe(r)
|
|
if err != nil {
|
|
return 0, errors.Wrap(err, "error reading ue(v)")
|
|
}
|
|
|
|
return int(math.Pow(-1, float64(codeNum+1)) * math.Ceil(float64(codeNum)/2.0)), nil
|
|
}
|
|
|
|
// readMe parses a syntax element of me(v) descriptor, i.e. mapped
|
|
// Exp-Golomb-coded element, using methods described in sections 9.1 and 9.1.2
|
|
// in Rec. ITU-T H.264 (04/2017).
|
|
func readMe(r *bits.BitReader, chromaArrayType uint, mpm mbPartPredMode) (uint, error) {
|
|
// Indexes to codedBlockPattern map.
|
|
var i1, i2, i3 uint64
|
|
|
|
// ChromaArrayType selects first index.
|
|
switch chromaArrayType {
|
|
case 1, 2:
|
|
i1 = 0
|
|
case 0, 3:
|
|
i1 = 1
|
|
default:
|
|
return 0, errInvalidCAT
|
|
}
|
|
|
|
// CodeNum from readUe selects second index.
|
|
i2, err := readUe(r)
|
|
if err != nil {
|
|
return 0, errors.Wrap(err, "error from readUe")
|
|
}
|
|
|
|
// Need to check that we won't go out of bounds with this index.
|
|
if int(i2) >= len(codedBlockPattern[i1]) {
|
|
return 0, errInvalidCodeNum
|
|
}
|
|
|
|
// Macroblock prediction mode selects third index.
|
|
switch mpm {
|
|
case intra4x4, intra8x8:
|
|
i3 = 0
|
|
case inter:
|
|
i3 = 1
|
|
default:
|
|
return 0, errInvalidMPM
|
|
}
|
|
|
|
return codedBlockPattern[i1][i2][i3], nil
|
|
}
|
|
|
|
// Errors used by readMe.
|
|
var (
|
|
errInvalidCodeNum = errors.New("invalid codeNum")
|
|
errInvalidMPM = errors.New("invalid macroblock prediction mode")
|
|
errInvalidCAT = errors.New("invalid chroma array type")
|
|
)
|
|
|
|
// codedBlockPattern contains data from table 9-4 in ITU-T H.264 (04/2017)
|
|
// for mapping a chromaArrayType, codeNum and macroblock prediction mode to a
|
|
// coded block pattern.
|
|
var codedBlockPattern = [][][2]uint{
|
|
// Table 9-4 (a) for ChromaArrayType = 1 or 2
|
|
{
|
|
{47, 0}, {31, 16}, {15, 1}, {0, 2}, {23, 4}, {27, 8}, {29, 32}, {30, 3},
|
|
{7, 5}, {11, 10}, {13, 12}, {14, 15}, {39, 47}, {43, 7}, {45, 11}, {46, 13},
|
|
{16, 14}, {3, 6}, {31, 9}, {10, 31}, {12, 35}, {19, 37}, {21, 42}, {26, 44},
|
|
{28, 33}, {35, 34}, {37, 36}, {42, 40}, {44, 39}, {1, 43}, {2, 45}, {4, 46},
|
|
{8, 17}, {17, 18}, {18, 20}, {20, 24}, {24, 19}, {6, 21}, {9, 26}, {22, 28},
|
|
{25, 23}, {32, 27}, {33, 29}, {34, 30}, {36, 22}, {40, 25}, {38, 38}, {41, 41},
|
|
},
|
|
// Table 9-4 (b) for ChromaArrayType = 0 or 3
|
|
{
|
|
{15, 0}, {0, 1}, {7, 2}, {11, 4}, {13, 8}, {14, 3}, {3, 5}, {5, 10}, {10, 12},
|
|
{12, 15}, {1, 7}, {2, 11}, {4, 13}, {8, 14}, {6, 6}, {9, 9},
|
|
},
|
|
}
|