forked from orpinchasov/PyH323Plus
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathClient.py
234 lines (172 loc) · 7.63 KB
/
Client.py
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
"""A complete VoIP client capable of listening to and making
calls, using a gatekeeper and sending and receiving audio.
"""
import wave
import time
import argparse
from PTLib.Cython.PLibraryProcess import PLibraryProcess
from PTLib.Cython.Address import Address
from PTLib.Cython.PIndirectChannel import PIndirectChannel
from H323Plus.Cython.H323ListenerTCP import H323ListenerTCP
from H323Plus.Cython.H323EndPoint import H323EndPoint
def parse_args():
parser = argparse.ArgumentParser(description="H323 VoIP Client.")
parser.add_argument("-o", "--output-file", type=str, default=None, help="output file name")
parser.add_argument("-f", "--input-file", type=str, default=None, help="input file name")
parser.add_argument("-i", "--interface", type=str, default=b"*", help="interface (default '*')")
parser.add_argument("-g", "--gatekeeper", type=str, default=None, help="gatekeeper address")
parser.add_argument("-u", "--user", type=str, default=None, action="append", help="set local alias name(s)")
parser.add_argument("-p", "--port", type=int, default=1720, help="listening port (default 1720)")
group = parser.add_mutually_exclusive_group()
group.add_argument("-l", "--listen", action="store_true", help="listen for incoming calls")
group.add_argument("-d", "--destination", type=str, default=None, help="identifier of destination endpoint")
args = parser.parse_args()
return args
class WavChannel(PIndirectChannel):
"""Class for using a WAV file as an indirect channel
transferring data between the two end points.
"""
SAMPLES_PER_SECOND = 8000
DEFAULT_WAVE_PARAMETERS = (1, 2, SAMPLES_PER_SECOND, 40000, "NONE", "not compressed")
def __init__(self, input_filename=None, output_filename=None):
"""Initialise the channel.
Open output and input files as necessary.
"""
super(WavChannel, self).__init__()
if input_filename:
try:
self._input_wav = wave.open(input_filename, "rb")
except Exception as e:
print(f"Error opening wav input file: {e}")
self._input_wav = None
else:
self._input_wav = None
if output_filename:
try:
self._output_wav = wave.open(output_filename, "wb")
self._output_wav.setparams(WavChannel.DEFAULT_WAVE_PARAMETERS)
except Exception as e:
print(f"Error opening wav output file: {e}")
self._output_wav = None
else:
self._output_wav = None
self._read_previous_time = time.time()
self._write_previous_time = time.time()
def IsOpen(self):
"""The WAV channel is always open."""
return True
def Close(self):
"""Close input and output WAV files."""
if self._input_wav:
self._input_wav.close()
self._input_wav = None
if self._output_wav:
self._output_wav.close()
self._output_wav = None
return True
def Read(self, buf, length):
"""Read audio frames from the WAV file."""
if self._input_wav is not None:
# Frames are read in 16-bit boundaries and are
# coded to 8-bit by the chosen codec
b = self._input_wav.readframes(length / 2)
else:
b = b"\x00" * length
self._last_read_count = length
# Reading should be timed by us to be at
# 8000 samples per second
while time.time() < self._read_previous_time + (1.0 / (WavChannel.SAMPLES_PER_SECOND / (length / 2))):
time.sleep(0)
self._read_previous_time = time.time()
return (True, b)
def GetLastReadCount(self):
"""Return the number of bytes previously read."""
return self._last_read_count
def Write(self, buf, length):
"""Write audio frames to the WAV file."""
if self._output_wav is not None:
self._output_wav.writeframes(buf)
# Writing should be timed by us to be at
# 8000 samples per second
while time.time() < self._write_previous_time + (1.0 / (WavChannel.SAMPLES_PER_SECOND / (length / 2))):
time.sleep(0)
self._write_previous_time = time.time()
return True
class Client(H323EndPoint):
"""A complete VoIP client."""
def __init__(self, input_filename=None, output_filename=None):
super(Client, self).__init__()
self._input_filename = input_filename
self._output_filename = output_filename
def initialise(self, interface, port, user=None, gatekeeper=None):
"""Initialise client.
Set user and alias names, load features and available codecs,
start listening and register with gatekeeper if required.
"""
# Set user name and aliases
if user is not None:
self.SetLocalUserName(user[0])
for name in user[1:]:
self.AddAliasName(name)
# Load all features and capabilities (all codecs)
self.LoadBaseFeatureSet()
self.AddAllCapabilities(0, 0x7FFFFFFF, b"*")
self.AddAllUserInputCapabilities(0, 0x7FFFFFFF)
# Start a listener on the given interface and port. Listener
# would be used to make calls and register with the
# gatekeeper
address = Address(interface)
listener = H323ListenerTCP(self, address, port)
if not self.StartListener(listener):
print(f"Failed to start listener on {address}:{port}")
return False
# Register with a specific gatekeeper
if gatekeeper is not None:
if not self.UseGatekeeper(gatekeeper):
print(f"Failed to register with gatekeeper at {gatekeeper}")
return False
return True
def OnIncomingCall(self, connection, setupPDU, alertingPDU):
"""Print incoming call and answer immediately."""
print("Incoming call", connection)
return True
def OnConnectionEstablished(self, connection, token):
print(f"Connection established, token = {token}")
def OnConnectionCleared(self, connection, token):
print(f"Connection cleared, token = {token}")
def OpenAudioChannel(self, connection, isEncoding, bufferSize, codec):
"""Attach a WAV file channel to the audio channel
opened with the remote end point.
"""
print(f"Opening audio channel ({connection}, {isEncoding}, {bufferSize}, {codec})")
if isEncoding:
# Send data
wav_channel = WavChannel(input_filename=self._input_filename)
else:
# Receive data
wav_channel = WavChannel(output_filename=self._output_filename)
return codec.AttachChannel(wav_channel, autoDelete=False)
def run_client(args):
client = Client(args.input_file, args.output_file)
if not client.initialise(args.interface, args.port, args.user, args.gatekeeper):
print("Error initialising end point")
return
if args.listen:
print(f'Waiting for incoming calls for "{client.GetLocalUserName()}"')
elif args.destination is not None:
print(f'Initiating call to "{args.destination}"')
client.MakeCall(args.destination.encode("utf-8"), None)
print("Press enter to quit")
input()
client.ClearAllCalls(wait=False)
# Allow the connection to clear
time.sleep(1)
def main():
args = parse_args()
if not args.listen and args.destination is None:
print("Invalid operation mode (choose either to listen or to dial)")
else:
run_client(args)
if __name__ == "__main__":
p = PLibraryProcess(b"", b"H323 VoIP Client", 1, 0, 0, 0)
main()