mirror of
https://github.com/bluenviron/mediamtx.git
synced 2025-12-25 12:32:01 -08:00
hls: send PCR once, at the beginning of every TS segment
This commit is contained in:
parent
2d71e69e43
commit
93b143395e
2 changed files with 39 additions and 25 deletions
|
|
@ -34,7 +34,6 @@ type Muxer struct {
|
||||||
h264SPS []byte
|
h264SPS []byte
|
||||||
h264PPS []byte
|
h264PPS []byte
|
||||||
aacConfig rtpaac.MPEG4AudioConfig
|
aacConfig rtpaac.MPEG4AudioConfig
|
||||||
startPCR time.Time
|
|
||||||
videoDTSEst *h264.DTSEstimator
|
videoDTSEst *h264.DTSEstimator
|
||||||
audioAUCount int
|
audioAUCount int
|
||||||
tsCurrent *tsFile
|
tsCurrent *tsFile
|
||||||
|
|
@ -42,6 +41,7 @@ type Muxer struct {
|
||||||
tsByName map[string]*tsFile
|
tsByName map[string]*tsFile
|
||||||
tsDeleteCount int
|
tsDeleteCount int
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
|
startPCR time.Time
|
||||||
startPTS time.Duration
|
startPTS time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,7 +82,6 @@ func NewMuxer(
|
||||||
h264SPS: h264SPS,
|
h264SPS: h264SPS,
|
||||||
h264PPS: h264PPS,
|
h264PPS: h264PPS,
|
||||||
aacConfig: aacConfig,
|
aacConfig: aacConfig,
|
||||||
startPCR: time.Now(),
|
|
||||||
videoDTSEst: h264.NewDTSEstimator(),
|
videoDTSEst: h264.NewDTSEstimator(),
|
||||||
tsCurrent: newTSFile(videoTrack, audioTrack),
|
tsCurrent: newTSFile(videoTrack, audioTrack),
|
||||||
tsByName: make(map[string]*tsFile),
|
tsByName: make(map[string]*tsFile),
|
||||||
|
|
@ -122,11 +121,10 @@ func (m *Muxer) WriteH264(pts time.Duration, nalus [][]byte) error {
|
||||||
if idrPresent &&
|
if idrPresent &&
|
||||||
m.tsCurrent.firstPacketWritten &&
|
m.tsCurrent.firstPacketWritten &&
|
||||||
m.tsCurrent.duration() >= m.hlsSegmentDuration {
|
m.tsCurrent.duration() >= m.hlsSegmentDuration {
|
||||||
if m.tsCurrent != nil {
|
m.tsCurrent.close()
|
||||||
m.tsCurrent.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
m.tsCurrent = newTSFile(m.videoTrack, m.audioTrack)
|
m.tsCurrent = newTSFile(m.videoTrack, m.audioTrack)
|
||||||
|
m.tsCurrent.setStartPCR(m.startPCR)
|
||||||
|
|
||||||
m.tsByName[m.tsCurrent.name] = m.tsCurrent
|
m.tsByName[m.tsCurrent.name] = m.tsCurrent
|
||||||
m.tsQueue = append(m.tsQueue, m.tsCurrent)
|
m.tsQueue = append(m.tsQueue, m.tsCurrent)
|
||||||
|
|
@ -136,11 +134,12 @@ func (m *Muxer) WriteH264(pts time.Duration, nalus [][]byte) error {
|
||||||
m.tsDeleteCount++
|
m.tsDeleteCount++
|
||||||
}
|
}
|
||||||
} else if !m.tsCurrent.firstPacketWritten {
|
} else if !m.tsCurrent.firstPacketWritten {
|
||||||
|
m.startPCR = time.Now()
|
||||||
m.startPTS = pts
|
m.startPTS = pts
|
||||||
|
m.tsCurrent.setStartPCR(m.startPCR)
|
||||||
}
|
}
|
||||||
|
|
||||||
pts = pts + ptsOffset - m.startPTS
|
pts = pts + ptsOffset - m.startPTS
|
||||||
m.tsCurrent.setPCR(time.Since(m.startPCR))
|
|
||||||
err := m.tsCurrent.writeH264(
|
err := m.tsCurrent.writeH264(
|
||||||
m.h264SPS,
|
m.h264SPS,
|
||||||
m.h264PPS,
|
m.h264PPS,
|
||||||
|
|
@ -164,13 +163,13 @@ func (m *Muxer) WriteAAC(pts time.Duration, aus [][]byte) error {
|
||||||
if m.audioAUCount >= segmentMinAUCount &&
|
if m.audioAUCount >= segmentMinAUCount &&
|
||||||
m.tsCurrent.firstPacketWritten &&
|
m.tsCurrent.firstPacketWritten &&
|
||||||
m.tsCurrent.duration() >= m.hlsSegmentDuration {
|
m.tsCurrent.duration() >= m.hlsSegmentDuration {
|
||||||
|
m.tsCurrent.close()
|
||||||
if m.tsCurrent != nil {
|
|
||||||
m.tsCurrent.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
m.audioAUCount = 0
|
m.audioAUCount = 0
|
||||||
|
|
||||||
m.tsCurrent = newTSFile(m.videoTrack, m.audioTrack)
|
m.tsCurrent = newTSFile(m.videoTrack, m.audioTrack)
|
||||||
|
m.tsCurrent.setStartPCR(m.startPCR)
|
||||||
|
|
||||||
m.tsByName[m.tsCurrent.name] = m.tsCurrent
|
m.tsByName[m.tsCurrent.name] = m.tsCurrent
|
||||||
m.tsQueue = append(m.tsQueue, m.tsCurrent)
|
m.tsQueue = append(m.tsQueue, m.tsCurrent)
|
||||||
if len(m.tsQueue) > m.hlsSegmentCount {
|
if len(m.tsQueue) > m.hlsSegmentCount {
|
||||||
|
|
@ -179,7 +178,9 @@ func (m *Muxer) WriteAAC(pts time.Duration, aus [][]byte) error {
|
||||||
m.tsDeleteCount++
|
m.tsDeleteCount++
|
||||||
}
|
}
|
||||||
} else if !m.tsCurrent.firstPacketWritten {
|
} else if !m.tsCurrent.firstPacketWritten {
|
||||||
|
m.startPCR = time.Now()
|
||||||
m.startPTS = pts
|
m.startPTS = pts
|
||||||
|
m.tsCurrent.setStartPCR(m.startPCR)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !m.tsCurrent.firstPacketWritten {
|
if !m.tsCurrent.firstPacketWritten {
|
||||||
|
|
@ -192,8 +193,6 @@ func (m *Muxer) WriteAAC(pts time.Duration, aus [][]byte) error {
|
||||||
for i, au := range aus {
|
for i, au := range aus {
|
||||||
auPTS := pts + time.Duration(i)*1000*time.Second/time.Duration(m.aacConfig.SampleRate)
|
auPTS := pts + time.Duration(i)*1000*time.Second/time.Duration(m.aacConfig.SampleRate)
|
||||||
|
|
||||||
m.audioAUCount++
|
|
||||||
m.tsCurrent.setPCR(time.Since(m.startPCR))
|
|
||||||
err := m.tsCurrent.writeAAC(
|
err := m.tsCurrent.writeAAC(
|
||||||
m.aacConfig.SampleRate,
|
m.aacConfig.SampleRate,
|
||||||
m.aacConfig.ChannelCount,
|
m.aacConfig.ChannelCount,
|
||||||
|
|
@ -202,6 +201,8 @@ func (m *Muxer) WriteAAC(pts time.Duration, aus [][]byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.audioAUCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,10 @@ type tsFile struct {
|
||||||
name string
|
name string
|
||||||
buf *multiAccessBuffer
|
buf *multiAccessBuffer
|
||||||
mux *astits.Muxer
|
mux *astits.Muxer
|
||||||
pcr time.Duration
|
|
||||||
firstPacketWritten bool
|
firstPacketWritten bool
|
||||||
minPTS time.Duration
|
minPTS time.Duration
|
||||||
maxPTS time.Duration
|
maxPTS time.Duration
|
||||||
|
startPCR time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTSFile(videoTrack *gortsplib.Track, audioTrack *gortsplib.Track) *tsFile {
|
func newTSFile(videoTrack *gortsplib.Track, audioTrack *gortsplib.Track) *tsFile {
|
||||||
|
|
@ -69,8 +69,8 @@ func (t *tsFile) duration() time.Duration {
|
||||||
return t.maxPTS - t.minPTS
|
return t.maxPTS - t.minPTS
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tsFile) setPCR(pcr time.Duration) {
|
func (t *tsFile) setStartPCR(startPCR time.Time) {
|
||||||
t.pcr = pcr
|
t.startPCR = startPCR
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tsFile) newReader() io.Reader {
|
func (t *tsFile) newReader() io.Reader {
|
||||||
|
|
@ -81,7 +81,6 @@ func (t *tsFile) writeH264(
|
||||||
h264SPS []byte, h264PPS []byte,
|
h264SPS []byte, h264PPS []byte,
|
||||||
dts time.Duration, pts time.Duration, isIDR bool, nalus [][]byte) error {
|
dts time.Duration, pts time.Duration, isIDR bool, nalus [][]byte) error {
|
||||||
if !t.firstPacketWritten {
|
if !t.firstPacketWritten {
|
||||||
t.firstPacketWritten = true
|
|
||||||
t.minPTS = pts
|
t.minPTS = pts
|
||||||
t.maxPTS = pts
|
t.maxPTS = pts
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -115,17 +114,30 @@ func (t *tsFile) writeH264(
|
||||||
filteredNALUs = append(filteredNALUs, nalu)
|
filteredNALUs = append(filteredNALUs, nalu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var af *astits.PacketAdaptationField
|
||||||
|
|
||||||
|
if isIDR {
|
||||||
|
if af == nil {
|
||||||
|
af = &astits.PacketAdaptationField{}
|
||||||
|
}
|
||||||
|
af.RandomAccessIndicator = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !t.firstPacketWritten {
|
||||||
|
t.firstPacketWritten = true
|
||||||
|
if af == nil {
|
||||||
|
af = &astits.PacketAdaptationField{}
|
||||||
|
}
|
||||||
|
af.HasPCR = true
|
||||||
|
pcr := time.Since(t.startPCR)
|
||||||
|
af.PCR = &astits.ClockReference{Base: int64(pcr.Seconds() * 90000)}
|
||||||
|
}
|
||||||
|
|
||||||
enc, err := h264.EncodeAnnexB(filteredNALUs)
|
enc, err := h264.EncodeAnnexB(filteredNALUs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
af := &astits.PacketAdaptationField{
|
|
||||||
RandomAccessIndicator: isIDR,
|
|
||||||
HasPCR: true,
|
|
||||||
PCR: &astits.ClockReference{Base: int64(t.pcr.Seconds() * 90000)},
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = t.mux.WriteData(&astits.MuxerData{
|
_, err = t.mux.WriteData(&astits.MuxerData{
|
||||||
PID: 256,
|
PID: 256,
|
||||||
AdaptationField: af,
|
AdaptationField: af,
|
||||||
|
|
@ -148,7 +160,6 @@ func (t *tsFile) writeH264(
|
||||||
func (t *tsFile) writeAAC(sampleRate int, channelCount int, pts time.Duration, au []byte) error {
|
func (t *tsFile) writeAAC(sampleRate int, channelCount int, pts time.Duration, au []byte) error {
|
||||||
if t.videoTrack == nil {
|
if t.videoTrack == nil {
|
||||||
if !t.firstPacketWritten {
|
if !t.firstPacketWritten {
|
||||||
t.firstPacketWritten = true
|
|
||||||
t.minPTS = pts
|
t.minPTS = pts
|
||||||
t.maxPTS = pts
|
t.maxPTS = pts
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -176,9 +187,11 @@ func (t *tsFile) writeAAC(sampleRate int, channelCount int, pts time.Duration, a
|
||||||
RandomAccessIndicator: true,
|
RandomAccessIndicator: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.videoTrack == nil {
|
if t.videoTrack == nil && !t.firstPacketWritten {
|
||||||
|
t.firstPacketWritten = true
|
||||||
af.HasPCR = true
|
af.HasPCR = true
|
||||||
af.PCR = &astits.ClockReference{Base: int64(t.pcr.Seconds() * 90000)}
|
pcr := time.Since(t.startPCR)
|
||||||
|
af.PCR = &astits.ClockReference{Base: int64(pcr.Seconds() * 90000)}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = t.mux.WriteData(&astits.MuxerData{
|
_, err = t.mux.WriteData(&astits.MuxerData{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue