Skip to content

Commit

Permalink
New in v1.0.0:
Browse files Browse the repository at this point in the history
Originate Calls
Added blocking feature to readAudio, this makes readAudio not return until there is data to be returned.  If blocking is off and data is not available, bytes(length) will be returned.
Now properly generating SIP tags to comply with the RFC.
Other bug fixes
  • Loading branch information
tayler6000 committed Jan 25, 2021
1 parent 0015d3b commit 1a3dd57
Show file tree
Hide file tree
Showing 9 changed files with 397 additions and 145 deletions.
12 changes: 12 additions & 0 deletions NOTES
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
New in v1.0.0:
Originate Calls
Added blocking feature to readAudio, this makes readAudio not return until there is data to be returned. If blocking is off and data is not available, bytes(length) will be returned.
Now properly generating SIP tags to comply with the RFC.
Other bug fixes

Currently Known Issues:
BYE request on originated calls causes a 500 Error on Asterisk 13 (other versions not tested). Unsure what causes this, reach out if you have a fix.
Currently does not work with PJSIP (Only tested with Asterisk 18)

Upcoming patches/changes:
Adjust code to be compatible with Asterisk PJSIP.
7 changes: 5 additions & 2 deletions docs/RTP.rst
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,11 @@ The RTPClient is used to send and receive RTP packets and encode/decode the audi
**stop**\ ()
This method is called by :ref:`VoIPCall`.hangup() and :ref:`VoIPCall`.bye(). It stops the recv() and trans() threads. It will also close the bound port. **This should not be called by the** :term:`user`.

**read**\ (length=160)
This method is called by :ref:`VoIPCall`.readAudio(). It reads linear/raw audio data from the received buffer. Returns *length* amount of bytes. Default length is 160 as that is the amount of bytes sent per PCMU/PCMA packet.
**read**\ (length=160, blocking=True)
This method is called by :ref:`VoIPCall`.readAudio(). It reads linear/raw audio data from the received buffer. Returns *length* amount of bytes. Default length is 160 as that is the amount of bytes sent per PCMU/PCMA packet. When *blocking* is set to true, this function will not return until data is available. When *blocking* is set to false and data is not available, this function will return bytes(length).

**write**\ (data)
This method is called by :ref:`VoIPCall`.writeAudio(). It queues the data written to be sent to the :term:`client`.

**recv**\ ()
This method is called by RTPClient.start() and is responsible for receiving and parsing through RTP packets. **This should not be called by the** :term:`user`.
Expand Down
18 changes: 18 additions & 0 deletions docs/SIP.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ The SIPClient class is used to communicate with the PBX/VoIP server. It is resp
**recv**\ ()
This method is called by SIPClient.start() and is responsible for receiving and parsing through SIP requests. **This should not be called by the** :term:`user`.

**parseMessage**\ (message)
This method is called by SIPClient.recv() and is responsible for parsing through SIP responses. **This should not be called by the** :term:`user`.

**start**\ ()
This method is called by :ref:`VoIPPhone`.start(). It starts the REGISTER and recv() threads. It is also what initiates the bound port. **This should not be called by the** :term:`user`.

Expand All @@ -95,6 +98,9 @@ The SIPClient class is used to communicate with the PBX/VoIP server. It is resp
**genCallID**\ ()
This method is called by other 'gen' methods when a new Call-ID header is needed. See `RFC 3261 Section 20.8 <https://tools.ietf.org/html/rfc3261#section-20.8>`_. **This should not be called by the** :term:`user`.

**genTag**\ ()
This method is called by other 'gen' methods when a new tag is needed. See `RFC 3261 Section 8.2.6.2 <https://tools.ietf.org/html/rfc3261#section-8.2.6.2>`_. **This should not be called by the** :term:`user`.

**getSIPVersoinNotSupported**\ ()
This method is called by the recv() thread when it has received a SIP message that is not SIP version 2.0.

Expand All @@ -107,6 +113,12 @@ The SIPClient class is used to communicate with the PBX/VoIP server. It is resp
**genBusy**\ (request)
This method generates a SIP 486 'Busy Here' response. The *request* argument should be a SIP INVITE request.

**genOk**\ (request)
This method generates a SIP 200 'Ok' response. The *request* argument should be a SIP BYE request.

**genInvite**\ (number, sess_id, ms, sendtype, branch, call_id)
This method generates a SIP INVITE request. This is called by SIPClient.invite(). The *number* argument must be the number being called as a string. The *sess_id* argument must be a unique number. The *ms* argument is a dictionary of the media types to be used. Currently only PCMU and telephone-event is supported. The *sendtype* argument must be an instance of :ref:`TransmitType`. The *branch* argument must be a unique string starting with "z9hG4bK". See `RFC 3261 Section 8.1.1.7 <https://tools.ietf.org/html/rfc3261#section-8.1.1.7>`_. The *call_id* argument must be a unique string. See `RFC 3261 Section 8.1.1.4 <https://tools.ietf.org/html/rfc3261#section-8.1.1.4>`_.

**genRinging**\ (request)
This method generates a SIP 180 'Ringing' response. The *request* argument should be a SIP INVITE request.

Expand All @@ -124,6 +136,12 @@ The SIPClient class is used to communicate with the PBX/VoIP server. It is resp
**genBye**\ (request)
This method generates a SIP BYE request. This is used to end a call. The *request* argument should be a SIP INVITE request. **This should not be called by the** :term:`user`.

**genAck**\ (request)
This method generates a SIP ACK response. The *request* argument should be a SIP 401 response.

**invite**\ (number, ms, sendtype)
This method generates a SIP INVITE request. This method is called by :ref:`VoIPPhone`.call(). The *number* argument must be the number being called as a string. The *ms* argument is a dictionary of the media types to be used. Currently only PCMU and telephone-event is supported. The *sendtype* argument must be an instance of :ref:`TransmitType`.

**bye**\ (request)
This method is called by :ref:`VoIPCall`.hangup(). It calls genBye(), and then transmits the generated request. **This should not be called by the** :term:`user`.

Expand Down
15 changes: 11 additions & 4 deletions docs/VoIP.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ Enums
.. _callstate:

VoIP.\ **CallState**
CallState is an Enum with three attributes.
CallState is an Enum with four attributes.

CallState.\ **DIALING***
This CallState is used to describe when a :term:`user` has originated a call to a :term:`client`, but it has yet to be answered.

CallState.\ **RINGING**
This CallState is used to describe when a :term:`client` is calling, but the call has yet to be answered.
Expand Down Expand Up @@ -80,6 +83,9 @@ The VoIPCall class is used to represent a single VoIP Session, which may be to m
**answer**\ ()
Answers the call if the phone's state is CallState.RINGING.

**answered**\ (request)
This function is called by :ref:`SIPClient` when a call originated by the :term:`user` has been answered by the :term:`client`.

**deny**\ ()
Denies the call if the phone's state is CallState.RINGING.

Expand All @@ -92,8 +98,8 @@ The VoIPCall class is used to represent a single VoIP Session, which may be to m
**writeAudio**\ (data)
Writes linear/raw audio data to the transmit buffer before being encoded and sent. The *data* argument MUST be bytes. **This audio must be linear/not encoded,** :ref:`RTPClient` **will encode it before transmitting.**

**readAudio**\ (length=160)
Reads linear/raw audio data from the received buffer. Returns *length* amount of bytes. Default length is 160 as that is the amount of bytes sent per PCMU/PCMA packet.
**readAudio**\ (length=160, blocking=True)
Reads linear/raw audio data from the received buffer. Returns *length* amount of bytes. Default length is 160 as that is the amount of bytes sent per PCMU/PCMA packet. When *blocking* is set to true, this function will not return until data is available. When *blocking* is set to false and data is not available, this function will return bytes(length).

.. _VoIPPhone:

Expand Down Expand Up @@ -128,5 +134,6 @@ The VoIPPhone class is used to manage the :ref:`SIPClient` class and create :ref
**stop**\ ()
This method ends all currently ongoing calls, then stops the :ref:`SIPClient` class


**call**\ (number)
Originates a call using PCMU and telephone-event. The *number* argument must be a string, and it returns a :ref:`VoIPCall` class in CallState.DIALING. You should use a while loop to wait until the CallState is ANSWRED. **NOTE:** In testing with Asterisk 13, calls made this way could not hangup. This issue may exist on other PBXs as well.

2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Welcome to pyVoIP's documentation!

PyVoIP is a pure python VoIP/SIP/RTP library. Currently, it supports PCMA, PCMU, and telephone-event.

Please note this is a beta version and is currently only able to recieve calls, and transmit PCMU. In future, it will be able to initiate calls in PCMA as well.
Please note this is is still in development and can only originate calls with PCMU. In future, it will be able to initiate calls in PCMA as well.

This library does not depend on a sound library, i.e. you can use any sound library that can handle linear sound data such as pyaudio or even wave. Keep in mind PCMU only supports 8000Hz, 1 channel, 8 bit audio.

Expand Down
9 changes: 1 addition & 8 deletions pyVoIP/RTP.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,7 @@ def __str__(self):

#Non-codec
EVENT = "telephone-event", 8000, 0, "telephone-event"

'''
Note to self.

Probably should set the packet manager back to ByteIO to prevent out of order packets.
I'm thinking before write, log the cursor position, then go to the timestamp position,
write, then go to the logged possition. Again this will stop out of sync packet issues.
'''
class RTPPacketManager():
def __init__(self):
self.offset = 4294967296 #The largest number storable in 4 bytes + 1. This will ensure the offset adjustment in self.write(offset, data) works.
Expand Down Expand Up @@ -249,7 +242,7 @@ def stop(self):
self.sin.close()
self.sout.close()

def read(self, length=160, blocking=False):
def read(self, length=160, blocking=True):
if not blocking:
return self.pmin.read(length)
packet = self.pmin.read(length)
Expand Down
Loading

0 comments on commit 1a3dd57

Please sign in to comment.