frontends (tools/webrtc): improve ICE candidate buffering:
Buffer and wait for remote description to be set before sending ICE candidate. This avoids the sending of ICE candidates before the negotiation is done.
This commit is contained in:
parent
40b1c93f4c
commit
9a2830c7e2
1 changed files with 71 additions and 3 deletions
|
|
@ -233,6 +233,27 @@ class WebRTC:
|
|||
raise Exception("self._media_types_inv should not be None!")
|
||||
return self._media_types_inv
|
||||
|
||||
|
||||
@property
|
||||
def remote_desc_set(self) -> bool:
|
||||
return self._remote_desc_set
|
||||
|
||||
@remote_desc_set.setter
|
||||
def remote_desc_set(self, is_set: bool) -> None:
|
||||
"""Setter for the remote_desc_set flag."""
|
||||
if self._remote_desc_set == is_set:
|
||||
return
|
||||
|
||||
self._remote_desc_set = is_set
|
||||
if is_set:
|
||||
log.debug("Remote description has been set.")
|
||||
# Now that both descriptions are set, we can process buffered remote
|
||||
# candidates and send our buffered local candidates.
|
||||
self.on_ice_candidates_new(self.remote_candidates_buffer)
|
||||
|
||||
# Also send any local candidates we buffered before this point.
|
||||
self._send_buffered_local_candidates()
|
||||
|
||||
def bind(self, **kwargs: Callable) -> None:
|
||||
self.bindings.clear()
|
||||
for key, cb in kwargs.items():
|
||||
|
|
@ -448,6 +469,7 @@ class WebRTC:
|
|||
self._media_types = None
|
||||
self._media_types_inv = None
|
||||
self._sdp_set: bool = False
|
||||
self._remote_desc_set: bool = False
|
||||
self.remote_candidates_buffer: dict[str, dict[str, list]] = {
|
||||
"audio": {"candidates": []},
|
||||
"video": {"candidates": []},
|
||||
|
|
@ -1155,9 +1177,14 @@ class WebRTC:
|
|||
self.local_candidates_buffer.clear()
|
||||
|
||||
def _remote_sdp_set(self, promise) -> None:
|
||||
"""Called after set-remote-description with an ANSWER is complete (initiator)."""
|
||||
assert promise.wait() == Gst.PromiseResult.REPLIED
|
||||
self.sdp_set = True
|
||||
|
||||
# The remote description is now set. We can process remote candidates and send our
|
||||
# buffered ones.
|
||||
self.remote_desc_set = True
|
||||
|
||||
def on_accepted_call(self, sdp: str, profile: str) -> None:
|
||||
"""Outgoing call has been accepted.
|
||||
|
||||
|
|
@ -1204,6 +1231,46 @@ class WebRTC:
|
|||
promise = Gst.Promise.new_with_change_func(self.on_offer_set)
|
||||
self.webrtcbin.emit("set-remote-description", offer, promise)
|
||||
|
||||
# For the responder, the remote description is set synchronously. We can set the
|
||||
# flag here.
|
||||
self.remote_desc_set = True
|
||||
|
||||
def _send_buffered_local_candidates(self) -> None:
|
||||
"""Sends any local ICE candidates that were buffered before the remote description was set."""
|
||||
if not self.local_candidates_buffer:
|
||||
return
|
||||
|
||||
log.debug(
|
||||
f"sending buffered local ICE candidates: {self.local_candidates_buffer}"
|
||||
)
|
||||
sdp = self.webrtcbin.props.local_description.sdp.as_text()
|
||||
self.extract_ufrag_pwd(sdp)
|
||||
|
||||
ice_data = {}
|
||||
for media_type, candidates in self.local_candidates_buffer.items():
|
||||
if media_type not in self.ufrag or media_type not in self.pwd:
|
||||
log.error(f"Missing ICE credentials for {media_type}")
|
||||
continue
|
||||
|
||||
ice_data[media_type] = {
|
||||
"ufrag": self.ufrag[media_type],
|
||||
"pwd": self.pwd[media_type],
|
||||
"candidates": candidates,
|
||||
}
|
||||
|
||||
if ice_data:
|
||||
log.debug(f"Sending ICE data for media types: {list(ice_data.keys())}")
|
||||
self._a_call(
|
||||
self.bridge.ice_candidates_add,
|
||||
self.sid,
|
||||
data_format.serialise(ice_data),
|
||||
self.profile,
|
||||
)
|
||||
else:
|
||||
log.error("No ICE data to send - missing credentials!")
|
||||
|
||||
self.local_candidates_buffer.clear()
|
||||
|
||||
def on_ice_candidate(self, webrtc, mline_index, candidate_sdp):
|
||||
"""Handles the on-ice-candidate signal of webrtcbin.
|
||||
|
||||
|
|
@ -1231,15 +1298,16 @@ class WebRTC:
|
|||
log.debug(f"{self.media_types=}")
|
||||
return
|
||||
|
||||
if self.sid is None:
|
||||
# Buffer local candidates until the remote description is set to avoid a race
|
||||
# condition.
|
||||
if not self._remote_desc_set:
|
||||
log.debug(f"buffering local ICE candidate for {media_type}")
|
||||
self.local_candidates_buffer.setdefault(media_type, []).append(
|
||||
parsed_candidate
|
||||
)
|
||||
|
||||
else:
|
||||
# The remote description is set, so we can send the candidate immediately.
|
||||
sdp = self.webrtcbin.props.local_description.sdp.as_text()
|
||||
assert sdp is not None
|
||||
self.extract_ufrag_pwd(sdp)
|
||||
ice_data = {
|
||||
"ufrag": self.ufrag[media_type],
|
||||
|
|
|
|||
Loading…
Reference in a new issue