1
1
mirror of https://github.com/wader/fq.git synced 2024-12-26 15:02:28 +03:00
fq/format/mpeg/avc_nalu.go
Mattias Wadman 7c5215347d bitio,decode: Refactor bitio usage and make buffer slicing more correct
Remove bitio.Buffer layer. bitio.Buffer was a kitchen sink layer with helpers
now it's just a buffer and most functions have been moved to decode instead.

bitio package now only have primitive types and functions simialar to standard
library io and bytes packages.

Make nearly eveything internally use bitio.Bit* interfaces so that slicing work
correctly this will also make it possible to start experimenting with more
complicated silcing helpers, ex things like:
breplace(.header.bitrate; 123) to get a new buffer with bitrate changed.
2022-02-04 21:41:53 +01:00

132 lines
4.7 KiB
Go

package mpeg
// TODO: unescape configurable? merge with AVC_NAL? merge with HEVC?
import (
"github.com/wader/fq/format"
"github.com/wader/fq/format/registry"
"github.com/wader/fq/internal/mathextra"
"github.com/wader/fq/pkg/bitio"
"github.com/wader/fq/pkg/decode"
"github.com/wader/fq/pkg/scalar"
)
var avcSPSFormat decode.Group
var avcPPSFormat decode.Group
var avcSEIFormat decode.Group
func init() {
registry.MustRegister(decode.Format{
Name: format.AVC_NALU,
Description: "H.264/AVC Network Access Layer Unit",
DecodeFn: avcNALUDecode,
Dependencies: []decode.Dependency{
{Names: []string{format.AVC_SPS}, Group: &avcSPSFormat},
{Names: []string{format.AVC_PPS}, Group: &avcPPSFormat},
{Names: []string{format.AVC_SEI}, Group: &avcSEIFormat},
},
})
}
// 14496-10 9.1 Parsing process for Exp-Golomb codes
func expGolomb(d *decode.D) uint64 {
leadingZeroBits := -1
for b := false; !b; leadingZeroBits++ {
b = d.Bool()
}
var expN uint64
if leadingZeroBits == 0 {
expN = 1
} else {
expN = 2 << (leadingZeroBits - 1)
}
return expN - 1 + d.U(leadingZeroBits)
}
func uEV(d *decode.D) uint64 { return expGolomb(d) }
func sEV(d *decode.D) int64 {
v := expGolomb(d) + 1
return mathextra.ZigZag(v) - -int64(v&1)
}
const (
avcNALCodedSliceNonIDR = 1
avcNALCodedSlicePartitionA = 2
avcNALCodedSlicePartitionB = 3
avcNALCodedSlicePartitionC = 4
avcNALCodedSliceIDR = 5
avcNALSupplementalEnhancementInformation = 6
avcNALSequenceParameterSet = 7
avcNALPictureParameterSet = 8
avcNALCodedSliceAuxWithoutPartition = 19
avcNALCodedSliceExtension = 20
)
var avcNALNames = scalar.UToScalar{
1: {Sym: "SLICE", Description: "Coded slice of a non-IDR picture"},
2: {Sym: "DPA", Description: "Coded slice data partition A"},
3: {Sym: "DPB", Description: "Coded slice data partition B"},
4: {Sym: "DPC", Description: "Coded slice data partition C"},
5: {Sym: "IDR_SLICE", Description: "Coded slice of an IDR picture"},
avcNALSupplementalEnhancementInformation: {Sym: "SEI", Description: "Supplemental enhancement information"},
avcNALSequenceParameterSet: {Sym: "SPS", Description: "Sequence parameter set"},
avcNALPictureParameterSet: {Sym: "PPS", Description: "Picture parameter set"},
9: {Sym: "AUD", Description: "Access unit delimiter"},
10: {Sym: "EOSEQ", Description: "End of sequence"},
11: {Sym: "EOS", Description: "End of stream"},
12: {Sym: "FILLER", Description: "Filler data"},
13: {Sym: "SPS_EXT", Description: "Sequence parameter set extension"},
14: {Sym: "PREFIX", Description: "Prefix NAL unit"},
15: {Sym: "SUB_SPS", Description: "Subset sequence parameter set"},
19: {Sym: "AUX_SLICE", Description: "Coded slice of an auxiliary coded picture without partitioning"},
20: {Sym: "EXTEN_SLICE", Description: "Coded slice extension"},
}
var sliceNames = scalar.UToSymStr{
0: "P",
1: "B",
2: "I",
3: "SP",
4: "SI",
5: "P",
6: "B",
7: "I",
8: "SP",
9: "SI",
}
func avcNALUDecode(d *decode.D, in interface{}) interface{} {
d.FieldBool("forbidden_zero_bit")
d.FieldU2("nal_ref_idc")
nalType := d.FieldU5("nal_unit_type", avcNALNames)
unescapedBR := d.MustNewBitBufFromReader(decode.NALUnescapeReader{Reader: bitio.NewIOReader(d.BitBufRange(d.Pos(), d.BitsLeft()))})
switch nalType {
case avcNALCodedSliceNonIDR,
avcNALCodedSlicePartitionA,
avcNALCodedSlicePartitionB,
avcNALCodedSlicePartitionC,
avcNALCodedSliceIDR,
avcNALCodedSliceAuxWithoutPartition,
avcNALCodedSliceExtension:
d.FieldStruct("slice_header", func(d *decode.D) {
d.FieldUFn("first_mb_in_slice", uEV)
d.FieldUFn("slice_type", uEV, sliceNames)
d.FieldUFn("pic_parameter_set_id", uEV)
// TODO: if ( separate_colour_plane_flag from SPS ) colour_plane_id; frame_num
})
case avcNALSupplementalEnhancementInformation:
d.FieldFormatBitBuf("sei", unescapedBR, avcSEIFormat, nil)
case avcNALSequenceParameterSet:
d.FieldFormatBitBuf("sps", unescapedBR, avcSPSFormat, nil)
case avcNALPictureParameterSet:
d.FieldFormatBitBuf("pps", unescapedBR, avcPPSFormat, nil)
}
d.FieldRawLen("data", d.BitsLeft())
return nil
}