diff --git a/rtpconn/rtpconn.go b/rtpconn/rtpconn.go index 6fc30ab..b48e09a 100644 --- a/rtpconn/rtpconn.go +++ b/rtpconn/rtpconn.go @@ -515,25 +515,51 @@ func sendFIR(pc *webrtc.PeerConnection, ssrc uint32, seqno uint8) error { func (up *rtpUpConnection) sendNACK(track *rtpUpTrack, first uint16, bitmap uint16) error { if !track.hasRtcpFb("nack", "") { - return nil + return ErrUnsupportedFeedback } - err := sendNACK(up.pc, track.track.SSRC(), first, bitmap) + + err := sendNACKs(up.pc, track.track.SSRC(), + []rtcp.NackPair{{first, rtcp.PacketBitmap(bitmap)}}, + ) if err == nil { track.cache.Expect(1 + bits.OnesCount16(bitmap)) } return err } -func sendNACK(pc *webrtc.PeerConnection, ssrc uint32, first uint16, bitmap uint16) error { +func (up *rtpUpConnection) sendNACKs(track *rtpUpTrack, seqnos []uint16) error { + count := len(seqnos) + if count == 0 { + return nil + } + + if !track.hasRtcpFb("nack", "") { + return ErrUnsupportedFeedback + } + + var nacks []rtcp.NackPair + + for len(seqnos) > 0 { + if len(nacks) >= 240 { + log.Printf("NACK: packet overflow") + break + } + var f, b uint16 + f, b, seqnos = packetcache.ToBitmap(seqnos) + nacks = append(nacks, rtcp.NackPair{f, rtcp.PacketBitmap(b)}) + } + err := sendNACKs(up.pc, track.track.SSRC(), nacks) + if err == nil { + track.cache.Expect(count) + } + return err +} + +func sendNACKs(pc *webrtc.PeerConnection, ssrc uint32, nacks []rtcp.NackPair) error { packet := rtcp.Packet( &rtcp.TransportLayerNack{ MediaSSRC: ssrc, - Nacks: []rtcp.NackPair{ - { - first, - rtcp.PacketBitmap(bitmap), - }, - }, + Nacks: nacks, }, ) return pc.WriteRTCP([]rtcp.Packet{packet}) diff --git a/rtpconn/rtpreader.go b/rtpconn/rtpreader.go index 8511f96..65c850a 100644 --- a/rtpconn/rtpreader.go +++ b/rtpconn/rtpreader.go @@ -32,6 +32,7 @@ func readLoop(conn *rtpUpConnection, track *rtpUpTrack) { isvideo := track.track.Kind() == webrtc.RTPCodecTypeVideo codec := track.track.Codec().Name + sendNACK := track.hasRtcpFb("nack", "") buf := make([]byte, packetcache.BufSize) var packet rtp.Packet for { @@ -87,7 +88,7 @@ func readLoop(conn *rtpUpConnection, track *rtpUpTrack) { found, first, bitmap := track.cache.BitmapGet( packet.SequenceNumber - unnacked, ) - if found { + if found && sendNACK { err := conn.sendNACK(track, first, bitmap) if err != nil { log.Printf("%v", err) diff --git a/rtpconn/rtpwriter.go b/rtpconn/rtpwriter.go index 8e8e956..4044147 100644 --- a/rtpconn/rtpwriter.go +++ b/rtpconn/rtpwriter.go @@ -374,6 +374,10 @@ func nackWriter(conn *rtpUpConnection, track *rtpUpTrack) { track.bufferedNACKs = nil track.mu.Unlock() + if !track.hasRtcpFb("nack", "") { + return + } + // drop any nacks before the last keyframe var cutoff uint16 found, seqno, _ := track.cache.KeyframeSeqno() @@ -409,7 +413,7 @@ func nackWriter(conn *rtpUpConnection, track *rtpUpTrack) { return nacks[i]-cutoff < nacks[j]-cutoff }) - for _, nack := range nacks { - conn.sendNACK(track, nack, 0) + if len(nacks) > 0 { + conn.sendNACKs(track, nacks) } }