|
|
|
|
@ -16,7 +16,6 @@ import (
@@ -16,7 +16,6 @@ import (
|
|
|
|
|
"github.com/aler9/gortsplib/pkg/rtpaac" |
|
|
|
|
"github.com/aler9/gortsplib/pkg/rtph264" |
|
|
|
|
"github.com/notedit/rtmp/av" |
|
|
|
|
"github.com/pion/rtp" |
|
|
|
|
|
|
|
|
|
"github.com/aler9/rtsp-simple-server/internal/conf" |
|
|
|
|
"github.com/aler9/rtsp-simple-server/internal/externalcmd" |
|
|
|
|
@ -44,9 +43,9 @@ const (
@@ -44,9 +43,9 @@ const (
|
|
|
|
|
rtmpConnStatePublish |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
type rtmpConnTrackIDPayloadPair struct { |
|
|
|
|
type rtmpConnTrackIDDataPair struct { |
|
|
|
|
trackID int |
|
|
|
|
packet *rtp.Packet |
|
|
|
|
data *data |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type rtmpConnPathManager interface { |
|
|
|
|
@ -260,7 +259,6 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
@@ -260,7 +259,6 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
|
|
|
|
|
|
|
|
|
|
var videoTrack *gortsplib.TrackH264 |
|
|
|
|
videoTrackID := -1 |
|
|
|
|
var h264Decoder *rtph264.Decoder |
|
|
|
|
var audioTrack *gortsplib.TrackAAC |
|
|
|
|
audioTrackID := -1 |
|
|
|
|
var aacDecoder *rtpaac.Decoder |
|
|
|
|
@ -274,8 +272,6 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
@@ -274,8 +272,6 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
|
|
|
|
|
|
|
|
|
|
videoTrack = tt |
|
|
|
|
videoTrackID = i |
|
|
|
|
h264Decoder = &rtph264.Decoder{} |
|
|
|
|
h264Decoder.Init() |
|
|
|
|
|
|
|
|
|
case *gortsplib.TrackAAC: |
|
|
|
|
if audioTrack != nil { |
|
|
|
|
@ -338,20 +334,16 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
@@ -338,20 +334,16 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
|
|
|
|
|
if !ok { |
|
|
|
|
return fmt.Errorf("terminated") |
|
|
|
|
} |
|
|
|
|
pair := data.(rtmpConnTrackIDPayloadPair) |
|
|
|
|
pair := data.(rtmpConnTrackIDDataPair) |
|
|
|
|
|
|
|
|
|
if videoTrack != nil && pair.trackID == videoTrackID { |
|
|
|
|
nalus, pts, err := h264Decoder.DecodeUntilMarker(pair.packet) |
|
|
|
|
if err != nil { |
|
|
|
|
if err != rtph264.ErrMorePacketsNeeded && err != rtph264.ErrNonStartingPacketAndNoPrevious { |
|
|
|
|
c.log(logger.Warn, "unable to decode video track: %v", err) |
|
|
|
|
} |
|
|
|
|
if pair.data.h264NALUs == nil { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var nalusFiltered [][]byte |
|
|
|
|
|
|
|
|
|
for _, nalu := range nalus { |
|
|
|
|
for _, nalu := range pair.data.h264NALUs { |
|
|
|
|
// remove SPS, PPS and AUD, not needed by RTMP
|
|
|
|
|
typ := h264.NALUType(nalu[0] & 0x1F) |
|
|
|
|
switch typ { |
|
|
|
|
@ -362,24 +354,14 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
@@ -362,24 +354,14 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
|
|
|
|
|
nalusFiltered = append(nalusFiltered, nalu) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
idrPresent := func() bool { |
|
|
|
|
for _, nalu := range nalus { |
|
|
|
|
typ := h264.NALUType(nalu[0] & 0x1F) |
|
|
|
|
if typ == h264.NALUTypeIDR { |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return false |
|
|
|
|
}() |
|
|
|
|
|
|
|
|
|
// wait until we receive an IDR
|
|
|
|
|
if !videoFirstIDRFound { |
|
|
|
|
if !idrPresent { |
|
|
|
|
if !h264.IDRPresent(nalusFiltered) { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
videoFirstIDRFound = true |
|
|
|
|
videoStartPTS = pts |
|
|
|
|
videoStartPTS = pair.data.h264PTS |
|
|
|
|
videoDTSEst = h264.NewDTSEstimator() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -388,7 +370,7 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
@@ -388,7 +370,7 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
|
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pts -= videoStartPTS |
|
|
|
|
pts := pair.data.h264PTS - videoStartPTS |
|
|
|
|
dts := videoDTSEst.Feed(pts) |
|
|
|
|
|
|
|
|
|
c.conn.SetWriteDeadline(time.Now().Add(time.Duration(c.writeTimeout))) |
|
|
|
|
@ -402,7 +384,7 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
@@ -402,7 +384,7 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
|
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
} else if audioTrack != nil && pair.trackID == audioTrackID { |
|
|
|
|
aus, pts, err := aacDecoder.Decode(pair.packet) |
|
|
|
|
aus, pts, err := aacDecoder.Decode(pair.data.rtp) |
|
|
|
|
if err != nil { |
|
|
|
|
if err != rtpaac.ErrMorePacketsNeeded { |
|
|
|
|
c.log(logger.Warn, "unable to decode audio track: %v", err) |
|
|
|
|
@ -545,13 +527,28 @@ func (c *rtmpConn) runPublish(ctx context.Context) error {
@@ -545,13 +527,28 @@ func (c *rtmpConn) runPublish(ctx context.Context) error {
|
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pkts, err := h264Encoder.Encode(outNALUs, pkt.Time+pkt.CTime) |
|
|
|
|
pts := pkt.Time + pkt.CTime |
|
|
|
|
|
|
|
|
|
pkts, err := h264Encoder.Encode(outNALUs, pts) |
|
|
|
|
if err != nil { |
|
|
|
|
return fmt.Errorf("error while encoding H264: %v", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for _, pkt := range pkts { |
|
|
|
|
rres.stream.writePacketRTP(videoTrackID, pkt) |
|
|
|
|
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(outNALUs), |
|
|
|
|
h264NALUs: outNALUs, |
|
|
|
|
h264PTS: pts, |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
case av.AAC: |
|
|
|
|
@ -565,7 +562,10 @@ func (c *rtmpConn) runPublish(ctx context.Context) error {
@@ -565,7 +562,10 @@ func (c *rtmpConn) runPublish(ctx context.Context) error {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for _, pkt := range pkts { |
|
|
|
|
rres.stream.writePacketRTP(audioTrackID, pkt) |
|
|
|
|
rres.stream.writeData(audioTrackID, &data{ |
|
|
|
|
rtp: pkt, |
|
|
|
|
ptsEqualsDTS: true, |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -622,9 +622,9 @@ func (c *rtmpConn) onReaderAccepted() {
@@ -622,9 +622,9 @@ func (c *rtmpConn) onReaderAccepted() {
|
|
|
|
|
c.log(logger.Info, "is reading from path '%s'", c.path.Name()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// onReaderPacketRTP implements reader.
|
|
|
|
|
func (c *rtmpConn) onReaderPacketRTP(trackID int, pkt *rtp.Packet) { |
|
|
|
|
c.ringBuffer.Push(rtmpConnTrackIDPayloadPair{trackID, pkt}) |
|
|
|
|
// onReaderData implements reader.
|
|
|
|
|
func (c *rtmpConn) onReaderData(trackID int, data *data) { |
|
|
|
|
c.ringBuffer.Push(rtmpConnTrackIDDataPair{trackID, data}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// onReaderAPIDescribe implements reader.
|
|
|
|
|
|