aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2015-12-05 13:30:46 +0100
committerHarald Welte <laforge@gnumonks.org>2015-12-05 13:51:43 +0100
commit5b283e8942d845c5c6919c78a9ccf57c6c46da7c (patch)
tree4b50a35ff9c7c52ff30d2605871b1886d18dd54a
parenta5fce601217645242ac057ef1a32f7e830c0b50e (diff)
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
-rw-r--r--gsm_call_fsm.py72
-rwxr-xr-xmncc_test.py6
2 files changed, 68 insertions, 10 deletions
diff --git a/gsm_call_fsm.py b/gsm_call_fsm.py
index e993de1..1522f57 100644
--- a/gsm_call_fsm.py
+++ b/gsm_call_fsm.py
@@ -18,6 +18,53 @@ from mncc_sock import mncc_msg, mncc_number, mncc_rtp_msg, mncc_bridge_msg
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):
last_callref = 0
@@ -37,11 +84,20 @@ class GsmCallFsm(pykka.ThreadingActor):
called = mncc_number(self.called))
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):
msg_in = e.args[0]
- for i in msg_in.bearer_cap.speech_ver:
- print 'SPV: 0x%02x' % i,
- msg = mncc_msg(msg_type = mncc.MNCC_LCHAN_MODIFY, callref = msg_in.callref, lchan_mode = 1)
+ codec = self.find_matching_codec(msg_in.bearer_cap.speech_ver)
+ print 'CALL-CONF.ind(selected codec = %s)' % codec
+ # 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})
def _onmncc_setup_cnf(self, e):
@@ -65,13 +121,14 @@ class GsmCallFsm(pykka.ThreadingActor):
if e.event != 'startup':
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__()
self.mncc_ref = mncc_ref;
self.callref = self._get_next_callref()
self.ctrl_ref = ctrl_ref
self.rtp_bridge = rtp_bridge
self.rtp = None
+ self.codecs_permitted = codecs_permitted
self.fsm = Fysom(initial = 'NULL',
events = [
# MT call setup
@@ -221,14 +278,15 @@ class GsmCallFsm(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__()
self.mncc_act = mncc_act
self.rtp_bridge = rtp_bridge
+ self.codecs_permitted = codecs_permitted
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'
- 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_b = self.call_b.ask({'type':'get_callref'})
self.state_a = self_state_b = 'NULL'
diff --git a/mncc_test.py b/mncc_test.py
index 2427d53..f9d102b 100755
--- a/mncc_test.py
+++ b/mncc_test.py
@@ -9,7 +9,7 @@
# 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 thread import start_new_thread
import pykka
@@ -54,8 +54,8 @@ mncc_act = MnccActor.start(mncc_sock)
start_new_thread(mncc_rx_thread, (mncc_sock,))
# convenience wrapper
-def connect_call(msisdn_a, msisdn_b):
- call_conn = GsmCallConnector.start(mncc_act).proxy()
+def connect_call(msisdn_a, msisdn_b, rtp_bridge = True, codecs = GSM48.AllCodecs):
+ call_conn = GsmCallConnector.start(mncc_act, rtp_bridge, codecs).proxy()
call_conn.start_call_ab(msisdn_a, msisdn_b)
return call_conn