gcm_call_fsm: Allow user to specify which codec(s) are to be used
The user can submit a list of permitted codecs for a GsmCallFsm or GsmCallConnector. This list is ordered by priority (highest first), and the first matching codec is chosen. TODO: Proper error handling in case no matching codec is found
This commit is contained in:
parent
a5fce60121
commit
5b283e8942
|
@ -18,6 +18,53 @@ from mncc_sock import mncc_msg, mncc_number, mncc_rtp_msg, mncc_bridge_msg
|
||||||
|
|
||||||
Uint32Array2 = mncc.uint32_t * 2
|
Uint32Array2 = mncc.uint32_t * 2
|
||||||
|
|
||||||
|
class GSM48:
|
||||||
|
class BCAP_SV(object):
|
||||||
|
# GSM 04.08 bearer capability speech version
|
||||||
|
FR = 0
|
||||||
|
HR = 1
|
||||||
|
EFR = 2
|
||||||
|
AMR_F = 4
|
||||||
|
AMR_H = 5
|
||||||
|
|
||||||
|
def __init__(self, codec):
|
||||||
|
self.codec = codec;
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.codec == GSM48.BCAP_SV.FR:
|
||||||
|
return 'FR'
|
||||||
|
elif self.codec == GSM48.BCAP_SV.HR:
|
||||||
|
return 'HR'
|
||||||
|
elif self.codec == GSM48.BCAP_SV.EFR:
|
||||||
|
return 'EFR'
|
||||||
|
elif self.codec == GSM48.BCAP_SV.AMR_F:
|
||||||
|
return 'AMR-FR'
|
||||||
|
elif self.codec == GSM48.BCAP_SV.AMR_H:
|
||||||
|
return 'AMR-HR'
|
||||||
|
else:
|
||||||
|
return 'Unknown'
|
||||||
|
|
||||||
|
def to_lchan_mode(self):
|
||||||
|
if self.codec == GSM48.BCAP_SV.FR:
|
||||||
|
return GSM48.ChanMode.SPEECH_V1
|
||||||
|
elif self.codec == GSM48.BCAP_SV.HR:
|
||||||
|
return GSM48.ChanMode.SPEECH_V1
|
||||||
|
elif self.codec == GSM48.BCAP_SV.EFR:
|
||||||
|
return GSM48.ChanMode.SPEECH_EFR
|
||||||
|
elif self.codec == GSM48.BCAP_SV.AMR_F:
|
||||||
|
return GSM48.ChanMode.SPEECH_AMR
|
||||||
|
elif self.codec == GSM48.BCAP_SV.AMR_H:
|
||||||
|
return GSM48.ChanMode.SPEECH_AMR
|
||||||
|
|
||||||
|
AllCodecs = (BCAP_SV.FR, BCAP_SV.HR, BCAP_SV.EFR, BCAP_SV.AMR_F, BCAP_SV.AMR_H)
|
||||||
|
|
||||||
|
class ChanMode:
|
||||||
|
# GSM 04.08 Channel Mode
|
||||||
|
CMODE_SIGN = 0x00
|
||||||
|
SPEECH_V1 = 0x01
|
||||||
|
SPEECH_EFR = 0x21
|
||||||
|
SPEECH_AMR = 0x41
|
||||||
|
|
||||||
class GsmCallFsm(pykka.ThreadingActor):
|
class GsmCallFsm(pykka.ThreadingActor):
|
||||||
last_callref = 0
|
last_callref = 0
|
||||||
|
|
||||||
|
@ -37,11 +84,20 @@ class GsmCallFsm(pykka.ThreadingActor):
|
||||||
called = mncc_number(self.called))
|
called = mncc_number(self.called))
|
||||||
self.mncc_ref.tell({'type': 'send', 'msg': msg})
|
self.mncc_ref.tell({'type': 'send', 'msg': msg})
|
||||||
|
|
||||||
|
def find_matching_codec(self, ms_codecs):
|
||||||
|
# find common denominator of permitted codecs and MS codecs
|
||||||
|
for i in self.codecs_permitted:
|
||||||
|
if i in ms_codecs:
|
||||||
|
return GSM48.BCAP_SV(i)
|
||||||
|
return None
|
||||||
|
|
||||||
def _onmncc_call_conf_ind(self, e):
|
def _onmncc_call_conf_ind(self, e):
|
||||||
msg_in = e.args[0]
|
msg_in = e.args[0]
|
||||||
for i in msg_in.bearer_cap.speech_ver:
|
codec = self.find_matching_codec(msg_in.bearer_cap.speech_ver)
|
||||||
print 'SPV: 0x%02x' % i,
|
print 'CALL-CONF.ind(selected codec = %s)' % codec
|
||||||
msg = mncc_msg(msg_type = mncc.MNCC_LCHAN_MODIFY, callref = msg_in.callref, lchan_mode = 1)
|
# select the according lchan_mode
|
||||||
|
lchan_mode = codec.to_lchan_mode()
|
||||||
|
msg = mncc_msg(msg_type = mncc.MNCC_LCHAN_MODIFY, callref = msg_in.callref, lchan_mode = lchan_mode)
|
||||||
self.mncc_ref.tell({'type': 'send', 'msg': msg})
|
self.mncc_ref.tell({'type': 'send', 'msg': msg})
|
||||||
|
|
||||||
def _onmncc_setup_cnf(self, e):
|
def _onmncc_setup_cnf(self, e):
|
||||||
|
@ -65,13 +121,14 @@ class GsmCallFsm(pykka.ThreadingActor):
|
||||||
if e.event != 'startup':
|
if e.event != 'startup':
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
def __init__(self, mncc_ref, ctrl_ref = None, rtp_bridge = True):
|
def __init__(self, mncc_ref, ctrl_ref = None, rtp_bridge = True, codecs_permitted = GSM48.AllCodecs):
|
||||||
super(GsmCallFsm, self).__init__()
|
super(GsmCallFsm, self).__init__()
|
||||||
self.mncc_ref = mncc_ref;
|
self.mncc_ref = mncc_ref;
|
||||||
self.callref = self._get_next_callref()
|
self.callref = self._get_next_callref()
|
||||||
self.ctrl_ref = ctrl_ref
|
self.ctrl_ref = ctrl_ref
|
||||||
self.rtp_bridge = rtp_bridge
|
self.rtp_bridge = rtp_bridge
|
||||||
self.rtp = None
|
self.rtp = None
|
||||||
|
self.codecs_permitted = codecs_permitted
|
||||||
self.fsm = Fysom(initial = 'NULL',
|
self.fsm = Fysom(initial = 'NULL',
|
||||||
events = [
|
events = [
|
||||||
# MT call setup
|
# MT call setup
|
||||||
|
@ -221,14 +278,15 @@ class GsmCallFsm(pykka.ThreadingActor):
|
||||||
|
|
||||||
|
|
||||||
class GsmCallConnector(pykka.ThreadingActor):
|
class GsmCallConnector(pykka.ThreadingActor):
|
||||||
def __init__(self, mncc_act, rtp_bridge = True):
|
def __init__(self, mncc_act, rtp_bridge = True, codecs_permitted = GSM48.AllCodecs):
|
||||||
super(GsmCallConnector, self).__init__()
|
super(GsmCallConnector, self).__init__()
|
||||||
self.mncc_act = mncc_act
|
self.mncc_act = mncc_act
|
||||||
self.rtp_bridge = rtp_bridge
|
self.rtp_bridge = rtp_bridge
|
||||||
|
self.codecs_permitted = codecs_permitted
|
||||||
print 'Starting Call A actor'
|
print 'Starting Call A actor'
|
||||||
self.call_a = GsmCallFsm.start(self.mncc_act, self.actor_ref, self.rtp_bridge)
|
self.call_a = GsmCallFsm.start(self.mncc_act, self.actor_ref, self.rtp_bridge, self.codecs_permitted)
|
||||||
print 'Starting Call B actor'
|
print 'Starting Call B actor'
|
||||||
self.call_b = GsmCallFsm.start(self.mncc_act, self.actor_ref, self.rtp_bridge)
|
self.call_b = GsmCallFsm.start(self.mncc_act, self.actor_ref, self.rtp_bridge, self.codecs_permitted)
|
||||||
self.callref_a = self.call_a.ask({'type':'get_callref'})
|
self.callref_a = self.call_a.ask({'type':'get_callref'})
|
||||||
self.callref_b = self.call_b.ask({'type':'get_callref'})
|
self.callref_b = self.call_b.ask({'type':'get_callref'})
|
||||||
self.state_a = self_state_b = 'NULL'
|
self.state_a = self_state_b = 'NULL'
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
# option, any later version.
|
# option, any later version.
|
||||||
|
|
||||||
|
|
||||||
from gsm_call_fsm import GsmCallFsm, GsmCallConnector
|
from gsm_call_fsm import GsmCallFsm, GsmCallConnector, GSM48
|
||||||
from mncc_sock import MnccSocket
|
from mncc_sock import MnccSocket
|
||||||
from thread import start_new_thread
|
from thread import start_new_thread
|
||||||
import pykka
|
import pykka
|
||||||
|
@ -54,8 +54,8 @@ mncc_act = MnccActor.start(mncc_sock)
|
||||||
start_new_thread(mncc_rx_thread, (mncc_sock,))
|
start_new_thread(mncc_rx_thread, (mncc_sock,))
|
||||||
|
|
||||||
# convenience wrapper
|
# convenience wrapper
|
||||||
def connect_call(msisdn_a, msisdn_b):
|
def connect_call(msisdn_a, msisdn_b, rtp_bridge = True, codecs = GSM48.AllCodecs):
|
||||||
call_conn = GsmCallConnector.start(mncc_act).proxy()
|
call_conn = GsmCallConnector.start(mncc_act, rtp_bridge, codecs).proxy()
|
||||||
call_conn.start_call_ab(msisdn_a, msisdn_b)
|
call_conn.start_call_ab(msisdn_a, msisdn_b)
|
||||||
return call_conn
|
return call_conn
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue