mirror of
https://github.com/aler9/rtsp-simple-server
synced 2025-11-03 09:51:26 +08:00
hls muxer, rtmp server: extract DTS from samples
This commit is contained in:
@@ -27,6 +27,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
rtmpConnPauseAfterAuthError = 2 * time.Second
|
rtmpConnPauseAfterAuthError = 2 * time.Second
|
||||||
|
rtmpPTSDTSOffset = 400 * time.Millisecond // 2 samples @ 5fps
|
||||||
)
|
)
|
||||||
|
|
||||||
func pathNameAndQuery(inURL *url.URL) (string, url.Values, string) {
|
func pathNameAndQuery(inURL *url.URL) (string, url.Values, string) {
|
||||||
@@ -330,7 +331,8 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
|
|||||||
var videoInitialPTS *time.Duration
|
var videoInitialPTS *time.Duration
|
||||||
videoFirstIDRFound := false
|
videoFirstIDRFound := false
|
||||||
var videoFirstIDRPTS time.Duration
|
var videoFirstIDRPTS time.Duration
|
||||||
var videoDTSEst *h264.DTSEstimator
|
videoDTSExtractor := h264.NewDTSExtractor()
|
||||||
|
var videoSPS *h264.SPS
|
||||||
|
|
||||||
for {
|
for {
|
||||||
item, ok := c.ringBuffer.Pull()
|
item, ok := c.ringBuffer.Pull()
|
||||||
@@ -361,16 +363,18 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
|
|||||||
|
|
||||||
videoFirstIDRFound = true
|
videoFirstIDRFound = true
|
||||||
videoFirstIDRPTS = pts
|
videoFirstIDRPTS = pts
|
||||||
videoDTSEst = h264.NewDTSEstimator()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if h264.IDRPresent(data.h264NALUs) {
|
if h264.IDRPresent(data.h264NALUs) {
|
||||||
|
sps := videoTrack.SPS()
|
||||||
|
pps := videoTrack.PPS()
|
||||||
|
|
||||||
codec := nh264.Codec{
|
codec := nh264.Codec{
|
||||||
SPS: map[int][]byte{
|
SPS: map[int][]byte{
|
||||||
0: videoTrack.SPS(),
|
0: sps,
|
||||||
},
|
},
|
||||||
PPS: map[int][]byte{
|
PPS: map[int][]byte{
|
||||||
0: videoTrack.PPS(),
|
0: pps,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
b := make([]byte, 128)
|
b := make([]byte, 128)
|
||||||
@@ -385,6 +389,13 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var psps h264.SPS
|
||||||
|
err := psps.Unmarshal(sps)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
videoSPS = &psps
|
||||||
}
|
}
|
||||||
|
|
||||||
avcc, err := h264.AVCCEncode(data.h264NALUs)
|
avcc, err := h264.AVCCEncode(data.h264NALUs)
|
||||||
@@ -393,14 +404,18 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pts -= videoFirstIDRPTS
|
pts -= videoFirstIDRPTS
|
||||||
dts := videoDTSEst.Feed(pts)
|
dts, err := videoDTSExtractor.Extract(
|
||||||
|
data.h264NALUs, h264.IDRPresent(data.h264NALUs), pts, videoSPS)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
c.conn.SetWriteDeadline(time.Now().Add(time.Duration(c.writeTimeout)))
|
c.conn.SetWriteDeadline(time.Now().Add(time.Duration(c.writeTimeout)))
|
||||||
err = c.conn.WritePacket(av.Packet{
|
err = c.conn.WritePacket(av.Packet{
|
||||||
Type: av.H264,
|
Type: av.H264,
|
||||||
Data: avcc,
|
Data: avcc,
|
||||||
Time: dts,
|
Time: dts,
|
||||||
CTime: pts - dts,
|
CTime: rtmpPTSDTSOffset + pts - dts,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -428,7 +443,8 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
|
|||||||
err := c.conn.WritePacket(av.Packet{
|
err := c.conn.WritePacket(av.Packet{
|
||||||
Type: av.AAC,
|
Type: av.AAC,
|
||||||
Data: au,
|
Data: au,
|
||||||
Time: pts + time.Duration(i)*aac.SamplesPerAccessUnit*time.Second/time.Duration(audioTrack.ClockRate()),
|
Time: rtmpPTSDTSOffset + pts +
|
||||||
|
time.Duration(i)*aac.SamplesPerAccessUnit*time.Second/time.Duration(audioTrack.ClockRate()),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// an offset between PCR and PTS/DTS is needed to avoid PCR > PTS
|
mpegtsPCROffset = 400 * time.Millisecond // 2 samples @ 5fps
|
||||||
pcrOffset = 500 * time.Millisecond
|
mpegtsPTSDTSOffset = 400 * time.Millisecond // 2 samples @ 5fps
|
||||||
)
|
)
|
||||||
|
|
||||||
type muxerVariantMPEGTSSegment struct {
|
type muxerVariantMPEGTSSegment struct {
|
||||||
@@ -110,13 +110,15 @@ func (t *muxerVariantMPEGTSSegment) writeH264(
|
|||||||
MarkerBits: 2,
|
MarkerBits: 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pts += mpegtsPTSDTSOffset
|
||||||
|
|
||||||
if dts == pts {
|
if dts == pts {
|
||||||
oh.PTSDTSIndicator = astits.PTSDTSIndicatorOnlyPTS
|
oh.PTSDTSIndicator = astits.PTSDTSIndicatorOnlyPTS
|
||||||
oh.PTS = &astits.ClockReference{Base: int64((pts + pcrOffset).Seconds() * 90000)}
|
oh.PTS = &astits.ClockReference{Base: int64((pts + mpegtsPCROffset).Seconds() * 90000)}
|
||||||
} else {
|
} else {
|
||||||
oh.PTSDTSIndicator = astits.PTSDTSIndicatorBothPresent
|
oh.PTSDTSIndicator = astits.PTSDTSIndicatorBothPresent
|
||||||
oh.DTS = &astits.ClockReference{Base: int64((dts + pcrOffset).Seconds() * 90000)}
|
oh.DTS = &astits.ClockReference{Base: int64((dts + mpegtsPCROffset).Seconds() * 90000)}
|
||||||
oh.PTS = &astits.ClockReference{Base: int64((pts + pcrOffset).Seconds() * 90000)}
|
oh.PTS = &astits.ClockReference{Base: int64((pts + mpegtsPCROffset).Seconds() * 90000)}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = t.writeData(&astits.MuxerData{
|
_, err = t.writeData(&astits.MuxerData{
|
||||||
@@ -180,6 +182,8 @@ func (t *muxerVariantMPEGTSSegment) writeAAC(
|
|||||||
t.pcrSendCounter--
|
t.pcrSendCounter--
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pts += mpegtsPTSDTSOffset
|
||||||
|
|
||||||
_, err = t.writeData(&astits.MuxerData{
|
_, err = t.writeData(&astits.MuxerData{
|
||||||
PID: 257,
|
PID: 257,
|
||||||
AdaptationField: af,
|
AdaptationField: af,
|
||||||
@@ -188,7 +192,7 @@ func (t *muxerVariantMPEGTSSegment) writeAAC(
|
|||||||
OptionalHeader: &astits.PESOptionalHeader{
|
OptionalHeader: &astits.PESOptionalHeader{
|
||||||
MarkerBits: 2,
|
MarkerBits: 2,
|
||||||
PTSDTSIndicator: astits.PTSDTSIndicatorOnlyPTS,
|
PTSDTSIndicator: astits.PTSDTSIndicatorOnlyPTS,
|
||||||
PTS: &astits.ClockReference{Base: int64((pts + pcrOffset).Seconds() * 90000)},
|
PTS: &astits.ClockReference{Base: int64((pts + mpegtsPCROffset).Seconds() * 90000)},
|
||||||
},
|
},
|
||||||
PacketLength: uint16(len(enc) + 8),
|
PacketLength: uint16(len(enc) + 8),
|
||||||
StreamID: 192, // audio
|
StreamID: 192, // audio
|
||||||
|
|||||||
@@ -26,11 +26,12 @@ type muxerVariantMPEGTSSegmenter struct {
|
|||||||
audioTrack *gortsplib.TrackAAC
|
audioTrack *gortsplib.TrackAAC
|
||||||
onSegmentReady func(*muxerVariantMPEGTSSegment)
|
onSegmentReady func(*muxerVariantMPEGTSSegment)
|
||||||
|
|
||||||
writer *astits.Muxer
|
writer *astits.Muxer
|
||||||
currentSegment *muxerVariantMPEGTSSegment
|
currentSegment *muxerVariantMPEGTSSegment
|
||||||
videoDTSEst *h264.DTSEstimator
|
videoSPS *h264.SPS
|
||||||
startPCR time.Time
|
videoDTSExtractor *h264.DTSExtractor
|
||||||
startPTS time.Duration
|
startPCR time.Time
|
||||||
|
startPTS time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMuxerVariantMPEGTSSegmenter(
|
func newMuxerVariantMPEGTSSegmenter(
|
||||||
@@ -41,11 +42,12 @@ func newMuxerVariantMPEGTSSegmenter(
|
|||||||
onSegmentReady func(*muxerVariantMPEGTSSegment),
|
onSegmentReady func(*muxerVariantMPEGTSSegment),
|
||||||
) *muxerVariantMPEGTSSegmenter {
|
) *muxerVariantMPEGTSSegmenter {
|
||||||
m := &muxerVariantMPEGTSSegmenter{
|
m := &muxerVariantMPEGTSSegmenter{
|
||||||
segmentDuration: segmentDuration,
|
segmentDuration: segmentDuration,
|
||||||
segmentMaxSize: segmentMaxSize,
|
segmentMaxSize: segmentMaxSize,
|
||||||
videoTrack: videoTrack,
|
videoTrack: videoTrack,
|
||||||
audioTrack: audioTrack,
|
audioTrack: audioTrack,
|
||||||
onSegmentReady: onSegmentReady,
|
onSegmentReady: onSegmentReady,
|
||||||
|
videoDTSExtractor: h264.NewDTSExtractor(),
|
||||||
}
|
}
|
||||||
|
|
||||||
m.writer = astits.NewMuxer(
|
m.writer = astits.NewMuxer(
|
||||||
@@ -91,7 +93,6 @@ func (m *muxerVariantMPEGTSSegmenter) writeH264(pts time.Duration, nalus [][]byt
|
|||||||
m.currentSegment = newMuxerVariantMPEGTSSegment(now, m.segmentMaxSize,
|
m.currentSegment = newMuxerVariantMPEGTSSegment(now, m.segmentMaxSize,
|
||||||
m.videoTrack, m.audioTrack, m.writer.WriteData)
|
m.videoTrack, m.audioTrack, m.writer.WriteData)
|
||||||
m.startPCR = now
|
m.startPCR = now
|
||||||
m.videoDTSEst = h264.NewDTSEstimator()
|
|
||||||
m.startPTS = pts
|
m.startPTS = pts
|
||||||
pts = 0
|
pts = 0
|
||||||
} else {
|
} else {
|
||||||
@@ -108,10 +109,27 @@ func (m *muxerVariantMPEGTSSegmenter) writeH264(pts time.Duration, nalus [][]byt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dts := m.videoDTSEst.Feed(pts)
|
if idrPresent {
|
||||||
|
sps := m.videoTrack.SPS()
|
||||||
|
var psps h264.SPS
|
||||||
|
err := psps.Unmarshal(sps)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.videoSPS = &psps
|
||||||
|
}
|
||||||
|
|
||||||
err := m.currentSegment.writeH264(now.Sub(m.startPCR), dts,
|
dts, err := m.videoDTSExtractor.Extract(nalus, idrPresent, pts, m.videoSPS)
|
||||||
pts, idrPresent, nalus)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = m.currentSegment.writeH264(
|
||||||
|
now.Sub(m.startPCR),
|
||||||
|
dts,
|
||||||
|
pts,
|
||||||
|
idrPresent,
|
||||||
|
nalus)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if m.currentSegment.buf.Len() > 0 {
|
if m.currentSegment.buf.Len() > 0 {
|
||||||
m.onSegmentReady(m.currentSegment)
|
m.onSegmentReady(m.currentSegment)
|
||||||
|
|||||||
Reference in New Issue
Block a user