Hmmm, no one?
Is there some sort of forum somewhere where to post things like these??
Thank you :)
Cheers,
Oli
Gesendet: Freitag, 01. April 2016 um 21:38 Uhr
Von: "Oli Kah" <mj_fn@xxxxxx>
An: pjsip@xxxxxxxxxxxxxxx
Betreff: Sound issues: strange samplerates?
Von: "Oli Kah" <mj_fn@xxxxxx>
An: pjsip@xxxxxxxxxxxxxxx
Betreff: Sound issues: strange samplerates?
Hi there,
I am new to this list and want to say "Hello" to everyone listening :)
My issue using pjsib is rather strange. When calling someone (I tested it using two of my own telephone numbers) I get normal audio first during early call stages and then when the call is finally confirmed the audio rate suddenly is half or so. The voice then sounds monsterish and the remote site is no longer audible via speakers. This happens every time - reproducible!
What I did:
I have successfully compiled pjsua (release build) for Python using Visual Studio 2015 Community linking pjsua to Python 2.6 lib, 32bit running everything on Windows 8.1 Pro 64bit. So far so good.
These values were used for my config_site.h:
#define PJMEDIA_CONF_USE_SWITCH_BOARD 1
#define PJMEDIA_SOUND_BUFFER_COUNT 8
#define PJMEDIA_SND_DEFAULT_REC_LATENCY 50
#define PJMEDIA_SND_DEFAULT_PLAY_LATENCY 50
#define PJMEDIA_SOUND_BUFFER_COUNT 8
#define PJMEDIA_SND_DEFAULT_REC_LATENCY 50
#define PJMEDIA_SND_DEFAULT_PLAY_LATENCY 50
The attached python code shall define a simple SIPPhone that will be used in my larger scale application. It's unfinished but I ran into these unresolvable audio problems and hope that you might help me. In its current form the SIPPhone is totally useless!
During testing I have been using the onboard audio card of my mainboard with some Bose speakers and a Samson UB-1 USB microphone which is connected to the Windows-PC using an USB 2.0 port. For the "remote" side I use a dedicated VOIP telephone from Grandstream.
The test code in my attached file is then run using Pycharm + Python 2.6 runtimes. For testing you must replace the numbers + credientials by valid values on your side. Also note that you might have to specify another domain (mine here is "fritz.box").
The called Grandstream rings. Taking up the phone then starts the call confirmation which is going unexpectedly slow. It takes quite some time (between roughly 3-10 seconds) before the "confirmed" status is reached although the audio starts working before that. The scenario is running on my LAN where the SIP server (a FritzBox) is also running here. So quite strange why this takes so long?
Right after phone pickup the audio can't be heard at all (me constantly talking after phone pickup!) - in both directions! Then after a short time (1-4 sec) the voice can be heard normally and understandably as expected in both directions. But this only works until the "confirmed" status is reached for the call. When it is reached there is no longer any sound coming from the PC speaker and the voice heard on the Grandstream is monsterish (half sample rate?!). The bidirectional audio is lost and the remaining audio is really bad.
The observed behavior happens every time. Just the timing is different and the time from starting the program to hearing the monsterish voice is between 3 and 10 seconds. It is also rather strange that this time span can be so huge!
What happens here?! In its current form the SIPPhone is pretty unusable. I hope you have ideas how to fix that :) I have experimented with many of the MediaConfig parameters with no success.
Thank you!
Cheers,
Oliver
import pjsua as pj import socket import threading class SIPCallback(): def __init__(self): pass def register_ok(self, phone): pass def register_issue(self, phone, status): pass def media_active(self, phone, uri): pass def media_inactive(self, phone, uri): pass def call_outgoing(self, phone, uri): pass def callee_busy(self, phone, uri): pass def call_incoming(self, phone, uri): pass def call_while_busy(self, phone, call): pass def call_established(self, phone, uri): pass def call_on_hold(self, phone, uri): pass def call_end(self, phone, uri): pass class SIPPhone: STATUS_NORMAL = 0 STATUS_AWAY = 1 class SoundCardInfo: def __init__(self, name, clock_rate, id): self.name = name self.clock_rate = clock_rate self.id = id def __str__(self): return "name:{0}; clock:{1}; id:{2}".format(self.name, self.clock_rate, self.id) class CallCB(pj.CallCallback): def __init__(self, phone): self.phone = phone def on_state(self): try: if self.phone.call is None: self.phone.call = self.call info = self.call.info() if info.state == pj.CallState.CALLING: self.phone.callback.call_outgoing(self.phone, info.remote_uri) elif info.state == pj.CallState.CONFIRMED: self.phone.callback.call_established(self.phone, info.remote_uri) elif info.state == pj.CallState.DISCONNECTED: self.phone.callback.call_end(self.phone, info.remote_uri) self.phone.call = None except: pass # Notification when call's media state has changed. def on_media_state(self): try: info = self.call.info() if info.media_state == pj.MediaState.ACTIVE: call_slot = info.conf_slot self.phone.pj_lib.conf_connect(call_slot, 0) self.phone.pj_lib.conf_connect(0, call_slot) self.phone.callback.media_active(self.phone, info.remote_uri) else: self.phone.callback.media_inactive(self.phone, info.remote_uri) except: pass class AccountCB(pj.AccountCallback): def __init__(self, phone): self.phone = phone def on_incoming_call(self, call): try: info = call.info() if self.phone.call is None: self.phone.call = call call.set_callback(SIPPhone.CallCB(self.phone)) call.answer(180) self.phone.callback.call_incoming(self.phone, info.remote_uri) else: self.phone.callback.call_while_busy(self.phone, call) except: pass # Notification when call's media state has changed. def on_reg_state(self): try: info = self.account.info() if info.reg_status == 200: self.phone.reg_ok = True self.phone.callback.register_ok(self.phone) else: self.phone.callback.register_issue(self.phone, info.reg_status) except: pass def __init__(self, local_ip=None, local_port=5060, callback=None): if local_ip is None: local_ip = self.getIPs()[0] self.local_ip = local_ip self.local_port = local_port self.domain = None self.user_name = None self.password = None self.display_name = None self.registrar = None self.proxy = None if isinstance(callback, SIPCallback): self.callback = callback else: self.callback = SIPCallback() # status - 2DO self.status = SIPPhone.STATUS_NORMAL # sound self.sound_input = None self.sound_output = None # prepare lib self.pj_lib = pj.Lib() self.pj_lib.init() self.transport = None self.account = None self.call = None self.reg_ok = False def __del__(self): self.stop() self.callback = None def start(self): self.stop() self.pj_lib = pj.Lib() media_config = pj.MediaConfig() media_config.clock_rate = 22500 self.pj_lib.init(media_cfg=media_config) try: self.pj_lib.set_snd_dev( self.sound_input.id, self.sound_output.id ) except: pass transport_config = pj.TransportConfig( port=self.local_port, bound_addr=self.local_ip ) self.transport = self.pj_lib.create_transport( pj.TransportType.UDP, transport_config ) # account account_config = pj.AccountConfig( domain=self.domain, username=self.user_name, password=self.password, display=self.display_name, registrar=self.registrar, proxy=self.proxy ) self.account = self.pj_lib.create_account( acc_config=account_config, cb=SIPPhone.AccountCB(self) ) self.pj_lib.start() def stop(self): try: if self.call is not None: self.call.hangup() self.call = None except: pass try: self.transport.close() self.transport = None except: pass try: self.pj_lib.destroy() self.pj_lib = None self.reg_ok = False except: pass def getSoundCardInfoCount(self): return len(self.pj_lib.enum_snd_dev()) def getSoundCardInfo(self, index): try: sound_device_info = self.pj_lib.enum_snd_dev()[index] return SIPPhone.SoundCardInfo(sound_device_info.name, sound_device_info.default_clock_rate, index) except: return None def getSoundInputs(self, find=None): result = [] index = 0 for sound_device_info in self.pj_lib.enum_snd_dev(): if sound_device_info.input_channels > 0: if find is not None: ok = (sound_device_info.name.find(find) > -1) else: ok = True if ok: result.append(self.getSoundCardInfo(index)) index += 1 return result def getSoundOutputs(self, find=None): result = [] index = 0 sound_devices_info = self.pj_lib.enum_snd_dev() for sound_device_info in sound_devices_info: if sound_device_info.output_channels > 0: if find is not None: ok = (sound_device_info.name.find(find) > -1) else: ok = True if ok: result.append(self.getSoundCardInfo(index)) index += 1 return result def setSoundIO(self, input=None, output=None): current_input, current_output = self.pj_lib.get_snd_dev() if input is None: input = self.getSoundCardInfo(current_input) if output is None: output = self.getSoundCardInfo(current_output) self.sound_input = input self.sound_output = output self.pj_lib.set_snd_dev( input.id, output.id ) def setSoundIOByName(self, input_name=None, output_name=None): seek_input = (input_name is not None) seek_output = (output_name is not None) input, output = self.pj_lib.get_snd_dev() index = 0 for sound_device_info in self.pj_lib.enum_snd_dev(): if seek_input and sound_device_info.input_channels > 0: if sound_device_info.name.find(input_name) > 0: input = index seek_input = False elif seek_output and sound_device_info.output_channels > 0: if sound_device_info.name.find(output_name) > 0: output = index seek_output = False else: break index += 1 self.sound_input = self.getSoundCardInfo(input) self.sound_output = self.getSoundCardInfo(output) self.pj_lib.set_snd_dev( input, output ) def getSoundIO(self): input, output = self.pj_lib.get_snd_dev() info = self.pj_lib.enum_snd_dev() return SIPPhone.SoundCardInfo(info[input].name, input), SIPPhone.SoundCardInfo(info[output].name, output) def setStatus(self, status=STATUS_NORMAL): self.status = status def getStatus(self): return self.status def getIPs(self): addrs = socket.gethostbyname_ex(socket.gethostname())[2] addrs.sort(key=lambda a: [int(b) for b in a.split(u".", 4)]) return addrs def setCallback(self, callback): if isinstance(callback, SIPCallback): self.callback = callback else: self.callback = SIPCallback() def setTransport(self, local_ip=None, local_port=5060): if local_ip is None: local_ip = self.getIPs()[0] self.local_ip = local_ip self.local_port = local_port def setUser(self, domain="fritz.box", user_name="", password="", display_name="", registrar="", proxy=""): self.domain = domain self.user_name = user_name self.password = password self.display_name = display_name self.registrar = registrar self.proxy = proxy def makeCall(self, user=None): uri = pj.SIPUri() uri.scheme = "sip" uri.user = user uri.host = self.domain uri = uri.encode() self.account.make_call(uri, SIPPhone.CallCB(self)) def acceptCall(self): if self.call is not None: self.call.answer(200) def answerBusy(self): if self.call is not None: self.call.answer(486, "Busy") def blockCall(self): if self.call is not None: self.call.answer(403, "Forbidden") def hangup(self): if self.call is not None: self.call.hangup() def makeURI(self, user, host=None, port=0, transport="", scheme="sip"): sip_uri = pj.SIPUri() sip_uri.user = user if host is not None: sip_uri.host = host else: sip_uri.host = self.domain sip_uri.port = port sip_uri.transport = transport sip_uri.scheme = scheme uri = sip_uri.encode() return uri def getURIComponents(self, uri): user = None host = "" port = 0 transport = "" scheme = "" try: sip_uri = pj.SIPUri() sip_uri.decode(uri) user = sip_uri.user host = sip_uri.host port = sip_uri.port transport = sip_uri.transport scheme = sip_uri.scheme except: pass return user, host, port, transport, scheme def getURIUser(self, uri): user = None try: sip_uri = pj.SIPUri() sip_uri.decode(uri) user = sip_uri.user except: pass return user # -------------------------------------------------------------------- class MyCallback(SIPCallback): def __init__(self): pass def register_ok(self, phone): print "REGISTERED!" def register_issue(self, phone, status): print "REGISTER ISSUE!" def media_active(self, phone, uri): print "MEDIA ACTIVE!" def media_inactive(self, phone, uri): print "MEDIA INACTIVE!" def call_outgoing(self, phone, uri): print "CALL OUT!" def callee_busy(self, phone, uri): print "CALLEE BUSY!" def call_incoming(self, phone, uri): print "CALL!! -> " + phone.getURIUser(uri) phone.acceptCall() threading._sleep(20) phone.hangup() def call_while_busy(self, phone, call): print "BUSY!" def call_established(self, phone, uri): print "CALL ESTABLISHED!" def call_on_hold(self, phone, uri): print "CALL ON HOLD!" def call_end(self, phone, uri): print "CALL END!" # TEST if __name__ == '__main__': phone = SIPPhone(callback=MyCallback()) #input = phone.getSoundInputs("Samson")[0] #output = phone.getSoundOutputs(u"Speaker")[0] #phone.setSoundIO(input, output) phone.setUser(user_name="000", password="XXX") phone.start() phone.makeCall(user="000000000000") while True: threading._sleep(1)
_______________________________________________ Visit our blog: http://blog.pjsip.org pjsip mailing list pjsip@xxxxxxxxxxxxxxx http://lists.pjsip.org/mailman/listinfo/pjsip_lists.pjsip.org