Browse Source

RTMP client/source: support dynamic H264 SPS/PPS

pull/923/head
aler9 4 years ago committed by Alessandro Ros
parent
commit
a34a01ebd9
  1. 66
      internal/core/rtmp_conn.go
  2. 8
      internal/rtmp/conn.go

66
internal/core/rtmp_conn.go

@ -16,6 +16,7 @@ import (
"github.com/aler9/gortsplib/pkg/rtpaac" "github.com/aler9/gortsplib/pkg/rtpaac"
"github.com/aler9/gortsplib/pkg/rtph264" "github.com/aler9/gortsplib/pkg/rtph264"
"github.com/notedit/rtmp/av" "github.com/notedit/rtmp/av"
nh264 "github.com/notedit/rtmp/codec/h264"
"github.com/aler9/rtsp-simple-server/internal/conf" "github.com/aler9/rtsp-simple-server/internal/conf"
"github.com/aler9/rtsp-simple-server/internal/externalcmd" "github.com/aler9/rtsp-simple-server/internal/externalcmd"
@ -341,22 +342,36 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
continue continue
} }
var nalusFiltered [][]byte var filteredNALUs [][]byte
for _, nalu := range pair.data.h264NALUs { for _, nalu := range pair.data.h264NALUs {
// remove SPS, PPS and AUD, not needed by RTMP
typ := h264.NALUType(nalu[0] & 0x1F) typ := h264.NALUType(nalu[0] & 0x1F)
switch typ { switch typ {
case h264.NALUTypeSPS, h264.NALUTypePPS, h264.NALUTypeAccessUnitDelimiter: case h264.NALUTypeSPS, h264.NALUTypePPS:
// added automatically before every IDR
continue
case h264.NALUTypeAccessUnitDelimiter:
// not needed
continue continue
case h264.NALUTypeIDR:
// add SPS and PPS before every IDR
// TODO: send H264DecoderConfig instead of NALUs?
filteredNALUs = append(filteredNALUs, videoTrack.SPS(), videoTrack.PPS())
} }
nalusFiltered = append(nalusFiltered, nalu) 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(nalusFiltered) { if !h264.IDRPresent(filteredNALUs) {
continue continue
} }
@ -365,7 +380,7 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
videoDTSEst = h264.NewDTSEstimator() videoDTSEst = h264.NewDTSEstimator()
} }
data, err := h264.EncodeAVCC(nalusFiltered) data, err := h264.EncodeAVCC(filteredNALUs)
if err != nil { if err != nil {
return err return err
} }
@ -500,6 +515,40 @@ func (c *rtmpConn) runPublish(ctx context.Context) error {
} }
switch pkt.Type { switch pkt.Type {
case av.H264DecoderConfig:
codec, err := nh264.FromDecoderConfig(pkt.Data)
if err != nil {
return err
}
pts := pkt.Time + pkt.CTime
nalus := [][]byte{
codec.SPS[0],
codec.PPS[0],
}
pkts, err := h264Encoder.Encode(nalus, pts)
if err != nil {
return fmt.Errorf("error while encoding H264: %v", err)
}
lastPkt := len(pkts) - 1
for i, pkt := range pkts {
if i != lastPkt {
rres.stream.writeData(videoTrackID, &data{
rtp: pkt,
ptsEqualsDTS: false,
})
} else {
rres.stream.writeData(videoTrackID, &data{
rtp: pkt,
ptsEqualsDTS: h264.IDRPresent(nalus),
h264NALUs: nalus,
h264PTS: pts,
})
}
}
case av.H264: case av.H264:
if videoTrack == nil { if videoTrack == nil {
return fmt.Errorf("received an H264 packet, but track is not set up") return fmt.Errorf("received an H264 packet, but track is not set up")
@ -513,10 +562,9 @@ func (c *rtmpConn) runPublish(ctx context.Context) error {
var outNALUs [][]byte var outNALUs [][]byte
for _, nalu := range nalus { for _, nalu := range nalus {
// remove SPS, PPS and AUD, not needed by RTSP
typ := h264.NALUType(nalu[0] & 0x1F) typ := h264.NALUType(nalu[0] & 0x1F)
switch typ { if typ == h264.NALUTypeAccessUnitDelimiter {
case h264.NALUTypeSPS, h264.NALUTypePPS, h264.NALUTypeAccessUnitDelimiter: // not needed
continue continue
} }

8
internal/rtmp/conn.go

@ -306,11 +306,9 @@ func (c *Conn) WriteTracks(videoTrack *gortsplib.TrackH264, audioTrack *gortspli
return err return err
} }
if videoTrack != nil { // write decoder config only if SPS and PPS are available.
if videoTrack.SPS() == nil || videoTrack.PPS() == nil { // if they're not available yet, they're sent later as H264 NALUs.
return fmt.Errorf("invalid H264 track: SPS or PPS not provided into the SDP") if videoTrack != nil && videoTrack.SPS() != nil && videoTrack.PPS() != nil {
}
codec := nh264.Codec{ codec := nh264.Codec{
SPS: map[int][]byte{ SPS: map[int][]byte{
0: videoTrack.SPS(), 0: videoTrack.SPS(),

Loading…
Cancel
Save