diff --git a/pkg/sfu/downtrack.go b/pkg/sfu/downtrack.go index 11bca8b52..b68a60457 100644 --- a/pkg/sfu/downtrack.go +++ b/pkg/sfu/downtrack.go @@ -406,12 +406,18 @@ func (d *DownTrack) Bind(t webrtc.TrackLocalContext) (webrtc.RTPCodecParameters, return webrtc.RTPCodecParameters{}, err } else if strings.EqualFold(matchedUpstreamCodec.MimeType, "audio/red") { d.isRED = true - var primaryPT, secondaryPT int - if n, err := fmt.Sscanf(matchedUpstreamCodec.SDPFmtpLine, "%d/%d", &primaryPT, &secondaryPT); err != nil || n != 2 { - d.params.Logger.Errorw("failed to parse upstream primary and secondary payload type for RED", err, "matchedCodec", codec) + for _, c := range d.upstreamCodecs { + // assume upstream primary codec is opus since we only support it for audio now + if strings.EqualFold(c.MimeType, "audio/opus") { + d.upstreamPrimaryPT = uint8(c.PayloadType) + break + } + } + if d.upstreamPrimaryPT == 0 { + d.params.Logger.Errorw("failed to find upstream primary opus payload type for RED", nil, "matchedCodec", codec, "upstreamCodec", d.upstreamCodecs) } - d.upstreamPrimaryPT = uint8(primaryPT) + var primaryPT, secondaryPT int if n, err := fmt.Sscanf(codec.SDPFmtpLine, "%d/%d", &primaryPT, &secondaryPT); err != nil || n != 2 { d.params.Logger.Errorw("failed to parse primary and secondary payload type for RED", err, "matchedCodec", codec) } diff --git a/pkg/sfu/redreceiver.go b/pkg/sfu/redreceiver.go index e9b956bcd..9d24b997b 100644 --- a/pkg/sfu/redreceiver.go +++ b/pkg/sfu/redreceiver.go @@ -29,13 +29,15 @@ import ( ) const ( - maxRedCount = 2 - mtuSize = 1500 + maxRedCount = 2 + mtuSize = 1500 + maxRedPayload = 1 << 10 // fit into 10 bits length field // the RedReceiver is only for chrome / native webrtc now, we always negotiate opus payload to 111 with those clients, // so it is safe to use a fixed payload 111 here for performance(avoid encoding red blocks for each downtrack that // have a different opus payload type). - opusPT = 111 + opusPT = 111 + opusRedPT = 63 ) type RedReceiver struct { @@ -60,6 +62,14 @@ func (r *RedReceiver) ForwardRTP(pkt *buffer.ExtPacket, spatialLayer int32) int if r.downTrackSpreader.DownTrackCount() == 0 { return 0 } + + // fallback to primary codec if payload size exceeds redundant block length + if len(pkt.Packet.Payload) >= maxRedPayload { + return r.downTrackSpreader.Broadcast(func(dt TrackSender) { + _ = dt.WriteRTP(pkt, spatialLayer) + }) + } + redLen, err := r.encodeRedForPrimary(pkt.Packet, r.redPayloadBuf[:]) if err != nil { r.logger.Errorw("red encoding failed", err) @@ -68,6 +78,7 @@ func (r *RedReceiver) ForwardRTP(pkt *buffer.ExtPacket, spatialLayer int32) int pPkt := *pkt redRtpPacket := *pkt.Packet + redRtpPacket.PayloadType = 63 redRtpPacket.Payload = r.redPayloadBuf[:redLen] pPkt.Packet = &redRtpPacket @@ -117,7 +128,7 @@ func (r *RedReceiver) Close() { } func (r *RedReceiver) ReadRTP(buf []byte, layer uint8, sn uint16) (int, error) { - // red encoding don't support nack + // red encoding doesn't support nack return 0, bucket.ErrPacketMismatch }