mirror of
https://github.com/bluenviron/mediacommon.git
synced 2025-09-26 21:01:14 +08:00
194 lines
4.1 KiB
Go
194 lines
4.1 KiB
Go
package h264
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/bluenviron/mediabase/pkg/bits"
|
|
)
|
|
|
|
func getPictureOrderCount(buf []byte, sps *SPS) (uint32, error) {
|
|
if len(buf) < 6 {
|
|
return 0, fmt.Errorf("not enough bits")
|
|
}
|
|
|
|
buf = EmulationPreventionRemove(buf[:6])
|
|
|
|
buf = buf[1:]
|
|
pos := 0
|
|
|
|
_, err := bits.ReadGolombUnsigned(buf, &pos) // first_mb_in_slice
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
_, err = bits.ReadGolombUnsigned(buf, &pos) // slice_type
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
_, err = bits.ReadGolombUnsigned(buf, &pos) // pic_parameter_set_id
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
_, err = bits.ReadBits(buf, &pos, int(sps.Log2MaxFrameNumMinus4+4)) // frame_num
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
if !sps.FrameMbsOnlyFlag {
|
|
return 0, fmt.Errorf("frame_mbs_only_flag = 0 is not supported")
|
|
}
|
|
|
|
picOrderCntLsb, err := bits.ReadBits(buf, &pos, int(sps.Log2MaxPicOrderCntLsbMinus4+4))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return uint32(picOrderCntLsb), nil
|
|
}
|
|
|
|
func findPictureOrderCount(au [][]byte, sps *SPS) (uint32, error) {
|
|
for _, nalu := range au {
|
|
typ := NALUType(nalu[0] & 0x1F)
|
|
if typ == NALUTypeNonIDR {
|
|
poc, err := getPictureOrderCount(nalu, sps)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return poc, nil
|
|
}
|
|
}
|
|
return 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
|
|
prevDTSFilled bool
|
|
prevDTS time.Duration
|
|
expectedPOC uint32
|
|
reorderedFrames int
|
|
pauseDTS int
|
|
pocIncrement int
|
|
}
|
|
|
|
// NewDTSExtractor allocates a DTSExtractor.
|
|
func NewDTSExtractor() *DTSExtractor {
|
|
return &DTSExtractor{
|
|
pocIncrement: 2,
|
|
}
|
|
}
|
|
|
|
func (d *DTSExtractor) extractInner(au [][]byte, pts time.Duration) (time.Duration, error) {
|
|
idrPresent := false
|
|
|
|
for _, nalu := range au {
|
|
typ := NALUType(nalu[0] & 0x1F)
|
|
switch typ {
|
|
case NALUTypeSPS:
|
|
var spsp SPS
|
|
err := spsp.Unmarshal(nalu)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("invalid SPS: %v", err)
|
|
}
|
|
d.spsp = &spsp
|
|
|
|
case NALUTypeIDR:
|
|
idrPresent = true
|
|
}
|
|
}
|
|
|
|
if d.spsp == nil {
|
|
return 0, fmt.Errorf("SPS not received yet")
|
|
}
|
|
|
|
if d.spsp.PicOrderCntType == 2 {
|
|
return pts, nil
|
|
}
|
|
|
|
if d.spsp.PicOrderCntType == 1 {
|
|
return 0, fmt.Errorf("pic_order_cnt_type = 1 is not supported yet")
|
|
}
|
|
|
|
if idrPresent {
|
|
d.expectedPOC = 0
|
|
d.reorderedFrames = 0
|
|
d.pauseDTS = 0
|
|
d.pocIncrement = 2
|
|
return pts, nil
|
|
}
|
|
|
|
d.expectedPOC += uint32(d.pocIncrement)
|
|
d.expectedPOC &= ((1 << (d.spsp.Log2MaxPicOrderCntLsbMinus4 + 4)) - 1)
|
|
|
|
if d.pauseDTS > 0 {
|
|
d.pauseDTS--
|
|
return d.prevDTS + 1*time.Millisecond, nil
|
|
}
|
|
|
|
poc, err := findPictureOrderCount(au, d.spsp)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
if d.pocIncrement == 2 && (poc%2) != 0 {
|
|
d.pocIncrement = 1
|
|
d.expectedPOC /= 2
|
|
}
|
|
|
|
pocDiff := int(getPictureOrderCountDiff(poc, d.expectedPOC, d.spsp)) + d.reorderedFrames*d.pocIncrement
|
|
|
|
if pocDiff < 0 {
|
|
return 0, fmt.Errorf("invalid POC")
|
|
}
|
|
|
|
if pocDiff == 0 {
|
|
return pts, nil
|
|
}
|
|
|
|
reorderedFrames := (pocDiff - d.reorderedFrames*d.pocIncrement) / d.pocIncrement
|
|
if reorderedFrames > d.reorderedFrames {
|
|
d.pauseDTS = (reorderedFrames - d.reorderedFrames - 1)
|
|
d.reorderedFrames = reorderedFrames
|
|
return d.prevDTS + 1*time.Millisecond, nil
|
|
}
|
|
|
|
return d.prevDTS + ((pts - d.prevDTS) * time.Duration(d.pocIncrement) / time.Duration(pocDiff+d.pocIncrement)), 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.prevDTS = dts
|
|
d.prevDTSFilled = true
|
|
|
|
return dts, err
|
|
}
|