diff --git a/pkg/codecs/h265/sps.go b/pkg/codecs/h265/sps.go index 2968f47..eabc639 100644 --- a/pkg/codecs/h265/sps.go +++ b/pkg/codecs/h265/sps.go @@ -27,6 +27,64 @@ var subHeightC = []uint32{ 1, } +func min(a, b int) int { + if a < b { + return a + } + return b +} + +// SPS_ScalingListData is a scaling list data. +type SPS_ScalingListData struct { + ScalingListPredModeFlag [4][6]bool + ScalingListPredMatrixIdDelta [4][6]uint32 + ScalingListDcCoefMinus8 [4][6]int32 +} + +func (d *SPS_ScalingListData) unmarshal(buf []byte, pos *int) error { + for sizeId := 0; sizeId < 4; sizeId++ { + var matrixIdIncr int + if sizeId == 3 { + matrixIdIncr = 3 + } else { + matrixIdIncr = 1 + } + + for matrixId := 0; matrixId < 6; matrixId += matrixIdIncr { + var err error + d.ScalingListPredModeFlag[sizeId][matrixId], err = bits.ReadFlag(buf, pos) + if err != nil { + return err + } + + if !d.ScalingListPredModeFlag[sizeId][matrixId] { + d.ScalingListPredMatrixIdDelta[sizeId][matrixId], err = bits.ReadGolombUnsigned(buf, pos) + if err != nil { + return err + } + } else { + coefNum := min(64, 1<<(4+(sizeId<<1))) + + if sizeId > 1 { + d.ScalingListDcCoefMinus8[sizeId-2][matrixId], err = bits.ReadGolombSigned(buf, pos) + if err != nil { + return err + } + } + + for i := 0; i < coefNum; i++ { + _, err = bits.ReadGolombSigned(buf, pos) // scalingListDeltaCoef + if err != nil { + return err + } + } + } + } + } + + return nil +} + // SPS_DefaultDisplayWindow is a default display window. type SPS_DefaultDisplayWindow struct { //nolint:revive LeftOffset uint32 @@ -626,7 +684,7 @@ type SPS struct { MaxTransformHierarchyDepthInter uint32 MaxTransformHierarchyDepthIntra uint32 ScalingListEnabledFlag bool - ScalingListDataPresentFlag bool + ScalingListData *SPS_ScalingListData AmpEnabledFlag bool SampleAdaptiveOffsetEnabledFlag bool PcmEnabledFlag bool @@ -799,13 +857,17 @@ func (s *SPS) Unmarshal(buf []byte) error { } if s.ScalingListEnabledFlag { - s.ScalingListDataPresentFlag, err = bits.ReadFlag(buf, &pos) + scalingListDataPresentFlag, err := bits.ReadFlag(buf, &pos) if err != nil { return err } - if s.ScalingListDataPresentFlag { - return fmt.Errorf("ScalingListDataPresentFlag not supported yet") + if scalingListDataPresentFlag { + s.ScalingListData = &SPS_ScalingListData{} + err := s.ScalingListData.unmarshal(buf, &pos) + if err != nil { + return err + } } } diff --git a/pkg/codecs/h265/sps_test.go b/pkg/codecs/h265/sps_test.go index 938cf62..dd71931 100644 --- a/pkg/codecs/h265/sps_test.go +++ b/pkg/codecs/h265/sps_test.go @@ -421,6 +421,128 @@ var casesSPS = []struct { 720, 50, }, + { + "scaling list data", + []byte{ + 0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, + 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x96, 0xa0, 0x01, 0x00, 0x20, 0x06, 0x01, + 0x63, 0x4b, 0xb9, 0x08, 0x4e, 0x51, 0x11, 0x18, + 0x8e, 0x09, 0x30, 0x24, 0x40, 0xdd, 0x28, 0x52, + 0x1c, 0xc1, 0x27, 0x06, 0x18, 0x1b, 0xb2, 0xa1, + 0x91, 0x58, 0xae, 0x16, 0xc0, 0xf1, 0x07, 0xd0, + 0x80, 0x20, 0x82, 0x8c, 0x16, 0x70, 0x35, 0x7c, + 0xa5, 0x24, 0x99, 0x3a, 0xaf, 0x4b, 0xa4, 0xbb, + 0x49, 0x2f, 0x20, 0x81, 0x11, 0x32, 0x0c, 0x18, + 0x30, 0x68, 0xd1, 0x80, 0xb0, 0x08, 0x10, 0x20, + 0xc0, 0x80, 0x0f, 0x81, 0xfc, 0x1f, 0x7c, 0xa3, + 0x22, 0x30, 0x87, 0x19, 0xe3, 0x3e, 0x3b, 0xf0, + 0x97, 0xf0, 0xc7, 0xe1, 0x0f, 0x83, 0x0f, 0x07, + 0xdf, 0xf2, 0xa1, 0x12, 0x34, 0x4e, 0x4f, 0x25, + 0x5c, 0x95, 0xb9, 0x29, 0x5b, 0x9a, 0x23, 0x13, + 0x10, 0x08, 0x01, 0x04, 0x10, 0x82, 0x10, 0x20, + 0x01, 0x03, 0x02, 0x08, 0x1f, 0xbf, 0xf0, 0x80, + 0x42, 0x10, 0xc2, 0x1c, 0x31, 0xe1, 0x0f, 0x84, + 0x3f, 0x08, 0x7f, 0x0a, 0x7e, 0x14, 0xf8, 0x3e, + 0xff, 0xfc, 0xa5, 0x26, 0x4c, 0x9d, 0x57, 0xa5, + 0xd2, 0x5d, 0xa4, 0x97, 0x90, 0x40, 0x88, 0x99, + 0x06, 0x0c, 0x18, 0x34, 0x68, 0xc0, 0x58, 0x04, + 0x08, 0x10, 0x60, 0x40, 0x07, 0xc0, 0xfe, 0x0f, + 0xbe, 0x51, 0x04, 0x88, 0xc2, 0x1c, 0x67, 0x8c, + 0xf8, 0xef, 0xc2, 0x5f, 0xc3, 0x1f, 0x84, 0x3e, + 0x0c, 0x3c, 0x1f, 0x7f, 0xca, 0x88, 0x49, 0x1a, + 0x27, 0x27, 0x92, 0xae, 0x4a, 0xdc, 0x94, 0xad, + 0xcd, 0x11, 0x89, 0x88, 0x04, 0x00, 0x82, 0x08, + 0x41, 0x08, 0x10, 0x00, 0x81, 0x81, 0x04, 0x0f, + 0xdf, 0xf8, 0x42, 0x10, 0x84, 0x30, 0x87, 0x0c, + 0x78, 0x43, 0xe1, 0x0f, 0xc2, 0x1f, 0xc2, 0x9f, + 0x85, 0x3e, 0x0f, 0xbf, 0xff, 0x29, 0x49, 0x93, + 0x27, 0x55, 0xe9, 0x74, 0x97, 0x69, 0x25, 0xe4, + 0x10, 0x22, 0x26, 0x41, 0x83, 0x06, 0x0d, 0x1a, + 0x30, 0x16, 0x01, 0x02, 0x04, 0x18, 0x10, 0x01, + 0xf0, 0x3f, 0x83, 0xef, 0xa2, 0x12, 0x46, 0x89, + 0xc9, 0xe4, 0xab, 0x92, 0xb7, 0x25, 0x2b, 0x73, + 0x44, 0x62, 0x62, 0x01, 0x00, 0x20, 0x82, 0x10, + 0x42, 0x04, 0x00, 0x20, 0x60, 0x41, 0x03, 0xf7, + 0xfd, 0x3c, 0xb8, 0x9a, 0x81, 0x01, 0x01, 0x02, + 0x00, 0x00, 0x03, 0x00, 0xc8, 0x00, 0x00, 0x17, + 0x70, 0xe0, 0x0b, 0xbc, 0xae, 0x00, 0x03, 0xe8, + 0x00, 0x00, 0x03, 0x01, 0xf4, 0x00, 0x00, 0x03, + 0x00, 0x7d, 0x00, 0x00, 0x03, 0x00, 0x3e, 0x80, + 0x05, 0x70, 0x80, 0x41, + }, + SPS{ + TemporalIDNestingFlag: true, + ProfileTierLevel: SPS_ProfileTierLevel{ + GeneralProfileIdc: 1, + GeneralProfileCompatibilityFlag: [32]bool{ + false, true, true, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + }, + GeneralProgressiveSourceFlag: true, + GeneralFrameOnlyConstraintFlag: true, + GeneralLevelIdc: 150, + }, + ChromaFormatIdc: 1, + PicWidthInLumaSamples: 2048, + PicHeightInLumaSamples: 1536, + Log2MaxPicOrderCntLsbMinus4: 12, + MaxDecPicBufferingMinus1: []uint32{1}, + MaxNumReorderPics: []uint32{0}, + MaxLatencyIncreasePlus1: []uint32{0}, + Log2DiffMaxMinLumaCodingBlockSize: 2, + Log2DiffMaxMinLumaTransformBlockSize: 3, + MaxTransformHierarchyDepthInter: 3, + MaxTransformHierarchyDepthIntra: 3, + ScalingListEnabledFlag: true, + ScalingListData: &SPS_ScalingListData{ + ScalingListPredModeFlag: [4][6]bool{ + {true, true, false, true, true, false}, + {true, true, false, true, true, false}, + {true, true, false, true, true, false}, + {true, false, false, true, false, false}, + }, + ScalingListPredMatrixIdDelta: [4][6]uint32{ + {0x0, 0x0, 0x1, 0x0, 0x0, 0x1}, + {0x0, 0x0, 0x1, 0x0, 0x0, 0x1}, + {0x0, 0x0, 0x1, 0x0, 0x0, 0x1}, + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + ScalingListDcCoefMinus8: [4][6]int32{ + {-2, -2, 0, 1, 8, 0}, + {-2, 0, 0, 1, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + }, + }, + SampleAdaptiveOffsetEnabledFlag: true, + ShortTermRefPicSets: []*SPS_ShortTermRefPicSet{ + {}, + { + NumNegativePics: 1, + DeltaPocS0: []int32{-1}, + UsedByCurrPicS0Flag: []bool{true}, + }, + }, + VUI: &SPS_VUI{ + VideoSignalTypePresentFlag: true, + VideoFormat: 5, + ColourDescriptionPresentFlag: true, + ColourPrimaries: 2, + TransferCharacteristics: 2, + MatrixCoefficients: 2, + TimingInfo: &SPS_TimingInfo{ + NumUnitsInTick: 100, + TimeScale: 3000, + }, + }, + }, + 2048, + 1536, + 30, + }, } func TestSPSUnmarshal(t *testing.T) { diff --git a/pkg/codecs/h265/testdata/fuzz/FuzzSPSUnmarshal/301abd63e9c3f408 b/pkg/codecs/h265/testdata/fuzz/FuzzSPSUnmarshal/301abd63e9c3f408 new file mode 100644 index 0000000..1660dd2 --- /dev/null +++ b/pkg/codecs/h265/testdata/fuzz/FuzzSPSUnmarshal/301abd63e9c3f408 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("B00000000000000221001290B") diff --git a/pkg/codecs/h265/testdata/fuzz/FuzzSPSUnmarshal/618d21ef3d21de57 b/pkg/codecs/h265/testdata/fuzz/FuzzSPSUnmarshal/618d21ef3d21de57 new file mode 100644 index 0000000..61a79f7 --- /dev/null +++ b/pkg/codecs/h265/testdata/fuzz/FuzzSPSUnmarshal/618d21ef3d21de57 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("B00000000000000\xddV127\xd291?\xef\xfd72\xeb\xc32z72\xaa\xad") diff --git a/pkg/codecs/h265/testdata/fuzz/FuzzSPSUnmarshal/96e96c5ed5b1abd1 b/pkg/codecs/h265/testdata/fuzz/FuzzSPSUnmarshal/96e96c5ed5b1abd1 new file mode 100644 index 0000000..242ebcb --- /dev/null +++ b/pkg/codecs/h265/testdata/fuzz/FuzzSPSUnmarshal/96e96c5ed5b1abd1 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("B000000000000001Z007190")