Skip to content

Commit

Permalink
hls muxer: use right SPS/PPS for each sample
Browse files Browse the repository at this point in the history
  • Loading branch information
aler9 committed May 31, 2022
1 parent 5af27e1 commit 56516d2
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 61 deletions.
8 changes: 4 additions & 4 deletions internal/hls/muxer_variant_fmp4.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,12 @@ type fmp4VideoSample struct {
nalus [][]byte
avcc []byte
idrPresent bool
prev *fmp4VideoSample
next *fmp4VideoSample
pocDiff int32
}

func (s *fmp4VideoSample) fillDTS(
prev *fmp4VideoSample,
sps *h264.SPS,
expectedPOC *uint32,
) error {
Expand All @@ -164,13 +164,13 @@ func (s *fmp4VideoSample) fillDTS(
if s.pocDiff == 0 {
s.dts = s.pts
} else {
if prev.pocDiff == 0 {
if s.prev.pocDiff == 0 {
if s.pocDiff == -2 {
return fmt.Errorf("invalid frame POC")
}
s.dts = prev.pts + time.Duration(math.Round(float64(s.pts-prev.pts)/float64(s.pocDiff/2+1)))
s.dts = s.prev.pts + time.Duration(math.Round(float64(s.pts-s.prev.pts)/float64(s.pocDiff/2+1)))
} else {
s.dts = s.pts + time.Duration(math.Round(float64(prev.dts-prev.pts)*float64(s.pocDiff)/float64(prev.pocDiff)))
s.dts = s.pts + time.Duration(math.Round(float64(s.prev.dts-s.prev.pts)*float64(s.pocDiff)/float64(s.prev.pocDiff)))
}
}
}
Expand Down
24 changes: 5 additions & 19 deletions internal/hls/muxer_variant_fmp4_segment.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,14 @@ type muxerVariantFMP4Segment struct {
segmentMaxSize uint64
videoTrack *gortsplib.TrackH264
audioTrack *gortsplib.TrackAAC
videoSPSP *h264.SPS
genPartID func() uint64
onPartFinalized func(*muxerVariantFMP4Part)

sps *h264.SPS
size uint64
parts []*muxerVariantFMP4Part
currentPart *muxerVariantFMP4Part
renderedDuration time.Duration
expectedPOC uint32
}

func newMuxerVariantFMP4Segment(
Expand All @@ -69,9 +68,10 @@ func newMuxerVariantFMP4Segment(
segmentMaxSize uint64,
videoTrack *gortsplib.TrackH264,
audioTrack *gortsplib.TrackAAC,
videoSPSP *h264.SPS,
genPartID func() uint64,
onPartFinalized func(*muxerVariantFMP4Part),
) (*muxerVariantFMP4Segment, error) {
) *muxerVariantFMP4Segment {
s := &muxerVariantFMP4Segment{
lowLatency: lowLatency,
id: id,
Expand All @@ -81,28 +81,19 @@ func newMuxerVariantFMP4Segment(
segmentMaxSize: segmentMaxSize,
videoTrack: videoTrack,
audioTrack: audioTrack,
videoSPSP: videoSPSP,
genPartID: genPartID,
onPartFinalized: onPartFinalized,
}

if s.videoTrack != nil {
var spsp h264.SPS
err := spsp.Unmarshal(s.videoTrack.SPS())
if err != nil {
return nil, err
}

s.sps = &spsp
}

s.currentPart = newMuxerVariantFMP4Part(
s.videoTrack,
s.audioTrack,
s.genPartID(),
s.startDTS,
)

return s, nil
return s
}

func (s *muxerVariantFMP4Segment) name() string {
Expand Down Expand Up @@ -152,11 +143,6 @@ func (s *muxerVariantFMP4Segment) writeH264(sample *fmp4VideoSample) error {
return fmt.Errorf("reached maximum segment size")
}

err := sample.next.fillDTS(sample, s.sps, &s.expectedPOC)
if err != nil {
return err
}

s.currentPart.writeH264(sample)

s.size += size
Expand Down
95 changes: 57 additions & 38 deletions internal/hls/muxer_variant_fmp4_segmenter.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,19 @@ type muxerVariantFMP4Segmenter struct {
onSegmentFinalized func(*muxerVariantFMP4Segment)
onPartFinalized func(*muxerVariantFMP4Part)

currentSegment *muxerVariantFMP4Segment
startPTS time.Duration
lastSPS []byte
lastPPS []byte
nextSegmentID uint64
nextPartID uint64
nextVideoSample *fmp4VideoSample
nextAudioSample *fmp4AudioSample
currentSegment *muxerVariantFMP4Segment
startPTS time.Duration
videoSPSP *h264.SPS
videoSPS []byte
videoPPS []byte
videoNextSPSP *h264.SPS
videoNextSPS []byte
videoNextPPS []byte
nextSegmentID uint64
nextPartID uint64
nextVideoSample *fmp4VideoSample
nextAudioSample *fmp4AudioSample
videoExpectedPOC uint32
}

func newMuxerVariantFMP4Segmenter(
Expand Down Expand Up @@ -82,9 +87,35 @@ func (m *muxerVariantFMP4Segmenter) writeH264(pts time.Duration, nalus [][]byte)
}

func (m *muxerVariantFMP4Segmenter) writeH264Entry(sample *fmp4VideoSample) error {
// put SPS/PPS into a queue in order to sync them with the sample queue
m.videoSPSP = m.videoNextSPSP
m.videoSPS = m.videoNextSPS
m.videoPPS = m.videoNextPPS
spsChanged := false
if sample.idrPresent {
videoNextSPS := m.videoTrack.SPS()
videoNextPPS := m.videoTrack.PPS()

if m.videoSPS == nil ||
!bytes.Equal(m.videoNextSPS, videoNextSPS) ||
!bytes.Equal(m.videoNextPPS, videoNextPPS) {
spsChanged = true

var videoSPSP h264.SPS
err := videoSPSP.Unmarshal(videoNextSPS)
if err != nil {
return err
}

m.videoNextSPSP = &videoSPSP
m.videoNextSPS = videoNextSPS
m.videoNextPPS = videoNextPPS
}
}

sample.pts -= m.startPTS

// put sample into a queue in order to
// put samples into a queue in order to
// - allow to compute sample dts
// - allow to compute sample duration
// - check if next sample is IDR
Expand All @@ -93,6 +124,7 @@ func (m *muxerVariantFMP4Segmenter) writeH264Entry(sample *fmp4VideoSample) erro
return nil
}
sample.next = m.nextVideoSample
sample.next.prev = sample

now := time.Now()

Expand All @@ -103,8 +135,7 @@ func (m *muxerVariantFMP4Segmenter) writeH264Entry(sample *fmp4VideoSample) erro
}

// create first segment
var err error
m.currentSegment, err = newMuxerVariantFMP4Segment(
m.currentSegment = newMuxerVariantFMP4Segment(
m.lowLatency,
m.genSegmentID(),
now,
Expand All @@ -113,42 +144,37 @@ func (m *muxerVariantFMP4Segmenter) writeH264Entry(sample *fmp4VideoSample) erro
m.segmentMaxSize,
m.videoTrack,
m.audioTrack,
m.videoSPSP,
m.genPartID,
m.onPartFinalized,
)
if err != nil {
return err
}

m.lastSPS = m.videoTrack.SPS()
m.lastPPS = m.videoTrack.PPS()
m.startPTS = sample.pts
sample.pts = 0
sample.next.pts -= m.startPTS
}

err := m.currentSegment.writeH264(sample)
err := sample.next.fillDTS(m.videoNextSPSP, &m.videoExpectedPOC)
if err != nil {
return err
}

err = m.currentSegment.writeH264(sample)
if err != nil {
return err
}

// switch segment
if sample.next.idrPresent {
sps := m.videoTrack.SPS()
pps := m.videoTrack.PPS()

if (sample.next.pts-m.currentSegment.startDTS) >= m.segmentDuration ||
!bytes.Equal(m.lastSPS, sps) ||
!bytes.Equal(m.lastPPS, pps) {
spsChanged {
err := m.currentSegment.finalize(sample.next, nil)
if err != nil {
return err
}
m.onSegmentFinalized(m.currentSegment)

m.lastSPS = sps
m.lastPPS = pps
m.currentSegment, err = newMuxerVariantFMP4Segment(
m.currentSegment = newMuxerVariantFMP4Segment(
m.lowLatency,
m.genSegmentID(),
now,
Expand All @@ -157,12 +183,10 @@ func (m *muxerVariantFMP4Segmenter) writeH264Entry(sample *fmp4VideoSample) erro
m.segmentMaxSize,
m.videoTrack,
m.audioTrack,
m.videoNextSPSP,
m.genPartID,
m.onPartFinalized,
)
if err != nil {
return err
}
}
}

Expand All @@ -185,7 +209,7 @@ func (m *muxerVariantFMP4Segmenter) writeAAC(pts time.Duration, aus [][]byte) er
func (m *muxerVariantFMP4Segmenter) writeAACEntry(sample *fmp4AudioSample) error {
sample.pts -= m.startPTS

// put sample into a queue in order to
// put samples into a queue in order to
// allow to compute the sample duration
sample, m.nextAudioSample = m.nextAudioSample, sample
if sample == nil {
Expand All @@ -198,8 +222,7 @@ func (m *muxerVariantFMP4Segmenter) writeAACEntry(sample *fmp4AudioSample) error
if m.videoTrack == nil {
if m.currentSegment == nil {
// create first segment
var err error
m.currentSegment, err = newMuxerVariantFMP4Segment(
m.currentSegment = newMuxerVariantFMP4Segment(
m.lowLatency,
m.genSegmentID(),
now,
Expand All @@ -208,12 +231,10 @@ func (m *muxerVariantFMP4Segmenter) writeAACEntry(sample *fmp4AudioSample) error
m.segmentMaxSize,
m.videoTrack,
m.audioTrack,
nil,
m.genPartID,
m.onPartFinalized,
)
if err != nil {
return err
}

m.startPTS = sample.pts
sample.pts = 0
Expand All @@ -240,7 +261,7 @@ func (m *muxerVariantFMP4Segmenter) writeAACEntry(sample *fmp4AudioSample) error
}
m.onSegmentFinalized(m.currentSegment)

m.currentSegment, err = newMuxerVariantFMP4Segment(
m.currentSegment = newMuxerVariantFMP4Segment(
m.lowLatency,
m.genSegmentID(),
now,
Expand All @@ -249,12 +270,10 @@ func (m *muxerVariantFMP4Segmenter) writeAACEntry(sample *fmp4AudioSample) error
m.segmentMaxSize,
m.videoTrack,
m.audioTrack,
nil,
m.genPartID,
m.onPartFinalized,
)
if err != nil {
return err
}
}

return nil
Expand Down

0 comments on commit 56516d2

Please sign in to comment.