Hi, we ran into this on an outbound carrier call and I think the current
re-INVITE handling has a gap.
The initial exchange selected PCMU. Right after the ACK, the carrier sent an
in-dialog re-INVITE offering only PCMA. LiveKit answered 200 OK with the
cached PCMU SDP from the initial exchange and kept the media pipeline on PCMU.
That left no common audio codec between the new offer and the answer, and the
call had no usable return audio.
Simplified SDP sequence:
# Initial LiveKit offer
m=audio 10820 RTP/AVP 9 0 8 101
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
# Initial peer answer
m=audio 45832 RTP/AVP 0 101
a=rtpmap:0 PCMU/8000
# Peer re-INVITE
m=audio 45832 RTP/AVP 8 96
a=rtpmap:8 PCMA/8000
# LiveKit 200 OK
m=audio 10820 RTP/AVP 0 101
a=rtpmap:0 PCMU/8000
The last answer contains PCMU even though the re-INVITE offered only PCMA.
RFC 3264 section 6.1 requires a sendrecv answer to contain at least one codec
from the offer:
https://www.rfc-editor.org/rfc/rfc3264.html#section-6.1
I checked current main at
179b2574d8e6b97306855cf0c8628b17f53c3e92. The re-INVITE path still parses the
new SDP only to update the RTP address, then returns the old local SDP:
|
func updateRemoteFromSDP(media *MediaPort, log logger.Logger, codecs *msdk.CodecSet, body []byte) { |
|
if len(body) == 0 || media == nil { |
|
return |
|
} |
|
desc, err := sdp.ParseWith(codecs, body) |
|
if err != nil { |
|
log.Warnw("failed to parse re-INVITE SDP, RTP destination not updated", err) |
|
return |
|
} |
|
media.UpdateRemote(desc.Addr) |
|
} |
|
if s.cli != nil { // Process reinvite for existing outbound calls |
|
oc := s.cli.getActiveCall(cc.ID()) |
|
newCSeq := cc.InviteCSeq() |
|
if oc != nil && oc.cc != nil && oc.cc.InviteCSeq() < newCSeq { |
|
localSDP := oc.cc.LocalSDP() |
|
if len(localSDP) != 0 { |
|
oc.log.Infow("accepting reinvite", "content-length", req.ContentLength(), "cseq", cc.InviteCSeq()) |
|
oc.updateRemoteFromSDP(sdpBodyFromRequest(req)) |
|
oc.cc.RecordInvite(newCSeq) |
|
cc.AcceptAsKeepAlive(localSDP) |
|
return nil |
I also added a small regression test locally. Before the guard, LiveKit
returned 200; after checking that the currently negotiated codec and payload
types are still offered, the same re-INVITE returns 488 Not Acceptable Here
and leaves the existing RTP destination untouched.
I think either behavior below would be valid:
- Fully renegotiate the codec and update the media pipeline before answering
with matching SDP; or
- Reject a codec/payload change with
488 and keep the established session.
What should not happen is a successful answer containing a codec that was not
in the offer.
This looks related to #661 and #728. The RTP destination update from #728 works,
but the codec change mentioned in #661 still appears to be unhandled.
If you want I can also fix it myself and create a Pull Request for this.
Hi, we ran into this on an outbound carrier call and I think the current
re-INVITE handling has a gap.
The initial exchange selected PCMU. Right after the ACK, the carrier sent an
in-dialog re-INVITE offering only PCMA. LiveKit answered
200 OKwith thecached PCMU SDP from the initial exchange and kept the media pipeline on PCMU.
That left no common audio codec between the new offer and the answer, and the
call had no usable return audio.
Simplified SDP sequence:
The last answer contains PCMU even though the re-INVITE offered only PCMA.
RFC 3264 section 6.1 requires a
sendrecvanswer to contain at least one codecfrom the offer:
https://www.rfc-editor.org/rfc/rfc3264.html#section-6.1
I checked current
mainat179b2574d8e6b97306855cf0c8628b17f53c3e92. The re-INVITE path still parses thenew SDP only to update the RTP address, then returns the old local SDP:
sip/pkg/sip/inbound.go
Lines 323 to 333 in 179b257
sip/pkg/sip/inbound.go
Lines 408 to 418 in 179b257
I also added a small regression test locally. Before the guard, LiveKit
returned
200; after checking that the currently negotiated codec and payloadtypes are still offered, the same re-INVITE returns
488 Not Acceptable Hereand leaves the existing RTP destination untouched.
I think either behavior below would be valid:
with matching SDP; or
488and keep the established session.What should not happen is a successful answer containing a codec that was not
in the offer.
This looks related to #661 and #728. The RTP destination update from #728 works,
but the codec change mentioned in #661 still appears to be unhandled.
If you want I can also fix it myself and create a Pull Request for this.