Re: Fw: Sound issues: strange samplerates?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Bill, hi everyone,
 
thanks for answering but I can confirm that the config_site.h settings are NOT the problem. I have now recompiled the lib with nothing in config_site.h.
The result is exactly the same.
 
To illustrate what the called person hears I have attached an mp4 which also shows the progress of the Python app in form of PyCharms debug log.
 
There are important issues that can be seen + heard. Note that I am saying "1-2-3" from the beginning(!) of the video until the very end without ever stopping. Only the receiving side can be heard voice-wise (or not as you will see/hear).
 
1) When picking up the phone the sound CANNOT be heard on the receiving side although I am continously saying "1-2-3". It takes roughly 10(!!) seconds before the other side can hear me at all!!
2) It also takes 10 seconds after pickup before the call confirmation phase is reached. This is extremly slow and totally unexpected.
3) When the other side finally can hear me I sound like a monster... the sample rate seems to be off - I can't otherwise explain the strange sounding voice!
 
The Python code of this mini app is attached to this email once more.
 
I don't think I am doing anything exotic. It does not work as expected though. If I do the same with an app like Phoner (see http://www.phoner.de/download_en.htm) none of these problems occur within the same environment and using the same accounts and phone numbers. So it has to do with pjsip lib somehow.
 
Any ideas?
 
Thank you.
 
Cheers,
Oliver
 
Gesendet: Montag, 04. April 2016 um 20:49 Uhr
Von: "Bill Gardner" <billg@xxxxxxxxxxxx>
An: pjsip@xxxxxxxxxxxxxxx
Betreff: Re: Fw: Sound issues: strange samplerates?
Hi Oliver,

I think you should try a completely default configuration, i.e. use an empty config_site.h file. Your config_site.h params may be causing problems.

Regards,

Bill
 
On 4/4/2016 2:37 PM, Oli Kah wrote:
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?
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
 
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
 
 
 
 
_______________________________________________
Visit our blog: http://blog.pjsip.org

pjsip mailing list
pjsip@xxxxxxxxxxxxxxx
http://lists.pjsip.org/mailman/listinfo/pjsip_lists.pjsip.org

_______________________________________________ Visit our blog: http://blog.pjsip.org pjsip mailing list pjsip@xxxxxxxxxxxxxxx http://lists.pjsip.org/mailman/listinfo/pjsip_lists.pjsip.org

Attachment: 1-2-3.mp4
Description: video/mp4

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="000") # names / pwd have been removed!
	phone.start()
	phone.makeCall(user="000") # number been removed!
	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

[Index of Archives]     [Asterisk Users]     [Asterisk App Development]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [Linux API]
  Powered by Linux