'***************************************************************************
|
'* ClassRTPAgent *
|
'* *
|
'* This class manages the RTP communications with a E301 module. *
|
'* When you create an ClassRTPAgent objet (a RTPAgent) you must provide *
|
'* the Destination IP and port to send the packets and the local port *
|
'* where the E301 is sending us his packets. *
|
'* Once created, you must call SendAudioPacket each time you have to send *
|
'* a new audio packet and ReceiveAudioPacket when to need a new packet to *
|
'* send to the audio system *
|
'* *
|
'* This routine implements a reception FIFO to absorbe the jitter in the *
|
'* packet reception. But is NOT IMPLEMENTED the reordering of the incoming *
|
'* packets. This would be necessary in some cases. *
|
'* *
|
'* EQUITEL C.V. February 2007 ALMDI*
|
'***************************************************************************
|
|
|
|
Imports System
|
Imports System.Net
|
Imports System.Net.Sockets
|
Imports System.Threading
|
Imports System.Text
|
Imports System.IO
|
Imports System.Net.NetworkInformation
|
Imports System.Globalization
|
|
Public Class ClassRTPAgent
|
Implements System.IDisposable
|
|
#Region "Constants and definitions"
|
Private Const HeaderLength As Integer = 12
|
Private Const AudioPacketLength As Integer = 160
|
Private Const FIFOSize As Byte = 16
|
|
Private Structure BuffStruct
|
Public Data() As Byte
|
End Structure
|
#End Region
|
|
#Region "Fields"
|
Private mDisposed As Boolean = False
|
Private mDestinationIP As String
|
Private mDestinationPort As Integer
|
Private mLocalPort As Integer
|
Private mReceptionThread As Thread
|
Private mUdpClient As UdpClient
|
Private mOutputBuffer As BuffStruct
|
Private mSequence As UInt32
|
Private mTimestamp As UInt32
|
Private mInputFIFO(FIFOSize) As BuffStruct
|
Private mFIFOWritePointer As Integer = -1
|
Private mFIFOReadPointer As Integer = -1
|
#End Region
|
|
#Region "Properties"
|
|
Public Property DestinationIP() As String
|
Get
|
DestinationIP = mDestinationIP
|
End Get
|
Set(ByVal Value As String)
|
mDestinationIP = Value
|
End Set
|
End Property
|
|
Public Property DestinationPort() As Integer
|
Get
|
DestinationPort = mDestinationPort
|
End Get
|
Set(ByVal Value As Integer)
|
mDestinationPort = Value
|
End Set
|
End Property
|
|
Public Property LocalPort() As Integer
|
Get
|
LocalPort = mLocalPort
|
End Get
|
Set(ByVal Value As Integer)
|
mLocalPort = Value
|
End Set
|
End Property
|
|
#End Region
|
|
#Region "CONSTRUCTOR / DESTRUCTOR"
|
|
Public Sub New(ByVal DestinationIP As String, ByVal DestinationPort As Integer, ByVal LocalPort As Integer)
|
Dim i As Integer
|
mDestinationIP = DestinationIP
|
mDestinationPort = DestinationPort
|
mLocalPort = LocalPort
|
mUdpClient = New UdpClient(mLocalPort)
|
mSequence = 0
|
mTimestamp = 0
|
ReDim mOutputBuffer.Data(HeaderLength + AudioPacketLength)
|
For i = 0 To FIFOSize - 1
|
ReDim mInputFIFO(i).Data(HeaderLength + AudioPacketLength - 1)
|
Next
|
mReceptionThread = New Thread(AddressOf PacketReception)
|
mReceptionThread.Start()
|
End Sub
|
|
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
|
If Not Me.mDisposed Then
|
If disposing Then
|
mReceptionThread.Abort()
|
mReceptionThread = Nothing
|
mUdpClient.Close()
|
mUdpClient = Nothing
|
End If
|
End If
|
Me.mDisposed = True
|
End Sub
|
|
Public Sub Dispose() Implements IDisposable.Dispose
|
Dispose(True)
|
GC.SuppressFinalize(Me)
|
End Sub
|
|
Protected Overrides Sub Finalize()
|
Dispose(False)
|
MyBase.Finalize()
|
End Sub
|
|
#End Region
|
|
#Region "Private functions"
|
Private Sub CreateRTPHeader(ByRef RTP_Buf As Byte(), ByVal Sequence As UInt32, ByVal TimeStamp As UInt32)
|
'This routine creates the header for the RTP packet in accordance with the
|
'RFC 3550:
|
'
|
' 0 1 2 3
|
' 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
' +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
' |V=2|P|X| CC |M| PT | sequence number |
|
' +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
' | timestamp |
|
' +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
' | synchronization source (SSRC) identifier |
|
' +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
' version (V): 2 bits
|
' padding (P): 1 bit
|
' extension (X): 1 bit
|
' CSRC count (CC): 4 bits
|
' marker (M): 1 bit
|
' payload type (PT): 7 bits
|
' sequence number: 16 bits
|
' timestamp: 32 bits
|
|
'First 32 bit word
|
RTP_Buf(0) = &H80 'Version=2, P=X=CC=M=0
|
RTP_Buf(1) = &H8 'Payload type =8 (as defined in RFC3551 for G711-A law)
|
RTP_Buf(2) = (Sequence >> 8) And 255
|
RTP_Buf(3) = (Sequence) And 255
|
|
'Second word (TimeStamp)
|
RTP_Buf(4) = (TimeStamp >> 24) And 255
|
RTP_Buf(5) = (TimeStamp >> 16) And 255
|
RTP_Buf(6) = (TimeStamp >> 8) And 255
|
RTP_Buf(7) = (TimeStamp) And 255
|
|
'Third word (SSRC = Sincronization Source Code)
|
RTP_Buf(8) = &H12
|
RTP_Buf(9) = &H34
|
RTP_Buf(10) = &H56
|
RTP_Buf(11) = &H78
|
End Sub
|
|
Private Function PCM_to_G711_A(ByVal PCMSample As Short) As Byte
|
'This routine converts a 16 bit PCM sample to G711-A format
|
Dim p As Short = PCMSample
|
Dim a As Short
|
|
If (p < 0) Then
|
p = Not p
|
a = &H0
|
Else
|
a = &H80
|
End If
|
p >>= 4
|
If (p >= &H20) Then
|
If (p >= &H100) Then
|
p >>= 4
|
a += &H40
|
End If
|
If (p >= &H40) Then
|
p >>= 2
|
a += &H20
|
End If
|
If (p >= &H20) Then
|
p >>= 1
|
a += &H10
|
End If
|
End If
|
a += p
|
Return a Xor &H55
|
End Function
|
|
Private Function G711_A_to_PCM(ByVal G711Sample As Byte) As Short
|
'This routine converts a G711-A sample to PCM (16 bits)
|
Dim sign As Byte
|
Dim shift As UShort
|
Dim linear As Short
|
G711Sample = G711Sample Xor &H55
|
sign = G711Sample And &H80
|
linear = G711Sample And &H1F
|
linear <<= 4
|
linear += 8
|
G711Sample = G711Sample And &H7F
|
If (G711Sample >= &H20) Then
|
linear = linear Or &H100
|
shift = (G711Sample >> 4) - 1
|
linear <<= shift
|
End If
|
If (sign = &H80) Then
|
Return -linear
|
Else
|
Return linear
|
End If
|
End Function
|
|
Private Sub PacketReception()
|
'This routine reads the incoming RTP Packets from the localport
|
'and passes them to the FIFO. When the FIFO is half-filled for
|
'the first time, the FIFO reading is enabled
|
Dim Packet(HeaderLength + AudioPacketLength - 1) As Byte
|
Dim IpE As New System.Net.IPEndPoint(IPAddress.Any, mLocalPort)
|
|
While True
|
Try
|
Packet = mUdpClient.Receive(IpE)
|
mFIFOWritePointer += 1
|
If mFIFOWritePointer = FIFOSize Then mFIFOWritePointer = 0
|
mInputFIFO(mFIFOWritePointer).Data = Packet
|
If (mFIFOWritePointer = FIFOSize / 2) And mFIFOReadPointer = -1 Then
|
mFIFOReadPointer = 0
|
End If
|
Catch
|
Console.WriteLine("Packet reception error")
|
End Try
|
End While
|
End Sub
|
#End Region
|
|
#Region "Public functions"
|
Public Sub SendAudioPacket(ByRef AudioPCM() As Short)
|
'This processes the audio packet passed (in 16 bit/sample format)
|
'converting it to G711-A format (8 bits per sample) and
|
'creating an RTP packet wich is sent.
|
Dim i As Integer
|
CreateRTPHeader(mOutputBuffer.Data, mSequence, mTimestamp)
|
For i = 0 To AudioPacketLength
|
mOutputBuffer.Data(i + 12) = PCM_to_G711_A(AudioPCM(i))
|
Next
|
mUdpClient.Send(mOutputBuffer.Data, (HeaderLength + AudioPacketLength), mDestinationIP, mDestinationPort)
|
mTimestamp += AudioPacketLength
|
mSequence += 1
|
End Sub
|
|
|
Public Function ReceivedAudioPacket() As Short()
|
'Returns the next audio packet in the FIFO properly converted to PCM format
|
Dim P(AudioPacketLength) As Short
|
Dim i As Integer
|
|
If mFIFOReadPointer <> -1 Then
|
For i = 0 To AudioPacketLength - 1
|
P(i) = G711_A_to_PCM(mInputFIFO(mFIFOReadPointer).Data(i + HeaderLength))
|
Next
|
For i = AudioPacketLength To AudioPacketLength - 1
|
P(i) = mInputFIFO(mFIFOReadPointer).Data(i + HeaderLength)
|
Next
|
mFIFOReadPointer += 1
|
If mFIFOReadPointer = FIFOSize Then mFIFOReadPointer = 0
|
Return P
|
Else
|
For i = 0 To AudioPacketLength - 1
|
P(i) = 0
|
Next
|
Return P
|
End If
|
End Function
|
#End Region
|
|
End Class
|