mirror of
https://github.com/aler9/gortsplib
synced 2025-10-30 10:06:28 +08:00
h264: support extracting DTS from nvenc (https://github.com/aler9/rtsp-simple-server/issues/989)
This commit is contained in:
@@ -68,7 +68,7 @@ func getPOC(buf []byte, sps *SPS) (uint32, error) {
|
|||||||
return uint32(picOrderCntLsb), nil
|
return uint32(picOrderCntLsb), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNALUSPOC(nalus [][]byte, sps *SPS) (uint32, error) {
|
func findPOC(nalus [][]byte, sps *SPS) (uint32, error) {
|
||||||
for _, nalu := range nalus {
|
for _, nalu := range nalus {
|
||||||
typ := NALUType(nalu[0] & 0x1F)
|
typ := NALUType(nalu[0] & 0x1F)
|
||||||
if typ == NALUTypeIDR || typ == NALUTypeNonIDR {
|
if typ == NALUTypeIDR || typ == NALUTypeNonIDR {
|
||||||
@@ -94,6 +94,70 @@ func getPOCDiff(poc1 uint32, poc2 uint32, sps *SPS) int32 {
|
|||||||
return diff
|
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.
|
// DTSExtractor is a utility that allows to extract NALU DTS from PTS.
|
||||||
type DTSExtractor struct {
|
type DTSExtractor struct {
|
||||||
sps []byte
|
sps []byte
|
||||||
@@ -110,27 +174,22 @@ func NewDTSExtractor() *DTSExtractor {
|
|||||||
return &DTSExtractor{}
|
return &DTSExtractor{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DTSExtractor) extractInner(
|
func (d *DTSExtractor) extractInner(nalus [][]byte, pts time.Duration) (time.Duration, int32, error) {
|
||||||
nalus [][]byte,
|
|
||||||
pts time.Duration,
|
|
||||||
) (time.Duration, int32, error) {
|
|
||||||
idrPresent := false
|
idrPresent := false
|
||||||
|
|
||||||
for _, nalu := range nalus {
|
for _, nalu := range nalus {
|
||||||
typ := NALUType(nalu[0] & 0x1F)
|
typ := NALUType(nalu[0] & 0x1F)
|
||||||
switch typ {
|
switch typ {
|
||||||
// parse SPS
|
|
||||||
case NALUTypeSPS:
|
case NALUTypeSPS:
|
||||||
if d.sps == nil || !bytes.Equal(d.sps, nalu) {
|
if d.sps == nil || !bytes.Equal(d.sps, nalu) {
|
||||||
var spsp SPS
|
var spsp SPS
|
||||||
err := spsp.Unmarshal(nalu)
|
err := spsp.Unmarshal(nalu)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, fmt.Errorf("invalid SPS: %v", err)
|
||||||
}
|
}
|
||||||
d.sps = append([]byte(nil), nalu...)
|
d.sps = append([]byte(nil), nalu...)
|
||||||
d.spsp = &spsp
|
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 &&
|
if d.spsp.VUI != nil && d.spsp.VUI.TimingInfo != nil &&
|
||||||
d.spsp.VUI.BitstreamRestriction != nil {
|
d.spsp.VUI.BitstreamRestriction != nil {
|
||||||
d.ptsDTSOffset = time.Duration(math.Round(float64(
|
d.ptsDTSOffset = time.Duration(math.Round(float64(
|
||||||
@@ -141,7 +200,6 @@ func (d *DTSExtractor) extractInner(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set IDR present flag
|
|
||||||
case NALUTypeIDR:
|
case NALUTypeIDR:
|
||||||
idrPresent = true
|
idrPresent = true
|
||||||
}
|
}
|
||||||
@@ -151,51 +209,68 @@ func (d *DTSExtractor) extractInner(
|
|||||||
return 0, 0, fmt.Errorf("SPS not received yet")
|
return 0, 0, fmt.Errorf("SPS not received yet")
|
||||||
}
|
}
|
||||||
|
|
||||||
if idrPresent || d.spsp.PicOrderCntType == 2 {
|
switch {
|
||||||
d.expectedPOC = 0
|
// DTS is computed from SEI
|
||||||
return pts - d.ptsDTSOffset, 0, nil
|
case d.spsp.VUI != nil && d.spsp.VUI.TimingInfo != nil && d.spsp.VUI.NalHRD != nil:
|
||||||
}
|
dpbOutputDelay, ok := findSEIPicTimingDPBOutputDelay(nalus, d.spsp)
|
||||||
|
if !ok {
|
||||||
// compute expectedPOC immediately in order to store it even in case of errors
|
return 0, 0, fmt.Errorf("SEI / pic_timing not found")
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return d.prevPTS - d.ptsDTSOffset +
|
return pts - time.Duration(dpbOutputDelay)/2*time.Second*
|
||||||
time.Duration(math.Round(float64(pts-d.prevPTS)/float64(pocDiff/2+1))), pocDiff, nil
|
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)
|
// DTS is always equal to PTS
|
||||||
return pts - d.ptsDTSOffset + time.Duration(math.Round(float64(d.prevDTS-d.prevPTS+d.ptsDTSOffset)*
|
case d.spsp.PicOrderCntType == 2:
|
||||||
float64(pocDiff)/float64(d.prevPOCDiff))), pocDiff, nil
|
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.
|
// Extract extracts the DTS of a group of NALUs.
|
||||||
func (d *DTSExtractor) Extract(
|
func (d *DTSExtractor) Extract(nalus [][]byte, pts time.Duration) (time.Duration, error) {
|
||||||
nalus [][]byte,
|
|
||||||
pts time.Duration,
|
|
||||||
) (time.Duration, error) {
|
|
||||||
dts, pocDiff, err := d.extractInner(nalus, pts)
|
dts, pocDiff, err := d.extractInner(nalus, pts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|||||||
@@ -8,137 +8,195 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestDTSExtractor(t *testing.T) {
|
func TestDTSExtractor(t *testing.T) {
|
||||||
sequence := []struct {
|
type sequenceSample struct {
|
||||||
nalus [][]byte
|
nalus [][]byte
|
||||||
pts time.Duration
|
pts time.Duration
|
||||||
dts 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,
|
[][]byte{
|
||||||
0x02, 0x27, 0xe5, 0xc0, 0x44, 0x00, 0x00, 0x03,
|
{
|
||||||
0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x28, 0x3c,
|
0x67, 0x64, 0x00, 0x28, 0xac, 0xd9, 0x40, 0x78,
|
||||||
0x60, 0xc6, 0x58,
|
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,
|
[][]byte{
|
||||||
0xd3, 0x3f, 0xcc, 0xb2, 0xec, 0x9a, 0x24, 0xb5,
|
{
|
||||||
0xe3, 0xa8, 0xf7, 0xa2, 0x9e, 0x26, 0x5f, 0x43,
|
0x41, 0x9a, 0x24, 0x6c, 0x41, 0x4f, 0xfe, 0xd6,
|
||||||
0x75, 0x25, 0x01, 0x9b, 0x96, 0xc4, 0xed, 0x3a,
|
0x8c, 0xb0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||||
0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x55,
|
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
||||||
0xda, 0xf7, 0x10, 0xe5, 0xc4, 0x70, 0xe1, 0xfe,
|
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||||
0x83, 0xc0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
0x00, 0x1f, 0xa0, 0x00, 0x00, 0x05, 0x68, 0x00,
|
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
||||||
0x00, 0x03, 0x01, 0xc6, 0x00, 0x00, 0x03, 0x01,
|
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||||
0x0c, 0x00, 0x00, 0x03, 0x00, 0xb1, 0x00, 0x00,
|
0x00, 0x00, 0x6d, 0x40,
|
||||||
0x03, 0x00, 0x8f, 0x80, 0x00, 0x00, 0x8a, 0x80,
|
},
|
||||||
0x00, 0x00, 0x9d, 0x00, 0x00, 0x03, 0x00, 0xb2,
|
},
|
||||||
0x00, 0x00, 0x03, 0x01, 0x1c, 0x00, 0x00, 0x03,
|
800 * time.Millisecond,
|
||||||
0x01, 0x7c, 0x00, 0x00, 0x03, 0x02, 0xf0, 0x00,
|
-200 * time.Millisecond,
|
||||||
0x00, 0x04, 0x40, 0x00, 0x00, 0x08, 0x80, 0x00,
|
},
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
{
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
[][]byte{
|
||||||
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
{
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
0x41, 0x9e, 0x42, 0x78, 0x82, 0x1f, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
||||||
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
||||||
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x02,
|
||||||
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
0x0f,
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
},
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
},
|
||||||
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
400 * time.Millisecond,
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
0,
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
},
|
||||||
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
{
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
[][]byte{
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
{
|
||||||
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
0x01, 0x9e, 0x61, 0x74, 0x43, 0xff, 0x00, 0x00,
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||||
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||||
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x9c,
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
},
|
||||||
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
},
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
200 * time.Millisecond,
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
200 * time.Millisecond,
|
||||||
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,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
[][]byte{
|
"sei-based",
|
||||||
|
[]sequenceSample{
|
||||||
{
|
{
|
||||||
0x41, 0x9a, 0x24, 0x6c, 0x41, 0x4f, 0xfe, 0xd6,
|
[][]byte{
|
||||||
0x8c, 0xb0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
// SEI (buffering period)
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
{6, 0, 7, 128, 117, 48, 0, 0, 3, 0, 64, 128},
|
||||||
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
// SEI (pic timing)
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
{6, 1, 4, 0, 0, 8, 16, 128},
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
// SPS
|
||||||
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
{
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
103, 100, 0, 42, 172, 44, 172, 7,
|
||||||
0x00, 0x00, 0x6d, 0x40,
|
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{
|
t.Run(ca.name, func(t *testing.T) {
|
||||||
{
|
ex := NewDTSExtractor()
|
||||||
0x41, 0x9e, 0x42, 0x78, 0x82, 0x1f, 0x00, 0x00,
|
for _, sample := range ca.sequence {
|
||||||
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
dts, err := ex.Extract(sample.nalus, sample.pts)
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
require.NoError(t, err)
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
require.Equal(t, sample.dts, dts)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -841,7 +841,7 @@ func (s SPS) Height() int {
|
|||||||
return int((2 - f) * (s.PicHeightInMbsMinus1 + 1) * 16)
|
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 {
|
func (s SPS) FPS() float64 {
|
||||||
if s.VUI == nil || s.VUI.TimingInfo == nil {
|
if s.VUI == nil || s.VUI.TimingInfo == nil {
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
Reference in New Issue
Block a user