diff --git a/codec/h264/decode/.gitignore b/codec/h264/decode/.gitignore deleted file mode 100644 index 86ede22e..00000000 --- a/codec/h264/decode/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.*.swp -*.mp4 -*.h264 diff --git a/codec/h264/decode/mbtype.go b/codec/h264/decode/mbtype.go index 1e266446..2c3bc281 100644 --- a/codec/h264/decode/mbtype.go +++ b/codec/h264/decode/mbtype.go @@ -93,6 +93,7 @@ func MbTypeName(sliceType string, mbType int) string { var ( errNaMode = errors.New("no mode for given slice and mb type") errPartition = errors.New("partition must be 0") + errSliceType = errors.New("bad sliceType") ) // MbPartPredMode returns a macroblock partition prediction mode for the given @@ -111,8 +112,11 @@ func MbPartPredMode(data *SliceData, sliceType string, mbType, partition int) (m if mbType > 0 && mbType < 25 { return intra16x16, nil } - return -1, errNaMode + return naMbPartPredMode, errNaMode case "SI": + if mbType != 0 { + return naMbPartPredMode, errNaMode + } return intra4x4, nil case "P": fallthrough @@ -120,7 +124,7 @@ func MbPartPredMode(data *SliceData, sliceType string, mbType, partition int) (m if mbType >= 0 && mbType < 3 { return predL0, nil } else if mbType == 3 || mbType == 4 { - return -1, errNaMode + return naMbPartPredMode, errNaMode } else { return predL0, nil } @@ -128,6 +132,8 @@ func MbPartPredMode(data *SliceData, sliceType string, mbType, partition int) (m switch mbType { case 0: return direct, nil + case 3: + return biPred, nil case 1: fallthrough case 4: @@ -157,14 +163,16 @@ func MbPartPredMode(data *SliceData, sliceType string, mbType, partition int) (m case 15: return predL1, nil case 22: - return -1, errNaMode + return naMbPartPredMode, errNaMode default: if mbType > 15 && mbType < 22 { return biPred, nil } return direct, nil } + default: + return naMbPartPredMode, errSliceType } } - return -1, errPartition + return naMbPartPredMode, errPartition } diff --git a/codec/h264/decode/mbtype_test.go b/codec/h264/decode/mbtype_test.go new file mode 100644 index 00000000..d735f9c8 --- /dev/null +++ b/codec/h264/decode/mbtype_test.go @@ -0,0 +1,112 @@ +/* +NAME + parse.go + +DESCRIPTION + mbtype_test.go provides testing for functions provided in mbtype.go. + +AUTHORS + Saxon Nelson-Milton , The Australian Ocean Laboratory (AusOcean) +*/ +package h264 + +import ( + "testing" +) + +func TestMbPartPredMode(t *testing.T) { + tests := []struct { + sliceType string + mbType int + data *SliceData + want mbPartPredMode + err error + }{ + // Table 7-11 (I-slices). + 0: {"I", 0, &SliceData{TransformSize8x8Flag: false}, intra4x4, nil}, + 1: {"I", 0, &SliceData{TransformSize8x8Flag: true}, intra8x8, nil}, + 2: {"I", 1, nil, intra16x16, nil}, + 3: {"I", 2, nil, intra16x16, nil}, + 4: {"I", 3, nil, intra16x16, nil}, + 5: {"I", 4, nil, intra16x16, nil}, + 6: {"I", 5, nil, intra16x16, nil}, + 7: {"I", 6, nil, intra16x16, nil}, + 8: {"I", 7, nil, intra16x16, nil}, + 9: {"I", 8, nil, intra16x16, nil}, + 10: {"I", 9, nil, intra16x16, nil}, + 11: {"I", 10, nil, intra16x16, nil}, + 12: {"I", 11, nil, intra16x16, nil}, + 13: {"I", 12, nil, intra16x16, nil}, + 14: {"I", 13, nil, intra16x16, nil}, + 15: {"I", 14, nil, intra16x16, nil}, + 16: {"I", 15, nil, intra16x16, nil}, + 17: {"I", 16, nil, intra16x16, nil}, + 18: {"I", 17, nil, intra16x16, nil}, + 19: {"I", 18, nil, intra16x16, nil}, + 20: {"I", 19, nil, intra16x16, nil}, + 21: {"I", 20, nil, intra16x16, nil}, + 22: {"I", 21, nil, intra16x16, nil}, + 23: {"I", 22, nil, intra16x16, nil}, + 24: {"I", 23, nil, intra16x16, nil}, + 25: {"I", 24, nil, intra16x16, nil}, + 26: {"I", 25, nil, naMbPartPredMode, errNaMode}, + + // Table 7-12 (SI-slices). + 27: {"SI", 0, nil, intra4x4, nil}, + + // Table 7-13 (SP-slices). + 28: {"SP", 0, nil, predL0, nil}, + 29: {"SP", 1, nil, predL0, nil}, + 30: {"SP", 2, nil, predL0, nil}, + 31: {"SP", 3, nil, naMbPartPredMode, errNaMode}, + 32: {"SP", 4, nil, naMbPartPredMode, errNaMode}, + + // Table 7-14 (B-slices). + 33: {"B", 0, nil, direct, nil}, + 34: {"B", 1, nil, predL0, nil}, + 35: {"B", 2, nil, predL1, nil}, + 36: {"B", 3, nil, biPred, nil}, + 37: {"B", 4, nil, predL0, nil}, + 38: {"B", 5, nil, predL0, nil}, + 39: {"B", 6, nil, predL1, nil}, + 40: {"B", 7, nil, predL1, nil}, + 41: {"B", 8, nil, predL0, nil}, + 42: {"B", 9, nil, predL0, nil}, + 43: {"B", 10, nil, predL1, nil}, + 44: {"B", 11, nil, predL1, nil}, + 45: {"B", 12, nil, predL0, nil}, + 46: {"B", 13, nil, predL0, nil}, + 47: {"B", 14, nil, predL1, nil}, + 48: {"B", 15, nil, predL1, nil}, + 49: {"B", 16, nil, biPred, nil}, + 50: {"B", 17, nil, biPred, nil}, + 51: {"B", 18, nil, biPred, nil}, + 52: {"B", 19, nil, biPred, nil}, + 53: {"B", 20, nil, biPred, nil}, + 54: {"B", 21, nil, biPred, nil}, + 55: {"B", 22, nil, naMbPartPredMode, errNaMode}, + + // Test some weird cases where we expect error. + 56: {"O", 0, nil, naMbPartPredMode, errSliceType}, + 57: {"I", 26, nil, naMbPartPredMode, errNaMode}, + 58: {"I", -1, nil, naMbPartPredMode, errNaMode}, + 59: {"SI", 1, nil, naMbPartPredMode, errNaMode}, + + // Cases for inferred mbtype. + 60: {"SP", 5, nil, predL0, nil}, + 61: {"SP", -1, nil, predL0, nil}, + 62: {"B", -1, nil, direct, nil}, + 63: {"B", 23, nil, direct, nil}, + } + + for i, test := range tests { + m, err := MbPartPredMode(test.data, test.sliceType, test.mbType, 0) + if err != test.err { + t.Errorf("unexpected error from test %d.\nGot: %v\nWant: %v\n", i, err, test.err) + } + + if m != test.want { + t.Errorf("did not get expected result for test %d.\nGot: %v\nWant: %v\n", i, m, test.want) + } + } +} diff --git a/codec/h264/decode/parse.go b/codec/h264/decode/parse.go index 18245f3e..24ff222b 100644 --- a/codec/h264/decode/parse.go +++ b/codec/h264/decode/parse.go @@ -16,10 +16,12 @@ package h264 import ( "math" - "github.com/icza/bitio" + "github.com/ausocean/h264decode/h264/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 ( @@ -31,6 +33,7 @@ const ( direct biPred inter + naMbPartPredMode ) // readUe parses a syntax element of ue(v) descriptor, i.e. an unsigned integer @@ -38,7 +41,7 @@ const ( // // TODO: this should return uint, but rest of code needs to be changed for this // to happen. -func readUe(r bitio.Reader) (int, error) { +func readUe(r *bits.BitReader) (int, error) { nZeros := -1 var err error for b := uint64(0); b == 0; nZeros++ { @@ -47,7 +50,7 @@ func readUe(r bitio.Reader) (int, error) { return 0, err } } - rem, err := r.ReadBits(byte(nZeros)) + rem, err := r.ReadBits(int(nZeros)) if err != nil { return 0, err } @@ -59,7 +62,7 @@ func readUe(r bitio.Reader) (int, error) { // Rec. ITU-T H.264 (04/2017). // // TODO: this should also return uint. -func readTe(r bitio.Reader, x uint) (int, error) { +func readTe(r *bits.BitReader, x uint) (int, error) { if x > 1 { return readUe(r) } @@ -83,7 +86,7 @@ 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 bitio.Reader) (int, error) { +func readSe(r *bits.BitReader) (int, error) { codeNum, err := readUe(r) if err != nil { return 0, errors.Wrap(err, "error reading ue(v)") @@ -95,7 +98,7 @@ func readSe(r bitio.Reader) (int, error) { // 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 bitio.Reader, chromaArrayType uint, mpm mbPartPredMode) (uint, error) { +func readMe(r *bits.BitReader, chromaArrayType uint, mpm mbPartPredMode) (uint, error) { // Indexes to codedBlockPattern map. var i1, i2, i3 int diff --git a/codec/h264/decode/parse_test.go b/codec/h264/decode/parse_test.go index 4356fa50..cc2d47e0 100644 --- a/codec/h264/decode/parse_test.go +++ b/codec/h264/decode/parse_test.go @@ -15,7 +15,7 @@ import ( "bytes" "testing" - "github.com/icza/bitio" + "github.com/ausocean/h264decode/h264/bits" ) // TestReadUe checks that readUe correctly parses an Exp-Golomb-coded element @@ -41,7 +41,7 @@ func TestReadUe(t *testing.T) { } for i, test := range tests { - got, err := readUe(bitio.NewReader(bytes.NewReader(test.in))) + got, err := readUe(bits.NewBitReader(bytes.NewReader(test.in))) if err != nil { t.Fatalf("did not expect error: %v from readUe", err) } @@ -69,7 +69,7 @@ func TestReadTe(t *testing.T) { } for i, test := range tests { - got, err := readTe(bitio.NewReader(bytes.NewReader(test.in)), test.x) + got, err := readTe(bits.NewBitReader(bytes.NewReader(test.in)), test.x) if err != test.err { t.Fatalf("did not get expected error for test: %v\nGot: %v\nWant: %v\n", i, err, test.err) } @@ -99,7 +99,7 @@ func TestReadSe(t *testing.T) { } for i, test := range tests { - got, err := readSe(bitio.NewReader(bytes.NewReader(test.in))) + got, err := readSe(bits.NewBitReader(bytes.NewReader(test.in))) if err != nil { t.Fatalf("did not expect error: %v from readSe", err) } @@ -142,7 +142,7 @@ func TestReadMe(t *testing.T) { } for i, test := range tests { - got, err := readMe(bitio.NewReader(bytes.NewReader(test.in)), test.cat, test.mpm) + got, err := readMe(bits.NewBitReader(bytes.NewReader(test.in)), test.cat, test.mpm) if err != test.err { t.Fatalf("did not expect to get error: %v for test: %v", err, i) }