Skip to content

Commit 31abde4

Browse files
committed
[breaking] make RTCPeerConnection.addIceCandidate a coroutine
This will be needed to support mDNS candidates. It also matches the JavaScript WebRTC API more closely.
1 parent 20bbde1 commit 31abde4

File tree

11 files changed

+47
-35
lines changed

11 files changed

+47
-35
lines changed

docs/changelog.rst

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@ Changelog
33

44
.. currentmodule:: aiortc
55

6+
1.0.0
7+
-----
8+
9+
Breaking
10+
........
11+
12+
* Make :meth:`RTCPeerConnection.addIceCandidate` a coroutine.
13+
* Make :meth:`RTCIceTransport.addRemoteCandidate` a coroutine.
14+
615
0.9.28
716
------
817

examples/apprtc/apprtc.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def on_track(track):
8383
await pc.setLocalDescription(await pc.createAnswer())
8484
await signaling.send(pc.localDescription)
8585
elif isinstance(obj, RTCIceCandidate):
86-
pc.addIceCandidate(obj)
86+
await pc.addIceCandidate(obj)
8787
elif obj is BYE:
8888
print("Exiting")
8989
break

examples/datachannel-cli/cli.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ async def consume_signaling(pc, signaling):
2828
await pc.setLocalDescription(await pc.createAnswer())
2929
await signaling.send(pc.localDescription)
3030
elif isinstance(obj, RTCIceCandidate):
31-
pc.addIceCandidate(obj)
31+
await pc.addIceCandidate(obj)
3232
elif obj is BYE:
3333
print("Exiting")
3434
break

examples/datachannel-filexfer/filexfer.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ async def consume_signaling(pc, signaling):
2525
await pc.setLocalDescription(await pc.createAnswer())
2626
await signaling.send(pc.localDescription)
2727
elif isinstance(obj, RTCIceCandidate):
28-
pc.addIceCandidate(obj)
28+
await pc.addIceCandidate(obj)
2929
elif obj is BYE:
3030
print("Exiting")
3131
break

examples/datachannel-vpn/vpn.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ async def consume_signaling(pc, signaling):
2626
await pc.setLocalDescription(await pc.createAnswer())
2727
await signaling.send(pc.localDescription)
2828
elif isinstance(obj, RTCIceCandidate):
29-
pc.addIceCandidate(obj)
29+
await pc.addIceCandidate(obj)
3030
elif obj is BYE:
3131
print("Exiting")
3232
break

examples/videostream-cli/cli.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def on_track(track):
115115
await pc.setLocalDescription(await pc.createAnswer())
116116
await signaling.send(pc.localDescription)
117117
elif isinstance(obj, RTCIceCandidate):
118-
pc.addIceCandidate(obj)
118+
await pc.addIceCandidate(obj)
119119
elif obj is BYE:
120120
print("Exiting")
121121
break

src/aiortc/rtcicetransport.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ def state(self) -> str:
270270
"""
271271
return self.__state
272272

273-
def addRemoteCandidate(self, candidate: Optional[RTCIceCandidate]) -> None:
273+
async def addRemoteCandidate(self, candidate: Optional[RTCIceCandidate]) -> None:
274274
"""
275275
Add a remote candidate.
276276

src/aiortc/rtcpeerconnection.py

+9-8
Original file line numberDiff line numberDiff line change
@@ -149,13 +149,14 @@ def add_transport_description(
149149
media.dtls.fingerprints = dtlsTransport.getLocalParameters().fingerprints
150150

151151

152-
def add_remote_candidates(
152+
async def add_remote_candidates(
153153
iceTransport: RTCIceTransport, media: sdp.MediaDescription
154154
) -> None:
155-
for candidate in media.ice_candidates:
156-
iceTransport.addRemoteCandidate(candidate)
155+
coros = map(iceTransport.addRemoteCandidate, media.ice_candidates)
156+
await asyncio.gather(*coros)
157+
157158
if media.ice_candidates_complete:
158-
iceTransport.addRemoteCandidate(None)
159+
await iceTransport.addRemoteCandidate(None)
159160

160161

161162
def allocate_mid(mids: Set[str]) -> str:
@@ -335,7 +336,7 @@ def sctp(self) -> Optional[RTCSctpTransport]:
335336
def signalingState(self):
336337
return self.__signalingState
337338

338-
def addIceCandidate(self, candidate: RTCIceCandidate) -> None:
339+
async def addIceCandidate(self, candidate: RTCIceCandidate) -> None:
339340
"""
340341
Add a new :class:`RTCIceCandidate` received from the remote peer.
341342
@@ -349,7 +350,7 @@ def addIceCandidate(self, candidate: RTCIceCandidate) -> None:
349350
for transceiver in self.__transceivers:
350351
if candidate.sdpMid == transceiver.mid and not transceiver._bundled:
351352
iceTransport = transceiver._transport.transport
352-
iceTransport.addRemoteCandidate(candidate)
353+
await iceTransport.addRemoteCandidate(candidate)
353354
return
354355

355356
if (
@@ -358,7 +359,7 @@ def addIceCandidate(self, candidate: RTCIceCandidate) -> None:
358359
and not self.__sctp._bundled
359360
):
360361
iceTransport = self.__sctp.transport.transport
361-
iceTransport.addRemoteCandidate(candidate)
362+
await iceTransport.addRemoteCandidate(candidate)
362363

363364
def addTrack(self, track: MediaStreamTrack) -> RTCRtpSender:
364365
"""
@@ -835,7 +836,7 @@ async def setRemoteDescription(
835836
if dtlsTransport is not None:
836837
# add ICE candidates
837838
iceTransport = dtlsTransport.transport
838-
add_remote_candidates(iceTransport, media)
839+
await add_remote_candidates(iceTransport, media)
839840

840841
# set ICE role
841842
if description.type == "offer" and not iceTransport._role_set:

tests/test_ortc.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ async def start_ice_pair():
3131
await asyncio.gather(ice_a.iceGatherer.gather(), ice_b.iceGatherer.gather())
3232

3333
for candidate in ice_b.iceGatherer.getLocalCandidates():
34-
ice_a.addRemoteCandidate(candidate)
34+
await ice_a.addRemoteCandidate(candidate)
3535
for candidate in ice_a.iceGatherer.getLocalCandidates():
36-
ice_b.addRemoteCandidate(candidate)
36+
await ice_b.addRemoteCandidate(candidate)
3737
await asyncio.gather(
3838
ice_a.start(ice_b.iceGatherer.getLocalParameters()),
3939
ice_b.start(ice_a.iceGatherer.getLocalParameters()),

tests/test_rtcicetransport.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -261,11 +261,11 @@ def test_construct(self):
261261
)
262262

263263
# add candidate
264-
connection.addRemoteCandidate(candidate)
264+
run(connection.addRemoteCandidate(candidate))
265265
self.assertEqual(connection.getRemoteCandidates(), [candidate])
266266

267267
# end-of-candidates
268-
connection.addRemoteCandidate(None)
268+
run(connection.addRemoteCandidate(None))
269269
self.assertEqual(connection.getRemoteCandidates(), [candidate])
270270

271271
def test_connect(self):
@@ -278,9 +278,9 @@ def test_connect(self):
278278
# gather candidates
279279
run(asyncio.gather(gatherer_1.gather(), gatherer_2.gather()))
280280
for candidate in gatherer_2.getLocalCandidates():
281-
transport_1.addRemoteCandidate(candidate)
281+
run(transport_1.addRemoteCandidate(candidate))
282282
for candidate in gatherer_1.getLocalCandidates():
283-
transport_2.addRemoteCandidate(candidate)
283+
run(transport_2.addRemoteCandidate(candidate))
284284
self.assertEqual(transport_1.state, "new")
285285
self.assertEqual(transport_2.state, "new")
286286

@@ -309,9 +309,9 @@ def test_connect_fail(self):
309309
# gather candidates
310310
run(asyncio.gather(gatherer_1.gather(), gatherer_2.gather()))
311311
for candidate in gatherer_2.getLocalCandidates():
312-
transport_1.addRemoteCandidate(candidate)
312+
run(transport_1.addRemoteCandidate(candidate))
313313
for candidate in gatherer_1.getLocalCandidates():
314-
transport_2.addRemoteCandidate(candidate)
314+
run(transport_2.addRemoteCandidate(candidate))
315315
self.assertEqual(transport_1.state, "new")
316316
self.assertEqual(transport_2.state, "new")
317317

tests/test_rtcpeerconnection.py

+15-13
Original file line numberDiff line numberDiff line change
@@ -394,15 +394,17 @@ def tearDown(self):
394394
def test_addIceCandidate_no_sdpMid_or_sdpMLineIndex(self):
395395
pc = RTCPeerConnection()
396396
with self.assertRaises(ValueError) as cm:
397-
pc.addIceCandidate(
398-
RTCIceCandidate(
399-
component=1,
400-
foundation="0",
401-
ip="192.168.99.7",
402-
port=33543,
403-
priority=2122252543,
404-
protocol="UDP",
405-
type="host",
397+
run(
398+
pc.addIceCandidate(
399+
RTCIceCandidate(
400+
component=1,
401+
foundation="0",
402+
ip="192.168.99.7",
403+
port=33543,
404+
priority=2122252543,
405+
protocol="UDP",
406+
type="host",
407+
)
406408
)
407409
)
408410
self.assertEqual(
@@ -826,12 +828,12 @@ def test_connect_audio_bidirectional_with_trickle(self):
826828
iceGatherer = transceiver.sender.transport.transport.iceGatherer
827829
for candidate in iceGatherer.getLocalCandidates():
828830
candidate.sdpMid = transceiver.mid
829-
pc1.addIceCandidate(candidate)
831+
run(pc1.addIceCandidate(candidate))
830832
for transceiver in pc1.getTransceivers():
831833
iceGatherer = transceiver.sender.transport.transport.iceGatherer
832834
for candidate in iceGatherer.getLocalCandidates():
833835
candidate.sdpMid = transceiver.mid
834-
pc2.addIceCandidate(candidate)
836+
run(pc2.addIceCandidate(candidate))
835837

836838
# check outcome
837839
self.assertIceCompleted(pc1, pc2)
@@ -3721,10 +3723,10 @@ def on_message(message):
37213723
# trickle candidates
37223724
for candidate in pc2.sctp.transport.transport.iceGatherer.getLocalCandidates():
37233725
candidate.sdpMid = pc2.sctp.mid
3724-
pc1.addIceCandidate(candidate)
3726+
run(pc1.addIceCandidate(candidate))
37253727
for candidate in pc1.sctp.transport.transport.iceGatherer.getLocalCandidates():
37263728
candidate.sdpMid = pc1.sctp.mid
3727-
pc2.addIceCandidate(candidate)
3729+
run(pc2.addIceCandidate(candidate))
37283730

37293731
# check outcome
37303732
self.assertIceCompleted(pc1, pc2)

0 commit comments

Comments
 (0)