mediamtx/internal/hls/muxer_variant_mpegts_segment.go
2022-08-05 23:50:45 +02:00

212 lines
4.6 KiB
Go

package hls
import (
"bytes"
"fmt"
"io"
"strconv"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/h264"
"github.com/aler9/gortsplib/pkg/mpeg4audio"
"github.com/asticode/go-astits"
)
const (
mpegtsPCROffset = 400 * time.Millisecond // 2 samples @ 5fps
)
type muxerVariantMPEGTSSegment struct {
segmentMaxSize uint64
videoTrack *gortsplib.TrackH264
audioTrack *gortsplib.TrackMPEG4Audio
writeData func(*astits.MuxerData) (int, error)
startTime time.Time
name string
buf bytes.Buffer
startDTS *time.Duration
endDTS time.Duration
pcrSendCounter int
audioAUCount int
}
func newMuxerVariantMPEGTSSegment(
startTime time.Time,
segmentMaxSize uint64,
videoTrack *gortsplib.TrackH264,
audioTrack *gortsplib.TrackMPEG4Audio,
writeData func(*astits.MuxerData) (int, error),
) *muxerVariantMPEGTSSegment {
t := &muxerVariantMPEGTSSegment{
segmentMaxSize: segmentMaxSize,
videoTrack: videoTrack,
audioTrack: audioTrack,
writeData: writeData,
startTime: startTime,
name: strconv.FormatInt(startTime.Unix(), 10),
}
// WriteTable() is called automatically when WriteData() is called with
// - PID == PCRPID
// - AdaptationField != nil
// - RandomAccessIndicator = true
return t
}
func (t *muxerVariantMPEGTSSegment) duration() time.Duration {
return t.endDTS - *t.startDTS
}
func (t *muxerVariantMPEGTSSegment) write(p []byte) (int, error) {
if uint64(len(p)+t.buf.Len()) > t.segmentMaxSize {
return 0, fmt.Errorf("reached maximum segment size")
}
return t.buf.Write(p)
}
func (t *muxerVariantMPEGTSSegment) reader() io.Reader {
return bytes.NewReader(t.buf.Bytes())
}
func (t *muxerVariantMPEGTSSegment) writeH264(
pcr time.Duration,
dts time.Duration,
pts time.Duration,
idrPresent bool,
nalus [][]byte,
) error {
// prepend an AUD. This is required by video.js and iOS
nalus = append([][]byte{{byte(h264.NALUTypeAccessUnitDelimiter), 240}}, nalus...)
enc, err := h264.AnnexBMarshal(nalus)
if err != nil {
return err
}
var af *astits.PacketAdaptationField
if idrPresent {
af = &astits.PacketAdaptationField{}
af.RandomAccessIndicator = true
}
// send PCR once in a while
if t.pcrSendCounter == 0 {
if af == nil {
af = &astits.PacketAdaptationField{}
}
af.HasPCR = true
af.PCR = &astits.ClockReference{Base: int64(pcr.Seconds() * 90000)}
t.pcrSendCounter = 3
}
t.pcrSendCounter--
oh := &astits.PESOptionalHeader{
MarkerBits: 2,
}
if dts == pts {
oh.PTSDTSIndicator = astits.PTSDTSIndicatorOnlyPTS
oh.PTS = &astits.ClockReference{Base: int64((pts + mpegtsPCROffset).Seconds() * 90000)}
} else {
oh.PTSDTSIndicator = astits.PTSDTSIndicatorBothPresent
oh.DTS = &astits.ClockReference{Base: int64((dts + mpegtsPCROffset).Seconds() * 90000)}
oh.PTS = &astits.ClockReference{Base: int64((pts + mpegtsPCROffset).Seconds() * 90000)}
}
_, err = t.writeData(&astits.MuxerData{
PID: 256,
AdaptationField: af,
PES: &astits.PESData{
Header: &astits.PESHeader{
OptionalHeader: oh,
StreamID: 224, // video
},
Data: enc,
},
})
if err != nil {
return err
}
if t.startDTS == nil {
t.startDTS = &dts
}
t.endDTS = dts
return nil
}
func (t *muxerVariantMPEGTSSegment) writeAAC(
pcr time.Duration,
pts time.Duration,
aus [][]byte,
) error {
pkts := make(mpeg4audio.ADTSPackets, len(aus))
for i, au := range aus {
pkts[i] = &mpeg4audio.ADTSPacket{
Type: t.audioTrack.Config.Type,
SampleRate: t.audioTrack.Config.SampleRate,
ChannelCount: t.audioTrack.Config.ChannelCount,
AU: au,
}
}
enc, err := pkts.Marshal()
if err != nil {
return err
}
af := &astits.PacketAdaptationField{
RandomAccessIndicator: true,
}
if t.videoTrack == nil {
// send PCR once in a while
if t.pcrSendCounter == 0 {
af.HasPCR = true
af.PCR = &astits.ClockReference{Base: int64(pcr.Seconds() * 90000)}
t.pcrSendCounter = 3
}
t.pcrSendCounter--
}
_, err = t.writeData(&astits.MuxerData{
PID: 257,
AdaptationField: af,
PES: &astits.PESData{
Header: &astits.PESHeader{
OptionalHeader: &astits.PESOptionalHeader{
MarkerBits: 2,
PTSDTSIndicator: astits.PTSDTSIndicatorOnlyPTS,
PTS: &astits.ClockReference{Base: int64((pts + mpegtsPCROffset).Seconds() * 90000)},
},
PacketLength: uint16(len(enc) + 8),
StreamID: 192, // audio
},
Data: enc,
},
})
if err != nil {
return err
}
if t.videoTrack == nil {
t.audioAUCount += len(aus)
if t.startDTS == nil {
t.startDTS = &pts
}
t.endDTS = pts
}
return nil
}