|
4 | 4 | package nack
|
5 | 5 |
|
6 | 6 | import (
|
| 7 | + "encoding/binary" |
7 | 8 | "sync"
|
8 | 9 |
|
9 | 10 | "github.com/pion/interceptor"
|
@@ -62,6 +63,11 @@ type ResponderInterceptor struct {
|
62 | 63 | type localStream struct {
|
63 | 64 | sendBuffer *sendBuffer
|
64 | 65 | rtpWriter interceptor.RTPWriter
|
| 66 | + |
| 67 | + // Non-zero if Retransmissions should be sent on a distinct stream |
| 68 | + rtxSsrc uint32 |
| 69 | + rtxPayloadType uint8 |
| 70 | + rtxSequencer rtp.Sequencer |
65 | 71 | }
|
66 | 72 |
|
67 | 73 | // NewResponderInterceptor returns a new ResponderInterceptorFactor
|
@@ -108,7 +114,13 @@ func (n *ResponderInterceptor) BindLocalStream(info *interceptor.StreamInfo, wri
|
108 | 114 | // error is already checked in NewGeneratorInterceptor
|
109 | 115 | sendBuffer, _ := newSendBuffer(n.size)
|
110 | 116 | n.streamsMu.Lock()
|
111 |
| - n.streams[info.SSRC] = &localStream{sendBuffer: sendBuffer, rtpWriter: writer} |
| 117 | + n.streams[info.SSRC] = &localStream{ |
| 118 | + sendBuffer: sendBuffer, |
| 119 | + rtpWriter: writer, |
| 120 | + rtxSsrc: info.SSRCRetransmission, |
| 121 | + rtxPayloadType: info.PayloadTypeRetransmission, |
| 122 | + rtxSequencer: rtp.NewRandomSequencer(), |
| 123 | + } |
112 | 124 | n.streamsMu.Unlock()
|
113 | 125 |
|
114 | 126 | return interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
@@ -139,8 +151,43 @@ func (n *ResponderInterceptor) resendPackets(nack *rtcp.TransportLayerNack) {
|
139 | 151 | for i := range nack.Nacks {
|
140 | 152 | nack.Nacks[i].Range(func(seq uint16) bool {
|
141 | 153 | if p := stream.sendBuffer.get(seq); p != nil {
|
142 |
| - if _, err := stream.rtpWriter.Write(p.Header(), p.Payload(), interceptor.Attributes{}); err != nil { |
143 |
| - n.log.Warnf("failed resending nacked packet: %+v", err) |
| 154 | + if stream.rtxSsrc != 0 { |
| 155 | + // Store the original sequence number and rewrite the sequence number. |
| 156 | + originalSequenceNumber := p.Header().SequenceNumber |
| 157 | + p.Header().SequenceNumber = stream.rtxSequencer.NextSequenceNumber() |
| 158 | + |
| 159 | + // Rewrite the SSRC. |
| 160 | + p.Header().SSRC = stream.rtxSsrc |
| 161 | + // Rewrite the payload type. |
| 162 | + p.Header().PayloadType = stream.rtxPayloadType |
| 163 | + |
| 164 | + // Remove padding if present. |
| 165 | + paddingLength := 0 |
| 166 | + originPayload := p.Payload() |
| 167 | + if p.Header().Padding { |
| 168 | + paddingLength = int(originPayload[len(originPayload)-1]) |
| 169 | + p.Header().Padding = false |
| 170 | + } |
| 171 | + |
| 172 | + // Write the original sequence number at the beginning of the payload. |
| 173 | + payload := make([]byte, 2) |
| 174 | + binary.BigEndian.PutUint16(payload, originalSequenceNumber) |
| 175 | + payload = append(payload, originPayload[:len(originPayload)-paddingLength]...) |
| 176 | + |
| 177 | + // Send RTX packet. |
| 178 | + if _, err := stream.rtpWriter.Write(p.Header(), payload, interceptor.Attributes{}); err != nil { |
| 179 | + n.log.Warnf("failed sending rtx packet: %+v", err) |
| 180 | + } |
| 181 | + |
| 182 | + // Resore the Padding and SSRC. |
| 183 | + if paddingLength > 0 { |
| 184 | + p.Header().Padding = true |
| 185 | + } |
| 186 | + p.Header().SequenceNumber = originalSequenceNumber |
| 187 | + } else { |
| 188 | + if _, err := stream.rtpWriter.Write(p.Header(), p.Payload(), interceptor.Attributes{}); err != nil { |
| 189 | + n.log.Warnf("failed resending nacked packet: %+v", err) |
| 190 | + } |
144 | 191 | }
|
145 | 192 | p.Release()
|
146 | 193 | }
|
|
0 commit comments