Files
gortsplib/pkg/codecs/h265/sps.go

814 lines
19 KiB
Go

package h265
import (
"fmt"
"github.com/aler9/gortsplib/v2/pkg/bits"
"github.com/aler9/gortsplib/v2/pkg/codecs/h264"
)
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
ProgressiveSourceFlag bool
InterlacedSourceFlag bool
NonPackedConstraintFlag bool
FrameOnlyConstraintFlag bool
Max12bitConstraintFlag bool
Max10bitConstraintFlag bool
Max8bitConstraintFlag bool
Max422ChromeConstraintFlag bool
Max420ChromaConstraintFlag bool
MaxMonochromeConstraintFlag bool
IntraConstraintFlag bool
OnePictureOnlyConstraintFlag bool
LowerBitRateConstraintFlag bool
Max14BitConstraintFlag bool
LevelIdc 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.ProgressiveSourceFlag = bits.ReadFlagUnsafe(buf, pos)
p.InterlacedSourceFlag = bits.ReadFlagUnsafe(buf, pos)
p.NonPackedConstraintFlag = bits.ReadFlagUnsafe(buf, pos)
p.FrameOnlyConstraintFlag = bits.ReadFlagUnsafe(buf, pos)
p.Max12bitConstraintFlag = bits.ReadFlagUnsafe(buf, pos)
p.Max10bitConstraintFlag = bits.ReadFlagUnsafe(buf, pos)
p.Max8bitConstraintFlag = bits.ReadFlagUnsafe(buf, pos)
p.Max422ChromeConstraintFlag = bits.ReadFlagUnsafe(buf, pos)
p.Max420ChromaConstraintFlag = bits.ReadFlagUnsafe(buf, pos)
p.MaxMonochromeConstraintFlag = bits.ReadFlagUnsafe(buf, pos)
p.IntraConstraintFlag = bits.ReadFlagUnsafe(buf, pos)
p.OnePictureOnlyConstraintFlag = bits.ReadFlagUnsafe(buf, pos)
p.LowerBitRateConstraintFlag = 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.Max14BitConstraintFlag = bits.ReadFlagUnsafe(buf, pos)
*pos += 34
} else {
*pos += 35
}
p.LevelIdc = 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 {
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 {
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
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")
}
typ := NALUType((buf[0] >> 1) & 0b111111)
if typ != NALUTypeSPS {
return fmt.Errorf("not a SPS")
}
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-start+1)
s.MaxNumReorderPics = make([]uint32, s.MaxSubLayersMinus1-start+1)
s.MaxLatencyIncreasePlus1 = make([]uint32, s.MaxSubLayersMinus1-start+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 {
return fmt.Errorf("PcmEnabledFlag not supported yet")
}
numShortTermRefPicSets, err := bits.ReadGolombUnsigned(buf, &pos)
if err != nil {
return err
}
if numShortTermRefPicSets > 0 {
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 {
return fmt.Errorf("LongTermRefPicsPresentFlag 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)
}