add SPS and PTS before IDRs of all incoming H264 streams; stop filtering H264 inside single protocols
This commit is contained in:
parent
a34a01ebd9
commit
dffe63f1bc
|
@ -342,36 +342,11 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var filteredNALUs [][]byte
|
|
||||||
|
|
||||||
for _, nalu := range pair.data.h264NALUs {
|
|
||||||
typ := h264.NALUType(nalu[0] & 0x1F)
|
|
||||||
|
|
||||||
switch typ {
|
|
||||||
case h264.NALUTypeSPS, h264.NALUTypePPS:
|
|
||||||
// added automatically before every IDR
|
|
||||||
continue
|
|
||||||
|
|
||||||
case h264.NALUTypeAccessUnitDelimiter:
|
|
||||||
// not needed
|
|
||||||
continue
|
|
||||||
|
|
||||||
case h264.NALUTypeIDR:
|
|
||||||
// add SPS and PPS before every IDR
|
|
||||||
// TODO: send H264DecoderConfig instead of NALUs?
|
// TODO: send H264DecoderConfig instead of NALUs?
|
||||||
filteredNALUs = append(filteredNALUs, videoTrack.SPS(), videoTrack.PPS())
|
|
||||||
}
|
|
||||||
|
|
||||||
filteredNALUs = append(filteredNALUs, nalu)
|
|
||||||
}
|
|
||||||
|
|
||||||
if filteredNALUs == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait until we receive an IDR
|
// wait until we receive an IDR
|
||||||
if !videoFirstIDRFound {
|
if !videoFirstIDRFound {
|
||||||
if !h264.IDRPresent(filteredNALUs) {
|
if !h264.IDRPresent(pair.data.h264NALUs) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,7 +355,7 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
|
||||||
videoDTSEst = h264.NewDTSEstimator()
|
videoDTSEst = h264.NewDTSEstimator()
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := h264.EncodeAVCC(filteredNALUs)
|
data, err := h264.EncodeAVCC(pair.data.h264NALUs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -559,25 +534,9 @@ func (c *rtmpConn) runPublish(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var outNALUs [][]byte
|
|
||||||
|
|
||||||
for _, nalu := range nalus {
|
|
||||||
typ := h264.NALUType(nalu[0] & 0x1F)
|
|
||||||
if typ == h264.NALUTypeAccessUnitDelimiter {
|
|
||||||
// not needed
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
outNALUs = append(outNALUs, nalu)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(outNALUs) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
pts := pkt.Time + pkt.CTime
|
pts := pkt.Time + pkt.CTime
|
||||||
|
|
||||||
pkts, err := h264Encoder.Encode(outNALUs, pts)
|
pkts, err := h264Encoder.Encode(nalus, pts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while encoding H264: %v", err)
|
return fmt.Errorf("error while encoding H264: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -592,8 +551,8 @@ func (c *rtmpConn) runPublish(ctx context.Context) error {
|
||||||
} else {
|
} else {
|
||||||
rres.stream.writeData(videoTrackID, &data{
|
rres.stream.writeData(videoTrackID, &data{
|
||||||
rtp: pkt,
|
rtp: pkt,
|
||||||
ptsEqualsDTS: h264.IDRPresent(outNALUs),
|
ptsEqualsDTS: h264.IDRPresent(nalus),
|
||||||
h264NALUs: outNALUs,
|
h264NALUs: nalus,
|
||||||
h264PTS: pts,
|
h264PTS: pts,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,21 +184,9 @@ func (s *rtmpSource) runInner() bool {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var outNALUs [][]byte
|
|
||||||
for _, nalu := range nalus {
|
|
||||||
// remove SPS, PPS and AUD, not needed by RTSP / RTMP
|
|
||||||
typ := h264.NALUType(nalu[0] & 0x1F)
|
|
||||||
switch typ {
|
|
||||||
case h264.NALUTypeSPS, h264.NALUTypePPS, h264.NALUTypeAccessUnitDelimiter:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
outNALUs = append(outNALUs, nalu)
|
|
||||||
}
|
|
||||||
|
|
||||||
pts := pkt.Time + pkt.CTime
|
pts := pkt.Time + pkt.CTime
|
||||||
|
|
||||||
pkts, err := h264Encoder.Encode(outNALUs, pts)
|
pkts, err := h264Encoder.Encode(nalus, pts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while encoding H264: %v", err)
|
return fmt.Errorf("error while encoding H264: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -213,8 +201,8 @@ func (s *rtmpSource) runInner() bool {
|
||||||
} else {
|
} else {
|
||||||
res.stream.writeData(videoTrackID, &data{
|
res.stream.writeData(videoTrackID, &data{
|
||||||
rtp: pkt,
|
rtp: pkt,
|
||||||
ptsEqualsDTS: h264.IDRPresent(outNALUs),
|
ptsEqualsDTS: h264.IDRPresent(nalus),
|
||||||
h264NALUs: outNALUs,
|
h264NALUs: nalus,
|
||||||
h264PTS: pts,
|
h264PTS: pts,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,10 +98,39 @@ func (s *stream) updateH264TrackParameters(h264track *gortsplib.TrackH264, nalus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remux is needed to
|
||||||
|
// - fix corrupted streams
|
||||||
|
// - make streams compatible with all protocols
|
||||||
|
func (s *stream) remuxH264NALUs(h264track *gortsplib.TrackH264, data *data) {
|
||||||
|
var filteredNALUs [][]byte //nolint:prealloc
|
||||||
|
|
||||||
|
for _, nalu := range data.h264NALUs {
|
||||||
|
typ := h264.NALUType(nalu[0] & 0x1F)
|
||||||
|
switch typ {
|
||||||
|
case h264.NALUTypeSPS, h264.NALUTypePPS:
|
||||||
|
// remove since they're automatically added before every IDR
|
||||||
|
continue
|
||||||
|
|
||||||
|
case h264.NALUTypeAccessUnitDelimiter:
|
||||||
|
// remove since it is not needed
|
||||||
|
continue
|
||||||
|
|
||||||
|
case h264.NALUTypeIDR:
|
||||||
|
// add SPS and PPS before every IDR
|
||||||
|
filteredNALUs = append(filteredNALUs, h264track.SPS(), h264track.PPS())
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredNALUs = append(filteredNALUs, nalu)
|
||||||
|
}
|
||||||
|
|
||||||
|
data.h264NALUs = filteredNALUs
|
||||||
|
}
|
||||||
|
|
||||||
func (s *stream) writeData(trackID int, data *data) {
|
func (s *stream) writeData(trackID int, data *data) {
|
||||||
track := s.rtspStream.Tracks()[trackID]
|
track := s.rtspStream.Tracks()[trackID]
|
||||||
if h264track, ok := track.(*gortsplib.TrackH264); ok {
|
if h264track, ok := track.(*gortsplib.TrackH264); ok {
|
||||||
s.updateH264TrackParameters(h264track, data.h264NALUs)
|
s.updateH264TrackParameters(h264track, data.h264NALUs)
|
||||||
|
s.remuxH264NALUs(h264track, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// forward to RTSP readers
|
// forward to RTSP readers
|
||||||
|
|
|
@ -89,24 +89,7 @@ func (p *clientVideoProcessor) doProcess(
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
outNALUs := make([][]byte, 0, len(nalus))
|
p.onData(pts, nalus)
|
||||||
|
|
||||||
for _, nalu := range nalus {
|
|
||||||
typ := h264.NALUType(nalu[0] & 0x1F)
|
|
||||||
|
|
||||||
if typ == h264.NALUTypeAccessUnitDelimiter {
|
|
||||||
// remove since it's not needed
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
outNALUs = append(outNALUs, nalu)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(outNALUs) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
p.onData(pts, outNALUs)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -115,11 +115,9 @@ func (m *muxerTSGenerator) writeH264(pts time.Duration, nalus [][]byte) error {
|
||||||
dts := m.videoDTSEst.Feed(pts)
|
dts := m.videoDTSEst.Feed(pts)
|
||||||
|
|
||||||
// prepend an AUD. This is required by video.js and iOS
|
// prepend an AUD. This is required by video.js and iOS
|
||||||
filteredNALUs := [][]byte{
|
nalus = append([][]byte{{byte(h264.NALUTypeAccessUnitDelimiter), 240}}, nalus...)
|
||||||
{byte(h264.NALUTypeAccessUnitDelimiter), 240},
|
|
||||||
}
|
|
||||||
|
|
||||||
enc, err := h264.EncodeAnnexB(filteredNALUs)
|
enc, err := h264.EncodeAnnexB(nalus)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if m.currentSegment.buf.Len() > 0 {
|
if m.currentSegment.buf.Len() > 0 {
|
||||||
m.streamPlaylist.pushSegment(m.currentSegment)
|
m.streamPlaylist.pushSegment(m.currentSegment)
|
||||||
|
|
Loading…
Reference in New Issue