Examples
Here we will go over a few basic phone setups.
Setup
PyVoIP uses a VoIPPhone class to receive and initiate phone calls. The settings for our phone are passed via the VoIPPhoneParameter dataclass. When a call is received, a new instance of a VoIPCall is initialized. You can overwrite this class in initialization of VoIPPhone.
In this example, we are importing CredentialsManager, VoIPPhone, VoIPPhoneParameter, VoIPCall, and InvalidStateError. CredentialsManager stores and retreives passwords for authentication with registrars. VoIPPhone is the main class for our softphone. VoIPPhoneParameter is the settings for our VoIPPhone. VoIPCall will be used to create our custom answering class. An InvalidStateError is thrown when you try to perform an impossible command. For example, denying the call when the phone is already answered, answering when it’s already answered, etc.
The following will create a phone that answers and automatically hangs up:
from pyVoIP.credentials import CredentialsManager
from pyVoIP.VoIP.call import VoIPCall
from pyVoIP.VoIP.error import InvalidStateError
from pyVoIP.VoIP.phone import VoIPPhone, VoIPPhoneParamter
class Call(VoIPCall):
def ringing(self, invite_request):
try:
self.answer()
self.hangup()
except InvalidStateError:
pass
if __name__ == "__main__":
cm = CredentialsManager()
cm.add(<SIP server username>, <SIP server password>)
params = VoIPPhoneParamter(<SIP server IP>, <SIP server port>, <SIP server user>, cm, bind_ip=<Your computers local IP>, call_class=Call)
phone = VoIPPhone(params)
phone.start()
input('Press enter to disable the phone')
phone.stop()
Announcement Board
Let’s say you want to make a phone that when you call it, it plays an announcement message, then hangs up. We can accomplish this with the builtin libraries wave, audioop, time, and by importing CallState.
from pyVoIP.credentials import CredentialsManager
from pyVoIP.VoIP.call import VoIPCall
from pyVoIP.VoIP.error import InvalidStateError
from pyVoIP.VoIP.phone import VoIPPhone, VoIPPhoneParamter
import time
import wave
class Call(VoIPCall):
def ringing(self, invite_request):
try:
f = wave.open('announcment.wav', 'rb')
frames = f.getnframes()
data = f.readframes(frames)
f.close()
call.answer()
call.write_audio(data) # This writes the audio data to the transmit buffer, this must be bytes.
stop = time.time() + (frames / 8000) # frames/8000 is the length of the audio in seconds. 8000 is the hertz of PCMU.
while time.time() <= stop and call.state == CallState.ANSWERED:
time.sleep(0.1)
call.hangup()
except InvalidStateError:
pass
except:
call.hangup()
if __name__ == "__main__":
cm = CredentialsManager()
cm.add(<SIP server username>, <SIP server password>)
params = VoIPPhoneParamter(<SIP server IP>, <SIP server port>, <SIP server user>, cm, bind_ip=<Your computer's local IP>, call_class=Call)
phone = VoIPPhone(params)
phone.start()
input('Press enter to disable the phone')
phone.stop()
Something important to note is our wait function. We are currently using:
stop = time.time() + (frames / 8000) # The number of frames/8000 is the length of the audio in seconds.
while time.time() <= stop and call.state == CallState.ANSWERED:
time.sleep(0.1)
This could be replaced with time.sleep(frames / 8000)
. However, doing so will not cause the thread to automatically close if the user hangs up, or if VoIPPhone().stop()
is called. Using the while loop method will fix this issue. The time.sleep(0.1)
inside the while loop is also important. Supplementing time.sleep(0.1)
for pass
will cause your CPU to ramp up while running the loop, making the RTP (audio being sent out and received) lag. This can make the voice audibly slow or choppy.
Important Note: Audio must be 8 bit, 8000Hz, and Mono/1 channel. You can accomplish this in a free program called Audacity. To make an audio recording Mono, go to Tracks > Mix > Mix Stereo Down to Mono. To make an audio recording 8000 Hz, go to Tracks > Resample… and select 8000, then ensure that your ‘Project Rate’ in the bottom left is also set to 8000. To make an audio recording 8 bit, go to File > Export > Export as WAV, then change ‘Save as type:’ to ‘Other uncompressed files’, then set ‘Header:’ to ‘WAV (Microsoft)’, then set the ‘Encoding:’ to ‘Unsigned 8-bit PCM’
Call State Handling
We can use the following code to handle various states for calls:
from pyVoIP.credentials import CredentialsManager
from pyVoIP.VoIP.call import VoIPCall
from pyVoIP.VoIP.error import InvalidStateError
from pyVoIP.VoIP.phone import VoIPPhone, VoIPPhoneParamter
import time
import wave
class Call(VoIPCall):
def progress(self, request):
print('Progress')
super().progress(request)
def busy(self, request):
print('Call ended - callee is busy')
super().busy(request)
def answered(self, request):
print('Answered')
super().answered()
def bye(self):
print('Bye')
super().bye()
if __name__ == '__main__':
cm = CredentialsManager()
cm.add(<SIP server username>, <SIP server password>)
params = VoIPPhoneParamter(<SIP server IP>, <SIP server port>, <SIP server user>, cm, bind_ip=<Your computer's local IP>, call_class=Call)
phone = VoIPPhone(params)
phone.start()
phone.call(<Phone Number>)
input('Press enter to disable the phone\n')
phone.stop()