mediamtx/internal/hls/muxer.go

181 lines
4.2 KiB
Go
Raw Normal View History

2021-07-24 16:31:54 +00:00
package hls
import (
"io"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/rtsp-simple-server/internal/h264"
)
const (
// an offset between PCR and PTS/DTS is needed to avoid PCR > PTS
pcrOffset = 500 * time.Millisecond
2021-07-24 16:31:54 +00:00
segmentMinAUCount = 100
)
// Muxer is a HLS muxer.
type Muxer struct {
hlsSegmentCount int
hlsSegmentDuration time.Duration
videoTrack *gortsplib.Track
audioTrack *gortsplib.Track
2021-08-25 17:51:59 +00:00
h264Conf *gortsplib.TrackConfigH264
aacConf *gortsplib.TrackConfigAAC
videoDTSEst *h264.DTSEstimator
audioAUCount int
currentSegment *segment
startPCR time.Time
startPTS time.Duration
primaryPlaylist *primaryPlaylist
streamPlaylist *streamPlaylist
2021-07-24 16:31:54 +00:00
}
// NewMuxer allocates a Muxer.
func NewMuxer(
hlsSegmentCount int,
hlsSegmentDuration time.Duration,
videoTrack *gortsplib.Track,
audioTrack *gortsplib.Track) (*Muxer, error) {
2021-08-25 17:51:59 +00:00
var h264Conf *gortsplib.TrackConfigH264
if videoTrack != nil {
var err error
2021-08-25 17:51:59 +00:00
h264Conf, err = videoTrack.ExtractConfigH264()
if err != nil {
return nil, err
}
}
2021-08-25 17:51:59 +00:00
var aacConf *gortsplib.TrackConfigAAC
2021-07-24 16:31:54 +00:00
if audioTrack != nil {
2021-08-25 17:51:59 +00:00
var err error
aacConf, err = audioTrack.ExtractConfigAAC()
2021-07-24 16:31:54 +00:00
if err != nil {
return nil, err
}
}
m := &Muxer{
hlsSegmentCount: hlsSegmentCount,
hlsSegmentDuration: hlsSegmentDuration,
videoTrack: videoTrack,
audioTrack: audioTrack,
2021-08-25 17:51:59 +00:00
h264Conf: h264Conf,
aacConf: aacConf,
currentSegment: newSegment(videoTrack, audioTrack, h264Conf, aacConf),
primaryPlaylist: newPrimaryPlaylist(videoTrack, audioTrack, h264Conf),
streamPlaylist: newStreamPlaylist(hlsSegmentCount),
2021-07-24 16:31:54 +00:00
}
return m, nil
}
// Close closes a Muxer.
func (m *Muxer) Close() {
m.streamPlaylist.close()
2021-07-24 16:31:54 +00:00
}
// WriteH264 writes H264 NALUs, grouped by PTS, into the muxer.
func (m *Muxer) WriteH264(pts time.Duration, nalus [][]byte) error {
idrPresent := func() bool {
for _, nalu := range nalus {
typ := h264.NALUType(nalu[0] & 0x1F)
if typ == h264.NALUTypeIDR {
return true
}
}
return false
}()
// skip group silently until we find one with a IDR
if !m.currentSegment.firstPacketWritten && !idrPresent {
2021-07-24 16:31:54 +00:00
return nil
}
if m.currentSegment.firstPacketWritten {
if idrPresent &&
m.currentSegment.duration() >= m.hlsSegmentDuration {
m.streamPlaylist.pushSegment(m.currentSegment)
2021-07-24 16:31:54 +00:00
2021-08-25 17:51:59 +00:00
m.currentSegment = newSegment(m.videoTrack, m.audioTrack, m.h264Conf, m.aacConf)
m.currentSegment.setStartPCR(m.startPCR)
2021-07-24 16:31:54 +00:00
}
} else {
m.startPCR = time.Now()
m.startPTS = pts
m.currentSegment.setStartPCR(m.startPCR)
m.videoDTSEst = h264.NewDTSEstimator()
2021-07-24 16:31:54 +00:00
}
pts -= m.startPTS
err := m.currentSegment.writeH264(
m.videoDTSEst.Feed(pts)+pcrOffset,
pts+pcrOffset,
2021-07-24 16:31:54 +00:00
idrPresent,
nalus)
if err != nil {
return err
}
return nil
}
// WriteAAC writes AAC AUs, grouped by PTS, into the muxer.
func (m *Muxer) WriteAAC(pts time.Duration, aus [][]byte) error {
if m.videoTrack == nil {
if m.currentSegment.firstPacketWritten {
if m.audioAUCount >= segmentMinAUCount &&
m.currentSegment.duration() >= m.hlsSegmentDuration {
m.audioAUCount = 0
2021-07-24 16:31:54 +00:00
m.streamPlaylist.pushSegment(m.currentSegment)
2021-08-25 17:51:59 +00:00
m.currentSegment = newSegment(m.videoTrack, m.audioTrack, m.h264Conf, m.aacConf)
m.currentSegment.setStartPCR(m.startPCR)
2021-07-24 16:31:54 +00:00
}
} else {
m.startPCR = time.Now()
m.startPTS = pts
m.currentSegment.setStartPCR(m.startPCR)
2021-07-24 16:31:54 +00:00
}
} else {
if !m.currentSegment.firstPacketWritten {
2021-07-24 16:31:54 +00:00
return nil
}
}
pts = pts - m.startPTS + pcrOffset
2021-07-24 16:31:54 +00:00
for i, au := range aus {
2021-08-25 17:51:59 +00:00
auPTS := pts + time.Duration(i)*1000*time.Second/time.Duration(m.aacConf.SampleRate)
2021-07-24 16:31:54 +00:00
2021-08-25 17:51:59 +00:00
err := m.currentSegment.writeAAC(auPTS, au)
2021-07-24 16:31:54 +00:00
if err != nil {
return err
}
m.audioAUCount++
2021-07-24 16:31:54 +00:00
}
return nil
}
2021-08-16 16:07:10 +00:00
// PrimaryPlaylist returns a reader to read the primary playlist
func (m *Muxer) PrimaryPlaylist() io.Reader {
return m.primaryPlaylist.reader()
2021-08-16 16:07:10 +00:00
}
2021-07-24 16:31:54 +00:00
2021-08-16 16:07:10 +00:00
// StreamPlaylist returns a reader to read the stream playlist.
func (m *Muxer) StreamPlaylist() io.Reader {
return m.streamPlaylist.reader()
2021-07-24 16:31:54 +00:00
}
// Segment returns a reader to read a segment.
func (m *Muxer) Segment(fname string) io.Reader {
return m.streamPlaylist.segment(fname)
2021-07-24 16:31:54 +00:00
}