comparison libervia/backend/plugins/plugin_xep_0167/mapping.py @ 4121:b2709504586a

plugin XEP-0167: mapping adjustments: - use `9` as port placeholder - add `a=ice-options:trickle` to indicate that ICE candidates may be sent after SDP - moved senders mapping at media level - fix `rtpmap` channel setting - don't overwrite existing `fingerprint` data rel 424
author Goffi <goffi@goffi.org>
date Tue, 03 Oct 2023 15:25:52 +0200
parents 23fa52acf72c
children e11b13418ba6
comparison
equal deleted inserted replaced
4120:832a7bdb3aea 4121:b2709504586a
42 else: 42 else:
43 return "a=recvonly" 43 return "a=recvonly"
44 44
45 45
46 def generate_sdp_from_session( 46 def generate_sdp_from_session(
47 session: dict, local: bool = False, port: int = 9999 47 session: dict, local: bool = False, port: int = 9
48 ) -> str: 48 ) -> str:
49 """Generate an SDP string from session data. 49 """Generate an SDP string from session data.
50 50
51 @param session: A dictionary containing the session data. It should have the 51 @param session: A dictionary containing the session data. It should have the
52 following structure: 52 following structure:
76 and not needed in backend, except for debugging purpose. 76 and not needed in backend, except for debugging purpose.
77 @param port: The preferred port for communications. 77 @param port: The preferred port for communications.
78 78
79 @return: The generated SDP string. 79 @return: The generated SDP string.
80 """ 80 """
81 contents = session["contents"]
81 sdp_lines = ["v=0"] 82 sdp_lines = ["v=0"]
82 83
83 # Add originator (o=) line after the version (v=) line 84 # Add originator (o=) line after the version (v=) line
84 username = base64.b64encode(session["local_jid"].full().encode()).decode() 85 username = base64.b64encode(session["local_jid"].full().encode()).decode()
85 session_id = "1" # Increment this for each session 86 session_id = "1" # Increment this for each session
99 100
100 # stream direction 101 # stream direction
101 all_senders = {c["senders"] for c in session["contents"].values()} 102 all_senders = {c["senders"] for c in session["contents"].values()}
102 # if we don't have a common senders for all contents, we set them at media level 103 # if we don't have a common senders for all contents, we set them at media level
103 senders = all_senders.pop() if len(all_senders) == 1 else None 104 senders = all_senders.pop() if len(all_senders) == 1 else None
104 if senders is not None:
105 sdp_lines.append(senders_to_sdp(senders, session))
106 105
107 sdp_lines.append("a=msid-semantic:WMS *") 106 sdp_lines.append("a=msid-semantic:WMS *")
107 sdp_lines.append("a=ice-options:trickle")
108 108
109 host.trigger.point( 109 host.trigger.point(
110 "XEP-0167_generate_sdp_session", 110 "XEP-0167_generate_sdp_session",
111 session, 111 session,
112 local, 112 local,
113 sdp_lines, 113 sdp_lines,
114 triggers_no_cancel=True 114 triggers_no_cancel=True
115 ) 115 )
116 116
117 contents = session["contents"]
118 for content_name, content_data in contents.items(): 117 for content_name, content_data in contents.items():
119 app_data_key = "local_data" if local else "peer_data" 118 app_data_key = "local_data" if local else "peer_data"
120 application_data = content_data["application_data"] 119 application_data = content_data["application_data"]
121 media_data = application_data[app_data_key] 120 media_data = application_data[app_data_key]
122 media = application_data["media"] 121 media = application_data["media"]
129 sdp_lines.append(m_line) 128 sdp_lines.append(m_line)
130 129
131 sdp_lines.append(f"c={network_type} {address_type} {connection_address}") 130 sdp_lines.append(f"c={network_type} {address_type} {connection_address}")
132 131
133 sdp_lines.append(f"a=mid:{content_name}") 132 sdp_lines.append(f"a=mid:{content_name}")
133 if senders is not None:
134 sdp_lines.append(senders_to_sdp(senders, session))
134 135
135 # stream direction 136 # stream direction
136 if senders is None: 137 if senders is None:
137 sdp_lines.append(senders_to_sdp(content_data["senders"], session)) 138 sdp_lines.append(senders_to_sdp(content_data["senders"], session))
139
138 140
139 # Generate a= lines for rtpmap and fmtp 141 # Generate a= lines for rtpmap and fmtp
140 for pt_id, pt in payload_types.items(): 142 for pt_id, pt in payload_types.items():
141 name = pt["name"] 143 name = pt["name"]
142 clockrate = pt.get("clockrate", "") 144 clockrate = pt.get("clockrate", "")
143 sdp_lines.append(f"a=rtpmap:{pt_id} {name}/{clockrate}") 145
146 # Check if "channels" is in pt and append it to the line
147 channels = pt.get("channels")
148 if channels:
149 sdp_lines.append(f"a=rtpmap:{pt_id} {name}/{clockrate}/{channels}")
150 else:
151 sdp_lines.append(f"a=rtpmap:{pt_id} {name}/{clockrate}")
144 152
145 if "ptime" in pt: 153 if "ptime" in pt:
146 sdp_lines.append(f"a=ptime:{pt['ptime']}") 154 sdp_lines.append(f"a=ptime:{pt['ptime']}")
147 155
148 if "parameters" in pt: 156 if "parameters" in pt:
381 389
382 elif attribute == "fingerprint": 390 elif attribute == "fingerprint":
383 algorithm, fingerprint = parts[0], parts[1] 391 algorithm, fingerprint = parts[0], parts[1]
384 fingerprint_data = {"hash": algorithm, "fingerprint": fingerprint} 392 fingerprint_data = {"hash": algorithm, "fingerprint": fingerprint}
385 if transport_data is not None: 393 if transport_data is not None:
386 transport_data["fingerprint"] = fingerprint_data 394 transport_data.setdefault("fingerprint", {}).update(
395 fingerprint_data
396 )
387 elif attribute == "setup": 397 elif attribute == "setup":
388 assert transport_data is not None 398 assert transport_data is not None
389 setup = parts[0] 399 setup = parts[0]
390 transport_data.setdefault("fingerprint", {})["setup"] = setup 400 transport_data.setdefault("fingerprint", {})["setup"] = setup
391 401
427 # to handle session data at media level)) 437 # to handle session data at media level))
428 for key in [k for k in call_data if k.startswith("_")]: 438 for key in [k for k in call_data if k.startswith("_")]:
429 log.debug(f"cleaning remaining private data {key!r}") 439 log.debug(f"cleaning remaining private data {key!r}")
430 del call_data[key] 440 del call_data[key]
431 441
442 # FIXME: is this really useful?
432 # ICE candidates may only be specified for the first media, this 443 # ICE candidates may only be specified for the first media, this
433 # duplicate the candidate for the other in this case 444 # duplicate the candidate for the other in this case
434 all_media = {k:v for k,v in call_data.items() if k in ("audio", "video")} 445 all_media = {k:v for k,v in call_data.items() if k in ("audio", "video")}
435 if len(all_media) > 1 and not all( 446 if len(all_media) > 1 and not all(
436 "candidates" in c["transport_data"] for c in all_media.values() 447 "candidates" in c["transport_data"] for c in all_media.values()