This commit is contained in:
aler9
2022-06-22 20:08:41 +02:00
parent 09865015c9
commit dc7d9d489d
3 changed files with 299 additions and 166 deletions

View File

@@ -68,7 +68,7 @@ func getPOC(buf []byte, sps *SPS) (uint32, error) {
return uint32(picOrderCntLsb), nil
}
func getNALUSPOC(nalus [][]byte, sps *SPS) (uint32, error) {
func findPOC(nalus [][]byte, sps *SPS) (uint32, error) {
for _, nalu := range nalus {
typ := NALUType(nalu[0] & 0x1F)
if typ == NALUTypeIDR || typ == NALUTypeNonIDR {
@@ -94,6 +94,70 @@ func getPOCDiff(poc1 uint32, poc2 uint32, sps *SPS) int32 {
return diff
}
func getSEIPicTimingDPBOutputDelay(buf []byte, sps *SPS) (uint32, bool) {
buf = AntiCompetitionRemove(buf)
pos := 1
for {
if pos >= (len(buf) - 1) {
return 0, false
}
payloadType := 0
for {
byt := buf[pos]
pos++
payloadType += int(byt)
if byt != 0xFF {
break
}
}
payloadSize := 0
for {
byt := buf[pos]
pos++
payloadSize += int(byt)
if byt != 0xFF {
break
}
}
if payloadType == 1 { // timing info
br := bitio.NewReader(bytes.NewReader(buf[pos : pos+payloadSize]))
// cpbRemovalDelay
_, err := br.ReadBits(sps.VUI.NalHRD.CpbRemovalDelayLengthMinus1 + 1)
if err != nil {
return 0, false
}
tmp, err := br.ReadBits(sps.VUI.NalHRD.DpbOutputDelayLengthMinus1 + 1)
if err != nil {
return 0, false
}
dpbOutputDelay := uint32(tmp)
return dpbOutputDelay, true
}
pos += payloadSize
}
}
func findSEIPicTimingDPBOutputDelay(nalus [][]byte, sps *SPS) (uint32, bool) {
for _, nalu := range nalus {
typ := NALUType(nalu[0] & 0x1F)
if typ == NALUTypeSEI {
ret, ok := getSEIPicTimingDPBOutputDelay(nalu, sps)
if ok {
return ret, true
}
}
}
return 0, false
}
// DTSExtractor is a utility that allows to extract NALU DTS from PTS.
type DTSExtractor struct {
sps []byte
@@ -110,27 +174,22 @@ func NewDTSExtractor() *DTSExtractor {
return &DTSExtractor{}
}
func (d *DTSExtractor) extractInner(
nalus [][]byte,
pts time.Duration,
) (time.Duration, int32, error) {
func (d *DTSExtractor) extractInner(nalus [][]byte, pts time.Duration) (time.Duration, int32, error) {
idrPresent := false
for _, nalu := range nalus {
typ := NALUType(nalu[0] & 0x1F)
switch typ {
// parse SPS
case NALUTypeSPS:
if d.sps == nil || !bytes.Equal(d.sps, nalu) {
var spsp SPS
err := spsp.Unmarshal(nalu)
if err != nil {
return 0, 0, err
return 0, 0, fmt.Errorf("invalid SPS: %v", err)
}
d.sps = append([]byte(nil), nalu...)
d.spsp = &spsp
// in case of B-frames, we have to subtract from DTS the maximum number of reordered frames
if d.spsp.VUI != nil && d.spsp.VUI.TimingInfo != nil &&
d.spsp.VUI.BitstreamRestriction != nil {
d.ptsDTSOffset = time.Duration(math.Round(float64(
@@ -141,7 +200,6 @@ func (d *DTSExtractor) extractInner(
}
}
// set IDR present flag
case NALUTypeIDR:
idrPresent = true
}
@@ -151,51 +209,68 @@ func (d *DTSExtractor) extractInner(
return 0, 0, fmt.Errorf("SPS not received yet")
}
if idrPresent || d.spsp.PicOrderCntType == 2 {
d.expectedPOC = 0
return pts - d.ptsDTSOffset, 0, nil
}
// compute expectedPOC immediately in order to store it even in case of errors
d.expectedPOC += 2
d.expectedPOC &= ((1 << (d.spsp.Log2MaxPicOrderCntLsbMinus4 + 4)) - 1)
poc, err := getNALUSPOC(nalus, d.spsp)
if err != nil {
return 0, 0, err
}
pocDiff := getPOCDiff(poc, d.expectedPOC, d.spsp)
if pocDiff == 0 {
return pts - d.ptsDTSOffset, 0, nil
}
// special case to eliminate errors near 0
if d.spsp.VUI != nil && d.spsp.VUI.TimingInfo != nil && d.spsp.VUI.BitstreamRestriction != nil &&
pocDiff == -int32(d.spsp.VUI.BitstreamRestriction.MaxNumReorderFrames)*2 {
return pts, pocDiff, nil
}
if d.prevPOCDiff == 0 {
if pocDiff == -2 {
return 0, 0, fmt.Errorf("invalid frame POC")
switch {
// DTS is computed from SEI
case d.spsp.VUI != nil && d.spsp.VUI.TimingInfo != nil && d.spsp.VUI.NalHRD != nil:
dpbOutputDelay, ok := findSEIPicTimingDPBOutputDelay(nalus, d.spsp)
if !ok {
return 0, 0, fmt.Errorf("SEI / pic_timing not found")
}
return d.prevPTS - d.ptsDTSOffset +
time.Duration(math.Round(float64(pts-d.prevPTS)/float64(pocDiff/2+1))), pocDiff, nil
}
return pts - time.Duration(dpbOutputDelay)/2*time.Second*
time.Duration(d.spsp.VUI.TimingInfo.NumUnitsInTick)*2/time.Duration(d.spsp.VUI.TimingInfo.TimeScale), 0, nil
// pocDiff : prevPOCDiff = (pts - dts - ptsDTSOffset) : (prevPTS - prevDTS - ptsDTSOffset)
return pts - d.ptsDTSOffset + time.Duration(math.Round(float64(d.prevDTS-d.prevPTS+d.ptsDTSOffset)*
float64(pocDiff)/float64(d.prevPOCDiff))), pocDiff, nil
// DTS is always equal to PTS
case d.spsp.PicOrderCntType == 2:
return pts, 0, nil
// DTS is computed by using POC, timing infos and max_num_reorder_frames
case d.spsp.VUI != nil && d.spsp.VUI.TimingInfo != nil && d.spsp.VUI.BitstreamRestriction != nil:
if idrPresent {
d.expectedPOC = 0
return pts - d.ptsDTSOffset, 0, nil
}
// compute expectedPOC immediately in order to store it even in case of errors
d.expectedPOC += 2
d.expectedPOC &= ((1 << (d.spsp.Log2MaxPicOrderCntLsbMinus4 + 4)) - 1)
poc, err := findPOC(nalus, d.spsp)
if err != nil {
return 0, 0, err
}
pocDiff := getPOCDiff(poc, d.expectedPOC, d.spsp)
if pocDiff == 0 {
return pts - d.ptsDTSOffset, 0, nil
}
// special case to eliminate errors near 0
if pocDiff == -int32(d.spsp.VUI.BitstreamRestriction.MaxNumReorderFrames)*2 {
return pts, pocDiff, nil
}
if d.prevPOCDiff == 0 {
if pocDiff == -2 {
return 0, 0, fmt.Errorf("invalid frame POC")
}
return d.prevPTS - d.ptsDTSOffset +
time.Duration(math.Round(float64(pts-d.prevPTS)/float64(pocDiff/2+1))), pocDiff, nil
}
// pocDiff : prevPOCDiff = (pts - dts - ptsDTSOffset) : (prevPTS - prevDTS - ptsDTSOffset)
return pts - d.ptsDTSOffset + time.Duration(math.Round(float64(d.prevDTS-d.prevPTS+d.ptsDTSOffset)*
float64(pocDiff)/float64(d.prevPOCDiff))), pocDiff, nil
default:
return 0, 0, fmt.Errorf("unable to compute H264 DTS")
}
}
// Extract extracts the DTS of a NALU group.
func (d *DTSExtractor) Extract(
nalus [][]byte,
pts time.Duration,
) (time.Duration, error) {
// Extract extracts the DTS of a group of NALUs.
func (d *DTSExtractor) Extract(nalus [][]byte, pts time.Duration) (time.Duration, error) {
dts, pocDiff, err := d.extractInner(nalus, pts)
if err != nil {
return 0, err

View File

@@ -8,137 +8,195 @@ import (
)
func TestDTSExtractor(t *testing.T) {
sequence := []struct {
type sequenceSample struct {
nalus [][]byte
pts time.Duration
dts time.Duration
}
for _, ca := range []struct {
name string
sequence []sequenceSample
}{
{
[][]byte{
"max_num_reorder_frames-based",
[]sequenceSample{
{
0x67, 0x64, 0x00, 0x28, 0xac, 0xd9, 0x40, 0x78,
0x02, 0x27, 0xe5, 0xc0, 0x44, 0x00, 0x00, 0x03,
0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x28, 0x3c,
0x60, 0xc6, 0x58,
[][]byte{
{
0x67, 0x64, 0x00, 0x28, 0xac, 0xd9, 0x40, 0x78,
0x02, 0x27, 0xe5, 0xc0, 0x44, 0x00, 0x00, 0x03,
0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x28, 0x3c,
0x60, 0xc6, 0x58,
},
{0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0},
{
0x65, 0x88, 0x82, 0x00, 0x05, 0xbf, 0xfe, 0xf7,
0xd3, 0x3f, 0xcc, 0xb2, 0xec, 0x9a, 0x24, 0xb5,
0xe3, 0xa8, 0xf7, 0xa2, 0x9e, 0x26, 0x5f, 0x43,
0x75, 0x25, 0x01, 0x9b, 0x96, 0xc4, 0xed, 0x3a,
0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x55,
0xda, 0xf7, 0x10, 0xe5, 0xc4, 0x70, 0xe1, 0xfe,
0x83, 0xc0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x1f, 0xa0, 0x00, 0x00, 0x05, 0x68, 0x00,
0x00, 0x03, 0x01, 0xc6, 0x00, 0x00, 0x03, 0x01,
0x0c, 0x00, 0x00, 0x03, 0x00, 0xb1, 0x00, 0x00,
0x03, 0x00, 0x8f, 0x80, 0x00, 0x00, 0x8a, 0x80,
0x00, 0x00, 0x9d, 0x00, 0x00, 0x03, 0x00, 0xb2,
0x00, 0x00, 0x03, 0x01, 0x1c, 0x00, 0x00, 0x03,
0x01, 0x7c, 0x00, 0x00, 0x03, 0x02, 0xf0, 0x00,
0x00, 0x04, 0x40, 0x00, 0x00, 0x08, 0x80, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0b,
0x78,
},
},
0,
-400 * time.Millisecond,
},
{0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0},
{
0x65, 0x88, 0x82, 0x00, 0x05, 0xbf, 0xfe, 0xf7,
0xd3, 0x3f, 0xcc, 0xb2, 0xec, 0x9a, 0x24, 0xb5,
0xe3, 0xa8, 0xf7, 0xa2, 0x9e, 0x26, 0x5f, 0x43,
0x75, 0x25, 0x01, 0x9b, 0x96, 0xc4, 0xed, 0x3a,
0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x55,
0xda, 0xf7, 0x10, 0xe5, 0xc4, 0x70, 0xe1, 0xfe,
0x83, 0xc0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x1f, 0xa0, 0x00, 0x00, 0x05, 0x68, 0x00,
0x00, 0x03, 0x01, 0xc6, 0x00, 0x00, 0x03, 0x01,
0x0c, 0x00, 0x00, 0x03, 0x00, 0xb1, 0x00, 0x00,
0x03, 0x00, 0x8f, 0x80, 0x00, 0x00, 0x8a, 0x80,
0x00, 0x00, 0x9d, 0x00, 0x00, 0x03, 0x00, 0xb2,
0x00, 0x00, 0x03, 0x01, 0x1c, 0x00, 0x00, 0x03,
0x01, 0x7c, 0x00, 0x00, 0x03, 0x02, 0xf0, 0x00,
0x00, 0x04, 0x40, 0x00, 0x00, 0x08, 0x80, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0b,
0x78,
[][]byte{
{
0x41, 0x9a, 0x24, 0x6c, 0x41, 0x4f, 0xfe, 0xd6,
0x8c, 0xb0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x6d, 0x40,
},
},
800 * time.Millisecond,
-200 * time.Millisecond,
},
{
[][]byte{
{
0x41, 0x9e, 0x42, 0x78, 0x82, 0x1f, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x02,
0x0f,
},
},
400 * time.Millisecond,
0,
},
{
[][]byte{
{
0x01, 0x9e, 0x61, 0x74, 0x43, 0xff, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x9c,
},
},
200 * time.Millisecond,
200 * time.Millisecond,
},
},
0,
-400 * time.Millisecond,
},
{
[][]byte{
"sei-based",
[]sequenceSample{
{
0x41, 0x9a, 0x24, 0x6c, 0x41, 0x4f, 0xfe, 0xd6,
0x8c, 0xb0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x6d, 0x40,
[][]byte{
// SEI (buffering period)
{6, 0, 7, 128, 117, 48, 0, 0, 3, 0, 64, 128},
// SEI (pic timing)
{6, 1, 4, 0, 0, 8, 16, 128},
// SPS
{
103, 100, 0, 42, 172, 44, 172, 7,
128, 34, 126, 92, 5, 168, 8, 8,
10, 0, 0, 7, 208, 0, 3, 169,
129, 192, 0, 0, 76, 75, 0, 0,
38, 37, 173, 222, 92, 20,
},
},
0,
-16666666 * time.Nanosecond,
},
{
[][]byte{
// SEI
{6, 1, 4, 0, 2, 32, 16, 128},
},
66666666 * time.Nanosecond,
0,
},
{
[][]byte{
// SEI
{6, 1, 4, 0, 4, 0, 16, 128},
},
16666666 * time.Nanosecond,
16666666 * time.Nanosecond,
},
{
[][]byte{
// SEI
{6, 1, 4, 0, 6, 0, 16, 128},
},
33333333 * time.Nanosecond,
33333333 * time.Nanosecond,
},
},
800 * time.Millisecond,
-200 * time.Millisecond,
},
{
[][]byte{
{
0x41, 0x9e, 0x42, 0x78, 0x82, 0x1f, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x02,
0x0f,
},
},
400 * time.Millisecond,
0,
},
{
[][]byte{
{
0x01, 0x9e, 0x61, 0x74, 0x43, 0xff, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x9c,
},
},
200 * time.Millisecond,
200 * time.Millisecond,
},
}
ex := NewDTSExtractor()
for _, sample := range sequence {
dts, err := ex.Extract(sample.nalus, sample.pts)
require.NoError(t, err)
require.Equal(t, sample.dts, dts)
} {
t.Run(ca.name, func(t *testing.T) {
ex := NewDTSExtractor()
for _, sample := range ca.sequence {
dts, err := ex.Extract(sample.nalus, sample.pts)
require.NoError(t, err)
require.Equal(t, sample.dts, dts)
}
})
}
}

View File

@@ -841,7 +841,7 @@ func (s SPS) Height() int {
return int((2 - f) * (s.PicHeightInMbsMinus1 + 1) * 16)
}
// FPS returns the frame per second of the video.
// FPS returns the frames per second of the video.
func (s SPS) FPS() float64 {
if s.VUI == nil || s.VUI.TimingInfo == nil {
return 0