|
23 | 23 | ErrDataChannelNotAvailable = errors.New("data channel is not available") |
24 | 24 | ErrDataChannelNotReady = errors.New("data channel is not ready") |
25 | 25 | ErrCantSubscribeToTrack = errors.New("can't subscribe to track") |
26 | | - ErrCantWriteRTCP = errors.New("can't write RTCP") |
| 26 | + ErrTrackNotFound = errors.New("track not found") |
27 | 27 | ) |
28 | 28 |
|
29 | 29 | // A wrapped representation of the peer connection (single peer in the call). |
@@ -106,71 +106,56 @@ func (p *Peer[ID]) SubscribeTo(track *webrtc.TrackLocalStaticRTP) error { |
106 | 106 | packets, _, err := rtpSender.ReadRTCP() |
107 | 107 | if err != nil { |
108 | 108 | if errors.Is(err, io.ErrClosedPipe) || errors.Is(err, io.EOF) { |
| 109 | + p.logger.WithError(err).Warn("failed to read RTCP on track") |
109 | 110 | return |
110 | 111 | } |
| 112 | + } |
111 | 113 |
|
112 | | - p.logger.WithError(err).Warn("failed to read RTCP on track") |
| 114 | + // We only want to inform others about PLIs and FIRs. We skip the rest of the packets for now. |
| 115 | + toForward := []RTCPPacketType{} |
| 116 | + for _, packet := range packets { |
| 117 | + // TODO: Should we also handle NACKs? |
| 118 | + switch packet.(type) { |
| 119 | + case *rtcp.PictureLossIndication: |
| 120 | + toForward = append(toForward, PictureLossIndicator) |
| 121 | + case *rtcp.FullIntraRequest: |
| 122 | + toForward = append(toForward, FullIntraRequest) |
| 123 | + } |
113 | 124 | } |
114 | 125 |
|
115 | | - p.sink.Send(RTCPReceived{Packets: packets, TrackID: track.ID(), StreamID: track.StreamID()}) |
| 126 | + p.sink.Send(RTCPReceived{Packets: toForward, TrackID: track.ID()}) |
116 | 127 | } |
117 | 128 | }() |
118 | 129 |
|
119 | 130 | return nil |
120 | 131 | } |
121 | 132 |
|
122 | | -func (p *Peer[ID]) WriteRTCP(packets []rtcp.Packet, streamID string, trackID string, lastPLITimestamp time.Time) error { |
123 | | - const minimalPLIInterval = time.Millisecond * 500 |
124 | | - |
125 | | - packetsToSend := []rtcp.Packet{} |
126 | | - var mediaSSRC uint32 |
| 133 | +// Writes the specified packets to the `trackID`. |
| 134 | +func (p *Peer[ID]) WriteRTCP(trackID string, packets []RTCPPacketType) error { |
| 135 | + // Find the right track. |
127 | 136 | receivers := p.peerConnection.GetReceivers() |
128 | 137 | receiverIndex := slices.IndexFunc(receivers, func(receiver *webrtc.RTPReceiver) bool { |
129 | | - return receiver.Track().ID() == trackID && receiver.Track().StreamID() == streamID |
| 138 | + return receiver.Track().ID() == trackID |
130 | 139 | }) |
131 | | - |
132 | 140 | if receiverIndex == -1 { |
133 | | - p.logger.Error("failed to find track to write RTCP on") |
134 | | - return ErrCantWriteRTCP |
135 | | - } else { |
136 | | - mediaSSRC = uint32(receivers[receiverIndex].Track().SSRC()) |
| 141 | + return ErrTrackNotFound |
137 | 142 | } |
138 | 143 |
|
139 | | - for _, packet := range packets { |
140 | | - switch typedPacket := packet.(type) { |
141 | | - // We mung the packets here, so that the SSRCs match what the |
142 | | - // receiver expects: |
143 | | - // The media SSRC is the SSRC of the media about which the packet is |
144 | | - // reporting; therefore, we mung it to be the SSRC of the publishing |
145 | | - // participant's track. Without this, it would be SSRC of the SFU's |
146 | | - // track which isn't right |
147 | | - case *rtcp.PictureLossIndication: |
148 | | - // Since we sometimes spam the sender with PLIs, make sure we don't send |
149 | | - // them way too often |
150 | | - if time.Now().UnixNano()-lastPLITimestamp.UnixNano() < minimalPLIInterval.Nanoseconds() { |
151 | | - continue |
152 | | - } |
153 | | - |
154 | | - typedPacket.MediaSSRC = mediaSSRC |
155 | | - packetsToSend = append(packetsToSend, typedPacket) |
156 | | - case *rtcp.FullIntraRequest: |
157 | | - typedPacket.MediaSSRC = mediaSSRC |
158 | | - packetsToSend = append(packetsToSend, typedPacket) |
159 | | - } |
160 | | - |
161 | | - packetsToSend = append(packetsToSend, packet) |
162 | | - } |
163 | | - |
164 | | - if len(packetsToSend) != 0 { |
165 | | - if err := p.peerConnection.WriteRTCP(packetsToSend); err != nil { |
166 | | - if !errors.Is(err, io.ErrClosedPipe) { |
167 | | - p.logger.WithError(err).Error("failed to write RTCP on track") |
168 | | - return err |
169 | | - } |
| 144 | + // The ssrc that we must use when sending the RTCP packet. |
| 145 | + // Otherwise the peer won't understand where the packet comes from. |
| 146 | + ssrc := uint32(receivers[receiverIndex].Track().SSRC()) |
| 147 | + |
| 148 | + toSend := make([]rtcp.Packet, len(packets)) |
| 149 | + for i, packet := range packets { |
| 150 | + switch packet { |
| 151 | + case PictureLossIndicator: |
| 152 | + toSend[i] = &rtcp.PictureLossIndication{MediaSSRC: ssrc} |
| 153 | + case FullIntraRequest: |
| 154 | + toSend[i] = &rtcp.FullIntraRequest{MediaSSRC: ssrc} |
170 | 155 | } |
171 | 156 | } |
172 | 157 |
|
173 | | - return nil |
| 158 | + return p.peerConnection.WriteRTCP(toSend) |
174 | 159 | } |
175 | 160 |
|
176 | 161 | // Unsubscribes from the given list of tracks. |
|
0 commit comments