Skip to content

Commit

Permalink
Add some basic support for offering ice-lite using rtpproxy's
Browse files Browse the repository at this point in the history
module with the same name.
  • Loading branch information
sobomax committed Jul 11, 2024
1 parent cac83b1 commit 98f9e25
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 0 deletions.
7 changes: 7 additions & 0 deletions sippy/Rtp_proxy/Session/side.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
from sippy.Rtp_proxy.Session.update import update_params
from sippy.Rtp_proxy.Session.subcommand import subcommand_dtls, \
subcommand_dedtls, DTLS_TRANSPORTS
from sippy.Rtp_proxy.Session.subcommand_ice import subcommand_ice, \
subcommand_deice

try:
strtypes = (str, unicode)
Expand All @@ -49,6 +51,7 @@ class _rtpps_side(object):
oh_remote = None
repacketize = None
gateway_dtls = 'pass'
deice = False
soft_repacketize = False
after_sdp_change = None
needs_new_port = False
Expand Down Expand Up @@ -189,8 +192,12 @@ def _on_sdp_change(self, rtpps, sdp_body, result_callback, en_excpt):
up.index = si
if otherside.gateway_dtls == 'dtls':
up.subcommands.append(subcommand_dtls())
if otherside.deice:
up.subcommands.append(subcommand_ice())
if self.gateway_dtls == 'dtls' and sect.m_header.transport in DTLS_TRANSPORTS:
up.subcommands.append(subcommand_dedtls(sdp_bc, sect))
if self.deice:
up.subcommands.append(subcommand_deice(sdp_bc, sect))
up.result_callback = partial(self._sdp_change_finish, sdp_body, sect, sects, result_callback)
self.update(up)
return
Expand Down
98 changes: 98 additions & 0 deletions sippy/Rtp_proxy/Session/subcommand_ice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Copyright (c) 2024 Sippy Software, Inc. All rights reserved.
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation and/or
# other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from urllib.parse import unquote

from sippy.Exceptions.RtpProxyError import RtpProxyError
from sippy.Exceptions.SipParseError import SdpParseError
from sippy.Rtp_proxy.Session.subcommand import subcommand

ICE_ATTRS = ('ice-ufrag', 'ice-pwd')
ICE_ATTRS_RM = ICE_ATTRS + ('candidate', 'ice-options', 'rtcp-mux',
'end-of-candidates', 'ice-options')
ICE_ATTRS_RM += ('extmap', 'extmap-allow-mixed', 'msid',
'msid-semantic', 'mid', 'group', 'rtcp-fb')

class subcommand_ice(subcommand):
name = 'ICE'
ice_lufrag: str
ice_lpwd: str
ice_candidates: list
i_mod = subcommand.mod_2_i_mod['ice_lite']

def __init__(self):
super().__init__(f'{self.i_mod} S')

def handle_results(self, results, ur):
if ex:=super().handle_results(results, ur): return ex
ice_res = results[0].split()
if len(ice_res) < 3:
return RtpProxyError(f'RTPProxy errored: {self.name}: {results=}')
self.ice_lufrag, self.ice_lpwd = ice_res[:2]
self.ice_candidates = [unquote(r[2:]) for r in ice_res[2:]]
ur.sdp_sect_fins.append(self.sdp_sect_fin)
return None

def sdp_sect_fin(self, sdp_bc, sect):
self._sdp_attrs_rm(sdp_bc, sect, ICE_ATTRS_RM)
sect.addHeader('a', 'rtcp-mux')
sect.addHeader('a', 'ice-lite')
sect.addHeader('a', F'ice-ufrag:{self.ice_lufrag}')
sect.addHeader('a', F'ice-pwd:{self.ice_lpwd}')
for cand in self.ice_candidates:
sect.addHeader('a', F'candidate:{cand}')
sect.m_header.transport = 'UDP/TLS/RTP/SAVP'

class subcommand_deice(subcommand):
name = 'deICE'
i_mod = subcommand.mod_2_i_mod['ice_lite']

def __init__(self, sdp_bc, sect):
adict = dict([(x.name, x.value) for x in sect.a_headers
if x.name in ICE_ATTRS])
for rattr in ('ice-ufrag', 'ice-pwd'):
if rattr not in adict:
raise SdpParseError(f'Missing ICE {rattr} parameter')
super().__init__(f'{self.i_mod} A {adict["ice-ufrag"]} {adict["ice-pwd"]}')
for cand in (x.value for x in sect.a_headers
if x.name == 'candidate'):
pts = cand.split(None, 4)
if pts[2].lower() != 'udp':
continue
self.append(f'{self.i_mod} C {cand}')

def handle_results(self, results, ur):
if ex:=super().handle_results(results, ur): return ex
if any(True for r in results if r != '0'):
return RtpProxyError(f'RTPProxy errored: {self.name}: {results=}')
ur.sdp_sect_fins.append(self.sdp_sect_fin)
return None

def sdp_sect_fin(self, sdp_bc, sect):
self._sdp_attrs_rm(sdp_bc, sect, ICE_ATTRS_RM)
for a in [a for a in sect.a_headers if a.name == 'ssrc']:
ssrc, attr = a.value.split(None, 1)
if not attr.lower().startswith('msid:'): continue
sect.a_headers.remove(a)

0 comments on commit 98f9e25

Please sign in to comment.