mirror of
https://github.com/livekit/livekit.git
synced 2026-10-07 01:07:32 +00:00
do not NACK on fec recovered packet
This commit is contained in:
+42
-18
@@ -279,22 +279,17 @@ func (b *Buffer) Write(pkt []byte) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
rtcpPackets := b.calc(pkt, &rtpPacket, now, false, false)
|
||||
b.ingestLocked(pkt, &rtpPacket, now, false, false)
|
||||
var fecDelta fecRecoveryDelta
|
||||
var onFECRecovery func(received int, recovered int, discarded int, bytesReceived int)
|
||||
if b.fecDecoder != nil {
|
||||
// feed media into the FEC decoder, a media arrival can complete a
|
||||
// previously unrecoverable FEC window
|
||||
// previously unrecoverable FEC window. Recovered packets update NACK
|
||||
// state before feedback is generated below, so a packet repaired by
|
||||
// FEC is not needlessly NACKed.
|
||||
fecDelta, onFECRecovery = b.feedFECLocked(&rtpPacket, now)
|
||||
}
|
||||
b.Unlock()
|
||||
fecDelta.invoke(onFECRecovery)
|
||||
|
||||
if len(rtcpPackets) != 0 {
|
||||
if cb := b.getOnRtcpFeedback(); cb != nil {
|
||||
cb(rtcpPackets)
|
||||
}
|
||||
}
|
||||
b.finishWriteLocked(now, fecDelta, onFECRecovery)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -412,14 +407,13 @@ func (b *Buffer) writeRTX(rtxPkt *rtp.Packet, arrivalTime int64) {
|
||||
return
|
||||
}
|
||||
|
||||
b.calc(b.rtxPktBuf[:n], &repairedPkt, arrivalTime, false, true)
|
||||
b.ingestLocked(b.rtxPktBuf[:n], &repairedPkt, arrivalTime, false, true)
|
||||
var fecDelta fecRecoveryDelta
|
||||
var onFECRecovery func(received int, recovered int, discarded int, bytesReceived int)
|
||||
if b.fecDecoder != nil {
|
||||
fecDelta, onFECRecovery = b.feedFECLocked(&repairedPkt, arrivalTime)
|
||||
}
|
||||
b.Unlock()
|
||||
fecDelta.invoke(onFECRecovery)
|
||||
b.finishWriteLocked(arrivalTime, fecDelta, onFECRecovery)
|
||||
}
|
||||
|
||||
func (b *Buffer) SetPrimaryBufferForFEC(primaryBuffer *Buffer) {
|
||||
@@ -565,8 +559,7 @@ func (b *Buffer) writeFEC(fecPkt *rtp.Packet, arrivalTime int64) {
|
||||
}
|
||||
|
||||
fecDelta, onFECRecovery := b.feedFECLocked(fecPkt, arrivalTime)
|
||||
b.Unlock()
|
||||
fecDelta.invoke(onFECRecovery)
|
||||
b.finishWriteLocked(arrivalTime, fecDelta, onFECRecovery)
|
||||
}
|
||||
|
||||
// feedFECLocked runs a media or FEC packet through the FEC decoder and
|
||||
@@ -592,8 +585,9 @@ func (b *Buffer) feedFECLocked(
|
||||
// recovered packets flow through the regular pipeline: they are
|
||||
// forwarded downstream and stop NACKs for the lost sequence numbers.
|
||||
// They do not re-enter the decoder because chained recovery already
|
||||
// completed within DecodeFEC.
|
||||
b.calc(b.fecPktBuf[:n], rp, arrivalTime, false, true)
|
||||
// completed within DecodeFEC. RTCP is generated once by the caller
|
||||
// after all recovered packets have updated NACK state.
|
||||
b.ingestLocked(b.fecPktBuf[:n], rp, arrivalTime, false, true)
|
||||
}
|
||||
|
||||
if cb := b.onFECRecovery; cb != nil {
|
||||
@@ -683,7 +677,11 @@ func (b *Buffer) sendPLI() {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Buffer) calc(rawPkt []byte, rtpPacket *rtp.Packet, arrivalTime int64, isBuffered bool, isRTX bool) []rtcp.Packet {
|
||||
// ingestLocked runs a packet through the incoming packet pipeline, updating RTP
|
||||
// stats, NACK state and the packet bucket. It intentionally does not generate
|
||||
// RTCP so that FEC recovery can update NACK state before feedback is generated
|
||||
// once. Must be called with the buffer lock held.
|
||||
func (b *Buffer) ingestLocked(rawPkt []byte, rtpPacket *rtp.Packet, arrivalTime int64, isBuffered bool, isRTX bool) {
|
||||
b.BufferBase.HandleIncomingPacketLocked(
|
||||
rawPkt,
|
||||
rtpPacket,
|
||||
@@ -693,10 +691,36 @@ func (b *Buffer) calc(rawPkt []byte, rtpPacket *rtp.Packet, arrivalTime int64, i
|
||||
nil,
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
func (b *Buffer) calc(rawPkt []byte, rtpPacket *rtp.Packet, arrivalTime int64, isBuffered bool, isRTX bool) []rtcp.Packet {
|
||||
b.ingestLocked(rawPkt, rtpPacket, arrivalTime, isBuffered, isRTX)
|
||||
return b.getRTCPPackets(arrivalTime)
|
||||
}
|
||||
|
||||
// finishWriteLocked generates RTCP feedback from the current NACK state, which
|
||||
// already reflects any FEC recovery performed during this write, releases the
|
||||
// buffer lock, then invokes the FEC recovery callback and delivers RTCP
|
||||
// feedback. Generating RTCP after recovery ensures sequence numbers repaired by
|
||||
// FEC are not needlessly NACKed. Must be called with the buffer lock held; it
|
||||
// releases the lock before returning.
|
||||
func (b *Buffer) finishWriteLocked(
|
||||
arrivalTime int64,
|
||||
fecDelta fecRecoveryDelta,
|
||||
onFECRecovery func(received int, recovered int, discarded int, bytesReceived int),
|
||||
) {
|
||||
rtcpPackets := b.getRTCPPackets(arrivalTime)
|
||||
b.Unlock()
|
||||
|
||||
fecDelta.invoke(onFECRecovery)
|
||||
|
||||
if len(rtcpPackets) != 0 {
|
||||
if cb := b.getOnRtcpFeedback(); cb != nil {
|
||||
cb(rtcpPackets)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Buffer) getRTCPPackets(arrivalTime int64) []rtcp.Packet {
|
||||
var pkts []rtcp.Packet
|
||||
if nackPkt := b.getNACKPacket(); nackPkt != nil {
|
||||
|
||||
Reference in New Issue
Block a user