2021-07-24 13:55:42 +00:00
|
|
|
package hls
|
2021-04-11 17:05:08 +00:00
|
|
|
|
|
|
|
import (
|
2021-08-23 10:26:06 +00:00
|
|
|
"bytes"
|
2022-01-29 15:10:08 +00:00
|
|
|
"fmt"
|
2021-07-24 13:55:42 +00:00
|
|
|
"io"
|
2021-04-11 17:05:08 +00:00
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
|
2021-08-14 13:39:29 +00:00
|
|
|
"github.com/aler9/gortsplib"
|
2022-05-31 17:17:26 +00:00
|
|
|
"github.com/aler9/gortsplib/pkg/h264"
|
2022-08-05 21:50:45 +00:00
|
|
|
"github.com/aler9/gortsplib/pkg/mpeg4audio"
|
2021-04-11 17:05:08 +00:00
|
|
|
"github.com/asticode/go-astits"
|
|
|
|
)
|
|
|
|
|
2022-03-20 17:20:32 +00:00
|
|
|
const (
|
2022-06-03 09:31:39 +00:00
|
|
|
mpegtsPCROffset = 400 * time.Millisecond // 2 samples @ 5fps
|
2022-03-20 17:20:32 +00:00
|
|
|
)
|
|
|
|
|
2022-05-31 17:17:26 +00:00
|
|
|
type muxerVariantMPEGTSSegment struct {
|
|
|
|
segmentMaxSize uint64
|
|
|
|
videoTrack *gortsplib.TrackH264
|
2022-08-05 21:50:45 +00:00
|
|
|
audioTrack *gortsplib.TrackMPEG4Audio
|
2022-05-31 17:17:26 +00:00
|
|
|
writeData func(*astits.MuxerData) (int, error)
|
2021-08-23 10:26:06 +00:00
|
|
|
|
2022-02-21 16:13:09 +00:00
|
|
|
startTime time.Time
|
2022-01-28 22:30:16 +00:00
|
|
|
name string
|
|
|
|
buf bytes.Buffer
|
2022-06-03 09:31:39 +00:00
|
|
|
startDTS *time.Duration
|
|
|
|
endDTS time.Duration
|
2022-01-28 22:30:16 +00:00
|
|
|
pcrSendCounter int
|
2022-01-29 15:10:08 +00:00
|
|
|
audioAUCount int
|
2021-04-11 17:05:08 +00:00
|
|
|
}
|
|
|
|
|
2022-05-31 17:17:26 +00:00
|
|
|
func newMuxerVariantMPEGTSSegment(
|
|
|
|
startTime time.Time,
|
|
|
|
segmentMaxSize uint64,
|
2022-01-30 17:34:54 +00:00
|
|
|
videoTrack *gortsplib.TrackH264,
|
2022-08-05 21:50:45 +00:00
|
|
|
audioTrack *gortsplib.TrackMPEG4Audio,
|
2022-03-20 16:58:39 +00:00
|
|
|
writeData func(*astits.MuxerData) (int, error),
|
2022-05-31 17:17:26 +00:00
|
|
|
) *muxerVariantMPEGTSSegment {
|
|
|
|
t := &muxerVariantMPEGTSSegment{
|
|
|
|
segmentMaxSize: segmentMaxSize,
|
|
|
|
videoTrack: videoTrack,
|
|
|
|
audioTrack: audioTrack,
|
|
|
|
writeData: writeData,
|
|
|
|
startTime: startTime,
|
|
|
|
name: strconv.FormatInt(startTime.Unix(), 10),
|
2021-04-11 17:05:08 +00:00
|
|
|
}
|
|
|
|
|
2021-08-14 11:05:42 +00:00
|
|
|
// WriteTable() is called automatically when WriteData() is called with
|
|
|
|
// - PID == PCRPID
|
|
|
|
// - AdaptationField != nil
|
|
|
|
// - RandomAccessIndicator = true
|
2021-04-22 21:27:55 +00:00
|
|
|
|
2021-04-11 17:05:08 +00:00
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
2022-05-31 17:17:26 +00:00
|
|
|
func (t *muxerVariantMPEGTSSegment) duration() time.Duration {
|
2022-06-03 09:31:39 +00:00
|
|
|
return t.endDTS - *t.startDTS
|
2021-04-11 17:05:08 +00:00
|
|
|
}
|
|
|
|
|
2022-05-31 17:17:26 +00:00
|
|
|
func (t *muxerVariantMPEGTSSegment) write(p []byte) (int, error) {
|
|
|
|
if uint64(len(p)+t.buf.Len()) > t.segmentMaxSize {
|
2022-01-29 15:10:08 +00:00
|
|
|
return 0, fmt.Errorf("reached maximum segment size")
|
|
|
|
}
|
|
|
|
|
2021-09-07 10:02:44 +00:00
|
|
|
return t.buf.Write(p)
|
2021-04-11 17:05:08 +00:00
|
|
|
}
|
|
|
|
|
2022-05-31 17:17:26 +00:00
|
|
|
func (t *muxerVariantMPEGTSSegment) reader() io.Reader {
|
2021-08-23 10:26:06 +00:00
|
|
|
return bytes.NewReader(t.buf.Bytes())
|
2021-07-24 13:55:42 +00:00
|
|
|
}
|
|
|
|
|
2022-05-31 17:17:26 +00:00
|
|
|
func (t *muxerVariantMPEGTSSegment) writeH264(
|
2022-03-20 17:07:51 +00:00
|
|
|
pcr time.Duration,
|
2021-08-15 07:06:55 +00:00
|
|
|
dts time.Duration,
|
|
|
|
pts time.Duration,
|
2021-09-07 10:02:44 +00:00
|
|
|
idrPresent bool,
|
2022-05-31 17:17:26 +00:00
|
|
|
nalus [][]byte,
|
2022-04-07 10:50:35 +00:00
|
|
|
) error {
|
2022-05-31 17:17:26 +00:00
|
|
|
// prepend an AUD. This is required by video.js and iOS
|
|
|
|
nalus = append([][]byte{{byte(h264.NALUTypeAccessUnitDelimiter), 240}}, nalus...)
|
|
|
|
|
2022-06-27 15:47:07 +00:00
|
|
|
enc, err := h264.AnnexBMarshal(nalus)
|
2022-05-31 17:17:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-08-14 15:25:40 +00:00
|
|
|
var af *astits.PacketAdaptationField
|
|
|
|
|
2021-09-07 10:02:44 +00:00
|
|
|
if idrPresent {
|
2022-03-13 13:49:50 +00:00
|
|
|
af = &astits.PacketAdaptationField{}
|
2021-08-14 15:25:40 +00:00
|
|
|
af.RandomAccessIndicator = true
|
2021-08-21 19:13:23 +00:00
|
|
|
}
|
2021-08-14 15:25:40 +00:00
|
|
|
|
2021-08-21 19:13:23 +00:00
|
|
|
// send PCR once in a while
|
|
|
|
if t.pcrSendCounter == 0 {
|
|
|
|
if af == nil {
|
|
|
|
af = &astits.PacketAdaptationField{}
|
|
|
|
}
|
2021-08-14 15:25:40 +00:00
|
|
|
af.HasPCR = true
|
2022-03-20 17:07:51 +00:00
|
|
|
af.PCR = &astits.ClockReference{Base: int64(pcr.Seconds() * 90000)}
|
2021-08-21 19:13:23 +00:00
|
|
|
t.pcrSendCounter = 3
|
2021-08-14 15:25:40 +00:00
|
|
|
}
|
2021-08-21 19:13:23 +00:00
|
|
|
t.pcrSendCounter--
|
2021-08-14 15:25:40 +00:00
|
|
|
|
2021-08-20 11:07:35 +00:00
|
|
|
oh := &astits.PESOptionalHeader{
|
|
|
|
MarkerBits: 2,
|
|
|
|
}
|
|
|
|
|
2022-06-03 09:31:39 +00:00
|
|
|
if dts == pts {
|
2021-08-20 11:07:35 +00:00
|
|
|
oh.PTSDTSIndicator = astits.PTSDTSIndicatorOnlyPTS
|
2022-06-03 09:31:39 +00:00
|
|
|
oh.PTS = &astits.ClockReference{Base: int64((pts + mpegtsPCROffset).Seconds() * 90000)}
|
2021-08-20 11:07:35 +00:00
|
|
|
} else {
|
|
|
|
oh.PTSDTSIndicator = astits.PTSDTSIndicatorBothPresent
|
2022-06-02 10:42:59 +00:00
|
|
|
oh.DTS = &astits.ClockReference{Base: int64((dts + mpegtsPCROffset).Seconds() * 90000)}
|
2022-06-03 09:31:39 +00:00
|
|
|
oh.PTS = &astits.ClockReference{Base: int64((pts + mpegtsPCROffset).Seconds() * 90000)}
|
2021-04-11 17:05:08 +00:00
|
|
|
}
|
|
|
|
|
2022-05-31 17:17:26 +00:00
|
|
|
_, err = t.writeData(&astits.MuxerData{
|
2021-04-11 17:05:08 +00:00
|
|
|
PID: 256,
|
|
|
|
AdaptationField: af,
|
|
|
|
PES: &astits.PESData{
|
|
|
|
Header: &astits.PESHeader{
|
2021-08-20 11:07:35 +00:00
|
|
|
OptionalHeader: oh,
|
2021-09-23 18:14:20 +00:00
|
|
|
StreamID: 224, // video
|
2021-04-11 17:05:08 +00:00
|
|
|
},
|
|
|
|
Data: enc,
|
|
|
|
},
|
|
|
|
})
|
2022-01-29 15:10:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-04-11 17:05:08 +00:00
|
|
|
|
2022-06-03 09:31:39 +00:00
|
|
|
if t.startDTS == nil {
|
|
|
|
t.startDTS = &dts
|
2021-04-11 17:05:08 +00:00
|
|
|
}
|
2022-02-21 15:56:07 +00:00
|
|
|
|
2022-06-03 09:31:39 +00:00
|
|
|
t.endDTS = dts
|
2022-02-21 15:56:07 +00:00
|
|
|
|
2022-01-29 15:10:08 +00:00
|
|
|
return nil
|
|
|
|
}
|
2021-04-11 17:05:08 +00:00
|
|
|
|
2022-05-31 17:17:26 +00:00
|
|
|
func (t *muxerVariantMPEGTSSegment) writeAAC(
|
2022-03-20 17:07:51 +00:00
|
|
|
pcr time.Duration,
|
2022-01-29 15:10:08 +00:00
|
|
|
pts time.Duration,
|
2022-05-31 17:17:26 +00:00
|
|
|
aus [][]byte,
|
2022-04-07 10:50:35 +00:00
|
|
|
) error {
|
2022-08-05 21:50:45 +00:00
|
|
|
pkts := make(mpeg4audio.ADTSPackets, len(aus))
|
2022-05-31 17:17:26 +00:00
|
|
|
|
|
|
|
for i, au := range aus {
|
2022-08-05 21:50:45 +00:00
|
|
|
pkts[i] = &mpeg4audio.ADTSPacket{
|
2022-06-24 15:00:28 +00:00
|
|
|
Type: t.audioTrack.Config.Type,
|
|
|
|
SampleRate: t.audioTrack.Config.SampleRate,
|
|
|
|
ChannelCount: t.audioTrack.Config.ChannelCount,
|
2022-05-31 17:17:26 +00:00
|
|
|
AU: au,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-27 15:47:07 +00:00
|
|
|
enc, err := pkts.Marshal()
|
2022-05-31 17:17:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-04-11 17:05:08 +00:00
|
|
|
af := &astits.PacketAdaptationField{
|
|
|
|
RandomAccessIndicator: true,
|
|
|
|
}
|
|
|
|
|
2021-08-15 07:06:55 +00:00
|
|
|
if t.videoTrack == nil {
|
2021-08-21 19:13:23 +00:00
|
|
|
// send PCR once in a while
|
|
|
|
if t.pcrSendCounter == 0 {
|
|
|
|
af.HasPCR = true
|
2022-03-20 17:07:51 +00:00
|
|
|
af.PCR = &astits.ClockReference{Base: int64(pcr.Seconds() * 90000)}
|
2021-08-21 19:13:23 +00:00
|
|
|
t.pcrSendCounter = 3
|
|
|
|
}
|
2022-04-28 11:04:13 +00:00
|
|
|
t.pcrSendCounter--
|
2021-04-11 17:05:08 +00:00
|
|
|
}
|
|
|
|
|
2022-05-31 17:17:26 +00:00
|
|
|
_, err = t.writeData(&astits.MuxerData{
|
2021-04-11 17:05:08 +00:00
|
|
|
PID: 257,
|
|
|
|
AdaptationField: af,
|
|
|
|
PES: &astits.PESData{
|
|
|
|
Header: &astits.PESHeader{
|
|
|
|
OptionalHeader: &astits.PESOptionalHeader{
|
|
|
|
MarkerBits: 2,
|
|
|
|
PTSDTSIndicator: astits.PTSDTSIndicatorOnlyPTS,
|
2022-06-03 09:31:39 +00:00
|
|
|
PTS: &astits.ClockReference{Base: int64((pts + mpegtsPCROffset).Seconds() * 90000)},
|
2021-04-11 17:05:08 +00:00
|
|
|
},
|
2021-09-07 10:02:44 +00:00
|
|
|
PacketLength: uint16(len(enc) + 8),
|
2021-09-23 18:14:20 +00:00
|
|
|
StreamID: 192, // audio
|
2021-04-11 17:05:08 +00:00
|
|
|
},
|
2021-09-07 10:02:44 +00:00
|
|
|
Data: enc,
|
2021-04-11 17:05:08 +00:00
|
|
|
},
|
|
|
|
})
|
2022-01-29 15:10:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if t.videoTrack == nil {
|
2022-05-31 17:17:26 +00:00
|
|
|
t.audioAUCount += len(aus)
|
2022-01-29 15:10:08 +00:00
|
|
|
|
2022-06-03 09:31:39 +00:00
|
|
|
if t.startDTS == nil {
|
|
|
|
t.startDTS = &pts
|
|
|
|
}
|
2022-02-21 15:56:07 +00:00
|
|
|
|
2022-06-03 09:31:39 +00:00
|
|
|
t.endDTS = pts
|
2022-02-21 15:56:07 +00:00
|
|
|
}
|
|
|
|
|
2022-01-29 15:10:08 +00:00
|
|
|
return nil
|
2021-04-11 17:05:08 +00:00
|
|
|
}
|