mirror of
https://github.com/aler9/gortsplib
synced 2025-10-04 06:46:42 +08:00
243 lines
5.7 KiB
Go
243 lines
5.7 KiB
Go
package h265
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/aler9/gortsplib/v2/pkg/bits"
|
|
"github.com/aler9/gortsplib/v2/pkg/codecs/h264"
|
|
)
|
|
|
|
func getPictureOrderCount(buf []byte, sps *SPS, pps *PPS) (uint32, uint32, error) {
|
|
if len(buf) < 12 {
|
|
return 0, 0, fmt.Errorf("not enough bits")
|
|
}
|
|
|
|
buf = h264.EmulationPreventionRemove(buf[:12])
|
|
|
|
typ := NALUType((buf[0] >> 1) & 0b111111)
|
|
|
|
buf = buf[2:]
|
|
pos := 0
|
|
|
|
firstSliceSegmentInPicFlag, err := bits.ReadFlag(buf, &pos)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
if !firstSliceSegmentInPicFlag {
|
|
return 0, 0, fmt.Errorf("first_slice_segment_in_pic_flag = 0 is not supported")
|
|
}
|
|
|
|
if typ >= NALUType_BLA_W_LP && typ <= NALUType_RSV_IRAP_VCL23 {
|
|
_, err := bits.ReadFlag(buf, &pos) // no_output_of_prior_pics_flag
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
}
|
|
|
|
_, err = bits.ReadGolombUnsigned(buf, &pos) // slice_pic_parameter_set_id
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
if pps.NumExtraSliceHeaderBits > 0 {
|
|
err := bits.HasSpace(buf, pos, int(pps.NumExtraSliceHeaderBits))
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
pos += int(pps.NumExtraSliceHeaderBits)
|
|
}
|
|
|
|
sliceType, err := bits.ReadGolombUnsigned(buf, &pos) // slice_type
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
if pps.OutputFlagPresentFlag {
|
|
_, err := bits.ReadFlag(buf, &pos) // pic_output_flag
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
}
|
|
|
|
if sps.SeparateColourPlaneFlag {
|
|
_, err := bits.ReadBits(buf, &pos, 2) // colour_plane_id
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
}
|
|
|
|
picOrderCntLsb, err := bits.ReadBits(buf, &pos, int(sps.Log2MaxPicOrderCntLsbMinus4+4))
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
shortTermRefPicSetSpsFlag, err := bits.ReadFlag(buf, &pos)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
if shortTermRefPicSetSpsFlag {
|
|
return 0, 0, fmt.Errorf("short_term_ref_pic_set_sps_flag = true is not supported")
|
|
}
|
|
|
|
var rps SPS_ShortTermRefPicSet
|
|
err = rps.unmarshal(buf, &pos, uint32(len(sps.ShortTermRefPicSets)), uint32(len(sps.ShortTermRefPicSets)), nil)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
var v uint32
|
|
|
|
if sliceType == 0 { // B-frame
|
|
if typ == NALUType_TRAIL_N || typ == NALUType_RASL_N {
|
|
v = sps.MaxNumReorderPics[0] - uint32(len(rps.DeltaPocS1Minus1))
|
|
} else if typ == NALUType_TRAIL_R || typ == NALUType_RASL_R {
|
|
if len(rps.DeltaPocS0Minus1) == 0 {
|
|
return 0, 0, fmt.Errorf("invalid delta_poc_s0_minus1")
|
|
}
|
|
v = rps.DeltaPocS0Minus1[0] + sps.MaxNumReorderPics[0] - 1
|
|
}
|
|
} else { // I or P-frame
|
|
if len(rps.DeltaPocS0Minus1) == 0 {
|
|
return 0, 0, fmt.Errorf("invalid delta_poc_s0_minus1")
|
|
}
|
|
v = rps.DeltaPocS0Minus1[0] + sps.MaxNumReorderPics[0]
|
|
}
|
|
|
|
dtsPOC := uint32(picOrderCntLsb) - v
|
|
dtsPOC &= ((1 << (sps.Log2MaxPicOrderCntLsbMinus4 + 4)) - 1)
|
|
|
|
return uint32(picOrderCntLsb), dtsPOC, nil
|
|
}
|
|
|
|
func findPictureOrderCount(au [][]byte, sps *SPS, pps *PPS) (uint32, uint32, error) {
|
|
for _, nalu := range au {
|
|
typ := NALUType((nalu[0] >> 1) & 0b111111)
|
|
switch typ {
|
|
case NALUType_TRAIL_N, NALUType_TRAIL_R, NALUType_CRA_NUT, NALUType_RASL_N, NALUType_RASL_R:
|
|
poc, dtsPOC, err := getPictureOrderCount(nalu, sps, pps)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
return poc, dtsPOC, nil
|
|
}
|
|
}
|
|
return 0, 0, fmt.Errorf("POC not found")
|
|
}
|
|
|
|
func getPictureOrderCountDiff(poc1 uint32, poc2 uint32, sps *SPS) int32 {
|
|
diff := int32(poc1) - int32(poc2)
|
|
switch {
|
|
case diff < -((1 << (sps.Log2MaxPicOrderCntLsbMinus4 + 3)) - 1):
|
|
diff += (1 << (sps.Log2MaxPicOrderCntLsbMinus4 + 4))
|
|
|
|
case diff > ((1 << (sps.Log2MaxPicOrderCntLsbMinus4 + 3)) - 1):
|
|
diff -= (1 << (sps.Log2MaxPicOrderCntLsbMinus4 + 4))
|
|
}
|
|
return diff
|
|
}
|
|
|
|
// DTSExtractor allows to extract DTS from PTS.
|
|
type DTSExtractor struct {
|
|
spsp *SPS
|
|
ppsp *PPS
|
|
prevDTSFilled bool
|
|
prevDTS time.Duration
|
|
}
|
|
|
|
// NewDTSExtractor allocates a DTSExtractor.
|
|
func NewDTSExtractor() *DTSExtractor {
|
|
return &DTSExtractor{}
|
|
}
|
|
|
|
func (d *DTSExtractor) extractInner(au [][]byte, pts time.Duration) (time.Duration, error) {
|
|
idrPresent := false
|
|
|
|
for _, nalu := range au {
|
|
typ := NALUType((nalu[0] >> 1) & 0b111111)
|
|
|
|
switch typ {
|
|
case NALUType_SPS_NUT:
|
|
var spsp SPS
|
|
err := spsp.Unmarshal(nalu)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("invalid SPS: %v", err)
|
|
}
|
|
d.spsp = &spsp
|
|
|
|
case NALUType_PPS_NUT:
|
|
var ppsp PPS
|
|
err := ppsp.Unmarshal(nalu)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("invalid PPS: %v", err)
|
|
}
|
|
d.ppsp = &ppsp
|
|
|
|
case NALUType_IDR_W_RADL, NALUType_IDR_N_LP:
|
|
idrPresent = true
|
|
}
|
|
}
|
|
|
|
if d.spsp == nil {
|
|
return 0, fmt.Errorf("SPS not received yet")
|
|
}
|
|
|
|
if d.ppsp == nil {
|
|
return 0, fmt.Errorf("PPS not received yet")
|
|
}
|
|
|
|
if len(d.spsp.MaxNumReorderPics) != 1 || d.spsp.MaxNumReorderPics[0] == 0 {
|
|
return pts, nil
|
|
}
|
|
|
|
if d.spsp.VUI == nil || d.spsp.VUI.TimingInfo == nil {
|
|
return pts, nil
|
|
}
|
|
|
|
var poc uint32
|
|
var dtsPOC uint32
|
|
|
|
if idrPresent {
|
|
poc = 0
|
|
dtsPOC = poc - 2
|
|
dtsPOC &= ((1 << (d.spsp.Log2MaxPicOrderCntLsbMinus4 + 4)) - 1)
|
|
} else {
|
|
var err error
|
|
poc, dtsPOC, err = findPictureOrderCount(au, d.spsp, d.ppsp)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
}
|
|
|
|
pocDiff := getPictureOrderCountDiff(poc, dtsPOC, d.spsp)
|
|
timeDiff := time.Duration(pocDiff) * time.Second *
|
|
time.Duration(d.spsp.VUI.TimingInfo.NumUnitsInTick) / time.Duration(d.spsp.VUI.TimingInfo.TimeScale)
|
|
dts := pts - timeDiff
|
|
|
|
return dts, nil
|
|
}
|
|
|
|
// Extract extracts the DTS of a access unit.
|
|
func (d *DTSExtractor) Extract(au [][]byte, pts time.Duration) (time.Duration, error) {
|
|
dts, err := d.extractInner(au, pts)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
if dts > pts {
|
|
return 0, fmt.Errorf("DTS is greater than PTS")
|
|
}
|
|
|
|
if d.prevDTSFilled && dts <= d.prevDTS {
|
|
return 0, fmt.Errorf("DTS is not monotonically increasing, was %v, now is %v",
|
|
d.prevDTS, dts)
|
|
}
|
|
|
|
d.prevDTSFilled = true
|
|
d.prevDTS = dts
|
|
|
|
return dts, err
|
|
}
|