From a755ccfc587342372ec67ec9ebc8b70ddd1ddc1c Mon Sep 17 00:00:00 2001 From: Saxon Date: Sun, 21 Jul 2019 22:11:24 +0930 Subject: [PATCH] codec/h264/h264dec: separated VUI and HRD from SPS struct Took out all VUI and HRD parameters from the SPS struct and gave them their own structs - VUIParameters and HRDParameters, along with 'contructors' NewVUIParameters and NewHRDParameters to parse from a bits.BitReader and populate the fields of the struct. --- codec/h264/h264dec/cabac.go | 4 +- codec/h264/h264dec/pps.go | 5 +- codec/h264/h264dec/slice.go | 57 ++- codec/h264/h264dec/slice_test.go | 24 +- codec/h264/h264dec/sps.go | 807 ++++++++++++++++--------------- 5 files changed, 451 insertions(+), 446 deletions(-) diff --git a/codec/h264/h264dec/cabac.go b/codec/h264/h264dec/cabac.go index 47d90835..8b3a6e4c 100644 --- a/codec/h264/h264dec/cabac.go +++ b/codec/h264/h264dec/cabac.go @@ -35,14 +35,14 @@ func YOffset(yRefMin16, refMbH int) int { } func MbWidthC(sps *SPS) int { mbWidthC := 16 / SubWidthC(sps) - if sps.ChromaFormat == chromaMonochrome || sps.UseSeparateColorPlane { + if sps.ChromaFormatIDC == chromaMonochrome || sps.SeparateColorPlaneFlag { mbWidthC = 0 } return mbWidthC } func MbHeightC(sps *SPS) int { mbHeightC := 16 / SubHeightC(sps) - if sps.ChromaFormat == chromaMonochrome || sps.UseSeparateColorPlane { + if sps.ChromaFormatIDC == chromaMonochrome || sps.SeparateColorPlaneFlag { mbHeightC = 0 } return mbHeightC diff --git a/codec/h264/h264dec/pps.go b/codec/h264/h264dec/pps.go index 139306c6..1f618c2e 100644 --- a/codec/h264/h264dec/pps.go +++ b/codec/h264/h264dec/pps.go @@ -195,7 +195,7 @@ func NewPPS(sps *SPS, rbsp []byte, showPacket bool) (*PPS, error) { if pps.PicScalingMatrixPresent { v := 6 - if sps.ChromaFormat != chroma444 { + if sps.ChromaFormatIDC != chroma444 { v = 2 } for i := 0; i < 6+(v*pps.Transform8x8Mode); i++ { @@ -231,9 +231,6 @@ func NewPPS(sps *SPS, rbsp []byte, showPacket bool) (*PPS, error) { // rbspTrailingBits() } - if showPacket { - debugPacket("PPS", pps) - } return &pps, nil } diff --git a/codec/h264/h264dec/slice.go b/codec/h264/h264dec/slice.go index b25b4502..8192d465 100644 --- a/codec/h264/h264dec/slice.go +++ b/codec/h264/h264dec/slice.go @@ -145,13 +145,13 @@ func (d SliceData) ae(v int) int { // 8.2.2 func MbToSliceGroupMap(sps *SPS, pps *PPS, header *SliceHeader) []int { mbaffFrameFlag := 0 - if sps.MBAdaptiveFrameField && !header.FieldPic { + if sps.MBAdaptiveFrameFieldFlag && !header.FieldPic { mbaffFrameFlag = 1 } mapUnitToSliceGroupMap := MapUnitToSliceGroupMap(sps, pps, header) mbToSliceGroupMap := []int{} for i := 0; i <= PicSizeInMbs(sps, header)-1; i++ { - if sps.FrameMbsOnly || header.FieldPic { + if sps.FrameMBSOnlyFlag || header.FieldPic { mbToSliceGroupMap = append(mbToSliceGroupMap, mapUnitToSliceGroupMap[i]) continue } @@ -159,7 +159,7 @@ func MbToSliceGroupMap(sps *SPS, pps *PPS, header *SliceHeader) []int { mbToSliceGroupMap = append(mbToSliceGroupMap, mapUnitToSliceGroupMap[i/2]) continue } - if !sps.FrameMbsOnly && !sps.MBAdaptiveFrameField && !header.FieldPic { + if !sps.FrameMBSOnlyFlag && !sps.MBAdaptiveFrameFieldFlag && !header.FieldPic { mbToSliceGroupMap = append( mbToSliceGroupMap, mapUnitToSliceGroupMap[(i/(2*PicWidthInMbs(sps)))*PicWidthInMbs(sps)+(i%PicWidthInMbs(sps))]) @@ -169,7 +169,7 @@ func MbToSliceGroupMap(sps *SPS, pps *PPS, header *SliceHeader) []int { } func PicWidthInMbs(sps *SPS) int { - return sps.PicWidthInMbsMinus1 + 1 + return sps.PicWidthInMBSMinus1 + 1 } func PicHeightInMapUnits(sps *SPS) int { return sps.PicHeightInMapUnitsMinus1 + 1 @@ -178,7 +178,7 @@ func PicSizeInMapUnits(sps *SPS) int { return PicWidthInMbs(sps) * PicHeightInMapUnits(sps) } func FrameHeightInMbs(sps *SPS) int { - return (2 - flagVal(sps.FrameMbsOnly)) * PicHeightInMapUnits(sps) + return (2 - flagVal(sps.FrameMBSOnlyFlag)) * PicHeightInMapUnits(sps) } func PicHeightInMbs(sps *SPS, header *SliceHeader) int { return FrameHeightInMbs(sps) / (1 + flagVal(header.FieldPic)) @@ -190,13 +190,13 @@ func PicSizeInMbs(sps *SPS, header *SliceHeader) int { // table 6-1 func SubWidthC(sps *SPS) int { n := 17 - if sps.UseSeparateColorPlane { - if sps.ChromaFormat == chroma444 { + if sps.SeparateColorPlaneFlag { + if sps.ChromaFormatIDC == chroma444 { return n } } - switch sps.ChromaFormat { + switch sps.ChromaFormatIDC { case chromaMonochrome: return n case chroma420: @@ -211,12 +211,12 @@ func SubWidthC(sps *SPS) int { } func SubHeightC(sps *SPS) int { n := 17 - if sps.UseSeparateColorPlane { - if sps.ChromaFormat == chroma444 { + if sps.SeparateColorPlaneFlag { + if sps.ChromaFormatIDC == chroma444 { return n } } - switch sps.ChromaFormat { + switch sps.ChromaFormatIDC { case chromaMonochrome: return n case chroma420: @@ -578,17 +578,17 @@ func nextMbAddress(n int, sps *SPS, pps *PPS, header *SliceHeader) int { i := n + 1 // picSizeInMbs is the number of macroblocks in picture 0 // 7-13 - // PicWidthInMbs = sps.PicWidthInMbsMinus1 + 1 + // PicWidthInMbs = sps.PicWidthInMBSMinus1 + 1 // PicHeightInMapUnits = sps.PicHeightInMapUnitsMinus1 + 1 // 7-29 // picSizeInMbs = PicWidthInMbs * PicHeightInMbs // 7-26 // PicHeightInMbs = FrameHeightInMbs / (1 + header.fieldPicFlag) // 7-18 - // FrameHeightInMbs = (2 - ps.FrameMbsOnly) * PicHeightInMapUnits - picWidthInMbs := sps.PicWidthInMbsMinus1 + 1 + // FrameHeightInMbs = (2 - ps.FrameMBSOnlyFlag) * PicHeightInMapUnits + picWidthInMbs := sps.PicWidthInMBSMinus1 + 1 picHeightInMapUnits := sps.PicHeightInMapUnitsMinus1 + 1 - frameHeightInMbs := (2 - flagVal(sps.FrameMbsOnly)) * picHeightInMapUnits + frameHeightInMbs := (2 - flagVal(sps.FrameMBSOnlyFlag)) * picHeightInMapUnits picHeightInMbs := frameHeightInMbs / (1 + flagVal(header.FieldPic)) picSizeInMbs := picWidthInMbs * picHeightInMbs mbToSliceGroupMap := MbToSliceGroupMap(sps, pps, header) @@ -600,7 +600,7 @@ func nextMbAddress(n int, sps *SPS, pps *PPS, header *SliceHeader) int { func CurrMbAddr(sps *SPS, header *SliceHeader) int { mbaffFrameFlag := 0 - if sps.MBAdaptiveFrameField && !header.FieldPic { + if sps.MBAdaptiveFrameFieldFlag && !header.FieldPic { mbaffFrameFlag = 1 } @@ -608,7 +608,7 @@ func CurrMbAddr(sps *SPS, header *SliceHeader) int { } func MbaffFrameFlag(sps *SPS, header *SliceHeader) int { - if sps.MBAdaptiveFrameField && !header.FieldPic { + if sps.MBAdaptiveFrameFieldFlag && !header.FieldPic { return 1 } return 0 @@ -630,7 +630,7 @@ func NewSliceData(sliceContext *SliceContext, br *bits.BitReader) (*SliceData, e } } mbaffFrameFlag := 0 - if sliceContext.SPS.MBAdaptiveFrameField && !sliceContext.Slice.Header.FieldPic { + if sliceContext.SPS.MBAdaptiveFrameFieldFlag && !sliceContext.Slice.Header.FieldPic { mbaffFrameFlag = 1 } currMbAddr := sliceContext.Slice.Header.FirstMbInSlice * (1 * mbaffFrameFlag) @@ -791,7 +791,7 @@ func NewSliceData(sliceContext *SliceContext, br *bits.BitReader) (*SliceData, e mbWidthC := 16 / SubWidthC(sliceContext.SPS) mbHeightC := 16 / SubHeightC(sliceContext.SPS) // if monochrome - if sliceContext.SPS.ChromaFormat == chromaMonochrome || sliceContext.SPS.UseSeparateColorPlane { + if sliceContext.SPS.ChromaFormatIDC == chromaMonochrome || sliceContext.SPS.SeparateColorPlaneFlag { mbWidthC = 0 mbHeightC = 0 } @@ -824,7 +824,7 @@ func NewSliceData(sliceContext *SliceContext, br *bits.BitReader) (*SliceData, e if NumbSubMbPart(subMbType[mbPartIdx]) > 1 { noSubMbPartSizeLessThan8x8Flag = 0 } - } else if !sliceContext.SPS.Direct8x8Inference { + } else if !sliceContext.SPS.Direct8x8InferenceFlag { noSubMbPartSizeLessThan8x8Flag = 0 } } @@ -876,7 +876,7 @@ func NewSliceData(sliceContext *SliceContext, br *bits.BitReader) (*SliceData, e } // sliceContext.Slice.Data.CodedBlockPattern = me(v) | ae(v) - if CodedBlockPatternLuma(sliceContext.Slice.Data) > 0 && sliceContext.PPS.Transform8x8Mode == 1 && sliceContext.Slice.Data.MbTypeName != "I_NxN" && noSubMbPartSizeLessThan8x8Flag == 1 && (sliceContext.Slice.Data.MbTypeName != "B_Direct_16x16" || sliceContext.SPS.Direct8x8Inference) { + if CodedBlockPatternLuma(sliceContext.Slice.Data) > 0 && sliceContext.PPS.Transform8x8Mode == 1 && sliceContext.Slice.Data.MbTypeName != "I_NxN" && noSubMbPartSizeLessThan8x8Flag == 1 && (sliceContext.Slice.Data.MbTypeName != "B_Direct_16x16" || sliceContext.SPS.Direct8x8InferenceFlag) { // TODO: 1 bit or ae(v) if sliceContext.PPS.EntropyCodingMode == 1 { binarization := NewBinarization("Transform8x8Flag", sliceContext.Slice.Data) @@ -956,10 +956,10 @@ func NewSliceContext(videoStream *VideoStream, nalUnit *NalUnit, rbsp []byte, sh idrPic = true } header := SliceHeader{} - if sps.UseSeparateColorPlane { + if sps.SeparateColorPlaneFlag { header.ChromaArrayType = 0 } else { - header.ChromaArrayType = sps.ChromaFormat + header.ChromaArrayType = sps.ChromaFormatIDC } br := bits.NewBitReader(bytes.NewReader(rbsp)) @@ -980,7 +980,7 @@ func NewSliceContext(videoStream *VideoStream, nalUnit *NalUnit, rbsp []byte, sh return nil, errors.Wrap(err, "could not parse PPSID") } - if sps.UseSeparateColorPlane { + if sps.SeparateColorPlaneFlag { b, err := br.ReadBits(2) if err != nil { return nil, errors.Wrap(err, "could not read ColorPlaneID") @@ -989,7 +989,7 @@ func NewSliceContext(videoStream *VideoStream, nalUnit *NalUnit, rbsp []byte, sh } // TODO: See 7.4.3 // header.FrameNum = b.NextField("FrameNum", 0) - if !sps.FrameMbsOnly { + if !sps.FrameMBSOnlyFlag { b, err := br.ReadBits(1) if err != nil { return nil, errors.Wrap(err, "could not read FieldPic") @@ -1023,7 +1023,7 @@ func NewSliceContext(videoStream *VideoStream, nalUnit *NalUnit, rbsp []byte, sh } } } - if sps.PicOrderCountType == 1 && !sps.DeltaPicOrderAlwaysZero { + if sps.PicOrderCountType == 1 && !sps.DeltaPicOrderAlwaysZeroFlag { header.DeltaPicOrderCnt[0], err = readSe(br) if err != nil { return nil, errors.Wrap(err, "could not parse DeltaPicOrderCnt") @@ -1361,9 +1361,6 @@ func NewSliceContext(videoStream *VideoStream, nalUnit *NalUnit, rbsp []byte, sh if err != nil { return nil, errors.Wrap(err, "could not create slice data") } - if showPacket { - debugPacket("debug: Header", sliceContext.Slice.Header) - debugPacket("debug: Data", sliceContext.Slice.Data) - } + return sliceContext, nil } diff --git a/codec/h264/h264dec/slice_test.go b/codec/h264/h264dec/slice_test.go index e7988a7e..d1db5df1 100644 --- a/codec/h264/h264dec/slice_test.go +++ b/codec/h264/h264dec/slice_test.go @@ -7,12 +7,12 @@ var subWidthCTests = []struct { want int }{ {SPS{}, 17}, - {SPS{ChromaFormat: 0}, 17}, - {SPS{ChromaFormat: 1}, 2}, - {SPS{ChromaFormat: 2}, 2}, - {SPS{ChromaFormat: 3}, 1}, - {SPS{ChromaFormat: 3, UseSeparateColorPlane: true}, 17}, - {SPS{ChromaFormat: 999}, 17}, + {SPS{ChromaFormatIDC: 0}, 17}, + {SPS{ChromaFormatIDC: 1}, 2}, + {SPS{ChromaFormatIDC: 2}, 2}, + {SPS{ChromaFormatIDC: 3}, 1}, + {SPS{ChromaFormatIDC: 3, SeparateColorPlaneFlag: true}, 17}, + {SPS{ChromaFormatIDC: 999}, 17}, } // TestSubWidthC tests that the correct SubWidthC is returned given @@ -30,12 +30,12 @@ var subHeightCTests = []struct { want int }{ {SPS{}, 17}, - {SPS{ChromaFormat: 0}, 17}, - {SPS{ChromaFormat: 1}, 2}, - {SPS{ChromaFormat: 2}, 1}, - {SPS{ChromaFormat: 3}, 1}, - {SPS{ChromaFormat: 3, UseSeparateColorPlane: true}, 17}, - {SPS{ChromaFormat: 999}, 17}, + {SPS{ChromaFormatIDC: 0}, 17}, + {SPS{ChromaFormatIDC: 1}, 2}, + {SPS{ChromaFormatIDC: 2}, 1}, + {SPS{ChromaFormatIDC: 3}, 1}, + {SPS{ChromaFormatIDC: 3, SeparateColorPlaneFlag: true}, 17}, + {SPS{ChromaFormatIDC: 999}, 17}, } // TestSubHeightC tests that the correct SubHeightC is returned given diff --git a/codec/h264/h264dec/sps.go b/codec/h264/h264dec/sps.go index c4aabd22..bd3e1184 100644 --- a/codec/h264/h264dec/sps.go +++ b/codec/h264/h264dec/sps.go @@ -2,112 +2,11 @@ package h264dec import ( "bytes" - "fmt" - "strings" "bitbucket.org/ausocean/av/codec/h264/h264dec/bits" "github.com/pkg/errors" ) -// Specification Page 43 7.3.2.1.1 -// Range is always inclusive -// XRange is always exclusive -type SPS struct { - // 8 bits - Profile int - // 6 bits - Constraint0, Constraint1 int - Constraint2, Constraint3 int - Constraint4, Constraint5 int - // 2 bit reserved 0 bits - // 8 bits - Level int - // Range 0 - 31 ; 6 bits - ID int - ChromaFormat int - UseSeparateColorPlane bool - BitDepthLumaMinus8 int - BitDepthChromaMinus8 int - QPrimeYZeroTransformBypass bool - SeqScalingMatrixPresent bool - // Delta is (0-12)-1 ; 4 bits - SeqScalingList []bool // se - // Range 0 - 12; 4 bits - Log2MaxFrameNumMinus4 int - // Range 0 - 2; 2 bits - PicOrderCountType int - // Range 0 - 12; 4 bits - Log2MaxPicOrderCntLSBMin4 int - DeltaPicOrderAlwaysZero bool - // Range (-2^31)+1 to (2^31)-1 ; 31 bits - OffsetForNonRefPic int // Value - 1 (se) - // Range (-2^31)+1 to (2^31)-1 ; 31 bits - OffsetForTopToBottomField int // Value - 1 (se) - // Range 0 - 255 ; 8 bits - NumRefFramesInPicOrderCntCycle int - // Range (-2^31)+1 to (2^31)-1 ; 31 bits - OffsetForRefFrameList []int // Value - 1 ([]se) - // Range 0 - MaxDpbFrames - MaxNumRefFrames int - GapsInFrameNumValueAllowed bool - // Page 77 - PicWidthInMbsMinus1 int - // Page 77 - PicHeightInMapUnitsMinus1 int - FrameMbsOnly bool - MBAdaptiveFrameField bool - Direct8x8Inference bool - FrameCropping bool - FrameCropLeftOffset int - FrameCropRightOffset int - FrameCropTopOffset int - FrameCropBottomOffset int - VuiParametersPresent bool - VuiParameters []int - AspectRatioInfoPresent bool - AspectRatio int - SarWidth int - SarHeight int - OverscanInfoPresent bool - OverscanAppropriate bool - VideoSignalTypePresent bool - VideoFormat int - VideoFullRange bool - ColorDescriptionPresent bool - ColorPrimaries int - TransferCharacteristics int - MatrixCoefficients int - ChromaLocInfoPresent bool - ChromaSampleLocTypeTopField int - ChromaSampleLocTypeBottomField int - CpbCntMinus1 int - BitRateScale int - CpbSizeScale int - BitRateValueMinus1 []int - Cbr []bool - InitialCpbRemovalDelayLengthMinus1 int - CpbRemovalDelayLengthMinus1 int - CpbSizeValueMinus1 []int - DpbOutputDelayLengthMinus1 int - TimeOffsetLength int - TimingInfoPresent bool - NumUnitsInTick int - TimeScale int - NalHrdParametersPresent bool - FixedFrameRate bool - VclHrdParametersPresent bool - LowHrdDelay bool - PicStructPresent bool - BitstreamRestriction bool - MotionVectorsOverPicBoundaries bool - MaxBytesPerPicDenom int - MaxBitsPerMbDenom int - Log2MaxMvLengthHorizontal int - Log2MaxMvLengthVertical int - MaxDecFrameBuffering int - MaxNumReorderFrames int -} - var ( DefaultScalingMatrix4x4 = [][]int{ {6, 13, 20, 28, 13, 20, 28, 32, 20, 28, 32, 37, 28, 32, 37, 42}, @@ -161,98 +60,62 @@ var ( ScalingList8x8 = ScalingList4x4 ) -func isInList(l []int, term int) bool { - for _, m := range l { - if m == term { - return true - } - } - return false -} -func debugPacket(name string, packet interface{}) { - logger.Printf("debug: %s packet\n", name) - for _, line := range strings.Split(fmt.Sprintf("%+v", packet), " ") { - logger.Printf("debug: \t%#v\n", line) - } -} -func scalingList(br *bits.BitReader, scalingList []int, sizeOfScalingList int, defaultScalingMatrix []int) error { - lastScale := 8 - nextScale := 8 - for i := 0; i < sizeOfScalingList; i++ { - if nextScale != 0 { - deltaScale, err := readSe(br) - if err != nil { - return errors.Wrap(err, "could not parse deltaScale") - } - nextScale = (lastScale + deltaScale + 256) % 256 - if i == 0 && nextScale == 0 { - // Scaling list should use the default list for this point in the matrix - _ = defaultScalingMatrix - } - } - if nextScale == 0 { - scalingList[i] = lastScale - } else { - scalingList[i] = nextScale - } - lastScale = scalingList[i] - } - return nil +// SPS describes a sequence parameter set as defined by section 7.3.2.1.1 in +// the Specifications. +type SPS struct { + Profile int + Constraint0 int + Constraint1 int + Constraint2 int + Constraint3 int + Constraint4 int + Constraint5 int + LevelIDC int + SPSID int + ChromaFormatIDC int + SeparateColorPlaneFlag bool + BitDepthLumaMinus8 int + BitDepthChromaMinus8 int + QPPrimeYZeroTransformBypassFlag bool + SeqScalingMatrixPresentFlag bool + SeqScalingListPresentFlag []bool + ScalingList4x4 [][]int + UseDefaultScalingMatrix4x4Flag []bool + ScalingList8x8 [][]int + UseDefaultScalingMatrix8x8Flag []bool + Log2MaxFrameNumMinus4 int + PicOrderCountType int + Log2MaxPicOrderCntLSBMin4 int + DeltaPicOrderAlwaysZeroFlag bool + OffsetForNonRefPic int + OffsetForTopToBottomField int + NumRefFramesInPicOrderCntCycle int + OffsetForRefFrameList []int + MaxNumRefFrames int + GapsInFrameNumValueAllowed bool + PicWidthInMBSMinus1 int + PicHeightInMapUnitsMinus1 int + FrameMBSOnlyFlag bool + MBAdaptiveFrameFieldFlag bool + Direct8x8InferenceFlag bool + FrameCroppingFlag bool + FrameCropLeftOffset int + FrameCropRightOffset int + FrameCropTopOffset int + FrameCropBottomOffset int + VUIParametersPresentFlag bool + VUIParameters *VUIParameters } + +// NewSPS parses a sequence parameter set raw byte sequence from br following +// the syntax structure specified in section 7.3.2.1.1, and returns as a new +// SPS. func NewSPS(rbsp []byte, showPacket bool) (*SPS, error) { logger.Printf("debug: SPS RBSP %d bytes %d bits\n", len(rbsp), len(rbsp)*8) logger.Printf("debug: \t%#v\n", rbsp[0:8]) sps := SPS{} br := bits.NewBitReader(bytes.NewReader(rbsp)) var err error - hrdParameters := func() error { - sps.CpbCntMinus1, err = readUe(br) - if err != nil { - return errors.Wrap(err, "could not parse CpbCntMinus1") - } - - err := readFields(br, []field{ - {&sps.BitRateScale, "BitRateScale", 4}, - {&sps.CpbSizeScale, "CpbSizeScale", 4}, - }) - if err != nil { - return err - } - - // SchedSelIdx E1.2 - for sseli := 0; sseli <= sps.CpbCntMinus1; sseli++ { - ue, err := readUe(br) - if err != nil { - return errors.Wrap(err, "could not parse BitRateValueMinus1") - } - sps.BitRateValueMinus1 = append(sps.BitRateValueMinus1, ue) - - ue, err = readUe(br) - if err != nil { - return errors.Wrap(err, "could not parse CpbSizeValueMinus1") - } - sps.CpbSizeValueMinus1 = append(sps.CpbSizeValueMinus1, ue) - - if v, _ := br.ReadBits(1); v == 1 { - sps.Cbr = append(sps.Cbr, true) - } else { - sps.Cbr = append(sps.Cbr, false) - } - - err = readFields(br, - []field{ - {&sps.InitialCpbRemovalDelayLengthMinus1, "InitialCpbRemovalDelayLengthMinus1", 5}, - {&sps.CpbRemovalDelayLengthMinus1, "CpbRemovalDelayLengthMinus1", 5}, - {&sps.DpbOutputDelayLengthMinus1, "DpbOutputDelayLengthMinus1", 5}, - {&sps.TimeOffsetLength, "TimeOffsetLength", 5}, - }, - ) - if err != nil { - return err - } - } - return nil - } err = readFields(br, []field{ @@ -275,30 +138,30 @@ func NewSPS(rbsp []byte, showPacket bool) (*SPS, error) { if err != nil { return nil, errors.Wrap(err, "could not read Level") } - sps.Level = int(b) + sps.LevelIDC = int(b) // sps.ID = b.NextField("SPSID", 6) // proper - sps.ID, err = readUe(br) + sps.SPSID, err = readUe(br) if err != nil { return nil, errors.Wrap(err, "could not parse ID") } - sps.ChromaFormat, err = readUe(br) + sps.ChromaFormatIDC, err = readUe(br) if err != nil { - return nil, errors.Wrap(err, "could not parse ChromaFormat") + return nil, errors.Wrap(err, "could not parse ChromaFormatIDC") } // This should be done only for certain ProfileIDC: isProfileIDC := []int{100, 110, 122, 244, 44, 83, 86, 118, 128, 138, 139, 134, 135} // SpecialProfileCase1 if isInList(isProfileIDC, sps.Profile) { - if sps.ChromaFormat == chroma444 { + if sps.ChromaFormatIDC == chroma444 { // TODO: should probably deal with error here. b, err := br.ReadBits(1) if err != nil { return nil, errors.Wrap(err, "could not read UseSeparateColorPlaneFlag") } - sps.UseSeparateColorPlane = b == 1 + sps.SeparateColorPlaneFlag = b == 1 } sps.BitDepthLumaMinus8, err = readUe(br) @@ -315,17 +178,17 @@ func NewSPS(rbsp []byte, showPacket bool) (*SPS, error) { if err != nil { return nil, errors.Wrap(err, "could not read QPrimeYZeroTransformBypass") } - sps.QPrimeYZeroTransformBypass = b == 1 + sps.QPPrimeYZeroTransformBypassFlag = b == 1 b, err = br.ReadBits(1) if err != nil { return nil, errors.Wrap(err, "could not read SeqScalingMatrixPresent") } - sps.SeqScalingMatrixPresent = b == 1 + sps.SeqScalingMatrixPresentFlag = b == 1 - if sps.SeqScalingMatrixPresent { + if sps.SeqScalingMatrixPresentFlag { max := 12 - if sps.ChromaFormat != chroma444 { + if sps.ChromaFormatIDC != chroma444 { max = 8 } logger.Printf("debug: \tbuilding Scaling matrix for %d elements\n", max) @@ -334,9 +197,9 @@ func NewSPS(rbsp []byte, showPacket bool) (*SPS, error) { if err != nil { return nil, errors.Wrap(err, "could not read SeqScalingList") } - sps.SeqScalingList = append(sps.SeqScalingList, b == 1) + sps.SeqScalingListPresentFlag = append(sps.SeqScalingListPresentFlag, b == 1) - if sps.SeqScalingList[i] { + if sps.SeqScalingListPresentFlag[i] { if i < 6 { scalingList( br, @@ -380,7 +243,7 @@ func NewSPS(rbsp []byte, showPacket bool) (*SPS, error) { if err != nil { return nil, errors.Wrap(err, "could not read DeltaPicOrderAlwaysZero") } - sps.DeltaPicOrderAlwaysZero = b == 1 + sps.DeltaPicOrderAlwaysZeroFlag = b == 1 sps.OffsetForNonRefPic, err = readSe(br) if err != nil { @@ -420,7 +283,7 @@ func NewSPS(rbsp []byte, showPacket bool) (*SPS, error) { } sps.GapsInFrameNumValueAllowed = b == 1 - sps.PicWidthInMbsMinus1, err = readUe(br) + sps.PicWidthInMBSMinus1, err = readUe(br) if err != nil { return nil, errors.Wrap(err, "could not parse PicWidthInMbsMinus1") } @@ -434,25 +297,25 @@ func NewSPS(rbsp []byte, showPacket bool) (*SPS, error) { if err != nil { return nil, errors.Wrap(err, "could not read FrameMbsOnly") } - sps.FrameMbsOnly = b == 1 + sps.FrameMBSOnlyFlag = b == 1 - if !sps.FrameMbsOnly { + if !sps.FrameMBSOnlyFlag { b, err = br.ReadBits(1) if err != nil { return nil, errors.Wrap(err, "could not read MBAdaptiveFrameField") } - sps.MBAdaptiveFrameField = b == 1 + sps.MBAdaptiveFrameFieldFlag = b == 1 } err = readFlags(br, []flag{ - {&sps.Direct8x8Inference, "Direct8x8Inference"}, - {&sps.FrameCropping, "FrameCropping"}, + {&sps.Direct8x8InferenceFlag, "Direct8x8Inference"}, + {&sps.FrameCroppingFlag, "FrameCropping"}, }) if err != nil { return nil, err } - if sps.FrameCropping { + if sps.FrameCroppingFlag { sps.FrameCropLeftOffset, err = readUe(br) if err != nil { return nil, errors.Wrap(err, "could not parse FrameCropLeftOffset") @@ -478,213 +341,361 @@ func NewSPS(rbsp []byte, showPacket bool) (*SPS, error) { if err != nil { return nil, errors.Wrap(err, "could not read VuiParametersPresent") } - sps.VuiParametersPresent = b == 1 + sps.VUIParametersPresentFlag = b == 1 - if sps.VuiParametersPresent { - // vui_parameters + if sps.VUIParametersPresentFlag { + + } // End VuiParameters Annex E.1.1 + + return &sps, nil +} + +// SPS describes a sequence parameter set as defined by section E.1.1 in the +// Specifications. +type VUIParameters struct { + AspectRatioInfoPresentFlag bool + AspectRatioIDC int + SARWidth int + SARHeight int + OverscanInfoPresentFlag bool + OverscanAppropriateFlag bool + VideoSignalTypePresentFlag bool + VideoFormat int + VideoFullRangeFlag bool + ColorDescriptionPresentFlag bool + ColorPrimaries int + TransferCharacteristics int + MatrixCoefficients int + ChromaLocInfoPresentFlag bool + ChromaSampleLocTypeTopField int + ChromaSampleLocTypeBottomField int + TimingInfoPresentFlag bool + NumUnitsInTick int + TimeScale int + FixedFrameRateFlag bool + NALHRDParametersPresentFlag bool + NALHRDParameters *HRDParameters + VCLHRDParametersPresentFlag bool + VCLHRDParameters *HRDParameters + LowDelayHRDFlag bool + PicStructPresentFlag bool + BitstreamRestrictionFlag bool + MotionVectorsOverPicBoundariesFlag bool + MaxBytesPerPicDenom int + MaxBitsPerMBDenom int + Log2MaxMVLengthHorizontal int + Log2MaxMVLengthVertical int + MaxNumReorderFrames int + MaxDecFrameBuffering int +} + +// NewVUIParameters parses video usability information parameters from br +// following the syntax structure specified in section E.1.1, and returns as a +// new VUIParameters. +func NewVUIParameters(br *bits.BitReader) (*VUIParameters, error) { + p := &VUIParameters{} + + b, err := br.ReadBits(1) + if err != nil { + return nil, errors.Wrap(err, "could not read AspectRatioInfoPresent") + } + p.AspectRatioInfoPresentFlag = b == 1 + + if p.AspectRatioInfoPresentFlag { + b, err = br.ReadBits(8) + if err != nil { + return nil, errors.Wrap(err, "could not read AspectRatio") + } + p.AspectRatioIDC = int(b) + + EXTENDED_SAR := 999 + if p.AspectRatioIDC == EXTENDED_SAR { + b, err = br.ReadBits(16) + if err != nil { + return nil, errors.Wrap(err, "could not read SarWidth") + } + p.SARWidth = int(b) + + b, err = br.ReadBits(16) + if err != nil { + return nil, errors.Wrap(err, "could not read SarHeight") + } + p.SARHeight = int(b) + } + } + + b, err = br.ReadBits(1) + if err != nil { + return nil, errors.Wrap(err, "could not read OverscanInfoPresent") + } + p.OverscanInfoPresentFlag = b == 1 + + if p.OverscanInfoPresentFlag { b, err = br.ReadBits(1) if err != nil { - return nil, errors.Wrap(err, "could not read AspectRatioInfoPresent") + return nil, errors.Wrap(err, "could not read OverscanAppropriate") } - sps.AspectRatioInfoPresent = b == 1 + p.OverscanAppropriateFlag = b == 1 + } - if sps.AspectRatioInfoPresent { - b, err = br.ReadBits(8) - if err != nil { - return nil, errors.Wrap(err, "could not read AspectRatio") - } - sps.AspectRatio = int(b) + b, err = br.ReadBits(1) + if err != nil { + return nil, errors.Wrap(err, "could not read VideoSignalTypePresent") + } + p.VideoSignalTypePresentFlag = b == 1 - EXTENDED_SAR := 999 - if sps.AspectRatio == EXTENDED_SAR { - b, err = br.ReadBits(16) - if err != nil { - return nil, errors.Wrap(err, "could not read SarWidth") - } - sps.SarWidth = int(b) - - b, err = br.ReadBits(16) - if err != nil { - return nil, errors.Wrap(err, "could not read SarHeight") - } - sps.SarHeight = int(b) - } + if p.VideoSignalTypePresentFlag { + b, err = br.ReadBits(3) + if err != nil { + return nil, errors.Wrap(err, "could not read VideoFormat") } + p.VideoFormat = int(b) + } + + if p.VideoSignalTypePresentFlag { + b, err = br.ReadBits(1) + if err != nil { + return nil, errors.Wrap(err, "could not read VideoFullRange") + } + p.VideoFullRangeFlag = b == 1 b, err = br.ReadBits(1) if err != nil { - return nil, errors.Wrap(err, "could not read OverscanInfoPresent") + return nil, errors.Wrap(err, "could not read ColorDescriptionPresent") } - sps.OverscanInfoPresent = b == 1 + p.ColorDescriptionPresentFlag = b == 1 - if sps.OverscanInfoPresent { - b, err = br.ReadBits(1) - if err != nil { - return nil, errors.Wrap(err, "could not read OverscanAppropriate") - } - sps.OverscanAppropriate = b == 1 - } - - b, err = br.ReadBits(1) - if err != nil { - return nil, errors.Wrap(err, "could not read VideoSignalTypePresent") - } - sps.VideoSignalTypePresent = b == 1 - - if sps.VideoSignalTypePresent { - b, err = br.ReadBits(3) - if err != nil { - return nil, errors.Wrap(err, "could not read VideoFormat") - } - sps.VideoFormat = int(b) - } - - if sps.VideoSignalTypePresent { - b, err = br.ReadBits(1) - if err != nil { - return nil, errors.Wrap(err, "could not read VideoFullRange") - } - sps.VideoFullRange = b == 1 - - b, err = br.ReadBits(1) - if err != nil { - return nil, errors.Wrap(err, "could not read ColorDescriptionPresent") - } - sps.ColorDescriptionPresent = b == 1 - - if sps.ColorDescriptionPresent { - err = readFields(br, - []field{ - {&sps.ColorPrimaries, "ColorPrimaries", 8}, - {&sps.TransferCharacteristics, "TransferCharacteristics", 8}, - {&sps.MatrixCoefficients, "MatrixCoefficients", 8}, - }, - ) - if err != nil { - return nil, err - } - } - } - - b, err = br.ReadBits(1) - if err != nil { - return nil, errors.Wrap(err, "could not read ChromaLocInfoPresent") - } - sps.ChromaLocInfoPresent = b == 1 - - if sps.ChromaLocInfoPresent { - sps.ChromaSampleLocTypeTopField, err = readUe(br) - if err != nil { - return nil, errors.Wrap(err, "could not parse ChromaSampleLocTypeTopField") - } - - sps.ChromaSampleLocTypeBottomField, err = readUe(br) - if err != nil { - return nil, errors.Wrap(err, "could not parse ChromaSampleLocTypeBottomField") - } - } - - b, err = br.ReadBits(1) - if err != nil { - return nil, errors.Wrap(err, "could not read TimingInfoPresent") - } - sps.TimingInfoPresent = b == 1 - - if sps.TimingInfoPresent { - err := readFields(br, []field{ - {&sps.NumUnitsInTick, "NumUnitsInTick", 32}, - {&sps.TimeScale, "TimeScale", 32}, - }) + if p.ColorDescriptionPresentFlag { + err = readFields(br, + []field{ + {&p.ColorPrimaries, "ColorPrimaries", 8}, + {&p.TransferCharacteristics, "TransferCharacteristics", 8}, + {&p.MatrixCoefficients, "MatrixCoefficients", 8}, + }, + ) if err != nil { return nil, err } - - b, err = br.ReadBits(1) - if err != nil { - return nil, errors.Wrap(err, "could not read FixedFrameRate") - } - sps.FixedFrameRate = b == 1 } - - b, err = br.ReadBits(1) - if err != nil { - return nil, errors.Wrap(err, "could not read NalHrdParametersPresent") - } - sps.NalHrdParametersPresent = b == 1 - - if sps.NalHrdParametersPresent { - err = hrdParameters() - if err != nil { - return nil, errors.Wrap(err, "could not get hrdParameters") - } - } - - b, err = br.ReadBits(1) - if err != nil { - return nil, errors.Wrap(err, "could not read VclHrdParametersPresent") - } - sps.VclHrdParametersPresent = b == 1 - - if sps.VclHrdParametersPresent { - err = hrdParameters() - if err != nil { - return nil, errors.Wrap(err, "could not get hrdParameters") - } - } - if sps.NalHrdParametersPresent || sps.VclHrdParametersPresent { - b, err = br.ReadBits(1) - if err != nil { - return nil, errors.Wrap(err, "could not read LowHrdDelay") - } - sps.LowHrdDelay = b == 1 - } - - err := readFlags(br, []flag{ - {&sps.PicStructPresent, "PicStructPresent"}, - {&sps.BitstreamRestriction, "BitStreamRestriction"}, - }) - - if sps.BitstreamRestriction { - b, err = br.ReadBits(1) - if err != nil { - return nil, errors.Wrap(err, "could not read MotionVectorsOverPicBoundaries") - } - sps.MotionVectorsOverPicBoundaries = b == 1 - - sps.MaxBytesPerPicDenom, err = readUe(br) - if err != nil { - return nil, errors.Wrap(err, "could not parse MaxBytesPerPicDenom") - } - - sps.MaxBitsPerMbDenom, err = readUe(br) - if err != nil { - return nil, errors.Wrap(err, "could not parse MaxBitsPerMbDenom") - } - - sps.Log2MaxMvLengthHorizontal, err = readUe(br) - if err != nil { - return nil, errors.Wrap(err, "could not parse Log2MaxMvLengthHorizontal") - } - - sps.Log2MaxMvLengthVertical, err = readUe(br) - if err != nil { - return nil, errors.Wrap(err, "could not parse Log2MaxMvLengthVertical") - } - - sps.MaxNumReorderFrames, err = readUe(br) - if err != nil { - return nil, errors.Wrap(err, "could not parse MaxNumReorderFrames") - } - - sps.MaxDecFrameBuffering, err = readUe(br) - if err != nil { - return nil, errors.Wrap(err, "could not parse MaxDecFrameBuffering") - } - } - - } // End VuiParameters Annex E.1.1 - if showPacket { - debugPacket("SPS", sps) } - return &sps, nil + + b, err = br.ReadBits(1) + if err != nil { + return nil, errors.Wrap(err, "could not read ChromaLocInfoPresent") + } + p.ChromaLocInfoPresentFlag = b == 1 + + if p.ChromaLocInfoPresentFlag { + p.ChromaSampleLocTypeTopField, err = readUe(br) + if err != nil { + return nil, errors.Wrap(err, "could not parse ChromaSampleLocTypeTopField") + } + + p.ChromaSampleLocTypeBottomField, err = readUe(br) + if err != nil { + return nil, errors.Wrap(err, "could not parse ChromaSampleLocTypeBottomField") + } + } + + b, err = br.ReadBits(1) + if err != nil { + return nil, errors.Wrap(err, "could not read TimingInfoPresent") + } + p.TimingInfoPresentFlag = b == 1 + + if p.TimingInfoPresentFlag { + err := readFields(br, []field{ + {&p.NumUnitsInTick, "NumUnitsInTick", 32}, + {&p.TimeScale, "TimeScale", 32}, + }) + if err != nil { + return nil, err + } + + b, err = br.ReadBits(1) + if err != nil { + return nil, errors.Wrap(err, "could not read FixedFrameRate") + } + p.FixedFrameRateFlag = b == 1 + } + + b, err = br.ReadBits(1) + if err != nil { + return nil, errors.Wrap(err, "could not read NalHrdParametersPresent") + } + p.NALHRDParametersPresentFlag = b == 1 + + if p.NALHRDParametersPresentFlag { + p.NALHRDParameters, err = NewHRDParameters(br) + if err != nil { + return nil, errors.Wrap(err, "could not get hrdParameters") + } + } + + b, err = br.ReadBits(1) + if err != nil { + return nil, errors.Wrap(err, "could not read VclHrdParametersPresent") + } + p.VCLHRDParametersPresentFlag = b == 1 + + if p.VCLHRDParametersPresentFlag { + p.VCLHRDParameters, err = NewHRDParameters(br) + if err != nil { + return nil, errors.Wrap(err, "could not get hrdParameters") + } + } + if p.NALHRDParametersPresentFlag || p.VCLHRDParametersPresentFlag { + b, err = br.ReadBits(1) + if err != nil { + return nil, errors.Wrap(err, "could not read LowHrdDelay") + } + p.LowDelayHRDFlag = b == 1 + } + + err = readFlags(br, []flag{ + {&p.PicStructPresentFlag, "PicStructPresent"}, + {&p.BitstreamRestrictionFlag, "BitStreamRestriction"}, + }) + + if p.BitstreamRestrictionFlag { + b, err = br.ReadBits(1) + if err != nil { + return nil, errors.Wrap(err, "could not read MotionVectorsOverPicBoundaries") + } + p.MotionVectorsOverPicBoundariesFlag = b == 1 + + p.MaxBytesPerPicDenom, err = readUe(br) + if err != nil { + return nil, errors.Wrap(err, "could not parse MaxBytesPerPicDenom") + } + + p.MaxBitsPerMBDenom, err = readUe(br) + if err != nil { + return nil, errors.Wrap(err, "could not parse MaxBitsPerMbDenom") + } + + p.Log2MaxMVLengthHorizontal, err = readUe(br) + if err != nil { + return nil, errors.Wrap(err, "could not parse Log2MaxMvLengthHorizontal") + } + + p.Log2MaxMVLengthVertical, err = readUe(br) + if err != nil { + return nil, errors.Wrap(err, "could not parse Log2MaxMvLengthVertical") + } + + p.MaxNumReorderFrames, err = readUe(br) + if err != nil { + return nil, errors.Wrap(err, "could not parse MaxNumReorderFrames") + } + + p.MaxDecFrameBuffering, err = readUe(br) + if err != nil { + return nil, errors.Wrap(err, "could not parse MaxDecFrameBuffering") + } + } + return p, nil +} + +// HRDParameters describes hypothetical reference decoder parameters as defined +// by section E.1.2 in the specifications. +type HRDParameters struct { + CPBCntMinus1 int + BitRateScale int + CPBSizeScale int + BitRateValueMinus1 []int + CPBSizeValueMinus1 []int + CBRFlag []bool + InitialCPBRemovalDelayLenMinus1 int + CPBRemovalDelayLenMinus1 int + DPBOutputDelayLenMinus1 int + TimeOffsetLen int +} + +// NewHRDParameters parses hypothetical reference decoder parameter from br +// following the syntax structure specified in section E.1.2, and returns as a +// new HRDParameters. +func NewHRDParameters(br *bits.BitReader) (*HRDParameters, error) { + h := &HRDParameters{} + var err error + h.CPBCntMinus1, err = readUe(br) + if err != nil { + return nil, errors.Wrap(err, "could not parse CPBCntMinus1") + } + + err = readFields(br, []field{ + {&h.BitRateScale, "BitRateScale", 4}, + {&h.CPBSizeScale, "CPBSizeScale", 4}, + }) + if err != nil { + return nil, err + } + + // SchedSelIdx E1.2 + for sseli := 0; sseli <= h.CPBCntMinus1; sseli++ { + ue, err := readUe(br) + if err != nil { + return nil, errors.Wrap(err, "could not parse BitRateValueMinus1") + } + h.BitRateValueMinus1 = append(h.BitRateValueMinus1, ue) + + ue, err = readUe(br) + if err != nil { + return nil, errors.Wrap(err, "could not parse CPBSizeValueMinus1") + } + h.CPBSizeValueMinus1 = append(h.CPBSizeValueMinus1, ue) + + if v, _ := br.ReadBits(1); v == 1 { + h.CBRFlag = append(h.CBRFlag, true) + } else { + h.CBRFlag = append(h.CBRFlag, false) + } + + err = readFields(br, + []field{ + {&h.InitialCPBRemovalDelayLenMinus1, "InitialCPBRemovalDelayLenMinus1", 5}, + {&h.CPBRemovalDelayLenMinus1, "CPBRemovalDelayLenMinus1", 5}, + {&h.DPBOutputDelayLenMinus1, "DpbOutputDelayLenMinus1", 5}, + {&h.TimeOffsetLen, "TimeOffsetLen", 5}, + }, + ) + if err != nil { + return nil, err + } + } + return h, nil +} + +func isInList(l []int, term int) bool { + for _, m := range l { + if m == term { + return true + } + } + return false +} + +func scalingList(br *bits.BitReader, scalingList []int, sizeOfScalingList int, defaultScalingMatrix []int) error { + lastScale := 8 + nextScale := 8 + for i := 0; i < sizeOfScalingList; i++ { + if nextScale != 0 { + deltaScale, err := readSe(br) + if err != nil { + return errors.Wrap(err, "could not parse deltaScale") + } + nextScale = (lastScale + deltaScale + 256) % 256 + if i == 0 && nextScale == 0 { + // Scaling list should use the default list for this point in the matrix + _ = defaultScalingMatrix + } + } + if nextScale == 0 { + scalingList[i] = lastScale + } else { + scalingList[i] = nextScale + } + lastScale = scalingList[i] + } + return nil }