'*************************************************************************** '* 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