mediamtx/internal/hls/muxer_variant_mpegts_segmen...

194 lines
3.9 KiB
Go

package hls
import (
"fmt"
"time"
"github.com/aler9/gortsplib/v2/pkg/codecs/h264"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/rtsp-simple-server/internal/hls/mpegts"
)
const (
mpegtsSegmentMinAUCount = 100
)
type muxerVariantMPEGTSSegmenter struct {
segmentDuration time.Duration
segmentMaxSize uint64
videoTrack *format.H264
audioTrack *format.MPEG4Audio
onSegmentReady func(*muxerVariantMPEGTSSegment)
writer *mpegts.Writer
nextSegmentID uint64
currentSegment *muxerVariantMPEGTSSegment
videoDTSExtractor *h264.DTSExtractor
startPCR time.Time
startDTS time.Duration
}
func newMuxerVariantMPEGTSSegmenter(
segmentDuration time.Duration,
segmentMaxSize uint64,
videoTrack *format.H264,
audioTrack *format.MPEG4Audio,
onSegmentReady func(*muxerVariantMPEGTSSegment),
) *muxerVariantMPEGTSSegmenter {
m := &muxerVariantMPEGTSSegmenter{
segmentDuration: segmentDuration,
segmentMaxSize: segmentMaxSize,
videoTrack: videoTrack,
audioTrack: audioTrack,
onSegmentReady: onSegmentReady,
}
m.writer = mpegts.NewWriter(
videoTrack,
audioTrack)
return m
}
func (m *muxerVariantMPEGTSSegmenter) genSegmentID() uint64 {
id := m.nextSegmentID
m.nextSegmentID++
return id
}
func (m *muxerVariantMPEGTSSegmenter) writeH264(ntp time.Time, pts time.Duration, nalus [][]byte) error {
idrPresent := false
nonIDRPresent := false
for _, nalu := range nalus {
typ := h264.NALUType(nalu[0] & 0x1F)
switch typ {
case h264.NALUTypeIDR:
idrPresent = true
case h264.NALUTypeNonIDR:
nonIDRPresent = true
}
}
var dts time.Duration
if m.currentSegment == nil {
// skip groups silently until we find one with a IDR
if !idrPresent {
return nil
}
m.videoDTSExtractor = h264.NewDTSExtractor()
var err error
dts, err = m.videoDTSExtractor.Extract(nalus, pts)
if err != nil {
return fmt.Errorf("unable to extract DTS: %v", err)
}
m.startPCR = ntp
m.startDTS = dts
dts = 0
pts -= m.startDTS
// create first segment
m.currentSegment = newMuxerVariantMPEGTSSegment(
m.genSegmentID(),
ntp,
m.segmentMaxSize,
m.videoTrack,
m.audioTrack,
m.writer)
} else {
if !idrPresent && !nonIDRPresent {
return nil
}
var err error
dts, err = m.videoDTSExtractor.Extract(nalus, pts)
if err != nil {
return fmt.Errorf("unable to extract DTS: %v", err)
}
dts -= m.startDTS
pts -= m.startDTS
// switch segment
if idrPresent &&
(dts-*m.currentSegment.startDTS) >= m.segmentDuration {
m.currentSegment.finalize(dts)
m.onSegmentReady(m.currentSegment)
m.currentSegment = newMuxerVariantMPEGTSSegment(
m.genSegmentID(),
ntp,
m.segmentMaxSize,
m.videoTrack,
m.audioTrack,
m.writer)
}
}
err := m.currentSegment.writeH264(
ntp.Sub(m.startPCR),
dts,
pts,
idrPresent,
nalus)
if err != nil {
return err
}
return nil
}
func (m *muxerVariantMPEGTSSegmenter) writeAAC(ntp time.Time, pts time.Duration, au []byte) error {
if m.videoTrack == nil {
if m.currentSegment == nil {
m.startPCR = ntp
m.startDTS = pts
pts = 0
// create first segment
m.currentSegment = newMuxerVariantMPEGTSSegment(
m.genSegmentID(),
ntp,
m.segmentMaxSize,
m.videoTrack,
m.audioTrack,
m.writer)
} else {
pts -= m.startDTS
// switch segment
if m.currentSegment.audioAUCount >= mpegtsSegmentMinAUCount &&
(pts-*m.currentSegment.startDTS) >= m.segmentDuration {
m.currentSegment.finalize(pts)
m.onSegmentReady(m.currentSegment)
m.currentSegment = newMuxerVariantMPEGTSSegment(
m.genSegmentID(),
ntp,
m.segmentMaxSize,
m.videoTrack,
m.audioTrack,
m.writer)
}
}
} else {
// wait for the video track
if m.currentSegment == nil {
return nil
}
pts -= m.startDTS
}
err := m.currentSegment.writeAAC(ntp.Sub(m.startPCR), pts, au)
if err != nil {
return err
}
return nil
}