mirror of
https://github.com/aler9/gortsplib
synced 2025-10-18 21:14:38 +08:00
294 lines
7.1 KiB
Go
294 lines
7.1 KiB
Go
package h265
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/aler9/gortsplib/v2/pkg/bits"
|
|
"github.com/aler9/gortsplib/v2/pkg/h264"
|
|
)
|
|
|
|
var subWidthC = []uint32{
|
|
1,
|
|
2,
|
|
2,
|
|
1,
|
|
}
|
|
|
|
var subHeightC = []uint32{
|
|
1,
|
|
2,
|
|
1,
|
|
1,
|
|
}
|
|
|
|
// SPS_ProfileLevelTier is a profile level tier of a SPS.
|
|
type SPS_ProfileLevelTier 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
|
|
IntraConstraintFlag bool
|
|
OnePictureOnlyConstraintFlag bool
|
|
LowerBitRateConstraintFlag bool
|
|
Max14BitConstraintFlag bool
|
|
LevelIdc uint8
|
|
SubLayerProfilePresentFlag []bool
|
|
SubLayerLevelPresentFlag []bool
|
|
}
|
|
|
|
func (p *SPS_ProfileLevelTier) unmarshal(buf []byte, pos *int, maxNumSubLayersMinus1 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.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 += 33
|
|
} else {
|
|
*pos += 34
|
|
}
|
|
|
|
p.LevelIdc = uint8(bits.ReadBitsUnsafe(buf, pos, 8))
|
|
|
|
if maxNumSubLayersMinus1 > 0 {
|
|
p.SubLayerProfilePresentFlag = make([]bool, maxNumSubLayersMinus1)
|
|
p.SubLayerLevelPresentFlag = make([]bool, maxNumSubLayersMinus1)
|
|
|
|
err := bits.HasSpace(buf, *pos, int(2*maxNumSubLayersMinus1))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
for j := uint8(0); j < maxNumSubLayersMinus1; j++ {
|
|
p.SubLayerProfilePresentFlag[j] = bits.ReadFlagUnsafe(buf, pos)
|
|
p.SubLayerLevelPresentFlag[j] = bits.ReadFlagUnsafe(buf, pos)
|
|
}
|
|
|
|
if maxNumSubLayersMinus1 > 0 {
|
|
err := bits.HasSpace(buf, *pos, int(8-maxNumSubLayersMinus1)*2)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
*pos += int(8-maxNumSubLayersMinus1) * 2
|
|
}
|
|
|
|
for i := uint8(0); i < maxNumSubLayersMinus1; 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 is a H265 sequence parameter set.
|
|
type SPS struct {
|
|
VPSID uint8
|
|
MaxNumSubLayersMinus1 uint8
|
|
TemporalIDNestingFlag bool
|
|
ProfileLevelTier SPS_ProfileLevelTier
|
|
ID uint8
|
|
ChromaFormatIdc uint32
|
|
SeparateColourPlaneFlag bool
|
|
PicWidthInLumaSamples uint32
|
|
PicHeightInLumaSamples uint32
|
|
|
|
ConformanceWindow *SPS_ConformanceWindow
|
|
|
|
BitDepthLumaMinus8 uint32
|
|
BitDepthChromaMinus8 uint32
|
|
Log2MaxPicOrderCntLsbMinus4 uint32
|
|
}
|
|
|
|
// Unmarshal decodes a SPS from bytes.
|
|
func (s *SPS) Unmarshal(buf []byte) error {
|
|
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:]
|
|
buf = h264.EmulationPreventionRemove(buf)
|
|
pos := 0
|
|
|
|
err := bits.HasSpace(buf, pos, 8)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
s.VPSID = uint8(bits.ReadBitsUnsafe(buf, &pos, 4))
|
|
s.MaxNumSubLayersMinus1 = uint8(bits.ReadBitsUnsafe(buf, &pos, 3))
|
|
s.TemporalIDNestingFlag = bits.ReadFlagUnsafe(buf, &pos)
|
|
|
|
err = s.ProfileLevelTier.unmarshal(buf, &pos, s.MaxNumSubLayersMinus1)
|
|
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
|
|
}
|
|
|
|
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)
|
|
}
|