Alejandro Acuña
2024-10-25 bf65497ed7abf9c669eb3cc0ba219dfa4494b759
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
'***************************************************************************
'* 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