h265: fix DTS extractor with libx265 (#245)

This commit is contained in:
Alessandro Ros
2025-09-13 15:52:02 +02:00
committed by GitHub
parent ff8c341f31
commit 66e4ac94ea
3 changed files with 133 additions and 39 deletions

View File

@@ -121,7 +121,6 @@ func (d *DTSExtractor) extractInner(au [][]byte, pts int64) (int64, bool, error)
d.spsp = &spsp
// reset state
d.prevDTSFilled = false
d.expectedPOC = 0
d.reorderedFrames = 0
d.pause = 0

View File

@@ -1,6 +1,7 @@
package h265
import (
"bytes"
"fmt"
"math"
@@ -128,7 +129,9 @@ func getPTSDTSDiff(buf []byte, sps *SPS, pps *PPS) (int, error) {
// DTSExtractor computes DTS from PTS.
type DTSExtractor struct {
sps []byte
spsp *SPS
pps []byte
ppsp *PPS
prevDTSFilled bool
prevDTS int64
@@ -155,34 +158,36 @@ func (d *DTSExtractor) extractInner(au [][]byte, pts int64) (int64, error) {
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: %w", err)
}
if !bytes.Equal(d.sps, nalu) {
var spsp SPS
err := spsp.Unmarshal(nalu)
if err != nil {
return 0, fmt.Errorf("invalid SPS: %w", err)
}
if spsp.VUI != nil && spsp.VUI.TimingInfo != nil && spsp.VUI.TimingInfo.TimeScale == 0 {
return 0, fmt.Errorf("invalid SPS VUI TimeScale")
}
d.spsp = &spsp
d.sps = nalu
d.spsp = &spsp
// reset state
d.prevDTSFilled = false
if len(d.spsp.MaxNumReorderPics) == 1 {
d.reorderedFrames = int(d.spsp.MaxNumReorderPics[0])
} else {
d.reorderedFrames = 0
// reset state
if len(d.spsp.MaxNumReorderPics) == 1 {
d.reorderedFrames = int(d.spsp.MaxNumReorderPics[0])
} else {
d.reorderedFrames = 0
}
d.pause = d.reorderedFrames
}
d.pause = d.reorderedFrames
case NALUType_PPS_NUT:
var ppsp PPS
err := ppsp.Unmarshal(nalu)
if err != nil {
return 0, fmt.Errorf("invalid PPS: %w", err)
if !bytes.Equal(d.pps, nalu) {
var ppsp PPS
err := ppsp.Unmarshal(nalu)
if err != nil {
return 0, fmt.Errorf("invalid PPS: %w", err)
}
d.ppsp = &ppsp
d.pps = nalu
}
d.ppsp = &ppsp
case NALUType_IDR_W_RADL, NALUType_IDR_N_LP:
idr = nalu
@@ -226,13 +231,6 @@ func (d *DTSExtractor) extractInner(au [][]byte, pts int64) (int64, error) {
if d.pause > 0 {
d.pause--
if !d.prevDTSFilled {
if d.spsp.VUI != nil && d.spsp.VUI.TimingInfo != nil {
timeDiff := int64(d.pause+1) * 90000 *
int64(d.spsp.VUI.TimingInfo.NumUnitsInTick) / int64(d.spsp.VUI.TimingInfo.TimeScale)
dts := pts - timeDiff
d.pause = 0
return dts, nil
}
return pts, nil
}
return d.prevDTS + 90, nil

View File

@@ -45,7 +45,7 @@ var casesDTSExtractor = []struct {
},
},
0,
-6000,
0,
},
{
[][]byte{{ // TRAIL_R
@@ -54,7 +54,7 @@ var casesDTSExtractor = []struct {
0x54, 0x57, 0x4e, 0x0a,
}},
9000,
-3000,
90,
},
{
[][]byte{{ // TRAIL_R
@@ -63,7 +63,7 @@ var casesDTSExtractor = []struct {
0x26, 0x5f, 0x10, 0x9c,
}},
5999,
-1,
2059,
},
{
[][]byte{{ // TRAIL_N
@@ -132,7 +132,7 @@ var casesDTSExtractor = []struct {
},
},
100,
-4400,
100,
},
{
[][]byte{
@@ -143,7 +143,7 @@ var casesDTSExtractor = []struct {
},
},
6130,
865,
3115,
},
{
[][]byte{
@@ -154,7 +154,7 @@ var casesDTSExtractor = []struct {
},
},
9100,
4982,
6107,
},
},
},
@@ -199,7 +199,7 @@ var casesDTSExtractor = []struct {
},
},
182999,
179999,
182999,
},
{
[][]byte{
@@ -217,7 +217,7 @@ var casesDTSExtractor = []struct {
},
},
188999,
181499,
183089,
},
{
[][]byte{
@@ -235,7 +235,7 @@ var casesDTSExtractor = []struct {
},
},
185999,
182999,
184059,
},
{
[][]byte{
@@ -397,6 +397,103 @@ var casesDTSExtractor = []struct {
},
},
},
{
"libx265, bframes=4",
[]sequenceSample{
{
[][]byte{
{
0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60,
0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x78, 0x95, 0x98, 0x09,
},
{
0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03,
0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x78, 0xa0, 0x03, 0xc0, 0x80, 0x11, 0x07,
0xcb, 0x96, 0x56, 0x64, 0xa4, 0xc2, 0xf0, 0x16,
0x80, 0x80, 0x00, 0x00, 0x03, 0x00, 0x80, 0x00,
0x00, 0x0f, 0x04,
},
{
0x44, 0x01, 0xc0, 0x73, 0xc1, 0x89,
},
{
0x28, 0x01, 0xac, 0x64, 0x11, 0x0b, 0x35, 0x45,
0xb4, 0xa0, 0x95, 0x12, 0x90, 0x42, 0x48, 0xe1,
0x2e, 0x21, 0xc4, 0x7c, 0xd4, 0x95, 0x63, 0x44,
0x2f, 0x04, 0x98, 0x87, 0x03, 0xe2, 0xdc, 0x43,
0x09, 0x61, 0x4c, 0x6f, 0x4a, 0x39, 0x6e, 0x27,
0xb6, 0x50, 0x3b, 0x03, 0xd0, 0x45, 0x1f, 0x4c,
0xf1, 0x66,
},
},
0,
0,
},
{
[][]byte{
{
0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60,
0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x78, 0x95, 0x98, 0x09,
},
{
0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03,
0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x78, 0xa0, 0x03, 0xc0, 0x80, 0x11, 0x07,
0xcb, 0x96, 0x56, 0x64, 0xa4, 0xc2, 0xf0, 0x16,
0x80, 0x80, 0x00, 0x00, 0x03, 0x00, 0x80, 0x00,
0x00, 0x0f, 0x04,
},
{
0x44, 0x01, 0xc0, 0x73, 0xc1, 0x89,
},
{
0x2a, 0x01, 0xac, 0x14, 0xa5, 0x46, 0x01, 0x10,
0xb2, 0xca, 0x41, 0x47, 0x18, 0xd2, 0x20, 0x02,
0xec, 0x71, 0x8b, 0xd1, 0x58, 0x36, 0x88, 0xc9,
0x1d, 0x2d, 0x82, 0x80, 0x37, 0x05, 0x80, 0x46,
0x29, 0x43, 0x38, 0x71, 0x10, 0x05, 0x8c, 0x84,
0x10, 0x61, 0xcd, 0x15, 0xc4, 0x20, 0x2c, 0x02,
0x8e, 0x30,
},
},
15000,
90,
},
{
[][]byte{
{
0x12, 0x01, 0xe0, 0x64, 0x9d, 0x60, 0x81, 0x40,
0x22, 0x28, 0xc4, 0x21, 0x08, 0x42, 0x10, 0x84,
0x20, 0x84, 0x21, 0x08, 0x42, 0x10, 0x84, 0x21,
0x08, 0x78, 0x92, 0x0d, 0xca, 0x31, 0xa0, 0x92,
0x80, 0x27, 0x20, 0x00, 0x05, 0x04, 0x00, 0x09,
0x88, 0x00, 0x11, 0xf0, 0x00, 0x1d, 0xf0, 0x00,
0x31, 0x20,
},
},
9000,
2317,
},
{
[][]byte{
{
0x10, 0x01, 0xe0, 0x24, 0xf5, 0x5e, 0x89, 0x02,
0xd0, 0x44, 0x43, 0x22, 0x22, 0x22, 0x22, 0x21,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x76,
0x33, 0xa7, 0x22, 0x80, 0x83, 0x80, 0x3e, 0xe0,
0x00, 0x05, 0xfc, 0x00, 0x0b, 0x78, 0x00, 0x15,
0x10, 0x00, 0x24, 0xa0, 0x00, 0x36, 0xa0, 0x00,
0x54, 0xc0,
},
},
3000,
3000,
},
},
},
}
func TestDTSExtractor(t *testing.T) {