package h265 import ( "fmt" "github.com/bluenviron/gortsplib/v3/pkg/bits" "github.com/bluenviron/gortsplib/v3/pkg/codecs/h264" ) const ( maxNegativePics = 255 maxPositivePics = 255 maxShortTermRefPics = 64 ) var subWidthC = []uint32{ 1, 2, 2, 1, } var subHeightC = []uint32{ 1, 2, 1, 1, } // SPS_DefaultDisplayWindow is a default display window. type SPS_DefaultDisplayWindow struct { //nolint:revive LeftOffset uint32 RightOffset uint32 TopOffset uint32 BottomOffset uint32 } func (w *SPS_DefaultDisplayWindow) unmarshal(buf []byte, pos *int) error { var err error w.LeftOffset, err = bits.ReadGolombUnsigned(buf, pos) if err != nil { return err } w.RightOffset, err = bits.ReadGolombUnsigned(buf, pos) if err != nil { return err } w.TopOffset, err = bits.ReadGolombUnsigned(buf, pos) if err != nil { return err } w.BottomOffset, err = bits.ReadGolombUnsigned(buf, pos) if err != nil { return err } return nil } // SPS_TimingInfo is a timing info. type SPS_TimingInfo struct { //nolint:revive NumUnitsInTick uint32 TimeScale uint32 POCProportionalToTimingFlag bool // POCProportionalToTimingFlag == true NumTicksPOCDiffOneMinus1 uint32 } func (t *SPS_TimingInfo) unmarshal(buf []byte, pos *int) error { err := bits.HasSpace(buf, *pos, 32+32+1) if err != nil { return err } t.NumUnitsInTick = uint32(bits.ReadBitsUnsafe(buf, pos, 32)) t.TimeScale = uint32(bits.ReadBitsUnsafe(buf, pos, 32)) t.POCProportionalToTimingFlag = bits.ReadFlagUnsafe(buf, pos) if t.POCProportionalToTimingFlag { t.NumTicksPOCDiffOneMinus1, err = bits.ReadGolombUnsigned(buf, pos) if err != nil { return err } } return nil } // SPS_VUI is a video usability information. type SPS_VUI struct { //nolint:revive AspectRatioInfoPresentFlag bool // AspectRatioInfoPresentFlag == true AspectRatioIdc uint8 SarWidth uint16 SarHeight uint16 OverscanInfoPresentFlag bool // OverscanInfoPresentFlag == true OverscanAppropriateFlag bool VideoSignalTypePresentFlag bool // VideoSignalTypePresentFlag == true VideoFormat uint8 VideoFullRangeFlag bool ColourDescriptionPresentFlag bool // ColourDescriptionPresentFlag == true ColourPrimaries uint8 TransferCharacteristics uint8 MatrixCoefficients uint8 ChromaLocInfoPresentFlag bool // ChromaLocInfoPresentFlag == true ChromaSampleLocTypeTopField uint32 ChromaSampleLocTypeBottomField uint32 NeutralChromaIndicationFlag bool FieldSeqFlag bool FrameFieldInfoPresentFlag bool DefaultDisplayWindow *SPS_DefaultDisplayWindow TimingInfo *SPS_TimingInfo } func (v *SPS_VUI) unmarshal(buf []byte, pos *int) error { var err error v.AspectRatioInfoPresentFlag, err = bits.ReadFlag(buf, pos) if err != nil { return err } if v.AspectRatioInfoPresentFlag { tmp, err := bits.ReadBits(buf, pos, 8) if err != nil { return err } v.AspectRatioIdc = uint8(tmp) if v.AspectRatioIdc == 255 { // EXTENDED_SAR err := bits.HasSpace(buf, *pos, 32) if err != nil { return err } v.SarWidth = uint16(bits.ReadBitsUnsafe(buf, pos, 16)) v.SarHeight = uint16(bits.ReadBitsUnsafe(buf, pos, 16)) } } v.OverscanInfoPresentFlag, err = bits.ReadFlag(buf, pos) if err != nil { return err } if v.OverscanInfoPresentFlag { v.OverscanAppropriateFlag, err = bits.ReadFlag(buf, pos) if err != nil { return err } } v.VideoSignalTypePresentFlag, err = bits.ReadFlag(buf, pos) if err != nil { return err } if v.VideoSignalTypePresentFlag { err := bits.HasSpace(buf, *pos, 5) if err != nil { return err } v.VideoFormat = uint8(bits.ReadBitsUnsafe(buf, pos, 3)) v.VideoFullRangeFlag = bits.ReadFlagUnsafe(buf, pos) v.ColourDescriptionPresentFlag = bits.ReadFlagUnsafe(buf, pos) if v.ColourDescriptionPresentFlag { err := bits.HasSpace(buf, *pos, 24) if err != nil { return err } v.ColourPrimaries = uint8(bits.ReadBitsUnsafe(buf, pos, 8)) v.TransferCharacteristics = uint8(bits.ReadBitsUnsafe(buf, pos, 8)) v.MatrixCoefficients = uint8(bits.ReadBitsUnsafe(buf, pos, 8)) } } v.ChromaLocInfoPresentFlag, err = bits.ReadFlag(buf, pos) if err != nil { return err } if v.ChromaLocInfoPresentFlag { v.ChromaSampleLocTypeTopField, err = bits.ReadGolombUnsigned(buf, pos) if err != nil { return err } v.ChromaSampleLocTypeBottomField, err = bits.ReadGolombUnsigned(buf, pos) if err != nil { return err } } v.NeutralChromaIndicationFlag, err = bits.ReadFlag(buf, pos) if err != nil { return err } v.FieldSeqFlag, err = bits.ReadFlag(buf, pos) if err != nil { return err } v.FrameFieldInfoPresentFlag, err = bits.ReadFlag(buf, pos) if err != nil { return err } defaultDisplayWindowFlag, err := bits.ReadFlag(buf, pos) if err != nil { return err } if defaultDisplayWindowFlag { v.DefaultDisplayWindow = &SPS_DefaultDisplayWindow{} err := v.DefaultDisplayWindow.unmarshal(buf, pos) if err != nil { return err } } else { v.DefaultDisplayWindow = nil } timingInfoPresentFlag, err := bits.ReadFlag(buf, pos) if err != nil { return err } if timingInfoPresentFlag { v.TimingInfo = &SPS_TimingInfo{} err := v.TimingInfo.unmarshal(buf, pos) if err != nil { return err } } else { v.TimingInfo = nil } return nil } // SPS_ProfileTierLevel is a profile level tier of a SPS. type SPS_ProfileTierLevel struct { //nolint:revive GeneralProfileSpace uint8 GeneralTierFlag uint8 GeneralProfileIdc uint8 GeneralProfileCompatibilityFlag [32]bool GeneralProgressiveSourceFlag bool GeneralInterlacedSourceFlag bool GeneralNonPackedConstraintFlag bool GeneralFrameOnlyConstraintFlag bool GeneralMax12bitConstraintFlag bool GeneralMax10bitConstraintFlag bool GeneralMax8bitConstraintFlag bool GeneralMax422ChromeConstraintFlag bool GeneralMax420ChromaConstraintFlag bool GeneralMaxMonochromeConstraintFlag bool GeneralIntraConstraintFlag bool GeneralOnePictureOnlyConstraintFlag bool GeneralLowerBitRateConstraintFlag bool GeneralMax14BitConstraintFlag bool GeneralLevelIdc uint8 SubLayerProfilePresentFlag []bool SubLayerLevelPresentFlag []bool } func (p *SPS_ProfileTierLevel) unmarshal(buf []byte, pos *int, maxSubLayersMinus1 uint8) error { err := bits.HasSpace(buf, *pos, 8+32+12+34+8) if err != nil { return err } p.GeneralProfileSpace = uint8(bits.ReadBitsUnsafe(buf, pos, 2)) p.GeneralTierFlag = uint8(bits.ReadBitsUnsafe(buf, pos, 1)) p.GeneralProfileIdc = uint8(bits.ReadBitsUnsafe(buf, pos, 5)) for j := 0; j < 32; j++ { p.GeneralProfileCompatibilityFlag[j] = bits.ReadFlagUnsafe(buf, pos) } p.GeneralProgressiveSourceFlag = bits.ReadFlagUnsafe(buf, pos) p.GeneralInterlacedSourceFlag = bits.ReadFlagUnsafe(buf, pos) p.GeneralNonPackedConstraintFlag = bits.ReadFlagUnsafe(buf, pos) p.GeneralFrameOnlyConstraintFlag = bits.ReadFlagUnsafe(buf, pos) p.GeneralMax12bitConstraintFlag = bits.ReadFlagUnsafe(buf, pos) p.GeneralMax10bitConstraintFlag = bits.ReadFlagUnsafe(buf, pos) p.GeneralMax8bitConstraintFlag = bits.ReadFlagUnsafe(buf, pos) p.GeneralMax422ChromeConstraintFlag = bits.ReadFlagUnsafe(buf, pos) p.GeneralMax420ChromaConstraintFlag = bits.ReadFlagUnsafe(buf, pos) p.GeneralMaxMonochromeConstraintFlag = bits.ReadFlagUnsafe(buf, pos) p.GeneralIntraConstraintFlag = bits.ReadFlagUnsafe(buf, pos) p.GeneralOnePictureOnlyConstraintFlag = bits.ReadFlagUnsafe(buf, pos) p.GeneralLowerBitRateConstraintFlag = bits.ReadFlagUnsafe(buf, pos) if p.GeneralProfileIdc == 5 || p.GeneralProfileIdc == 9 || p.GeneralProfileIdc == 10 || p.GeneralProfileIdc == 11 || p.GeneralProfileCompatibilityFlag[5] || p.GeneralProfileCompatibilityFlag[9] || p.GeneralProfileCompatibilityFlag[10] || p.GeneralProfileCompatibilityFlag[11] { p.GeneralMax14BitConstraintFlag = bits.ReadFlagUnsafe(buf, pos) *pos += 34 } else { *pos += 35 } p.GeneralLevelIdc = uint8(bits.ReadBitsUnsafe(buf, pos, 8)) if maxSubLayersMinus1 > 0 { p.SubLayerProfilePresentFlag = make([]bool, maxSubLayersMinus1) p.SubLayerLevelPresentFlag = make([]bool, maxSubLayersMinus1) err := bits.HasSpace(buf, *pos, int(2*maxSubLayersMinus1)) if err != nil { return err } for j := uint8(0); j < maxSubLayersMinus1; j++ { p.SubLayerProfilePresentFlag[j] = bits.ReadFlagUnsafe(buf, pos) p.SubLayerLevelPresentFlag[j] = bits.ReadFlagUnsafe(buf, pos) } } if maxSubLayersMinus1 > 0 { err := bits.HasSpace(buf, *pos, int(8-maxSubLayersMinus1)*2) if err != nil { return err } *pos += int(8-maxSubLayersMinus1) * 2 } for i := uint8(0); i < maxSubLayersMinus1; i++ { if p.SubLayerProfilePresentFlag[i] { return fmt.Errorf("SubLayerProfilePresentFlag not supported yet") } if p.SubLayerLevelPresentFlag[i] { return fmt.Errorf("SubLayerLevelPresentFlag not supported yet") } } return nil } // SPS_ConformanceWindow is a conformance window of a SPS. type SPS_ConformanceWindow struct { //nolint:revive LeftOffset uint32 RightOffset uint32 TopOffset uint32 BottomOffset uint32 } func (c *SPS_ConformanceWindow) unmarshal(buf []byte, pos *int) error { var err error c.LeftOffset, err = bits.ReadGolombUnsigned(buf, pos) if err != nil { return err } c.RightOffset, err = bits.ReadGolombUnsigned(buf, pos) if err != nil { return err } c.TopOffset, err = bits.ReadGolombUnsigned(buf, pos) if err != nil { return err } c.BottomOffset, err = bits.ReadGolombUnsigned(buf, pos) if err != nil { return err } return nil } // SPS_ShortTermRefPicSet is a short-term reference picture set. type SPS_ShortTermRefPicSet struct { //nolint:revive InterRefPicSetPredictionFlag bool DeltaIdxMinus1 uint32 DeltaRpsSign bool AbsDeltaRpsMinus1 uint32 NumNegativePics uint32 NumPositivePics uint32 DeltaPocS0Minus1 []uint32 UsedByCurrPicS0Flag []bool DeltaPocS1Minus1 []uint32 UsedByCurrPicS1Flag []bool } func (r *SPS_ShortTermRefPicSet) unmarshal(buf []byte, pos *int, stRpsIdx uint32, numShortTermRefPicSets uint32, shortTermRefPicSets []*SPS_ShortTermRefPicSet, ) error { var err error if stRpsIdx != 0 { r.InterRefPicSetPredictionFlag, err = bits.ReadFlag(buf, pos) if err != nil { return err } } if r.InterRefPicSetPredictionFlag { if stRpsIdx == numShortTermRefPicSets { r.DeltaIdxMinus1, err = bits.ReadGolombUnsigned(buf, pos) if err != nil { return err } } r.DeltaRpsSign, err = bits.ReadFlag(buf, pos) if err != nil { return err } r.AbsDeltaRpsMinus1, err = bits.ReadGolombUnsigned(buf, pos) if err != nil { return err } refRpsIdx := stRpsIdx - (r.DeltaIdxMinus1 + 1) numDeltaPocs := shortTermRefPicSets[refRpsIdx].NumNegativePics + shortTermRefPicSets[refRpsIdx].NumPositivePics for j := uint32(0); j <= numDeltaPocs; j++ { usedByCurrPicFlag, err := bits.ReadFlag(buf, pos) if err != nil { return err } if usedByCurrPicFlag { _, err := bits.ReadGolombUnsigned(buf, pos) // use_delta_flag if err != nil { return err } } } } else { r.NumNegativePics, err = bits.ReadGolombUnsigned(buf, pos) if err != nil { return err } r.NumPositivePics, err = bits.ReadGolombUnsigned(buf, pos) if err != nil { return err } if r.NumNegativePics > 0 { if r.NumNegativePics > maxNegativePics { return fmt.Errorf("num_negative_pics exceeds %d", maxNegativePics) } r.DeltaPocS0Minus1 = make([]uint32, r.NumNegativePics) r.UsedByCurrPicS0Flag = make([]bool, r.NumNegativePics) for i := uint32(0); i < r.NumNegativePics; i++ { r.DeltaPocS0Minus1[i], err = bits.ReadGolombUnsigned(buf, pos) if err != nil { return err } r.UsedByCurrPicS0Flag[i], err = bits.ReadFlag(buf, pos) if err != nil { return err } } } if r.NumPositivePics > 0 { if r.NumPositivePics > maxPositivePics { return fmt.Errorf("num_positive_pics exceeds %d", maxPositivePics) } r.DeltaPocS1Minus1 = make([]uint32, r.NumPositivePics) r.UsedByCurrPicS1Flag = make([]bool, r.NumPositivePics) for i := uint32(0); i < r.NumPositivePics; i++ { r.DeltaPocS1Minus1[i], err = bits.ReadGolombUnsigned(buf, pos) if err != nil { return err } r.UsedByCurrPicS1Flag[i], err = bits.ReadFlag(buf, pos) if err != nil { return err } } } } return nil } // SPS is a H265 sequence parameter set. type SPS struct { VPSID uint8 MaxSubLayersMinus1 uint8 TemporalIDNestingFlag bool ProfileTierLevel SPS_ProfileTierLevel ID uint8 ChromaFormatIdc uint32 SeparateColourPlaneFlag bool PicWidthInLumaSamples uint32 PicHeightInLumaSamples uint32 ConformanceWindow *SPS_ConformanceWindow BitDepthLumaMinus8 uint32 BitDepthChromaMinus8 uint32 Log2MaxPicOrderCntLsbMinus4 uint32 SubLayerOrderingInfoPresentFlag bool MaxDecPicBufferingMinus1 []uint32 MaxNumReorderPics []uint32 MaxLatencyIncreasePlus1 []uint32 Log2MinLumaCodingBlockSizeMinus3 uint32 Log2DiffMaxMinLumaCodingBlockSize uint32 Log2MinLumaTransformBlockSizeMinus2 uint32 Log2DiffMaxMinLumaTransformBlockSize uint32 MaxTransformHierarchyDepthInter uint32 MaxTransformHierarchyDepthIntra uint32 ScalingListEnabledFlag bool ScalingListDataPresentFlag bool AmpEnabledFlag bool SampleAdaptiveOffsetEnabledFlag bool PcmEnabledFlag bool // PcmEnabledFlag == true PcmSampleBitDepthLumaMinus1 uint8 PcmSampleBitDepthChromaMinus1 uint8 Log2MinPcmLumaCodingBlockSizeMinus3 uint32 Log2DiffMaxMinPcmLumaCodingBlockSize uint32 PcmLoopFilterDisabledFlag bool ShortTermRefPicSets []*SPS_ShortTermRefPicSet LongTermRefPicsPresentFlag bool TemporalMvpEnabledFlag bool StrongIntraSmoothingEnabledFlag bool VUI *SPS_VUI } // Unmarshal decodes a SPS from bytes. func (s *SPS) Unmarshal(buf []byte) error { buf = h264.EmulationPreventionRemove(buf) if len(buf) < 2 { return fmt.Errorf("not enough bits") } buf = buf[2:] pos := 0 err := bits.HasSpace(buf, pos, 8) if err != nil { return err } s.VPSID = uint8(bits.ReadBitsUnsafe(buf, &pos, 4)) s.MaxSubLayersMinus1 = uint8(bits.ReadBitsUnsafe(buf, &pos, 3)) s.TemporalIDNestingFlag = bits.ReadFlagUnsafe(buf, &pos) err = s.ProfileTierLevel.unmarshal(buf, &pos, s.MaxSubLayersMinus1) if err != nil { return err } tmp2, err := bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } s.ID = uint8(tmp2) s.ChromaFormatIdc, err = bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } if s.ChromaFormatIdc == 3 { s.SeparateColourPlaneFlag, err = bits.ReadFlag(buf, &pos) if err != nil { return err } } s.PicWidthInLumaSamples, err = bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } s.PicHeightInLumaSamples, err = bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } conformanceWindowFlag, err := bits.ReadFlag(buf, &pos) if err != nil { return err } if conformanceWindowFlag { s.ConformanceWindow = &SPS_ConformanceWindow{} err := s.ConformanceWindow.unmarshal(buf, &pos) if err != nil { return err } } else { s.ConformanceWindow = nil } s.BitDepthLumaMinus8, err = bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } s.BitDepthChromaMinus8, err = bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } s.Log2MaxPicOrderCntLsbMinus4, err = bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } s.SubLayerOrderingInfoPresentFlag, err = bits.ReadFlag(buf, &pos) if err != nil { return err } var start uint8 if s.SubLayerOrderingInfoPresentFlag { start = 0 } else { start = s.MaxSubLayersMinus1 } s.MaxDecPicBufferingMinus1 = make([]uint32, s.MaxSubLayersMinus1+1) s.MaxNumReorderPics = make([]uint32, s.MaxSubLayersMinus1+1) s.MaxLatencyIncreasePlus1 = make([]uint32, s.MaxSubLayersMinus1+1) for i := start; i <= s.MaxSubLayersMinus1; i++ { s.MaxDecPicBufferingMinus1[i], err = bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } s.MaxNumReorderPics[i], err = bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } s.MaxLatencyIncreasePlus1[i], err = bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } } s.Log2MinLumaCodingBlockSizeMinus3, err = bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } s.Log2DiffMaxMinLumaCodingBlockSize, err = bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } s.Log2MinLumaTransformBlockSizeMinus2, err = bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } s.Log2DiffMaxMinLumaTransformBlockSize, err = bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } s.MaxTransformHierarchyDepthInter, err = bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } s.MaxTransformHierarchyDepthIntra, err = bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } s.ScalingListEnabledFlag, err = bits.ReadFlag(buf, &pos) if err != nil { return err } if s.ScalingListEnabledFlag { s.ScalingListDataPresentFlag, err = bits.ReadFlag(buf, &pos) if err != nil { return err } if s.ScalingListDataPresentFlag { return fmt.Errorf("ScalingListDataPresentFlag not supported yet") } } s.AmpEnabledFlag, err = bits.ReadFlag(buf, &pos) if err != nil { return err } s.SampleAdaptiveOffsetEnabledFlag, err = bits.ReadFlag(buf, &pos) if err != nil { return err } s.PcmEnabledFlag, err = bits.ReadFlag(buf, &pos) if err != nil { return err } if s.PcmEnabledFlag { err := bits.HasSpace(buf, pos, 8) if err != nil { return err } s.PcmSampleBitDepthLumaMinus1 = uint8(bits.ReadBitsUnsafe(buf, &pos, 4)) s.PcmSampleBitDepthChromaMinus1 = uint8(bits.ReadBitsUnsafe(buf, &pos, 4)) s.Log2MinPcmLumaCodingBlockSizeMinus3, err = bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } s.Log2DiffMaxMinPcmLumaCodingBlockSize, err = bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } s.PcmLoopFilterDisabledFlag, err = bits.ReadFlag(buf, &pos) if err != nil { return err } } numShortTermRefPicSets, err := bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } if numShortTermRefPicSets > 0 { if numShortTermRefPicSets > maxShortTermRefPics { return fmt.Errorf("num_short_term_ref_pic_sets exceeds %d", maxShortTermRefPics) } s.ShortTermRefPicSets = make([]*SPS_ShortTermRefPicSet, numShortTermRefPicSets) for i := uint32(0); i < numShortTermRefPicSets; i++ { s.ShortTermRefPicSets[i] = &SPS_ShortTermRefPicSet{} err := s.ShortTermRefPicSets[i].unmarshal(buf, &pos, i, numShortTermRefPicSets, s.ShortTermRefPicSets) if err != nil { return err } } } else { s.ShortTermRefPicSets = nil } s.LongTermRefPicsPresentFlag, err = bits.ReadFlag(buf, &pos) if err != nil { return err } if s.LongTermRefPicsPresentFlag { numLongTermRefPicsSPS, err := bits.ReadGolombUnsigned(buf, &pos) if err != nil { return err } if numLongTermRefPicsSPS > 0 { return fmt.Errorf("long term ref pics inside SPS are not supported yet") } } s.TemporalMvpEnabledFlag, err = bits.ReadFlag(buf, &pos) if err != nil { return err } s.StrongIntraSmoothingEnabledFlag, err = bits.ReadFlag(buf, &pos) if err != nil { return err } vuiParametersPresentFlag, err := bits.ReadFlag(buf, &pos) if err != nil { return err } if vuiParametersPresentFlag { s.VUI = &SPS_VUI{} err := s.VUI.unmarshal(buf, &pos) if err != nil { return err } } else { s.VUI = nil } return nil } // Width returns the video width. func (s SPS) Width() int { width := s.PicWidthInLumaSamples if s.ConformanceWindow != nil { cropUnitX := subWidthC[s.ChromaFormatIdc] width -= (s.ConformanceWindow.LeftOffset + s.ConformanceWindow.RightOffset) * cropUnitX } return int(width) } // Height returns the video height. func (s SPS) Height() int { height := s.PicHeightInLumaSamples if s.ConformanceWindow != nil { cropUnitY := subHeightC[s.ChromaFormatIdc] height -= (s.ConformanceWindow.TopOffset + s.ConformanceWindow.BottomOffset) * cropUnitY } return int(height) } // FPS returns the frames per second of the video. func (s SPS) FPS() float64 { if s.VUI == nil || s.VUI.TimingInfo == nil { return 0 } return float64(s.VUI.TimingInfo.TimeScale) / float64(s.VUI.TimingInfo.NumUnitsInTick) }