mirror of
https://github.com/pion/webrtc.git
synced 2025-12-24 11:51:03 +08:00
Support advertising ICE trickling (#3097)
As defined in [RFC8839], when an ICE restart occurs, a new SDP offer/answer exchange is triggered. However, as WHIP does not support renegotiation of non-ICE-related SDP information, a WHIP client will not send a new offer when an ICE restart occurs. Instead, the WHIP client and WHIP session will only exchange the relevant ICE information via an HTTP PATCH request as defined in Section 4.3.1 and MUST assume that the previously negotiated non-ICE-related SDP information still applies after the ICE restart. When performing an ICE restart, the WHIP client MUST include the updated "ice-pwd" and "ice-ufrag" in the SDP fragment of the HTTP PATCH request body as well as the new set of gathered ICE candidates as defined in [RFC8840]. Similar to what is defined in Section 4.3.2, as per [RFC9429], only "m=" sections not marked as bundle-only can gather ICE candidates, so given that the "max-bundle" policy is being used, the SDP fragment will contain only the offerer-tagged "m=" line of the bundle group. A WHIP client sending a PATCH request for performing ICE restart MUST contain an If-Match header field with a field-value of "*" as per Section 13.1.1 of [RFC9110]. Co-authored-by: Joe Turki <git@joeturki.com>
This commit is contained in:
@@ -9,6 +9,10 @@ type OfferAnswerOptions struct {
|
||||
// VoiceActivityDetection allows the application to provide information
|
||||
// about whether it wishes voice detection feature to be enabled or disabled.
|
||||
VoiceActivityDetection bool
|
||||
// ICETricklingSupported indicates whether the ICE agent should use trickle ICE
|
||||
// If set, the "a=ice-options:trickle" attribute is added to the generated SDP payload.
|
||||
// (See https://datatracker.ietf.org/doc/html/rfc9725#section-4.3.3)
|
||||
ICETricklingSupported bool
|
||||
}
|
||||
|
||||
// AnswerOptions structure describes the options used to control the answer
|
||||
|
||||
@@ -718,6 +718,10 @@ func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription
|
||||
return SessionDescription{}, err
|
||||
}
|
||||
|
||||
if options != nil && options.ICETricklingSupported {
|
||||
descr.WithICETrickleAdvertised()
|
||||
}
|
||||
|
||||
updateSDPOrigin(&pc.sdpOrigin, descr)
|
||||
sdpBytes, err := descr.Marshal()
|
||||
if err != nil {
|
||||
@@ -842,7 +846,7 @@ func (pc *PeerConnection) createICETransport() *ICETransport {
|
||||
// CreateAnswer starts the PeerConnection and generates the localDescription.
|
||||
//
|
||||
//nolint:cyclop
|
||||
func (pc *PeerConnection) CreateAnswer(*AnswerOptions) (SessionDescription, error) {
|
||||
func (pc *PeerConnection) CreateAnswer(options *AnswerOptions) (SessionDescription, error) {
|
||||
useIdentity := pc.idpLoginURL != nil
|
||||
remoteDesc := pc.RemoteDescription()
|
||||
switch {
|
||||
@@ -876,6 +880,10 @@ func (pc *PeerConnection) CreateAnswer(*AnswerOptions) (SessionDescription, erro
|
||||
return SessionDescription{}, err
|
||||
}
|
||||
|
||||
if options != nil && options.ICETricklingSupported {
|
||||
descr.WithICETrickleAdvertised()
|
||||
}
|
||||
|
||||
updateSDPOrigin(&pc.sdpOrigin, descr)
|
||||
sdpBytes, err := descr.Marshal()
|
||||
if err != nil {
|
||||
|
||||
@@ -1787,6 +1787,58 @@ func TestPeerConnectionNoNULLCipherDefault(t *testing.T) {
|
||||
closePairNow(t, offerPC, answerPC)
|
||||
}
|
||||
|
||||
func TestICETricklingSupported(t *testing.T) {
|
||||
report := test.CheckRoutines(t)
|
||||
defer report()
|
||||
|
||||
pc, err := NewPeerConnection(Configuration{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
offer, err := pc.CreateOffer(&OfferOptions{
|
||||
OfferAnswerOptions: OfferAnswerOptions{ICETricklingSupported: true},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, offer.SDP, "a=ice-options:trickle")
|
||||
assert.NoError(t, pc.Close())
|
||||
|
||||
pcAnswer, err := NewPeerConnection(Configuration{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
offerSDP := strings.Join([]string{
|
||||
"v=0",
|
||||
"o=- 0 0 IN IP4 127.0.0.1",
|
||||
"s=-",
|
||||
"t=0 0",
|
||||
"a=group:BUNDLE 0",
|
||||
"a=ice-ufrag:someufrag",
|
||||
"a=ice-pwd:somepwd",
|
||||
"a=fingerprint:sha-256 " +
|
||||
"F7:BF:B4:42:5B:44:C0:B9:49:70:6D:26:D7:3E:E6:08:B1:5B:25:2E:32:88:50:B6:3C:BE:4E:18:A7:2C:85:7C",
|
||||
"a=msid-semantic: WMS *",
|
||||
"m=audio 9 UDP/TLS/RTP/SAVPF 111",
|
||||
"c=IN IP4 0.0.0.0",
|
||||
"a=rtcp:9 IN IP4 0.0.0.0",
|
||||
"a=mid:0",
|
||||
"a=rtcp-mux",
|
||||
"a=setup:actpass",
|
||||
"a=rtpmap:111 opus/48000/2",
|
||||
"",
|
||||
}, "\r\n")
|
||||
|
||||
assert.NoError(t, pcAnswer.SetRemoteDescription(SessionDescription{
|
||||
Type: SDPTypeOffer,
|
||||
SDP: offerSDP,
|
||||
}))
|
||||
|
||||
answer, err := pcAnswer.CreateAnswer(&AnswerOptions{
|
||||
OfferAnswerOptions: OfferAnswerOptions{ICETricklingSupported: true},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, answer.SDP, "a=ice-options:trickle")
|
||||
|
||||
assert.NoError(t, pcAnswer.Close())
|
||||
}
|
||||
|
||||
// https://github.com/pion/webrtc/issues/2690
|
||||
func TestPeerConnectionTrickleMediaStreamIdentification(t *testing.T) {
|
||||
const remoteSdp = `v=0
|
||||
|
||||
Reference in New Issue
Block a user